perf(render): batch solid meshes, fix per-pick freezes on heavy 3D models
Opening / navigating a heavy 3D DWG (e.g. a BIM model with ~10k ACIS solids) was effectively frozen. Several independent costs, all addressed here: - Face3D edge/fill buffers were re-uploaded on every camera move (keyed on camera_generation). They are world-space and selection-independent, so gate them on (geometry_epoch, fill_mode) instead — a pan no longer re-walks every wire. - Solid meshes were drawn one buffer + one draw call per solid (~10k draws/ frame), which strangled the GPU front end. Concatenate every solid's LOD0 into a few large buffers (split only to stay under the 256 MB buffer cap) and draw the whole set in a handful of calls. Built once per geometry epoch, so selection / hover never re-pack it. - Selection / hover highlight is now a small tinted overlay of just the picked solids, drawn last with an Always-depth pipeline so it shows on top even when the solid is occluded — instead of re-tinting and re-uploading all meshes. - Hover / pick hit-testing scanned every triangle of every solid each move (seconds on a heavy model). Add a 3D-AABB screen-rect broad phase (MeshLodSet now carries z_aabb) and reuse the renderer's cached expanded mesh set, so picking is O(solids) cheap projections plus the few solids actually under the cursor. - Block-internal hatch hit-testing exploded every INSERT on every hover. Cache the result per geometry epoch and skip blocks that contain no hatch. - Curved-surface (cone/sphere/torus) tessellation tolerance is now radius- relative (CURVE_REL_TOL) so a cylinder's facet count is size-independent and matches the circle/arc wire tessellation, instead of exploding on large radii. Also adds docs/tessellation.md mapping every EntityType to its tessellation path (truck B-rep vs truck curve topology vs direct). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
parent
27896e3401
commit
a994a148fe
10 changed files with 572 additions and 146 deletions
|
|
@ -263,6 +263,16 @@ impl shader::Primitive for Primitive {
|
|||
if geo_changed {
|
||||
inner.upload_images(device, queue, &vp.images[..]);
|
||||
}
|
||||
inner.cached_epoch = cur_key;
|
||||
}
|
||||
// Face3D edge/fill buffers are world-space and selection-independent
|
||||
// (upload_face3d takes no selection input), so they only change with
|
||||
// the geometry or the 3D-fill toggle — never on a pan/orbit. Gating
|
||||
// here on `(geometry_epoch, face3d_fill_active)` instead of inside the
|
||||
// `cur_key` block (which carries `camera_generation`) stops a camera
|
||||
// move from re-walking every wire to rebuild the Face3D fill buffer.
|
||||
let face3d_key = (vp.geometry_epoch, face3d_fill_active);
|
||||
if face3d_key != inner.cached_face3d_key {
|
||||
inner.upload_face3d(
|
||||
device,
|
||||
&vp.face3d_wires[..],
|
||||
|
|
@ -270,7 +280,7 @@ impl shader::Primitive for Primitive {
|
|||
!face3d_fill_active,
|
||||
&vp.draw_depths,
|
||||
);
|
||||
inner.cached_epoch = cur_key;
|
||||
inner.cached_face3d_key = face3d_key;
|
||||
}
|
||||
// Wire buffers are world-space, so a camera move alone doesn't
|
||||
// change them — only the view_proj uniform (uploaded every frame).
|
||||
|
|
@ -298,18 +308,25 @@ impl shader::Primitive for Primitive {
|
|||
);
|
||||
inner.cached_selection = sel_key;
|
||||
}
|
||||
// Solid meshes carry their selection/hover tint baked into the
|
||||
// vertex colours, so re-upload them when the geometry OR the
|
||||
// highlight set changes.
|
||||
let mesh_key = (vp.geometry_epoch, vp.selection_generation);
|
||||
if mesh_key != inner.cached_mesh_key {
|
||||
inner.upload_meshes(
|
||||
// Batched solid meshes — geometry-only, so they ride the geometry
|
||||
// epoch alone and stay resident across camera moves and selection /
|
||||
// hover changes (no per-pick rebuild of the whole solid set).
|
||||
if vp.geometry_epoch != inner.cached_mesh_batch_epoch {
|
||||
inner.upload_mesh_batch(device, &vp.meshes[..]);
|
||||
inner.cached_mesh_batch_epoch = vp.geometry_epoch;
|
||||
}
|
||||
// Selection / hover highlight overlay — tinted copies of just the
|
||||
// picked solids, rebuilt only when the highlight set (or geometry)
|
||||
// changes. Drawn over the static batch so the base never re-packs.
|
||||
let hl_key = (vp.geometry_epoch, vp.selection_generation);
|
||||
if hl_key != inner.cached_highlight_key {
|
||||
inner.upload_mesh_highlight(
|
||||
device,
|
||||
&vp.meshes[..],
|
||||
&vp.selected_handles,
|
||||
vp.hover_handle,
|
||||
);
|
||||
inner.cached_mesh_key = mesh_key;
|
||||
inner.cached_highlight_key = hl_key;
|
||||
}
|
||||
// Live overlay (command preview / interim / grip drag) — small and
|
||||
// refreshed every frame it's present, so a drag never re-uploads
|
||||
|
|
|
|||
Loading…
Reference in a new issue