fix: share block definition geometry across viewport slots

Zooming a paper layout out until several viewports fit on screen ran the
card out of memory. The log correlates exactly: every error cluster
follows a `nav-prepare … space=Paper` with `viewports=3`, `4` or `5`,
where earlier sessions had reported `viewports=2` and stayed inside the
budget.

Each viewport gets its own `Pipeline` slot, and `block_geometry` was a
slot field — so every slot uploaded its own copy of the same definition
segments. That is the single largest upload this renderer makes: 1 101
definitions, 1 040 267 segments, 59.5 MiB. Three of them land back to
back in one burst in the log, each bracketed by its own full arena
rebuild.

The cache key is `(source_id, mesh_edge, colour, base translation)` —
nothing a viewport contributes. Two viewports drawing the same definition
draw the same segments, so the cache belongs beside `wire_buffer_cache`
on the `MultiPipeline`, which already shares the finished `BlockWireGpu`
batches across slots for the same reason.

That existing sharing also settles the one question this raises: bind
groups built against one slot's layout are already handed to other slots
today, so nothing new is being asked of wgpu here. Both GPU regression
tests still pass on a real adapter with validation scopes armed.

This does not address the wire arena, which stays per slot and rebuilds
52 MiB of instances each time a slot changes hands — `arena-base
ok=false` on every single build in the session, never `ok=true`.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
PanosandClaude Opus 5 committed 2026-09-05 18:53:09 +03:00
1 parent d9c998fa17
commit a9a2c82478
3 files changed
+33 -13

No files matched your search

+8 -1
View File
@@ -860,6 +860,7 @@ impl shader::Primitive for Primitive {
queue,
&instanced,
&draw_depths,
&mut pipeline.block_geometry,
),
);
if _patched {
@@ -936,7 +937,13 @@ impl shader::Primitive for Primitive {
let errors_before =
crate::scene::pipeline::gpu_errors_seen();
let entry =
inner.build_wire_buffers(device, queue, &vp_wires[..], &draw_depths);
inner.build_wire_buffers(
device,
queue,
&vp_wires[..],
&draw_depths,
&mut pipeline.block_geometry,
);
if let Some(start) = t_upload {
crate::perf_record!(
"[perf] wire-upload {:.1}ms wires={} content_id={}",