mirror of
https://github.com/IfcOpenShell/IfcOpenShell.git
synced 2026-08-30 16:43:00 +00:00
wgpu streaming: screen-space AABB priority + grace period + interactive heartbeat
The chunk priority metric is now the 2D projected pixel area of the chunk's AABB on screen — 8 corners projected through view-projection, 2D axis-aligned bbox of the projected points, clamped to viewport. This replaces the prior bounding-sphere-radius² metric, which was a 3D approximation: it treated a 322 × 55 × 5 m slab as a 163 m sphere, giving it the same huge priority face-on or edge-on. The new metric genuinely answers "what would this chunk's AABB cover if rendered solid given the current camera and viewport." Newly-loaded chunks get a 30-frame grace period at full priority (visibility_history floor temporarily forced to 1.0). Without it, just-loaded chunks crashed to history=0 → effective priority = pri × 0.05 → immediately reverse-swapped by the chunk they displaced. Cycle starved the per-frame load budget so candidates ranked below the cyclers never got attempted. 30 frames = HISTORY_ALPHA's time constant — enough for visibility_history to develop meaningfully. EVICT_PRIORITY_RATIO bumped 1.21 → 2.0 to suppress more swap noise between similar-priority chunks. Interactive heartbeat log added: every render in non-bench mode prints [frame] with fps, ms, obj, sub_draws, hiz_rej, cull, stream, chunks breakdown (resident/frustum/total + missing count), VRAM, model count. Every 30 frames when something's missing, also dumps: - top 8 models by missing chunk count - top 20 missing chunks by priority (with AABBs) - bottom 5 residents by effective priority - all chunks of brace.ifc (one-off diagnostic, hardcoded for the brace-visibility investigation) The heartbeat made the streaming bug visible: a brace model that isolation-loads correctly is missing in the full set because slabs covering more pixels win the priority contest. Per-model fairness or manual pinning are the remaining options if pixel-area + grace + hysteresis isn't enough — left for follow-up so the user can decide based on real testing. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
@@ -178,6 +178,28 @@ struct WgpuModelGpuData {
|
||||
// total_visible_draws > 0). The evictor picks the smallest value
|
||||
// among non-visible resident chunks when it needs to free VRAM.
|
||||
uint64_t last_visible_frame_idx = 0;
|
||||
// EMA-smoothed visibility score, in [0, 1]. Bumped each frame
|
||||
// toward 1 when total_visible_draws > 0 (the chunk's instances
|
||||
// passed frustum + contribution + HiZ), toward 0 otherwise.
|
||||
// Time constant ~30 frames. Used by the streaming evictor to
|
||||
// de-prioritise chunks that are technically in the frustum but
|
||||
// consistently HiZ-occluded — e.g. interior pipes behind a
|
||||
// building's exterior walls. The smoothing prevents thrash from
|
||||
// momentary HiZ flicker (a wall briefly visible behind a panning
|
||||
// window doesn't displace the window from the pool).
|
||||
float visibility_history = 0.0f;
|
||||
// streaming_frame_idx_ when this chunk was last loaded. The
|
||||
// evictor grants newly-loaded chunks ~30 frames of grace at
|
||||
// full priority (max history factor = 1.0) so they have time
|
||||
// for visibility_history to develop. Without this, a just-
|
||||
// loaded chunk's effective priority drops to contribution ×
|
||||
// 0.05 next frame, and the chunk it displaced — back as a
|
||||
// candidate at full priority — re-displaces it: infinite
|
||||
// cycle between equal-priority chunks. The cycle prevents any
|
||||
// lower-priority candidate (e.g. a structural-brace chunk
|
||||
// ranked position 20 in the missing list) from ever getting
|
||||
// attempted.
|
||||
uint64_t loaded_frame_idx = 0;
|
||||
};
|
||||
std::vector<Chunk> chunks;
|
||||
|
||||
|
||||
Reference in New Issue
Block a user