mirror of
https://github.com/IfcOpenShell/IfcOpenShell.git
synced 2026-08-12 10:33:20 +00:00
ifcviewer: fix blank-until-interaction stall on web (streaming settle burst)
On first web load the sample stayed blank until a click/drag, then popped in. Root cause: the main draw + cull run before driveStreamingLoads in render(), so a chunk that becomes resident there is only painted a frame later. On desktop the streaming thread keeps inFlightApprox() > 0 during a load, so the render loop keeps ticking and the next frame paints it. On web the sync MEMFS / Blob load finishes instantly (inFlightApprox stays 0), so the single post-load requestFrame fired once and the on-demand loop went idle before the geometry was ever drawn — until some input re-armed it. Fix: arm a bounded settle burst (kStreamingSettleFrames) whenever there's streaming activity — a load this frame, work still queued, or a visible chunk not yet resident — and bleed it down over the next few frames, each requesting one more. Covers the cull→display latency under an on-demand loop and still quiesces at idle (no busy-rendering). General, not web-only. Regression test: the sample must render with NO pointer input — a centred patch (the framed cube) differs from a corner patch (background); a blank stall leaves both as background. Verified empirically with a no-interaction probe (canvas went from a static blank hash to a stable rendered one). 107/107 unit tests pass; 4/4 web smoke tests pass. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -2305,7 +2305,35 @@ void ViewportCore::driveStreamingLoads() {
|
||||
}
|
||||
}
|
||||
loads += enqueued;
|
||||
if (loads > 0 || streaming_thread_.inFlightApprox() > 0) host_->requestFrame();
|
||||
|
||||
// Keep the render loop alive while streaming settles. The main draw + cull
|
||||
// run *before* this point in render(), so a chunk that becomes resident
|
||||
// here is only drawn on a later frame — and on web a sync load finishes
|
||||
// instantly (inFlightApprox stays 0), so a single requestFrame after a
|
||||
// load isn't enough to flush that cull→display latency. Arm a short settle
|
||||
// burst whenever there's streaming activity (a load this frame, work still
|
||||
// queued, or a visible chunk not yet resident) and bleed it down over the
|
||||
// next few frames so an on-demand render loop doesn't stall before the
|
||||
// geometry actually appears. Bounded, so the loop still quiesces at idle.
|
||||
bool visible_pending = false;
|
||||
for (const auto& [mid, m] : models_gpu_) {
|
||||
if (m.streaming_file_path.empty() || m.hidden) continue;
|
||||
for (const auto& c : m.chunks) {
|
||||
if (!c.is_resident && (c.frustum_visible_count > 0 || c.is_loading)) {
|
||||
visible_pending = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (visible_pending) break;
|
||||
}
|
||||
if (loads > 0 || more_pending || visible_pending
|
||||
|| streaming_thread_.inFlightApprox() > 0) {
|
||||
streaming_settle_frames_ = kStreamingSettleFrames;
|
||||
}
|
||||
if (streaming_settle_frames_ > 0) {
|
||||
--streaming_settle_frames_;
|
||||
host_->requestFrame();
|
||||
}
|
||||
|
||||
streaming_loads_this_frame_ = loads;
|
||||
streaming_more_pending_ = more_pending;
|
||||
|
||||
Reference in New Issue
Block a user