From f898089b27a4f4acafe06f07aa0b06e013a51f38 Mon Sep 17 00:00:00 2001 From: Dion Moult Date: Wed, 22 Apr 2026 11:33:22 +1000 Subject: [PATCH] Queue viewer ops before GL init Buffer viewport model mutations until the OpenGL context is initialized so loads that start before first exposure do not silently drop geometry or model state. Generated with the assistance of an AI coding tool. --- src/ifcviewer/ViewportWindow.cpp | 114 +++++++++++++++++++++++++++++-- src/ifcviewer/ViewportWindow.h | 24 +++++++ 2 files changed, 131 insertions(+), 7 deletions(-) diff --git a/src/ifcviewer/ViewportWindow.cpp b/src/ifcviewer/ViewportWindow.cpp index 6ae5c04368..a38e726f08 100644 --- a/src/ifcviewer/ViewportWindow.cpp +++ b/src/ifcviewer/ViewportWindow.cpp @@ -483,11 +483,53 @@ void ViewportWindow::initGL() { }); gl_initialized_ = true; + flushPendingOperations(); requestUpdate(); emit initialized(); } +void ViewportWindow::enqueuePendingOperation(PendingOperation op) { + pending_ops_.push_back(std::move(op)); +} + +void ViewportWindow::flushPendingOperations() { + while (!pending_ops_.empty()) { + PendingOperation op = std::move(pending_ops_.front()); + pending_ops_.pop_front(); + + switch (op.type) { + case PendingOpType::UploadMeshChunk: + uploadMeshChunk(op.mesh_chunk); + break; + case PendingOpType::UploadInstanceChunk: + uploadInstanceChunk(op.instance_chunk); + break; + case PendingOpType::FinalizeModel: + finalizeModel(op.model_id); + break; + case PendingOpType::ApplyCachedModel: + applyCachedModel(op.model_id, std::move(op.sidecar_data)); + break; + case PendingOpType::ApplyLodExtension: + applyLodExtension(op.model_id, op.sidecar_data); + break; + case PendingOpType::ResetScene: + resetScene(); + break; + case PendingOpType::HideModel: + hideModel(op.model_id); + break; + case PendingOpType::ShowModel: + showModel(op.model_id); + break; + case PendingOpType::RemoveModel: + removeModel(op.model_id); + break; + } + } +} + void ViewportWindow::setupVaoLayout(GLuint vao, GLuint vbo, GLuint ebo) { gl_->glVertexArrayVertexBuffer(vao, 0, vbo, 0, INSTANCED_VERTEX_STRIDE_BYTES); gl_->glVertexArrayElementBuffer(vao, ebo); @@ -643,7 +685,13 @@ ModelGpuData& ViewportWindow::getOrCreateModel(uint32_t model_id) { } void ViewportWindow::uploadMeshChunk(const MeshChunk& chunk) { - if (!gl_initialized_) return; + if (!gl_initialized_) { + PendingOperation op; + op.type = PendingOpType::UploadMeshChunk; + op.mesh_chunk = chunk; + enqueuePendingOperation(std::move(op)); + return; + } if (chunk.vertices.empty() || chunk.indices.empty()) return; context_->makeCurrent(this); @@ -745,7 +793,13 @@ void ViewportWindow::uploadMeshChunk(const MeshChunk& chunk) { } void ViewportWindow::uploadInstanceChunk(const InstanceChunk& chunk) { - if (!gl_initialized_) return; + if (!gl_initialized_) { + PendingOperation op; + op.type = PendingOpType::UploadInstanceChunk; + op.instance_chunk = chunk; + enqueuePendingOperation(std::move(op)); + return; + } context_->makeCurrent(this); ModelGpuData& m = getOrCreateModel(chunk.model_id); @@ -795,7 +849,13 @@ void ViewportWindow::uploadInstanceChunk(const InstanceChunk& chunk) { } void ViewportWindow::finalizeModel(uint32_t model_id) { - if (!gl_initialized_) return; + if (!gl_initialized_) { + PendingOperation op; + op.type = PendingOpType::FinalizeModel; + op.model_id = model_id; + enqueuePendingOperation(std::move(op)); + return; + } context_->makeCurrent(this); auto it = models_gpu_.find(model_id); @@ -852,7 +912,14 @@ bool ViewportWindow::snapshotModel(uint32_t model_id, SidecarData& out) const { } void ViewportWindow::applyCachedModel(uint32_t model_id, SidecarData data) { - if (!gl_initialized_) return; + if (!gl_initialized_) { + PendingOperation op; + op.type = PendingOpType::ApplyCachedModel; + op.model_id = model_id; + op.sidecar_data = std::move(data); + enqueuePendingOperation(std::move(op)); + return; + } context_->makeCurrent(this); // Drop any existing state for this model_id. @@ -965,7 +1032,14 @@ void ViewportWindow::applyCachedModel(uint32_t model_id, SidecarData data) { } void ViewportWindow::applyLodExtension(uint32_t model_id, const SidecarData& sd) { - if (!gl_initialized_) return; + if (!gl_initialized_) { + PendingOperation op; + op.type = PendingOpType::ApplyLodExtension; + op.model_id = model_id; + op.sidecar_data = sd; + enqueuePendingOperation(std::move(op)); + return; + } auto it = models_gpu_.find(model_id); if (it == models_gpu_.end() || !it->second.finalized) return; ModelGpuData& m = it->second; @@ -997,7 +1071,13 @@ void ViewportWindow::applyLodExtension(uint32_t model_id, const SidecarData& sd) } void ViewportWindow::resetScene() { - if (!gl_initialized_) return; + if (!gl_initialized_) { + PendingOperation op; + op.type = PendingOpType::ResetScene; + pending_ops_.clear(); + enqueuePendingOperation(std::move(op)); + return; + } context_->makeCurrent(this); for (auto& [mid, m] : models_gpu_) { if (m.vao) gl_->glDeleteVertexArrays(1, &m.vao); @@ -1015,6 +1095,13 @@ void ViewportWindow::resetScene() { } void ViewportWindow::hideModel(uint32_t model_id) { + if (!gl_initialized_) { + PendingOperation op; + op.type = PendingOpType::HideModel; + op.model_id = model_id; + enqueuePendingOperation(std::move(op)); + return; + } auto it = models_gpu_.find(model_id); if (it != models_gpu_.end()) { it->second.hidden = true; @@ -1024,6 +1111,13 @@ void ViewportWindow::hideModel(uint32_t model_id) { } void ViewportWindow::showModel(uint32_t model_id) { + if (!gl_initialized_) { + PendingOperation op; + op.type = PendingOpType::ShowModel; + op.model_id = model_id; + enqueuePendingOperation(std::move(op)); + return; + } auto it = models_gpu_.find(model_id); if (it != models_gpu_.end()) { it->second.hidden = false; @@ -1033,7 +1127,13 @@ void ViewportWindow::showModel(uint32_t model_id) { } void ViewportWindow::removeModel(uint32_t model_id) { - if (!gl_initialized_) return; + if (!gl_initialized_) { + PendingOperation op; + op.type = PendingOpType::RemoveModel; + op.model_id = model_id; + enqueuePendingOperation(std::move(op)); + return; + } context_->makeCurrent(this); auto it = models_gpu_.find(model_id); if (it != models_gpu_.end()) { diff --git a/src/ifcviewer/ViewportWindow.h b/src/ifcviewer/ViewportWindow.h index a8aa7b7577..0cdd712bd5 100644 --- a/src/ifcviewer/ViewportWindow.h +++ b/src/ifcviewer/ViewportWindow.h @@ -34,6 +34,7 @@ #include #include #include +#include #include "BvhAccel.h" #include "InstancedGeometry.h" @@ -194,7 +195,29 @@ protected: bool event(QEvent* event) override; private: + enum class PendingOpType { + UploadMeshChunk, + UploadInstanceChunk, + FinalizeModel, + ApplyCachedModel, + ApplyLodExtension, + ResetScene, + HideModel, + ShowModel, + RemoveModel, + }; + + struct PendingOperation { + PendingOpType type; + MeshChunk mesh_chunk; + InstanceChunk instance_chunk; + SidecarData sidecar_data; + uint32_t model_id = 0; + }; + void initGL(); + void flushPendingOperations(); + void enqueuePendingOperation(PendingOperation op); void render(); void renderPickPass(); void renderAxisGizmo(); @@ -253,6 +276,7 @@ private: QOpenGLContext* context_ = nullptr; QOpenGLFunctions_4_5_Core* gl_ = nullptr; bool gl_initialized_ = false; + std::deque pending_ops_; // Shaders GLuint main_program_ = 0;