Cull: read AABBs from compact bvh_items in the hot path

cullAndUploadVisible was reading each instance's AABB through
m.instances[idx] — a 104-byte InstanceCpu struct — for the frustum /
contribution / HiZ tests.  Only 24 of those bytes (the two float[3]
AABBs) are actually used by the tests; the rest (4×4 transform +
header) is pure cache-line waste, and with 569k instances the array
is 59 MB, well past any cache.

bvh_items[idx] already stores a 1:1 compact 28-byte record with the
same AABB, built unconditionally in buildBvhForModel().  Switch the
hot test path to read from it, and only touch InstanceCpu once an
instance has passed all three tests (for mesh_id).  Modest ~20 %
drop in cull-traverse time on a 569k-object overview (26 ms → 21 ms).

Also add four cull-phase timers (clr / trv / emt / upl) to the
per-second stats line so future optimisation work has concrete
numbers to chase.  Confirmed via these timers that bucket clears,
emit and GPU upload are all <1 ms combined; traversal is where the
remaining CPU cost lives.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
Dion Moult
2026-04-14 20:33:21 +10:00
parent 8596d53a4a
commit c03a7fe117
2 changed files with 47 additions and 6 deletions
+10
View File
@@ -259,6 +259,16 @@ private:
bool hiz_vp_valid_ = false;
uint32_t hiz_reject_count_ = 0; // per-frame stat
// Cull-phase timers. Accumulated across all frames in the current
// 1-second stats window; divided by frame_count_ at print time to
// give per-frame average ms. Reset each window. Lets us see where
// CPU time actually goes: bucket clears vs BVH traversal vs emit vs
// GPU upload.
uint64_t cull_clear_ns_ = 0;
uint64_t cull_traverse_ns_ = 0;
uint64_t cull_emit_ns_ = 0;
uint64_t cull_upload_ns_ = 0;
// Per-frame stats
uint32_t visible_triangles_ = 0;
uint32_t visible_objects_ = 0;