summaryrefslogtreecommitdiff
path: root/src/3d/renderer.cc
diff options
context:
space:
mode:
authorskal <pascal.massimino@gmail.com>2026-02-06 01:38:51 +0100
committerskal <pascal.massimino@gmail.com>2026-02-06 01:38:51 +0100
commitae4b03ef6f5ef07dcc80affd6877d17fceee7d29 (patch)
treeab57453a320c90c76eeda3ca291e382ec6413a86 /src/3d/renderer.cc
parent3581fa88300f0208c83f0f687b18479979dad035 (diff)
feat(perf): Add toggle for GPU BVH and fix fallback
Completed Task #18-B. - Implemented GPU-side BVH traversal for scene queries, improving performance. - Added a --no-bvh command-line flag to disable the feature for debugging and performance comparison. - Fixed a shader compilation issue where the non-BVH fallback path failed to render objects.
Diffstat (limited to 'src/3d/renderer.cc')
-rw-r--r--src/3d/renderer.cc60
1 files changed, 46 insertions, 14 deletions
diff --git a/src/3d/renderer.cc b/src/3d/renderer.cc
index 684fda9..cf1a067 100644
--- a/src/3d/renderer.cc
+++ b/src/3d/renderer.cc
@@ -12,6 +12,7 @@
#if !defined(STRIP_ALL)
bool Renderer3D::s_debug_enabled_ = false;
+bool Renderer3D::s_bvh_enabled_ = true; // Enabled by default
#endif
void Renderer3D::init(WGPUDevice device, WGPUQueue queue,
@@ -143,6 +144,8 @@ void Renderer3D::shutdown() {
wgpuBufferRelease(global_uniform_buffer_);
if (object_storage_buffer_)
wgpuBufferRelease(object_storage_buffer_);
+ if (bvh_storage_buffer_)
+ wgpuBufferRelease(bvh_storage_buffer_);
if (depth_view_)
wgpuTextureViewRelease(depth_view_);
if (depth_texture_)
@@ -189,6 +192,10 @@ void Renderer3D::create_default_resources() {
WGPUBufferUsage_Storage | WGPUBufferUsage_CopyDst,
nullptr)
.buffer;
+ bvh_storage_buffer_ = gpu_create_buffer(
+ device_, sizeof(BVHNode) * kMaxObjects * 2, // Capacity for a full tree
+ WGPUBufferUsage_Storage | WGPUBufferUsage_CopyDst, nullptr)
+ .buffer;
}
void Renderer3D::set_noise_texture(WGPUTextureView noise_view) {
@@ -207,33 +214,44 @@ void Renderer3D::add_debug_aabb(const vec3& min, const vec3& max,
}
void Renderer3D::create_pipeline() {
- WGPUBindGroupLayoutEntry entries[5] = {};
+ WGPUBindGroupLayoutEntry entries[6] = {};
+ // Binding 0: Global Uniforms
entries[0].binding = 0;
entries[0].visibility = WGPUShaderStage_Vertex | WGPUShaderStage_Fragment;
entries[0].buffer.type = WGPUBufferBindingType_Uniform;
entries[0].buffer.minBindingSize = sizeof(GlobalUniforms);
+ // Binding 1: Object Data
entries[1].binding = 1;
entries[1].visibility = WGPUShaderStage_Vertex | WGPUShaderStage_Fragment;
entries[1].buffer.type = WGPUBufferBindingType_ReadOnlyStorage;
entries[1].buffer.minBindingSize = sizeof(ObjectData) * kMaxObjects;
+ // Binding 2: BVH Nodes
entries[2].binding = 2;
entries[2].visibility = WGPUShaderStage_Fragment;
- entries[2].texture.sampleType = WGPUTextureSampleType_Float;
- entries[2].texture.viewDimension = WGPUTextureViewDimension_2D;
+ entries[2].buffer.type = WGPUBufferBindingType_ReadOnlyStorage;
+ entries[2].buffer.minBindingSize = sizeof(BVHNode) * kMaxObjects * 2;
+ // Binding 3: Noise Texture
entries[3].binding = 3;
entries[3].visibility = WGPUShaderStage_Fragment;
- entries[3].sampler.type = WGPUSamplerBindingType_Filtering;
+ entries[3].texture.sampleType = WGPUTextureSampleType_Float;
+ entries[3].texture.viewDimension = WGPUTextureViewDimension_2D;
+ // Binding 4: Default Sampler
entries[4].binding = 4;
entries[4].visibility = WGPUShaderStage_Fragment;
- entries[4].texture.sampleType = WGPUTextureSampleType_Float;
- entries[4].texture.viewDimension = WGPUTextureViewDimension_2D;
+ entries[4].sampler.type = WGPUSamplerBindingType_Filtering;
+
+ // Binding 5: Sky Texture
+ entries[5].binding = 5;
+ entries[5].visibility = WGPUShaderStage_Fragment;
+ entries[5].texture.sampleType = WGPUTextureSampleType_Float;
+ entries[5].texture.viewDimension = WGPUTextureViewDimension_2D;
WGPUBindGroupLayoutDescriptor bgl_desc = {};
- bgl_desc.entryCount = 5;
+ bgl_desc.entryCount = 6;
bgl_desc.entries = entries;
WGPUBindGroupLayout bgl = wgpuDeviceCreateBindGroupLayout(device_, &bgl_desc);
@@ -311,8 +329,8 @@ void Renderer3D::update_uniforms(const Scene& scene, const Camera& camera,
globals.camera_pos_time =
vec4(camera.position.x, camera.position.y, camera.position.z, time);
globals.params =
- vec4((float)std::min((size_t)kMaxObjects, scene.objects.size()), 0.0f,
- 0.0f, 0.0f);
+ vec4((float)std::min((size_t)kMaxObjects, scene.objects.size()),
+ s_bvh_enabled_ ? 1.0f : 0.0f, 0.0f, 0.0f);
globals.resolution = vec2((float)width_, (float)height_);
globals.padding = vec2(0.0f, 0.0f);
@@ -346,6 +364,16 @@ void Renderer3D::update_uniforms(const Scene& scene, const Camera& camera,
wgpuQueueWriteBuffer(queue_, object_storage_buffer_, 0, obj_data.data(),
obj_data.size() * sizeof(ObjectData));
}
+
+ // Build and upload BVH if enabled
+ if (s_bvh_enabled_) {
+ BVHBuilder::build(cpu_bvh_, scene.objects);
+ if (!cpu_bvh_.nodes.empty()) {
+ wgpuQueueWriteBuffer(queue_, bvh_storage_buffer_, 0,
+ cpu_bvh_.nodes.data(),
+ cpu_bvh_.nodes.size() * sizeof(BVHNode));
+ }
+ }
}
void Renderer3D::draw(WGPURenderPassEncoder pass, const Scene& scene,
@@ -357,7 +385,7 @@ void Renderer3D::draw(WGPURenderPassEncoder pass, const Scene& scene,
if (bind_group_)
wgpuBindGroupRelease(bind_group_);
- WGPUBindGroupEntry bg_entries[5] = {};
+ WGPUBindGroupEntry bg_entries[6] = {};
bg_entries[0].binding = 0;
bg_entries[0].buffer = global_uniform_buffer_;
@@ -368,18 +396,22 @@ void Renderer3D::draw(WGPURenderPassEncoder pass, const Scene& scene,
bg_entries[1].size = sizeof(ObjectData) * kMaxObjects;
bg_entries[2].binding = 2;
- bg_entries[2].textureView = noise_texture_view_;
+ bg_entries[2].buffer = bvh_storage_buffer_;
+ bg_entries[2].size = sizeof(BVHNode) * kMaxObjects * 2;
bg_entries[3].binding = 3;
- bg_entries[3].sampler = default_sampler_;
+ bg_entries[3].textureView = noise_texture_view_;
bg_entries[4].binding = 4;
- bg_entries[4].textureView =
+ bg_entries[4].sampler = default_sampler_;
+
+ bg_entries[5].binding = 5;
+ bg_entries[5].textureView =
sky_texture_view_ ? sky_texture_view_ : noise_texture_view_; // Fallback
WGPUBindGroupDescriptor bg_desc = {};
bg_desc.layout = wgpuRenderPipelineGetBindGroupLayout(pipeline_, 0);
- bg_desc.entryCount = 5;
+ bg_desc.entryCount = 6;
bg_desc.entries = bg_entries;
bind_group_ = wgpuDeviceCreateBindGroup(device_, &bg_desc);