wgpu backend: per-instance contribution culling in cullModelCpu

Quick win before the proper HiZ stage. Adds a min_pixel_radius
threshold (defaults 2.0 to match AppSettings::minPixelRadius() in GL):
instances whose projected bounding-sphere radius falls below it are
dropped from the per-mesh buckets entirely.

The projected_px math (radius_world * focal_px / view_z) is now
computed once per instance and shared with the LOD pick that uses the
same number. Saves one square root per instance per frame on dense
scenes vs the previous code path that only computed it inside the LOD
branch.

Expected impact on real BIM benchmarks: visible-objects count drops by
roughly 10×, matching the GL backend's number. Without this fix, wgpu
was drawing every frustum-surviving sub-pixel instance — most of the
work and most of the geometry the GL backend wasn't even submitting.

Motion-mode threshold bump (10.0 in GL during camera drag) lands
later when mouse-driven motion tracking is wired up.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
Dion Moult
2026-05-27 15:04:42 +10:00
parent 7893135790
commit 6ce2b564c1
2 changed files with 43 additions and 16 deletions
+18 -5
View File
@@ -129,14 +129,21 @@ private:
// per-LOD draw schedule for the frame.
//
// `eye` and `forward` (forward = unit (target - eye)) are used to compute
// each instance's view-space depth for the LOD-pick projected-radius
// formula. `focal_px` = viewport_height / (2 * tan(fov_y / 2)). Instances
// whose projected bounding-sphere radius is below `lod1_threshold_px`
// get the mesh's LOD1 index slice when one was baked.
// each instance's view-space depth for the projected-radius formula.
// `focal_px` = viewport_height / (2 * tan(fov_y / 2)).
//
// Two pixel-radius thresholds:
// `min_radius_px` — instances projected below this are dropped
// entirely (contribution culling).
// `lod1_threshold_px` — survivors projected below this get the mesh's
// LOD1 index slice when one was baked.
// min_radius_px == 0 disables contribution culling.
void cullModelCpu(WgpuModelGpuData& m,
const float planes[6][4],
const float eye[3], const float forward[3],
float focal_px, float lod1_threshold_px);
float focal_px,
float min_radius_px,
float lod1_threshold_px);
bool wgpu_initialized_ = false;
bool surface_configured_ = false;
@@ -187,6 +194,12 @@ private:
float camera_near_ = 0.1f;
float camera_far_ = 10000.0f;
// Drop instances whose projected bounding-sphere radius is below this
// many pixels. Mirrors AppSettings::minPixelRadius() (GL default 2.0;
// motion mode uses 10.0 but we don't differentiate yet — that arrives
// with mouse-driven motion-state tracking later).
float min_pixel_radius_ = 2.0f;
// Switch to LOD1 when an instance's projected bounding-sphere radius
// drops below this many pixels. 0 disables (always LOD0). Defaults
// mirror AppSettings::lod1PixelThreshold() in the GL backend.