forked from 1b-agent/opencadstudio-upstream
perf: store block-wire segments once instead of six times
The layout-switch OOM names its buffer: `Buffer with 'block_wire.vertices' label is invalid`, right after `uncaptured wgpu error #1 … Out of Memory`. That buffer is the largest thing this renderer allocates, and five sixths of it is duplication. A segment reaches the shader once per corner of its quad, and the packed path satisfies that by writing the whole 60-byte record six times — 360 bytes a segment — with `vs_main` recovering the corner from `vertex_index % 6`. Six identical copies, on a 2 GB card. `block_wire_storage.wgsl` puts the segments in a read-only storage buffer indexed by `vertex_index / 6` instead, so each record is stored once. The geometry vertex buffer disappears; the placements move to slot 0 and the segments arrive through the definition's bind group, which becomes per-chunk because each chunk binds its own slice. This is the pattern `wire_indexed.wgsl` already uses for per-wire constants, down to the capability gate: `supports_wire_storage` asks for exactly one vertex-stage storage binding, which is exactly what this needs. Where it is false — WebGL, the forced compatibility renderer — the packed shader and its vertex buffer are built as before. `Segment` is declared with scalar members rather than `vec3<f32>`, which would align to 16 in storage and reintroduce the padding this removes. A test validates the shader through naga and asserts the struct's stride equals `size_of::<BlockWireVertex>()`, because a layout mismatch would otherwise surface only as garbage geometry on a real device. `block-wire-geometry` reports the bytes uploaded and what the packed path would have taken, so the saving is measured rather than assumed. The cache value becomes `Arc<Vec<BlockGeometryChunk>>` while keeping the translation-aware key from #1006 — both modes still have to invalidate when the base instance moves, because the records hold its world coordinates either way. `const_bgl` and the mode are decided together when the pipeline is built, so they travel as one `BlockWireTarget` rather than pushing `from_wires` past the argument limit. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
1 parent
0e4f755e6f
commit
6599b82776
4 files changed
+634
-107
No files matched your search
@@ -30,19 +30,20 @@ fn block_edits_preserve_cache_coordinates_and_arena_partition() {
|
||||
let depth = rustc_hash::FxHashMap::from_iter([(1, [0.1, 0.01]), (2, [0.2, 0.01])]);
|
||||
let original = pipeline.upload_block_wires(&device, &queue, &[&first, &second], &depth);
|
||||
let unchanged = pipeline.upload_block_wires(&device, &queue, &[&first, &second], &depth);
|
||||
assert_eq!(original[0].vertex_buffer, unchanged[0].vertex_buffer);
|
||||
assert_eq!(original[0].geometry_id(), unchanged[0].geometry_id());
|
||||
assert_eq!(original[0].instance_count, 2);
|
||||
|
||||
let removed = pipeline.upload_block_wires(&device, &queue, &[&second], &depth);
|
||||
assert_ne!(
|
||||
original[0].vertex_buffer, removed[0].vertex_buffer,
|
||||
original[0].geometry_id(),
|
||||
removed[0].geometry_id(),
|
||||
"removing the base instance must replace vertices baked at its old position"
|
||||
);
|
||||
let moved = block(2, 30.0);
|
||||
let moved_gpu = pipeline.upload_block_wires(&device, &queue, &[&moved], &depth);
|
||||
assert_ne!(removed[0].vertex_buffer, moved_gpu[0].vertex_buffer);
|
||||
assert_ne!(removed[0].geometry_id(), moved_gpu[0].geometry_id());
|
||||
let restored = pipeline.upload_block_wires(&device, &queue, &[&first, &second], &depth);
|
||||
assert_ne!(moved_gpu[0].vertex_buffer, restored[0].vertex_buffer);
|
||||
assert_ne!(moved_gpu[0].geometry_id(), restored[0].geometry_id());
|
||||
assert_eq!(restored[0].instance_count, 2);
|
||||
assert_eq!(
|
||||
pipeline.block_geometry.len(),
|
||||
|
||||
Reference in new issue
Block a user