mirror of
https://github.com/IfcOpenShell/IfcOpenShell.git
synced 2026-08-10 09:48:32 +00:00
ifcviewer: upload per-instance world AABBs to a GPU SSBO
Scaffolding for Phase 3E (GPU compute cull). After finalizeModel / applyCachedModel, pack each InstanceCpu's world AABB + mesh_id + reflection bit into a std430-friendly 32 B record and push it to a per-model aabb_ssbo. No consumer yet — the CPU cull still drives rendering — but the next commits will point a compute shader at this buffer and have it produce the visible list + indirect commands directly on the GPU. Cost: 32 B per instance, ~18 MB for the 569 k-instance test scene. One-shot upload at finalize time; streaming-time appends aren't mirrored (the CPU cull doesn't need the SSBO, and finalizeModel rebuilds the whole thing in one go).
This commit is contained in:
@@ -408,6 +408,7 @@ ViewportWindow::~ViewportWindow() {
|
||||
if (m.mesh_info_ssbo) gl_->glDeleteBuffers(1, &m.mesh_info_ssbo);
|
||||
if (m.visible_ssbo) gl_->glDeleteBuffers(1, &m.visible_ssbo);
|
||||
if (m.indirect_buffer) gl_->glDeleteBuffers(1, &m.indirect_buffer);
|
||||
if (m.aabb_ssbo) gl_->glDeleteBuffers(1, &m.aabb_ssbo);
|
||||
}
|
||||
if (axis_vao_) gl_->glDeleteVertexArrays(1, &axis_vao_);
|
||||
if (axis_vbo_) gl_->glDeleteBuffers(1, &axis_vbo_);
|
||||
@@ -764,6 +765,48 @@ void ViewportWindow::uploadInstanceChunk(const InstanceChunk& chunk) {
|
||||
requestUpdate();
|
||||
}
|
||||
|
||||
// Matches the std430 layout the GPU compute cull will consume.
|
||||
struct InstanceAabbGpu {
|
||||
float min[3];
|
||||
uint32_t mesh_id;
|
||||
float max[3];
|
||||
uint32_t flags; // bit 0 = reflected
|
||||
};
|
||||
static_assert(sizeof(InstanceAabbGpu) == 32, "InstanceAabbGpu must be 32 bytes");
|
||||
|
||||
void ViewportWindow::uploadInstanceAabbs(ModelGpuData& m) {
|
||||
const size_t n = m.instances.size();
|
||||
const size_t bytes = n * sizeof(InstanceAabbGpu);
|
||||
|
||||
if (m.aabb_ssbo && m.aabb_ssbo_capacity < bytes) {
|
||||
gl_->glDeleteBuffers(1, &m.aabb_ssbo);
|
||||
m.aabb_ssbo = 0;
|
||||
m.aabb_ssbo_capacity = 0;
|
||||
}
|
||||
if (!m.aabb_ssbo) {
|
||||
gl_->glCreateBuffers(1, &m.aabb_ssbo);
|
||||
const size_t cap = std::max<size_t>(bytes, sizeof(InstanceAabbGpu));
|
||||
gl_->glNamedBufferStorage(m.aabb_ssbo, cap, nullptr, GL_DYNAMIC_STORAGE_BIT);
|
||||
m.aabb_ssbo_capacity = cap;
|
||||
}
|
||||
if (n == 0) return;
|
||||
|
||||
std::vector<InstanceAabbGpu> packed(n);
|
||||
for (size_t i = 0; i < n; ++i) {
|
||||
const InstanceCpu& src = m.instances[i];
|
||||
InstanceAabbGpu& dst = packed[i];
|
||||
dst.min[0] = src.world_aabb_min[0];
|
||||
dst.min[1] = src.world_aabb_min[1];
|
||||
dst.min[2] = src.world_aabb_min[2];
|
||||
dst.max[0] = src.world_aabb_max[0];
|
||||
dst.max[1] = src.world_aabb_max[1];
|
||||
dst.max[2] = src.world_aabb_max[2];
|
||||
dst.mesh_id = src.mesh_id;
|
||||
dst.flags = (i < m.instance_reflected.size() && m.instance_reflected[i]) ? 1u : 0u;
|
||||
}
|
||||
gl_->glNamedBufferSubData(m.aabb_ssbo, 0, bytes, packed.data());
|
||||
}
|
||||
|
||||
void ViewportWindow::finalizeModel(uint32_t model_id) {
|
||||
if (!gl_initialized_) return;
|
||||
context_->makeCurrent(this);
|
||||
@@ -783,6 +826,7 @@ void ViewportWindow::finalizeModel(uint32_t model_id) {
|
||||
}
|
||||
|
||||
buildBvhForModel(m, model_id);
|
||||
uploadInstanceAabbs(m);
|
||||
|
||||
m.finalized = true;
|
||||
have_cached_cull_ = false;
|
||||
@@ -835,6 +879,7 @@ void ViewportWindow::applyCachedModel(uint32_t model_id, SidecarData data) {
|
||||
if (existing->second.mesh_info_ssbo) gl_->glDeleteBuffers(1, &existing->second.mesh_info_ssbo);
|
||||
if (existing->second.visible_ssbo) gl_->glDeleteBuffers(1, &existing->second.visible_ssbo);
|
||||
if (existing->second.indirect_buffer) gl_->glDeleteBuffers(1, &existing->second.indirect_buffer);
|
||||
if (existing->second.aabb_ssbo) gl_->glDeleteBuffers(1, &existing->second.aabb_ssbo);
|
||||
models_gpu_.erase(existing);
|
||||
}
|
||||
|
||||
@@ -917,6 +962,7 @@ void ViewportWindow::applyCachedModel(uint32_t model_id, SidecarData data) {
|
||||
}
|
||||
|
||||
buildBvhForModel(m, model_id);
|
||||
uploadInstanceAabbs(m);
|
||||
|
||||
m.finalized = true;
|
||||
models_gpu_.emplace(model_id, std::move(m));
|
||||
@@ -977,6 +1023,7 @@ void ViewportWindow::resetScene() {
|
||||
if (m.mesh_info_ssbo) gl_->glDeleteBuffers(1, &m.mesh_info_ssbo);
|
||||
if (m.visible_ssbo) gl_->glDeleteBuffers(1, &m.visible_ssbo);
|
||||
if (m.indirect_buffer) gl_->glDeleteBuffers(1, &m.indirect_buffer);
|
||||
if (m.aabb_ssbo) gl_->glDeleteBuffers(1, &m.aabb_ssbo);
|
||||
}
|
||||
models_gpu_.clear();
|
||||
selected_object_id_ = 0;
|
||||
@@ -1014,6 +1061,7 @@ void ViewportWindow::removeModel(uint32_t model_id) {
|
||||
if (it->second.mesh_info_ssbo) gl_->glDeleteBuffers(1, &it->second.mesh_info_ssbo);
|
||||
if (it->second.visible_ssbo) gl_->glDeleteBuffers(1, &it->second.visible_ssbo);
|
||||
if (it->second.indirect_buffer) gl_->glDeleteBuffers(1, &it->second.indirect_buffer);
|
||||
if (it->second.aabb_ssbo) gl_->glDeleteBuffers(1, &it->second.aabb_ssbo);
|
||||
models_gpu_.erase(it);
|
||||
have_cached_cull_ = false;
|
||||
requestUpdate();
|
||||
|
||||
@@ -94,6 +94,14 @@ struct ModelGpuData {
|
||||
std::vector<BvhItem> bvh_items;
|
||||
ModelBvh bvh;
|
||||
|
||||
// Per-instance world AABB on the GPU, 1:1 with `instances`.
|
||||
// Populated at finalize / applyCachedModel. Consumed by the upcoming
|
||||
// GPU-compute cull (Phase 3E); the CPU cull still reads from bvh_items.
|
||||
// Layout: struct { vec3 min; uint mesh_id; vec3 max; uint flags; } = 32 B.
|
||||
// `flags` bit 0 = reflected (for winding-bucket selection).
|
||||
GLuint aabb_ssbo = 0;
|
||||
size_t aabb_ssbo_capacity = 0; // bytes
|
||||
|
||||
// Dynamic visible-instance index buffer (std430, binding = 1).
|
||||
// Re-uploaded each frame from visible_flat_.
|
||||
GLuint visible_ssbo = 0;
|
||||
@@ -215,6 +223,12 @@ private:
|
||||
bool growModelSsbo(ModelGpuData& m, size_t needed_total);
|
||||
ModelGpuData& getOrCreateModel(uint32_t model_id);
|
||||
|
||||
// (Re)build the per-instance world AABB SSBO from m.instances +
|
||||
// m.instance_reflected. One-shot upload called after finalizeModel /
|
||||
// applyCachedModel once instances are settled. Consumed by the GPU
|
||||
// compute cull (Phase 3E, in progress).
|
||||
void uploadInstanceAabbs(ModelGpuData& m);
|
||||
|
||||
// Frustum-cull m's instances (BVH if available, else linear scan),
|
||||
// build the per-mesh DrawElementsIndirectCommand array + flat visible
|
||||
// list, and upload both to m.indirect_buffer / m.visible_ssbo.
|
||||
|
||||
Reference in New Issue
Block a user