perf: cache hatch/wipeout/image/mesh models per geometry epoch (F+G+H)

Add Arc<Vec<...>>-backed epoch caches for hatches, wipeouts, images, and
meshes — the same pattern used for wires. build_primitive() now returns
an O(1) Arc refcount bump on navigation frames instead of rebuilding
every collection from scratch.

Also wrap ImageModel.pixels in Arc<Vec<u8>> so cloning an ImageModel is
O(1) regardless of image resolution.

Navigation frames no longer copy pixel data, tessellated hatch boundaries,
or mesh geometry — all per-frame CPU work on unchanged geometry is now
eliminated.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
Hakan SevenandClaude Sonnet 4.6 committed 2026-04-26 11:34:52 +03:00
1 parent 2bf4a348bf
commit 81db3c37d0
5 files changed
+209 -20

No files matched your search

+16 -16
View File
@@ -78,11 +78,11 @@ pub struct CameraState {
#[derive(Debug)]
pub struct Primitive {
pub(super) wires: Arc<Vec<WireModel>>,
pub(super) hatches: Vec<HatchModel>,
pub(super) hatches: Arc<Vec<HatchModel>>,
/// Wipeout fills — rendered in a separate pass AFTER wires.
pub(super) wipeout_hatches: Vec<HatchModel>,
pub(super) images: Vec<ImageModel>,
pub(super) meshes: Vec<MeshModel>,
pub(super) wipeout_hatches: Arc<Vec<HatchModel>>,
pub(super) images: Arc<Vec<ImageModel>>,
pub(super) meshes: Arc<Vec<MeshModel>>,
pub(super) uniforms: Uniforms,
/// Camera rotation matrix derived from the quaternion.
/// Used by the ViewCube pipeline — no gimbal lock.
@@ -120,10 +120,10 @@ impl shader::Primitive for Primitive {
pipeline.viewcube.ensure_depth_texture(device, full_size);
pipeline.upload_uniforms(queue, &self.uniforms);
if self.geometry_epoch != pipeline.cached_epoch {
pipeline.upload_hatches(device, &self.hatches);
pipeline.upload_wipeouts(device, &self.wipeout_hatches);
pipeline.upload_images(device, queue, &self.images);
pipeline.upload_meshes(device, &self.meshes);
pipeline.upload_hatches(device, &self.hatches[..]);
pipeline.upload_wipeouts(device, &self.wipeout_hatches[..]);
pipeline.upload_images(device, queue, &self.images[..]);
pipeline.upload_meshes(device, &self.meshes[..]);
pipeline.upload_wires(device, &self.wires[..]);
pipeline.cached_epoch = self.geometry_epoch;
}
@@ -264,10 +264,10 @@ impl Scene {
Primitive {
wires: all_wires,
hatches: self.synced_hatch_models(),
wipeout_hatches: self.wipeout_models(),
images: self.images.values().cloned().collect(),
meshes: self.meshes.values().cloned().collect(),
hatches: self.hatch_models_arc(),
wipeout_hatches: self.wipeout_models_arc(),
images: self.images_arc(),
meshes: self.meshes_arc(),
uniforms: Uniforms::new(&cam, bounds),
cam_rotation: cam.view_rotation_mat(),
hover_region,
@@ -305,10 +305,10 @@ impl Scene {
Primitive {
wires: all_wires,
hatches: self.synced_hatch_models(),
wipeout_hatches: self.wipeout_models(),
images: self.images.values().cloned().collect(),
meshes: self.meshes.values().cloned().collect(),
hatches: self.hatch_models_arc(),
wipeout_hatches: self.wipeout_models_arc(),
images: self.images_arc(),
meshes: self.meshes_arc(),
uniforms: Uniforms::new(&cam, bounds),
cam_rotation: cam.view_rotation_mat(),
hover_region,