[perf] TLAS instance+metadata upload: O(n) rebuild + whole-buffer flush every frame (RenderingElement3D.cpp) #118

Closed
opened 2026-06-17 21:18:53 +02:00 by jorijnvdgraaf · 0 comments

Found by an adversarially-verified performance audit (confirmed real after a skeptic pass).

This issue covers the per-frame TLAS instance+metadata upload end to end — both the O(n) host rebuild and the whole-buffer flush of those same buffers. They are one deliverable: the dirty range computed for the rebuild is exactly the range the flush should use. (Merged from the former #121.)

Locations: implementations/Crafter.Graphics-RenderingElement3D.cpp:144-159 (rebuild) and :161-173 (flush)
Severity: medium · Effort: moderate · Category: cpu + memory

Problem

  1. Host rebuild (:144-159): the TLAS host instance+metadata buffer is rebuilt with an O(n) loop every frame, unconditionally (outside the topologyChanged block), copying each 64 B VkAccelerationStructureInstanceKHR + metadata with no dirty tracking — even on cheap refits. At the millions-of-instances target this dominates the CPU frame.
  2. Flush (:161-173): both FlushDevice calls resolve to VK_WHOLE_SIZE, flushing the full high-water capacity every frame regardless of dirty bytes (steady state never calls Resize, so the span is capacity, not primitiveCount*stride). Self-gates to a no-op on HOST_COHERENT memory, so cost only materializes on non-coherent BAR/VRAM.

Fix

Add a dirty-set / generation counter to the rebuild so only changed host-authored fields are copied, then feed that same dirty range into the existing ranged FlushDevice(offset, bytes) overload (VulkanBuffer.cppm:336) over [0, primitiveCount*stride) (or, ideally, just the dirty sub-ranges). One dirty-set drives both the copy and the flush.

Constraint

transformOwnedByGpu elements have their transform written in-place by GPU compute, so only host-authored fields are dirty-trackable. (The AoS→SoA and branch-hoist suggestions on the rebuild were refuted: the loop is bandwidth-bound and VkAccelerationStructureInstanceKHR is a spec-frozen AoS struct read by device address.)

Found by an adversarially-verified performance audit (confirmed real after a skeptic pass). This issue covers the **per-frame TLAS instance+metadata upload** end to end — both the O(n) host rebuild and the whole-buffer flush of those same buffers. They are one deliverable: the dirty range computed for the rebuild is exactly the range the flush should use. (Merged from the former #121.) **Locations:** `implementations/Crafter.Graphics-RenderingElement3D.cpp:144-159` (rebuild) and `:161-173` (flush) **Severity:** medium · **Effort:** moderate · **Category:** cpu + memory ### Problem 1. **Host rebuild** (`:144-159`): the TLAS host instance+metadata buffer is rebuilt with an O(n) loop **every frame**, unconditionally (outside the `topologyChanged` block), copying each 64 B `VkAccelerationStructureInstanceKHR` + metadata with no dirty tracking — even on cheap refits. At the millions-of-instances target this dominates the CPU frame. 2. **Flush** (`:161-173`): both `FlushDevice` calls resolve to `VK_WHOLE_SIZE`, flushing the full high-water capacity every frame regardless of dirty bytes (steady state never calls `Resize`, so the span is capacity, not `primitiveCount*stride`). Self-gates to a no-op on HOST_COHERENT memory, so cost only materializes on non-coherent BAR/VRAM. ### Fix Add a dirty-set / generation counter to the rebuild so only changed host-authored fields are copied, then feed that same dirty range into the existing ranged `FlushDevice(offset, bytes)` overload (`VulkanBuffer.cppm:336`) over `[0, primitiveCount*stride)` (or, ideally, just the dirty sub-ranges). One dirty-set drives both the copy and the flush. ### Constraint `transformOwnedByGpu` elements have their transform written in-place by GPU compute, so only host-authored fields are dirty-trackable. (The AoS→SoA and branch-hoist suggestions on the rebuild were refuted: the loop is bandwidth-bound and `VkAccelerationStructureInstanceKHR` is a spec-frozen AoS struct read by device address.)
jorijnvdgraaf changed title from [perf] TLAS rebuilds entire host instance+metadata buffer every frame (RenderingElement3D.cpp) to [perf] TLAS instance+metadata upload: O(n) rebuild + whole-buffer flush every frame (RenderingElement3D.cpp) 2026-06-17 21:26:02 +02:00
catbot 2026-06-17 21:59:28 +02:00
Sign in to join this conversation.
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference
Catcrafts/Crafter.Graphics#118
No description provided.