forked from 1b-agent/opencadstudio-upstream
fix: let the wire arena serve drawings that contain blocks
Clicking to end a segment froze the UI until the whole drawing had been
re-uploaded. With block geometry cached the remaining cost is the regular
half, and on two drawings it dominates:
113 k wires regular=96ms block=13ms index=12ms total 122ms
198 k wires regular=85ms block= 7ms index=25ms total 118ms
It is rebuilt from scratch because `PersistentWireArena` -- the only
thing that patches wire buffers in place -- was disabled outright by:
&& !vp_wires.iter().any(|wire| wire.render_instance.is_some())
One block reference was enough; the reproducer has 11 363. The scene
already produces an incremental patch in 0.9 ms and the GPU side could
not use it.
Instanced wires are not something the arena has to handle: they are a
different batch type with their own pipeline. Partition them out, let the
arena own and patch the rest, and keep building `BlockWireGpu` for them.
The handle index is unaffected -- it maps into `vp_wires`, not into arena
slabs, and the patch path already maintains it.
Measured on the 198 k-wire drawing, per drawn segment:
before wire 350ms (shared-fullupload)
after wire 12ms (arena-patch)
with the whole click, including `bump_entities`, going from ~411 ms to
~20 ms.
Verified visually on drawings with and without blocks, through repeated
edits and undo/redo.
Co-Authored-By: Claude Opus 5 <[email protected]>
This commit is contained in:
1 parent
071b4b6a87
commit
fb64e76f04
2 files changed
+53
-3
No files matched your search
@@ -2340,6 +2340,29 @@ impl Pipeline {
|
||||
/// copy across every slot that renders that content (see
|
||||
/// `MultiPipeline::wire_buffer_cache`). Takes `&self` (reads only the const
|
||||
/// bind-group layout) so the shared cache — not the slot — owns the result.
|
||||
/// Batches for the instanced (block-reference) wires alone.
|
||||
///
|
||||
/// The arena serves the non-instanced wires and patches them in place;
|
||||
/// block references are a different batch type and still rebuild, but
|
||||
/// their geometry is cached by `source_id`, so that is cheap.
|
||||
pub fn upload_block_wires(
|
||||
&mut self,
|
||||
device: &wgpu::Device,
|
||||
queue: &wgpu::Queue,
|
||||
wires: &[&WireModel],
|
||||
depth_map: &rustc_hash::FxHashMap<u64, [f32; 2]>,
|
||||
) -> Vec<BlockWireGpu> {
|
||||
BlockWireGpu::from_wires(
|
||||
device,
|
||||
queue,
|
||||
wires,
|
||||
depth_map,
|
||||
None,
|
||||
&self.block_wire_const_bgl,
|
||||
Some(&mut self.block_geometry),
|
||||
)
|
||||
}
|
||||
|
||||
pub fn build_wire_buffers(
|
||||
&mut self,
|
||||
device: &wgpu::Device,
|
||||
|
||||
@@ -655,7 +655,14 @@ impl shader::Primitive for Primitive {
|
||||
|| ((vp.wire_patch.is_some()
|
||||
|| inner.wire_arena_id != u64::MAX)
|
||||
&& packed_arena_owner))
|
||||
&& !vp_wires.iter().any(|wire| wire.render_instance.is_some());
|
||||
;
|
||||
// Instanced wires used to disable the arena outright, and with
|
||||
// it incremental patching: one block reference in the drawing
|
||||
// meant every edit re-uploaded every wire, ~96 ms of the
|
||||
// ~122 ms a click cost. They are simply a different batch type
|
||||
// with their own pipeline, so the arena takes the rest and
|
||||
// `BlockWireGpu` keeps them -- cheaply now that its geometry is
|
||||
// cached by `source_id`.
|
||||
if use_wire_arena {
|
||||
use crate::scene::pipeline::wire_arena::{
|
||||
self, PersistentWireArena as WireArena,
|
||||
@@ -767,12 +774,16 @@ impl shader::Primitive for Primitive {
|
||||
.iter()
|
||||
.filter(|w| {
|
||||
!w.points.is_empty()
|
||||
&& w.render_instance.is_none()
|
||||
&& !wire_arena::is_mesh_edge(w, &mesh_names)
|
||||
})
|
||||
.collect();
|
||||
let mesh: Vec<&crate::scene::WireModel> = vp_wires
|
||||
.iter()
|
||||
.filter(|w| wire_arena::is_mesh_edge(w, &mesh_names))
|
||||
.filter(|w| {
|
||||
w.render_instance.is_none()
|
||||
&& wire_arena::is_mesh_edge(w, &mesh_names)
|
||||
})
|
||||
.collect();
|
||||
if !reg_ok {
|
||||
inner.wire_arena = WireArena::build(
|
||||
@@ -874,7 +885,23 @@ impl shader::Primitive for Primitive {
|
||||
gpus.extend(arena.wire_gpus());
|
||||
}
|
||||
inner.gpu_wires = std::sync::Arc::new(gpus);
|
||||
inner.gpu_block_wires = std::sync::Arc::new(Vec::new());
|
||||
// The arena holds only the non-instanced wires;
|
||||
// block references keep their own batch type. Their
|
||||
// geometry is cached by `source_id`, so rebuilding them
|
||||
// here is cheap against the ~96 ms of regular wires the
|
||||
// arena now patches instead of re-uploading.
|
||||
let instanced: Vec<&crate::scene::WireModel> = vp_wires
|
||||
.iter()
|
||||
.filter(|w| w.render_instance.is_some())
|
||||
.collect();
|
||||
inner.gpu_block_wires = std::sync::Arc::new(
|
||||
inner.upload_block_wires(
|
||||
device,
|
||||
queue,
|
||||
&instanced,
|
||||
&draw_depths,
|
||||
),
|
||||
);
|
||||
if _patched {
|
||||
wire_arena::patch_handle_index(
|
||||
&mut inner.wire_handle_index,
|
||||
|
||||
Reference in new issue
Block a user