mirror of
https://github.com/IfcOpenShell/IfcOpenShell.git
synced 2026-08-16 02:24:30 +00:00
ifcviewer-web: contribution-cull streaming + combined loaded/needed/total bar
Streaming picked candidates on raw frustum visibility, so viewAll over a big
federation (all models in-frustum) fetched every chunk — even fine chunks that
project to sub-pixel and the renderer never draws. Gate streaming on the SAME
contribution decision the render path already makes.
- New per-chunk contribution_visible_count: instances that passed frustum AND
the contribution cull (projected radius >= min_radius_px), counted BEFORE HiZ
— so it's stable while the camera is still (unlike the HiZ-post counters,
which flip frame-to-frame and would thrash the loader) and shifts only on
navigation, when the working set should. The candidate gate skips chunks with
count 0: the network pulls only what's resolvable now; the rest stream in as
you approach. Verified: fit view needs the geometry, zoomed-out needs 0.
- Loading UI reworked from per-model segments to a combined bar over the whole
federation: dark track = not needed for this view, dim = needed-but-unloaded,
bright = loaded. "loaded / needed" = how done THIS view is; "needed / total" =
how much of the model the view requires — "Loading 45 / 90 MB for this view ·
12% of 718 MB total". Driven by ViewportCore::streamingByteProgress + ifcv_bytes_*.
Two fixes from testing: (1) show a distinct overhead phase while total==0
("Loading model data — X MB · Y/N models ready") so the metadata download
isn't a dead-looking bar; (2) re-assert display:block every active frame so the
bar REAPPEARS when navigation reveals new chunks (was only set in
beginLoadProgress → stayed hidden after the first catch-up). Per-model exports
kept for a future detailed view.
111/111 unit + 6/6 web smoke pass.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -2280,7 +2280,12 @@ void ViewportCore::driveStreamingLoads() {
|
||||
auto& c = m.chunks[ci];
|
||||
if (c.is_resident) continue;
|
||||
if (c.is_loading) continue;
|
||||
if (c.frustum_visible_count == 0) continue;
|
||||
// Contribution cull: only fetch chunks big enough on screen to
|
||||
// actually draw at the current view — not everything in the
|
||||
// frustum. Zoomed out over a big federation this skips the fine
|
||||
// chunks that project to sub-pixel, so the network only pulls
|
||||
// what's resolvable now; the rest stream in as you approach.
|
||||
if (c.contribution_visible_count == 0) continue;
|
||||
if (c.blocked_cooldown_until_frame_idx > streaming_frame_idx_) continue;
|
||||
candidates.push_back({&m, ci, mid, candidate_priority(c)});
|
||||
}
|
||||
@@ -2589,6 +2594,7 @@ std::uint32_t ViewportCore::cullModelCpuCompute(
|
||||
c.opaque_visible_vertices = 0;
|
||||
c.opaque_visible_draws = 0;
|
||||
c.frustum_visible_count = 0;
|
||||
c.contribution_visible_count = 0;
|
||||
c.current_priority = 0.0f;
|
||||
}
|
||||
|
||||
@@ -2653,6 +2659,10 @@ std::uint32_t ViewportCore::cullModelCpuCompute(
|
||||
// first cuts the HiZ-tested population by ~5× on real scenes.
|
||||
if (contrib_enabled && projected_px < min_radius_px) return;
|
||||
|
||||
// Passed frustum + contribution (pre-HiZ). Streaming uses this to
|
||||
// decide what's worth fetching for the current view.
|
||||
++c.contribution_visible_count;
|
||||
|
||||
if (hiz_active
|
||||
&& hiz_occluded(inst.world_aabb_min, inst.world_aabb_max)) {
|
||||
++hiz_rejects;
|
||||
@@ -3656,6 +3666,29 @@ void ViewportCore::streamingModelProgress(int idx, int& resident_chunks,
|
||||
}
|
||||
}
|
||||
|
||||
void ViewportCore::streamingByteProgress(std::uint64_t& total_bytes,
|
||||
std::uint64_t& needed_bytes,
|
||||
std::uint64_t& loaded_bytes) const {
|
||||
// total = all geometry across every model (the whole federation).
|
||||
// needed = chunks the CURRENT view wants (passed contribution culling).
|
||||
// loaded = the needed chunks that are resident.
|
||||
// So loaded/needed = how done this view is; needed/total = how much of the
|
||||
// whole model this view even requires.
|
||||
total_bytes = needed_bytes = loaded_bytes = 0;
|
||||
for (const auto& [mid, m] : models_gpu_) {
|
||||
if (m.hidden) continue;
|
||||
for (const auto& c : m.chunks) {
|
||||
const std::uint64_t bytes =
|
||||
c.vertex_byte_size + c.index_count * sizeof(std::uint32_t);
|
||||
total_bytes += bytes;
|
||||
if (c.contribution_visible_count > 0) {
|
||||
needed_bytes += bytes;
|
||||
if (c.is_resident) loaded_bytes += bytes;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void ViewportCore::finalizeModel(std::uint32_t model_id) {
|
||||
auto it = pending_direct_loads_.find(model_id);
|
||||
if (it == pending_direct_loads_.end()) {
|
||||
|
||||
Reference in New Issue
Block a user