perf(render): split the wire arena into regular + mesh-edge batches

The single-batch arena required a set with no mesh/solid edges, so any drawing
with a 3D solid or mesh (this one: 1693 mesh-edge wires of 12200) disqualified the
WHOLE set — every edit fell back to build_wire_buffers re-uploading ~265 MB
(4.4M instances), stalling ~1s per edit on a shared-memory GPU.

Split the resident set into a regular-2D arena and a mesh-edge arena (the wires
that need the draw loops mesh-edge skip/black treatment), one WireArena each,
passed as &[&WireModel] subsets (no clone). Both patch incrementally; the mesh
arena carries is_3d_mesh_edge so the existing draw loop handles it. A changed
handle absent from an arenas subset is a no-op for it, so each edit touches only
the batch it belongs to. Result: the wire block drops from ~300ms + a 265 MB GPU
upload to ~2ms.

Two correctness points folded in from adversarial review:
- mesh-edge wires are stamped draw_depth 0 (matching WireGpu::from_run) so their
  hidden back edges dont peek through the shaded fill; the mesh arena also always
  bails on append (all its wires share depth 0, so relocation would reorder
  coincident edges).
- buffers are capped at wgpus 256 MB limit — a batch larger than one buffer
  returns None so the caller chunks it via the batched path (fixes a create_buffer
  panic on the 355 MB regular batch).

Still behind OCS_WIRE_GPU_PATCH (default off). OCS_PERF prints the per-edit wire
timing + outcome.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
Hakan SevenandClaude Opus 4.8 committed 2026-07-15 09:48:20 +03:00
1 parent 9761423b84
commit 95ef3b3890
3 files changed
+157 -55

No files matched your search

+8 -1
View File
@@ -154,7 +154,12 @@ pub struct Pipeline {
/// edit patches one entity's slab in place instead of rebuilding every wire.
#[cfg(not(target_arch = "wasm32"))]
pub(crate) wire_arena: Option<wire_arena::WireArena>,
/// The Model content id `wire_arena` currently mirrors (`u64::MAX` = none).
/// Second arena for the mesh/solid EDGE wires (drawn with the mesh-edge skip /
/// black treatment); the resident set is split into this + `wire_arena` so
/// both patch incrementally. Shares `wire_arena_id`.
#[cfg(not(target_arch = "wasm32"))]
pub(crate) wire_arena_mesh: Option<wire_arena::WireArena>,
/// The Model content id both arenas currently mirror (`u64::MAX` = none).
#[cfg(not(target_arch = "wasm32"))]
pub(crate) wire_arena_id: u64,
/// Pixel scissor rects [x, y, w, h] for viewport-clipped wires. Recomputed each frame.
@@ -1320,6 +1325,8 @@ impl Pipeline {
#[cfg(not(target_arch = "wasm32"))]
wire_arena: None,
#[cfg(not(target_arch = "wasm32"))]
wire_arena_mesh: None,
#[cfg(not(target_arch = "wasm32"))]
wire_arena_id: u64::MAX,
wire_pixel_scissors: vec![],
gpu_selected_wires: vec![],