mirror of
https://github.com/IfcOpenShell/IfcOpenShell.git
synced 2026-09-22 02:21:02 +00:00
Collapse per-mesh draws into glMultiDrawElementsIndirect
Each visible model now issues a single glMultiDrawElementsIndirect call instead of one glDrawElementsInstancedBaseVertex per mesh. The CPU BVH cull populates an array of DrawElementsIndirectCommand records plus the flat visible-instance list, uploads both, and draws the whole model in one GL call. Vertex shaders switch from a uniform u_instance_offset to gl_BaseInstanceARB (ARB_shader_draw_parameters), so per-draw offset comes from the indirect command's baseInstance field. Draw-call counts for BIM scenes with hundreds of unique meshes drop from hundreds-per-frame to one-per-model, cutting driver overhead. This also sets up the plumbing for the follow-up compute-shader cull that will populate the indirect buffer entirely on-GPU. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -35,6 +35,8 @@ static const size_t INITIAL_EBO_SIZE = 32 * 1024 * 1024; // 32 MB
|
|||||||
static const size_t INITIAL_SSBO_SIZE = 4 * 1024 * 1024; // 4 MB (~52k instances)
|
static const size_t INITIAL_SSBO_SIZE = 4 * 1024 * 1024; // 4 MB (~52k instances)
|
||||||
static const size_t MAX_BUFFER_SIZE = 4ull * 1024 * 1024 * 1024; // 4 GB
|
static const size_t MAX_BUFFER_SIZE = 4ull * 1024 * 1024 * 1024; // 4 GB
|
||||||
|
|
||||||
|
static_assert(sizeof(DrawElementsIndirectCommand) == 20, "indirect cmd must be 20 bytes");
|
||||||
|
|
||||||
// -----------------------------------------------------------------------------
|
// -----------------------------------------------------------------------------
|
||||||
// Shaders
|
// Shaders
|
||||||
// -----------------------------------------------------------------------------
|
// -----------------------------------------------------------------------------
|
||||||
@@ -55,6 +57,7 @@ static const size_t MAX_BUFFER_SIZE = 4ull * 1024 * 1024 * 1024; // 4 GB
|
|||||||
|
|
||||||
static const char* MAIN_VERTEX_SHADER = R"(
|
static const char* MAIN_VERTEX_SHADER = R"(
|
||||||
#version 450 core
|
#version 450 core
|
||||||
|
#extension GL_ARB_shader_draw_parameters : require
|
||||||
layout(location = 0) in vec3 a_position;
|
layout(location = 0) in vec3 a_position;
|
||||||
layout(location = 1) in vec3 a_normal;
|
layout(location = 1) in vec3 a_normal;
|
||||||
layout(location = 2) in vec4 a_color;
|
layout(location = 2) in vec4 a_color;
|
||||||
@@ -74,7 +77,6 @@ layout(std430, binding = 1) readonly buffer VisibleIndices {
|
|||||||
};
|
};
|
||||||
|
|
||||||
uniform mat4 u_view_projection;
|
uniform mat4 u_view_projection;
|
||||||
uniform uint u_instance_offset;
|
|
||||||
uniform uint u_selected_id;
|
uniform uint u_selected_id;
|
||||||
|
|
||||||
out vec3 v_normal;
|
out vec3 v_normal;
|
||||||
@@ -83,7 +85,8 @@ flat out uint v_object_id;
|
|||||||
flat out uint v_selected;
|
flat out uint v_selected;
|
||||||
|
|
||||||
void main() {
|
void main() {
|
||||||
uint iid = visible[u_instance_offset + uint(gl_InstanceID)];
|
uint slot = uint(gl_BaseInstanceARB) + uint(gl_InstanceID);
|
||||||
|
uint iid = visible[slot];
|
||||||
InstanceRecord inst = instances[iid];
|
InstanceRecord inst = instances[iid];
|
||||||
vec4 world = inst.transform * vec4(a_position, 1.0);
|
vec4 world = inst.transform * vec4(a_position, 1.0);
|
||||||
gl_Position = u_view_projection * world;
|
gl_Position = u_view_projection * world;
|
||||||
@@ -132,6 +135,7 @@ void main() {
|
|||||||
|
|
||||||
static const char* PICK_VERTEX_SHADER = R"(
|
static const char* PICK_VERTEX_SHADER = R"(
|
||||||
#version 450 core
|
#version 450 core
|
||||||
|
#extension GL_ARB_shader_draw_parameters : require
|
||||||
layout(location = 0) in vec3 a_position;
|
layout(location = 0) in vec3 a_position;
|
||||||
|
|
||||||
struct InstanceRecord {
|
struct InstanceRecord {
|
||||||
@@ -149,12 +153,12 @@ layout(std430, binding = 1) readonly buffer VisibleIndices {
|
|||||||
};
|
};
|
||||||
|
|
||||||
uniform mat4 u_view_projection;
|
uniform mat4 u_view_projection;
|
||||||
uniform uint u_instance_offset;
|
|
||||||
|
|
||||||
flat out uint v_object_id;
|
flat out uint v_object_id;
|
||||||
|
|
||||||
void main() {
|
void main() {
|
||||||
uint iid = visible[u_instance_offset + uint(gl_InstanceID)];
|
uint slot = uint(gl_BaseInstanceARB) + uint(gl_InstanceID);
|
||||||
|
uint iid = visible[slot];
|
||||||
InstanceRecord inst = instances[iid];
|
InstanceRecord inst = instances[iid];
|
||||||
gl_Position = u_view_projection * inst.transform * vec4(a_position, 1.0);
|
gl_Position = u_view_projection * inst.transform * vec4(a_position, 1.0);
|
||||||
v_object_id = inst.object_id;
|
v_object_id = inst.object_id;
|
||||||
@@ -302,6 +306,7 @@ ViewportWindow::~ViewportWindow() {
|
|||||||
if (m.ebo) gl_->glDeleteBuffers(1, &m.ebo);
|
if (m.ebo) gl_->glDeleteBuffers(1, &m.ebo);
|
||||||
if (m.ssbo) gl_->glDeleteBuffers(1, &m.ssbo);
|
if (m.ssbo) gl_->glDeleteBuffers(1, &m.ssbo);
|
||||||
if (m.visible_ssbo) gl_->glDeleteBuffers(1, &m.visible_ssbo);
|
if (m.visible_ssbo) gl_->glDeleteBuffers(1, &m.visible_ssbo);
|
||||||
|
if (m.indirect_buffer) gl_->glDeleteBuffers(1, &m.indirect_buffer);
|
||||||
}
|
}
|
||||||
if (axis_vao_) gl_->glDeleteVertexArrays(1, &axis_vao_);
|
if (axis_vao_) gl_->glDeleteVertexArrays(1, &axis_vao_);
|
||||||
if (axis_vbo_) gl_->glDeleteBuffers(1, &axis_vbo_);
|
if (axis_vbo_) gl_->glDeleteBuffers(1, &axis_vbo_);
|
||||||
@@ -630,6 +635,7 @@ void ViewportWindow::applyCachedModel(uint32_t model_id, SidecarData data) {
|
|||||||
if (existing->second.ebo) gl_->glDeleteBuffers(1, &existing->second.ebo);
|
if (existing->second.ebo) gl_->glDeleteBuffers(1, &existing->second.ebo);
|
||||||
if (existing->second.ssbo) gl_->glDeleteBuffers(1, &existing->second.ssbo);
|
if (existing->second.ssbo) gl_->glDeleteBuffers(1, &existing->second.ssbo);
|
||||||
if (existing->second.visible_ssbo) gl_->glDeleteBuffers(1, &existing->second.visible_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);
|
||||||
models_gpu_.erase(existing);
|
models_gpu_.erase(existing);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -706,6 +712,7 @@ void ViewportWindow::resetScene() {
|
|||||||
if (m.ebo) gl_->glDeleteBuffers(1, &m.ebo);
|
if (m.ebo) gl_->glDeleteBuffers(1, &m.ebo);
|
||||||
if (m.ssbo) gl_->glDeleteBuffers(1, &m.ssbo);
|
if (m.ssbo) gl_->glDeleteBuffers(1, &m.ssbo);
|
||||||
if (m.visible_ssbo) gl_->glDeleteBuffers(1, &m.visible_ssbo);
|
if (m.visible_ssbo) gl_->glDeleteBuffers(1, &m.visible_ssbo);
|
||||||
|
if (m.indirect_buffer) gl_->glDeleteBuffers(1, &m.indirect_buffer);
|
||||||
}
|
}
|
||||||
models_gpu_.clear();
|
models_gpu_.clear();
|
||||||
selected_object_id_ = 0;
|
selected_object_id_ = 0;
|
||||||
@@ -731,6 +738,7 @@ void ViewportWindow::removeModel(uint32_t model_id) {
|
|||||||
if (it->second.ebo) gl_->glDeleteBuffers(1, &it->second.ebo);
|
if (it->second.ebo) gl_->glDeleteBuffers(1, &it->second.ebo);
|
||||||
if (it->second.ssbo) gl_->glDeleteBuffers(1, &it->second.ssbo);
|
if (it->second.ssbo) gl_->glDeleteBuffers(1, &it->second.ssbo);
|
||||||
if (it->second.visible_ssbo) gl_->glDeleteBuffers(1, &it->second.visible_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);
|
||||||
models_gpu_.erase(it);
|
models_gpu_.erase(it);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -806,26 +814,36 @@ void ViewportWindow::cullAndUploadVisible(ModelGpuData& m, const float planes[6]
|
|||||||
for (uint32_t i = 0; i < m.instances.size(); ++i) test_and_push(i);
|
for (uint32_t i = 0; i < m.instances.size(); ++i) test_and_push(i);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Flatten into visible_flat_ and record per-mesh ranges.
|
// Flatten into visible_flat_ and build one DrawElementsIndirectCommand
|
||||||
|
// per non-empty mesh.
|
||||||
visible_flat_.clear();
|
visible_flat_.clear();
|
||||||
m.mesh_vis_first.assign(m.meshes.size(), 0);
|
indirect_scratch_.clear();
|
||||||
m.mesh_vis_count.assign(m.meshes.size(), 0);
|
|
||||||
for (size_t mi = 0; mi < m.meshes.size(); ++mi) {
|
for (size_t mi = 0; mi < m.meshes.size(); ++mi) {
|
||||||
m.mesh_vis_first[mi] = static_cast<uint32_t>(visible_flat_.size());
|
const auto& mesh = m.meshes[mi];
|
||||||
m.mesh_vis_count[mi] = static_cast<uint32_t>(visible_by_mesh_[mi].size());
|
const uint32_t vis_count = static_cast<uint32_t>(visible_by_mesh_[mi].size());
|
||||||
|
if (vis_count == 0 || mesh.index_count == 0) continue;
|
||||||
|
|
||||||
|
DrawElementsIndirectCommand cmd;
|
||||||
|
cmd.count = mesh.index_count;
|
||||||
|
cmd.instanceCount = vis_count;
|
||||||
|
cmd.firstIndex = mesh.ebo_byte_offset / sizeof(uint32_t);
|
||||||
|
cmd.baseVertex = mesh.vbo_byte_offset / INSTANCED_VERTEX_STRIDE_BYTES;
|
||||||
|
cmd.baseInstance = static_cast<uint32_t>(visible_flat_.size());
|
||||||
|
indirect_scratch_.push_back(cmd);
|
||||||
|
|
||||||
visible_flat_.insert(visible_flat_.end(),
|
visible_flat_.insert(visible_flat_.end(),
|
||||||
visible_by_mesh_[mi].begin(),
|
visible_by_mesh_[mi].begin(),
|
||||||
visible_by_mesh_[mi].end());
|
visible_by_mesh_[mi].end());
|
||||||
}
|
}
|
||||||
|
m.indirect_command_count = static_cast<uint32_t>(indirect_scratch_.size());
|
||||||
|
|
||||||
// Grow/create visible SSBO as needed. Keep at least 4 bytes so the binding
|
// Upload visible list (keep binding alive even when empty).
|
||||||
// is always valid even when nothing is visible.
|
size_t vis_bytes = std::max<size_t>(visible_flat_.size() * sizeof(uint32_t),
|
||||||
size_t bytes = std::max<size_t>(visible_flat_.size() * sizeof(uint32_t),
|
sizeof(uint32_t));
|
||||||
sizeof(uint32_t));
|
if (m.visible_ssbo == 0 || m.visible_ssbo_capacity < vis_bytes) {
|
||||||
if (m.visible_ssbo == 0 || m.visible_ssbo_capacity < bytes) {
|
|
||||||
if (m.visible_ssbo) gl_->glDeleteBuffers(1, &m.visible_ssbo);
|
if (m.visible_ssbo) gl_->glDeleteBuffers(1, &m.visible_ssbo);
|
||||||
size_t new_cap = m.visible_ssbo_capacity ? m.visible_ssbo_capacity : 4096;
|
size_t new_cap = m.visible_ssbo_capacity ? m.visible_ssbo_capacity : 4096;
|
||||||
while (new_cap < bytes) new_cap *= 2;
|
while (new_cap < vis_bytes) new_cap *= 2;
|
||||||
gl_->glCreateBuffers(1, &m.visible_ssbo);
|
gl_->glCreateBuffers(1, &m.visible_ssbo);
|
||||||
gl_->glNamedBufferStorage(m.visible_ssbo, new_cap, nullptr, GL_DYNAMIC_STORAGE_BIT);
|
gl_->glNamedBufferStorage(m.visible_ssbo, new_cap, nullptr, GL_DYNAMIC_STORAGE_BIT);
|
||||||
m.visible_ssbo_capacity = new_cap;
|
m.visible_ssbo_capacity = new_cap;
|
||||||
@@ -834,6 +852,19 @@ void ViewportWindow::cullAndUploadVisible(ModelGpuData& m, const float planes[6]
|
|||||||
gl_->glNamedBufferSubData(m.visible_ssbo, 0,
|
gl_->glNamedBufferSubData(m.visible_ssbo, 0,
|
||||||
visible_flat_.size() * sizeof(uint32_t), visible_flat_.data());
|
visible_flat_.size() * sizeof(uint32_t), visible_flat_.data());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Upload indirect command buffer.
|
||||||
|
size_t ind_bytes = indirect_scratch_.size() * sizeof(DrawElementsIndirectCommand);
|
||||||
|
if (ind_bytes == 0) return;
|
||||||
|
if (m.indirect_buffer == 0 || m.indirect_capacity < ind_bytes) {
|
||||||
|
if (m.indirect_buffer) gl_->glDeleteBuffers(1, &m.indirect_buffer);
|
||||||
|
size_t new_cap = m.indirect_capacity ? m.indirect_capacity : 4096;
|
||||||
|
while (new_cap < ind_bytes) new_cap *= 2;
|
||||||
|
gl_->glCreateBuffers(1, &m.indirect_buffer);
|
||||||
|
gl_->glNamedBufferStorage(m.indirect_buffer, new_cap, nullptr, GL_DYNAMIC_STORAGE_BIT);
|
||||||
|
m.indirect_capacity = new_cap;
|
||||||
|
}
|
||||||
|
gl_->glNamedBufferSubData(m.indirect_buffer, 0, ind_bytes, indirect_scratch_.data());
|
||||||
}
|
}
|
||||||
|
|
||||||
void ViewportWindow::updateCamera() {
|
void ViewportWindow::updateCamera() {
|
||||||
@@ -869,7 +900,6 @@ void ViewportWindow::render() {
|
|||||||
GLint u_vp = gl_->glGetUniformLocation(main_program_, "u_view_projection");
|
GLint u_vp = gl_->glGetUniformLocation(main_program_, "u_view_projection");
|
||||||
GLint u_light = gl_->glGetUniformLocation(main_program_, "u_light_dir");
|
GLint u_light = gl_->glGetUniformLocation(main_program_, "u_light_dir");
|
||||||
GLint u_sel = gl_->glGetUniformLocation(main_program_, "u_selected_id");
|
GLint u_sel = gl_->glGetUniformLocation(main_program_, "u_selected_id");
|
||||||
GLint u_inst_off = gl_->glGetUniformLocation(main_program_, "u_instance_offset");
|
|
||||||
gl_->glUniformMatrix4fv(u_vp, 1, GL_FALSE, vp.constData());
|
gl_->glUniformMatrix4fv(u_vp, 1, GL_FALSE, vp.constData());
|
||||||
gl_->glUniform3f(u_light, 0.3f, 0.5f, 0.8f);
|
gl_->glUniform3f(u_light, 0.3f, 0.5f, 0.8f);
|
||||||
gl_->glUniform1ui(u_sel, selected_object_id_);
|
gl_->glUniform1ui(u_sel, selected_object_id_);
|
||||||
@@ -882,29 +912,23 @@ void ViewportWindow::render() {
|
|||||||
if (m.hidden || !m.ssbo || m.ssbo_instance_count == 0) continue;
|
if (m.hidden || !m.ssbo || m.ssbo_instance_count == 0) continue;
|
||||||
|
|
||||||
cullAndUploadVisible(m, planes);
|
cullAndUploadVisible(m, planes);
|
||||||
if (visible_flat_.empty()) continue;
|
if (m.indirect_command_count == 0) continue;
|
||||||
|
|
||||||
gl_->glBindVertexArray(m.vao);
|
gl_->glBindVertexArray(m.vao);
|
||||||
gl_->glBindBufferBase(GL_SHADER_STORAGE_BUFFER, 0, m.ssbo);
|
gl_->glBindBufferBase(GL_SHADER_STORAGE_BUFFER, 0, m.ssbo);
|
||||||
gl_->glBindBufferBase(GL_SHADER_STORAGE_BUFFER, 1, m.visible_ssbo);
|
gl_->glBindBufferBase(GL_SHADER_STORAGE_BUFFER, 1, m.visible_ssbo);
|
||||||
|
gl_->glBindBuffer(GL_DRAW_INDIRECT_BUFFER, m.indirect_buffer);
|
||||||
|
gl_->glMultiDrawElementsIndirect(
|
||||||
|
GL_TRIANGLES, GL_UNSIGNED_INT, nullptr,
|
||||||
|
static_cast<GLsizei>(m.indirect_command_count), 0);
|
||||||
|
|
||||||
for (size_t mi = 0; mi < m.meshes.size(); ++mi) {
|
for (const auto& cmd : indirect_scratch_) {
|
||||||
const auto& mesh = m.meshes[mi];
|
visible_triangles_ += (cmd.count / 3) * cmd.instanceCount;
|
||||||
uint32_t vis_count = m.mesh_vis_count[mi];
|
visible_objects_ += cmd.instanceCount;
|
||||||
if (vis_count == 0 || mesh.index_count == 0) continue;
|
|
||||||
gl_->glUniform1ui(u_inst_off, m.mesh_vis_first[mi]);
|
|
||||||
gl_->glDrawElementsInstancedBaseVertex(
|
|
||||||
GL_TRIANGLES,
|
|
||||||
static_cast<GLsizei>(mesh.index_count),
|
|
||||||
GL_UNSIGNED_INT,
|
|
||||||
reinterpret_cast<const void*>(static_cast<uintptr_t>(mesh.ebo_byte_offset)),
|
|
||||||
static_cast<GLsizei>(vis_count),
|
|
||||||
static_cast<GLint>(mesh.vbo_byte_offset / INSTANCED_VERTEX_STRIDE_BYTES));
|
|
||||||
visible_triangles_ += (mesh.index_count / 3) * vis_count;
|
|
||||||
visible_objects_ += vis_count;
|
|
||||||
++instanced_draws_;
|
|
||||||
}
|
}
|
||||||
|
instanced_draws_ += m.indirect_command_count;
|
||||||
}
|
}
|
||||||
|
gl_->glBindBuffer(GL_DRAW_INDIRECT_BUFFER, 0);
|
||||||
|
|
||||||
renderAxisGizmo();
|
renderAxisGizmo();
|
||||||
|
|
||||||
@@ -971,33 +995,23 @@ void ViewportWindow::renderPickPass() {
|
|||||||
|
|
||||||
gl_->glUseProgram(pick_program_);
|
gl_->glUseProgram(pick_program_);
|
||||||
GLint u_vp = gl_->glGetUniformLocation(pick_program_, "u_view_projection");
|
GLint u_vp = gl_->glGetUniformLocation(pick_program_, "u_view_projection");
|
||||||
GLint u_inst_off = gl_->glGetUniformLocation(pick_program_, "u_instance_offset");
|
|
||||||
gl_->glUniformMatrix4fv(u_vp, 1, GL_FALSE, vp.constData());
|
gl_->glUniformMatrix4fv(u_vp, 1, GL_FALSE, vp.constData());
|
||||||
|
|
||||||
for (auto& [model_id, m] : models_gpu_) {
|
for (auto& [model_id, m] : models_gpu_) {
|
||||||
if (m.hidden || !m.ssbo || m.ssbo_instance_count == 0) continue;
|
if (m.hidden || !m.ssbo || m.ssbo_instance_count == 0) continue;
|
||||||
|
|
||||||
cullAndUploadVisible(m, planes);
|
cullAndUploadVisible(m, planes);
|
||||||
if (visible_flat_.empty()) continue;
|
if (m.indirect_command_count == 0) continue;
|
||||||
|
|
||||||
gl_->glBindVertexArray(m.vao);
|
gl_->glBindVertexArray(m.vao);
|
||||||
gl_->glBindBufferBase(GL_SHADER_STORAGE_BUFFER, 0, m.ssbo);
|
gl_->glBindBufferBase(GL_SHADER_STORAGE_BUFFER, 0, m.ssbo);
|
||||||
gl_->glBindBufferBase(GL_SHADER_STORAGE_BUFFER, 1, m.visible_ssbo);
|
gl_->glBindBufferBase(GL_SHADER_STORAGE_BUFFER, 1, m.visible_ssbo);
|
||||||
|
gl_->glBindBuffer(GL_DRAW_INDIRECT_BUFFER, m.indirect_buffer);
|
||||||
for (size_t mi = 0; mi < m.meshes.size(); ++mi) {
|
gl_->glMultiDrawElementsIndirect(
|
||||||
const auto& mesh = m.meshes[mi];
|
GL_TRIANGLES, GL_UNSIGNED_INT, nullptr,
|
||||||
uint32_t vis_count = m.mesh_vis_count[mi];
|
static_cast<GLsizei>(m.indirect_command_count), 0);
|
||||||
if (vis_count == 0 || mesh.index_count == 0) continue;
|
|
||||||
gl_->glUniform1ui(u_inst_off, m.mesh_vis_first[mi]);
|
|
||||||
gl_->glDrawElementsInstancedBaseVertex(
|
|
||||||
GL_TRIANGLES,
|
|
||||||
static_cast<GLsizei>(mesh.index_count),
|
|
||||||
GL_UNSIGNED_INT,
|
|
||||||
reinterpret_cast<const void*>(static_cast<uintptr_t>(mesh.ebo_byte_offset)),
|
|
||||||
static_cast<GLsizei>(vis_count),
|
|
||||||
static_cast<GLint>(mesh.vbo_byte_offset / INSTANCED_VERTEX_STRIDE_BYTES));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
gl_->glBindBuffer(GL_DRAW_INDIRECT_BUFFER, 0);
|
||||||
gl_->glBindFramebuffer(GL_FRAMEBUFFER, 0);
|
gl_->glBindFramebuffer(GL_FRAMEBUFFER, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -38,6 +38,15 @@
|
|||||||
#include "InstancedGeometry.h"
|
#include "InstancedGeometry.h"
|
||||||
#include "SidecarCache.h"
|
#include "SidecarCache.h"
|
||||||
|
|
||||||
|
// Matches GL_DRAW_INDIRECT_BUFFER layout for glMultiDrawElementsIndirect.
|
||||||
|
struct DrawElementsIndirectCommand {
|
||||||
|
uint32_t count;
|
||||||
|
uint32_t instanceCount;
|
||||||
|
uint32_t firstIndex;
|
||||||
|
uint32_t baseVertex;
|
||||||
|
uint32_t baseInstance;
|
||||||
|
};
|
||||||
|
|
||||||
// Per-model GPU state for the instanced render path.
|
// Per-model GPU state for the instanced render path.
|
||||||
//
|
//
|
||||||
// VBO: local-coord interleaved verts (pos3 + normal3 + color1_packed) — 28 B.
|
// VBO: local-coord interleaved verts (pos3 + normal3 + color1_packed) — 28 B.
|
||||||
@@ -71,13 +80,15 @@ struct ModelGpuData {
|
|||||||
ModelBvh bvh;
|
ModelBvh bvh;
|
||||||
|
|
||||||
// Dynamic visible-instance index buffer (std430, binding = 1).
|
// Dynamic visible-instance index buffer (std430, binding = 1).
|
||||||
// Re-uploaded each frame from frame_visible_scratch_.
|
// Re-uploaded each frame from visible_flat_.
|
||||||
GLuint visible_ssbo = 0;
|
GLuint visible_ssbo = 0;
|
||||||
size_t visible_ssbo_capacity = 0; // bytes
|
size_t visible_ssbo_capacity = 0; // bytes
|
||||||
|
|
||||||
// Per-mesh visible-list offset/count, rebuilt each frame.
|
// GL_DRAW_INDIRECT_BUFFER of DrawElementsIndirectCommand[], one per
|
||||||
std::vector<uint32_t> mesh_vis_first;
|
// non-empty mesh. Re-uploaded each frame.
|
||||||
std::vector<uint32_t> mesh_vis_count;
|
GLuint indirect_buffer = 0;
|
||||||
|
size_t indirect_capacity = 0; // bytes
|
||||||
|
uint32_t indirect_command_count = 0; // valid commands this frame
|
||||||
|
|
||||||
bool finalized = false;
|
bool finalized = false;
|
||||||
bool hidden = false;
|
bool hidden = false;
|
||||||
@@ -152,8 +163,9 @@ private:
|
|||||||
bool growModelSsbo(ModelGpuData& m, size_t needed_total);
|
bool growModelSsbo(ModelGpuData& m, size_t needed_total);
|
||||||
ModelGpuData& getOrCreateModel(uint32_t model_id);
|
ModelGpuData& getOrCreateModel(uint32_t model_id);
|
||||||
|
|
||||||
// Populate m.mesh_vis_first / mesh_vis_count and upload visible indices
|
// Frustum-cull m's instances (BVH if available, else linear scan),
|
||||||
// to m.visible_ssbo. Uses BVH when available, else linear scan.
|
// build the per-mesh DrawElementsIndirectCommand array + flat visible
|
||||||
|
// list, and upload both to m.indirect_buffer / m.visible_ssbo.
|
||||||
void cullAndUploadVisible(ModelGpuData& m, const float planes[6][4]);
|
void cullAndUploadVisible(ModelGpuData& m, const float planes[6][4]);
|
||||||
|
|
||||||
// Mouse interaction
|
// Mouse interaction
|
||||||
@@ -194,9 +206,12 @@ private:
|
|||||||
|
|
||||||
// Reused scratch: visible-instance index lists per mesh, flattened into
|
// Reused scratch: visible-instance index lists per mesh, flattened into
|
||||||
// `visible_flat_` for upload. Both live in the parent object to avoid
|
// `visible_flat_` for upload. Both live in the parent object to avoid
|
||||||
// per-frame allocation.
|
// per-frame allocation. indirect_scratch_ is the matching array of
|
||||||
std::vector<std::vector<uint32_t>> visible_by_mesh_;
|
// DrawElementsIndirectCommand records — forward-declared as bytes so
|
||||||
std::vector<uint32_t> visible_flat_;
|
// the header doesn't need the struct definition.
|
||||||
|
std::vector<std::vector<uint32_t>> visible_by_mesh_;
|
||||||
|
std::vector<uint32_t> visible_flat_;
|
||||||
|
std::vector<DrawElementsIndirectCommand> indirect_scratch_;
|
||||||
|
|
||||||
// Camera
|
// Camera
|
||||||
QVector3D camera_target_{0, 0, 0};
|
QVector3D camera_target_{0, 0, 0};
|
||||||
|
|||||||
Reference in New Issue
Block a user