feat(wasm): sequential rayon shim for the web build

wasm32 has no threads, so rayon's global pool would panic at runtime.
Add `crate::par::prelude`: native re-exports `rayon::prelude`, wasm
provides sequential `std`-iterator equivalents (par_iter / into_par_iter
/ flat_map_iter over slices, Vec, HashMap, HashSet). All call sites now
`use crate::par::prelude::*;` instead of rayon directly.

- wire_gpu's rayon `reduce` (2-arg, no std equivalent) is cfg-split to
  a sequential flat_map concat on wasm.
- rayon moves to a non-wasm target dependency.

Native still runs in parallel; all 75 tests pass. wasm bin compiles.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
Hakan Seven 2026-06-16 17:11:20 +03:00
commit 9ca19a9db8
8 changed files with 96 additions and 10 deletions

View file

@ -24,7 +24,7 @@
use crate::scene::model::wire_model::WireModel;
use iced::wgpu;
use rayon::prelude::*;
use crate::par::prelude::*;
/// Allocate a VERTEX buffer with `mapped_at_creation` and write `data` directly
/// into the mapped slice. Skips the intermediate staging copy that
@ -295,7 +295,7 @@ impl WireGpu {
scissor: Option<[f32; 4]>,
mesh_edge: bool,
) -> Vec<Self> {
use rayon::prelude::*;
use crate::par::prelude::*;
const MAX_INSTANCES: usize = 268_435_456 / std::mem::size_of::<WireInstance>();
let per: Vec<Vec<WireInstance>> = wires
.par_iter()
@ -340,6 +340,7 @@ impl WireGpu {
// within a batch does not affect correctness. `reduce` concatenates the
// per-wire Vec<WireInstance> chunks while letting rayon spread work
// across cores.
#[cfg(not(target_arch = "wasm32"))]
let instances: Vec<WireInstance> = wires
.par_iter()
.map(|wire| emit_wire_instances(wire, wire.color, wire_draw_depth(wire, depth_map)))
@ -353,6 +354,12 @@ impl WireGpu {
acc
}
});
// Web: no thread pool — concatenate the per-wire streams sequentially.
#[cfg(target_arch = "wasm32")]
let instances: Vec<WireInstance> = wires
.iter()
.flat_map(|wire| emit_wire_instances(wire, wire.color, wire_draw_depth(wire, depth_map)))
.collect();
if instances.is_empty() {
return vec![];