diff options
| author | skal <pascal.massimino@gmail.com> | 2026-02-06 02:14:30 +0100 |
|---|---|---|
| committer | skal <pascal.massimino@gmail.com> | 2026-02-06 02:14:30 +0100 |
| commit | 75bdd2f8c17169422d175ed2d9dfceb9acb0fe77 (patch) | |
| tree | 9a08a9cc1527c36d10cad71b34ab8ffa64c9cef0 /src/3d/renderer.cc | |
| parent | ae4b03ef6f5ef07dcc80affd6877d17fceee7d29 (diff) | |
refactor(gpu): Implement compile-time BVH toggle via shader composition
Completed Task #18-B optimization and refactoring.
- Replaced runtime branching in shader with compile-time snippet substitution in ShaderComposer.
- Added 'scene_query_bvh.wgsl' and 'scene_query_linear.wgsl' as distinct snippets.
- Refactored Renderer3D to manage two separate pipelines (with and without BVH).
- Updated ShaderComposer to support snippet substitution during composition.
- Verified both paths with test_3d_render (default and --no-bvh).
- Removed temporary shader hacks and cleaned up renderer_3d.wgsl.
Diffstat (limited to 'src/3d/renderer.cc')
| -rw-r--r-- | src/3d/renderer.cc | 195 |
1 files changed, 133 insertions, 62 deletions
diff --git a/src/3d/renderer.cc b/src/3d/renderer.cc index cf1a067..9dfc1f8 100644 --- a/src/3d/renderer.cc +++ b/src/3d/renderer.cc @@ -12,7 +12,6 @@ #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, @@ -134,6 +133,8 @@ void Renderer3D::shutdown() { wgpuSamplerRelease(default_sampler_); if (pipeline_) wgpuRenderPipelineRelease(pipeline_); + if (pipeline_no_bvh_) + wgpuRenderPipelineRelease(pipeline_no_bvh_); if (bind_group_) wgpuBindGroupRelease(bind_group_); if (skybox_pipeline_) @@ -214,45 +215,75 @@ void Renderer3D::add_debug_aabb(const vec3& min, const vec3& max, } void Renderer3D::create_pipeline() { - WGPUBindGroupLayoutEntry entries[6] = {}; + pipeline_ = create_pipeline_impl(true); // BVH enabled + pipeline_no_bvh_ = create_pipeline_impl(false); // BVH disabled +} + +WGPURenderPipeline Renderer3D::create_pipeline_impl(bool use_bvh) { + std::vector<WGPUBindGroupLayoutEntry> entries; + // 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); + { + WGPUBindGroupLayoutEntry e = {}; + e.binding = 0; + e.visibility = WGPUShaderStage_Vertex | WGPUShaderStage_Fragment; + e.buffer.type = WGPUBufferBindingType_Uniform; + e.buffer.minBindingSize = sizeof(GlobalUniforms); + entries.push_back(e); + } // 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; + { + WGPUBindGroupLayoutEntry e = {}; + e.binding = 1; + e.visibility = WGPUShaderStage_Vertex | WGPUShaderStage_Fragment; + e.buffer.type = WGPUBufferBindingType_ReadOnlyStorage; + e.buffer.minBindingSize = sizeof(ObjectData) * kMaxObjects; + entries.push_back(e); + } - // Binding 2: BVH Nodes - entries[2].binding = 2; - entries[2].visibility = WGPUShaderStage_Fragment; - entries[2].buffer.type = WGPUBufferBindingType_ReadOnlyStorage; - entries[2].buffer.minBindingSize = sizeof(BVHNode) * kMaxObjects * 2; + // Binding 2: BVH Nodes (only if BVH is used) + if (use_bvh) { + WGPUBindGroupLayoutEntry e = {}; + e.binding = 2; + e.visibility = WGPUShaderStage_Fragment; + e.buffer.type = WGPUBufferBindingType_ReadOnlyStorage; + e.buffer.minBindingSize = sizeof(BVHNode) * kMaxObjects * 2; + entries.push_back(e); + } // Binding 3: Noise Texture - entries[3].binding = 3; - entries[3].visibility = WGPUShaderStage_Fragment; - entries[3].texture.sampleType = WGPUTextureSampleType_Float; - entries[3].texture.viewDimension = WGPUTextureViewDimension_2D; + { + WGPUBindGroupLayoutEntry e = {}; + e.binding = 3; + e.visibility = WGPUShaderStage_Fragment; + e.texture.sampleType = WGPUTextureSampleType_Float; + e.texture.viewDimension = WGPUTextureViewDimension_2D; + entries.push_back(e); + } // Binding 4: Default Sampler - entries[4].binding = 4; - entries[4].visibility = WGPUShaderStage_Fragment; - entries[4].sampler.type = WGPUSamplerBindingType_Filtering; + { + WGPUBindGroupLayoutEntry e = {}; + e.binding = 4; + e.visibility = WGPUShaderStage_Fragment; + e.sampler.type = WGPUSamplerBindingType_Filtering; + entries.push_back(e); + } // 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; + { + WGPUBindGroupLayoutEntry e = {}; + e.binding = 5; + e.visibility = WGPUShaderStage_Fragment; + e.texture.sampleType = WGPUTextureSampleType_Float; + e.texture.viewDimension = WGPUTextureViewDimension_2D; + entries.push_back(e); + } WGPUBindGroupLayoutDescriptor bgl_desc = {}; - bgl_desc.entryCount = 6; - bgl_desc.entries = entries; + bgl_desc.entryCount = (uint32_t)entries.size(); + bgl_desc.entries = entries.data(); WGPUBindGroupLayout bgl = wgpuDeviceCreateBindGroupLayout(device_, &bgl_desc); WGPUPipelineLayoutDescriptor pl_desc = {}; @@ -264,7 +295,18 @@ void Renderer3D::create_pipeline() { const char* asset_data = (const char*)GetAsset(AssetId::ASSET_SHADER_RENDERER_3D); std::string main_code = asset_data; - std::string shader_source = ShaderComposer::Get().Compose({}, main_code); + + // Use ShaderComposer to dynamically include the correct scene_query snippet + ShaderComposer::CompositionMap composition_map; + if (use_bvh) { + composition_map["render/scene_query_mode"] = "render/scene_query_bvh"; + } else { + composition_map["render/scene_query_mode"] = "render/scene_query_linear"; + } + std::string shader_source = ShaderComposer::Get().Compose({}, main_code, composition_map); + + // DEBUG: Print shader source to check for include errors + std::cout << "Shader Source (BVH=" << use_bvh << "):" << std::endl << shader_source << std::endl; #if defined(DEMO_CROSS_COMPILE_WIN32) WGPUShaderModuleWGSLDescriptor wgsl_desc = {}; @@ -315,10 +357,12 @@ void Renderer3D::create_pipeline() { desc.multisample.count = 1; desc.multisample.mask = 0xFFFFFFFF; - pipeline_ = wgpuDeviceCreateRenderPipeline(device_, &desc); + WGPURenderPipeline pipeline = wgpuDeviceCreateRenderPipeline(device_, &desc); wgpuBindGroupLayoutRelease(bgl); wgpuPipelineLayoutRelease(pipeline_layout); wgpuShaderModuleRelease(shader_module); + + return pipeline; } void Renderer3D::update_uniforms(const Scene& scene, const Camera& camera, @@ -329,8 +373,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()), - s_bvh_enabled_ ? 1.0f : 0.0f, 0.0f, 0.0f); + vec4((float)std::min((size_t)kMaxObjects, scene.objects.size()), 0.0f, + 0.0f, 0.0f); globals.resolution = vec2((float)width_, (float)height_); globals.padding = vec2(0.0f, 0.0f); @@ -365,14 +409,11 @@ void Renderer3D::update_uniforms(const Scene& scene, const Camera& camera, 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)); - } + // Build and upload BVH (always uploaded, used by BVH pipeline) + 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)); } } @@ -385,40 +426,70 @@ void Renderer3D::draw(WGPURenderPassEncoder pass, const Scene& scene, if (bind_group_) wgpuBindGroupRelease(bind_group_); - WGPUBindGroupEntry bg_entries[6] = {}; + std::vector<WGPUBindGroupEntry> bg_entries; + + { + WGPUBindGroupEntry e = {}; + e.binding = 0; + e.buffer = global_uniform_buffer_; + e.size = sizeof(GlobalUniforms); + bg_entries.push_back(e); + } - bg_entries[0].binding = 0; - bg_entries[0].buffer = global_uniform_buffer_; - bg_entries[0].size = sizeof(GlobalUniforms); + { + WGPUBindGroupEntry e = {}; + e.binding = 1; + e.buffer = object_storage_buffer_; + e.size = sizeof(ObjectData) * kMaxObjects; + bg_entries.push_back(e); + } - bg_entries[1].binding = 1; - bg_entries[1].buffer = object_storage_buffer_; - bg_entries[1].size = sizeof(ObjectData) * kMaxObjects; + if (bvh_enabled_) { + WGPUBindGroupEntry e = {}; + e.binding = 2; + e.buffer = bvh_storage_buffer_; + e.size = sizeof(BVHNode) * kMaxObjects * 2; + bg_entries.push_back(e); + } - bg_entries[2].binding = 2; - bg_entries[2].buffer = bvh_storage_buffer_; - bg_entries[2].size = sizeof(BVHNode) * kMaxObjects * 2; + { + WGPUBindGroupEntry e = {}; + e.binding = 3; + e.textureView = noise_texture_view_; + bg_entries.push_back(e); + } - bg_entries[3].binding = 3; - bg_entries[3].textureView = noise_texture_view_; + { + WGPUBindGroupEntry e = {}; + e.binding = 4; + e.sampler = default_sampler_; + bg_entries.push_back(e); + } - bg_entries[4].binding = 4; - bg_entries[4].sampler = default_sampler_; + { + WGPUBindGroupEntry e = {}; + e.binding = 5; + e.textureView = + sky_texture_view_ ? sky_texture_view_ : noise_texture_view_; + bg_entries.push_back(e); + } - bg_entries[5].binding = 5; - bg_entries[5].textureView = - sky_texture_view_ ? sky_texture_view_ : noise_texture_view_; // Fallback + // Select the correct pipeline and bind group layout + WGPURenderPipeline current_pipeline = + bvh_enabled_ ? pipeline_ : pipeline_no_bvh_; + WGPUBindGroupLayout current_layout = + wgpuRenderPipelineGetBindGroupLayout(current_pipeline, 0); WGPUBindGroupDescriptor bg_desc = {}; - bg_desc.layout = wgpuRenderPipelineGetBindGroupLayout(pipeline_, 0); - bg_desc.entryCount = 6; - bg_desc.entries = bg_entries; + bg_desc.layout = current_layout; + bg_desc.entryCount = (uint32_t)bg_entries.size(); + bg_desc.entries = bg_entries.data(); bind_group_ = wgpuDeviceCreateBindGroup(device_, &bg_desc); - wgpuBindGroupLayoutRelease(bg_desc.layout); + wgpuBindGroupLayoutRelease(current_layout); - wgpuRenderPassEncoderSetPipeline(pass, pipeline_); + wgpuRenderPassEncoderSetPipeline(pass, current_pipeline); wgpuRenderPassEncoderSetBindGroup(pass, 0, bind_group_, 0, nullptr); |
