ifcviewer: fix progress bar capping at ~1/n during streaming

The streamer carved [0,100] evenly across N prioritised contexts (and
again across the net/gross passes).  In practice nearly every element
yields from the first (Body) context, so smooth progress only ever
filled range/n of the bar — typically ~20% — before snapping forward.
Drive progress directly from yielded element count over total instead.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
Dion Moult
2026-05-09 07:50:32 +10:00
parent 3e066443be
commit 994fa4bc03
+22 -33
View File
@@ -482,12 +482,15 @@ void GeometryStreamer::run(const std::string& path, int num_threads) {
QElapsedTimer stream_timer; QElapsedTimer stream_timer;
stream_timer.start(); stream_timer.start();
// Split the 0100 progress range proportionally to element counts so // Drive the bar from yields across all passes/contexts. Earlier the
// the bar advances roughly with wall time across both passes. // [0,100] range was carved evenly across N prioritised contexts, but
// in practice nearly every element yields from the first (Body)
// context, so smooth progress only ever filled 1/n of the bar before
// jumping to the next allocation — visually, a typical 5-context
// file looked like it capped at ~20%.
const size_t total_count = net_ids.size() + gross_ids.size(); const size_t total_count = net_ids.size() + gross_ids.size();
const int net_progress_end = total_count == 0 size_t yielded_count = 0;
? 100 int last_emitted_progress = 0;
: static_cast<int>(100.0 * net_ids.size() / total_count + 0.5);
// High-priority context first, so each element gets its preferred // High-priority context first, so each element gets its preferred
// representation; lower-priority contexts only pick up elements the // representation; lower-priority contexts only pick up elements the
@@ -497,9 +500,7 @@ void GeometryStreamer::run(const std::string& path, int num_threads) {
prioritisedContextIds(ifc_file_.get()); prioritisedContextIds(ifc_file_.get());
auto run_pass = [&](const std::set<int>& include_ids, auto run_pass = [&](const std::set<int>& include_ids,
bool is_gross, bool is_gross) -> bool {
int progress_lo,
int progress_hi) -> bool {
if (include_ids.empty()) return true; if (include_ids.empty()) return true;
ifcopenshell::geometry::Settings base_settings = settings; ifcopenshell::geometry::Settings base_settings = settings;
@@ -510,8 +511,7 @@ void GeometryStreamer::run(const std::string& path, int num_threads) {
// Elements that haven't yet produced geometry from any context. // Elements that haven't yet produced geometry from any context.
std::set<int> remaining = include_ids; std::set<int> remaining = include_ids;
auto run_iterator = [&](ifcopenshell::geometry::Settings& iter_settings, auto run_iterator = [&](ifcopenshell::geometry::Settings& iter_settings) -> bool {
int sub_lo, int sub_hi) -> bool {
if (remaining.empty()) return true; if (remaining.empty()) return true;
std::vector<ifcopenshell::geometry::filter_t> filters; std::vector<ifcopenshell::geometry::filter_t> filters;
@@ -535,14 +535,10 @@ void GeometryStreamer::run(const std::string& path, int num_threads) {
if (!iterator->initialize()) { if (!iterator->initialize()) {
// No geometry survived this context for the remaining ids. // No geometry survived this context for the remaining ids.
// Still advance progress so the bar doesn't stall. // Subsequent contexts will pick them up; nothing to emit.
progress_ = sub_hi;
emit progressChanged(sub_hi);
return true; return true;
} }
int last_progress = sub_lo;
do { do {
if (cancel_requested_.load()) break; if (cancel_requested_.load()) break;
@@ -656,11 +652,13 @@ void GeometryStreamer::run(const std::string& path, int num_threads) {
emit instanceReady(std::move(inst)); emit instanceReady(std::move(inst));
total_shapes++; total_shapes++;
yielded_count++;
const int p = sub_lo + const int p = total_count > 0
(iterator->progress() * (sub_hi - sub_lo)) / 100; ? static_cast<int>((100 * yielded_count) / total_count)
if (p != last_progress) { : 100;
last_progress = p; if (p != last_emitted_progress) {
last_emitted_progress = p;
progress_ = p; progress_ = p;
emit progressChanged(p); emit progressChanged(p);
} }
@@ -672,12 +670,10 @@ void GeometryStreamer::run(const std::string& path, int num_threads) {
if (prioritised_contexts.empty()) { if (prioritised_contexts.empty()) {
// No IfcGeometricRepresentationContext entities — fall back to // No IfcGeometricRepresentationContext entities — fall back to
// a single iterator pass without context-id filtering. // a single iterator pass without context-id filtering.
return run_iterator(base_settings, progress_lo, progress_hi); return run_iterator(base_settings);
} }
const int range = progress_hi - progress_lo; for (size_t i = 0; i < prioritised_contexts.size(); ++i) {
const int n = static_cast<int>(prioritised_contexts.size());
for (int i = 0; i < n; ++i) {
if (cancel_requested_.load()) break; if (cancel_requested_.load()) break;
if (remaining.empty()) break; if (remaining.empty()) break;
@@ -685,22 +681,15 @@ void GeometryStreamer::run(const std::string& path, int num_threads) {
iter_settings.set("context-ids", iter_settings.set("context-ids",
std::set<int>{ prioritised_contexts[i] }); std::set<int>{ prioritised_contexts[i] });
const int sub_lo = progress_lo + (range * i) / n; if (!run_iterator(iter_settings)) return false;
const int sub_hi = (i + 1 == n)
? progress_hi
: progress_lo + (range * (i + 1)) / n;
if (!run_iterator(iter_settings, sub_lo, sub_hi)) return false;
} }
progress_ = progress_hi;
emit progressChanged(progress_hi);
return true; return true;
}; };
if (!run_pass(net_ids, /*is_gross=*/false, 0, net_progress_end)) return; if (!run_pass(net_ids, /*is_gross=*/false)) return;
if (!cancel_requested_.load()) { if (!cancel_requested_.load()) {
run_pass(gross_ids, /*is_gross=*/true, net_progress_end, 100); run_pass(gross_ids, /*is_gross=*/true);
} }
progress_ = 100; progress_ = 100;