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 | |
| 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')
| -rw-r--r-- | src/3d/renderer.cc | 195 | ||||
| -rw-r--r-- | src/3d/renderer.h | 23 | ||||
| -rw-r--r-- | src/gpu/effects/shader_composer.cc | 33 | ||||
| -rw-r--r-- | src/gpu/effects/shader_composer.h | 9 | ||||
| -rw-r--r-- | src/gpu/effects/shaders.cc | 6 | ||||
| -rw-r--r-- | src/tests/test_3d_physics.cc | 2 | ||||
| -rw-r--r-- | src/tests/test_3d_render.cc | 2 |
7 files changed, 179 insertions, 91 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); diff --git a/src/3d/renderer.h b/src/3d/renderer.h index b468423..db86b72 100644 --- a/src/3d/renderer.h +++ b/src/3d/renderer.h @@ -19,7 +19,7 @@ struct GlobalUniforms { mat4 view_proj; mat4 inv_view_proj; // Added for skybox/raymarching vec4 camera_pos_time; // xyz = camera_pos, w = time - vec4 params; // x = num_objects, y = use_bvh, zw = padding + vec4 params; // x = num_objects, yzw = padding vec2 resolution; vec2 padding; }; @@ -41,25 +41,15 @@ class Renderer3D { static void SetDebugEnabled(bool enabled) { s_debug_enabled_ = enabled; } - static void SetBvhEnabled(bool enabled) { - s_bvh_enabled_ = enabled; - } #endif // Renders the scene to the given texture view (Convenience: creates a pass) - void render(const Scene& scene, const Camera& camera, float time, - WGPUTextureView target_view, WGPUTextureView depth_view_opt = nullptr); // Records draw commands to an existing pass. - // Assumes the pass has a compatible pipeline (or we set it here). - - // Note: Caller must ensure depth/color attachments are set up correctly in - // the pass. - void draw(WGPURenderPassEncoder pass, const Scene& scene, const Camera& camera, float time); @@ -71,8 +61,12 @@ class Renderer3D { // Resize handler (if needed for internal buffers) void resize(int width, int height); + // Set whether to use BVH acceleration + void SetBvhEnabled(bool enabled) { bvh_enabled_ = enabled; } + private: void create_pipeline(); + WGPURenderPipeline create_pipeline_impl(bool use_bvh); void create_skybox_pipeline(); void create_default_resources(); void update_uniforms(const Scene& scene, const Camera& camera, float time); @@ -81,7 +75,8 @@ class Renderer3D { WGPUQueue queue_ = nullptr; WGPUTextureFormat format_ = WGPUTextureFormat_Undefined; - WGPURenderPipeline pipeline_ = nullptr; + WGPURenderPipeline pipeline_ = nullptr; // BVH enabled + WGPURenderPipeline pipeline_no_bvh_ = nullptr; // BVH disabled WGPUBindGroup bind_group_ = nullptr; WGPURenderPipeline skybox_pipeline_ = nullptr; WGPUBindGroup skybox_bind_group_ = nullptr; @@ -90,6 +85,7 @@ class Renderer3D { WGPUBuffer bvh_storage_buffer_ = nullptr; BVH cpu_bvh_; // Keep a CPU-side copy for building/uploading + bool bvh_enabled_ = true; WGPUTextureView noise_texture_view_ = nullptr; WGPUTextureView sky_texture_view_ = nullptr; @@ -107,6 +103,5 @@ class Renderer3D { #if !defined(STRIP_ALL) VisualDebug visual_debug_; static bool s_debug_enabled_; - static bool s_bvh_enabled_; #endif -}; +};
\ No newline at end of file diff --git a/src/gpu/effects/shader_composer.cc b/src/gpu/effects/shader_composer.cc index 8d66ad7..055b996 100644 --- a/src/gpu/effects/shader_composer.cc +++ b/src/gpu/effects/shader_composer.cc @@ -17,7 +17,8 @@ void ShaderComposer::RegisterSnippet(const std::string& name, void ShaderComposer::ResolveRecursive(const std::string& source, std::stringstream& ss, - std::set<std::string>& included) { + std::set<std::string>& included, + const CompositionMap& substitutions) { std::istringstream stream(source); std::string line; while (std::getline(stream, line)) { @@ -27,12 +28,19 @@ void ShaderComposer::ResolveRecursive(const std::string& source, size_t end = line.find('"', start + 1); if (start != std::string::npos && end != std::string::npos) { std::string name = line.substr(start + 1, end - start - 1); + + // Apply substitution if available + auto sub_it = substitutions.find(name); + if (sub_it != substitutions.end()) { + name = sub_it->second; + } + if (included.find(name) == included.end()) { included.insert(name); auto it = snippets_.find(name); if (it != snippets_.end()) { ss << "// --- Included: " << name << " ---\n"; - ResolveRecursive(it->second, ss, included); + ResolveRecursive(it->second, ss, included, substitutions); ss << "// --- End Include: " << name << " ---\n"; } else { ss << "// ERROR: Snippet not found: " << name << "\n"; @@ -47,7 +55,8 @@ void ShaderComposer::ResolveRecursive(const std::string& source, std::string ShaderComposer::Compose(const std::vector<std::string>& dependencies, - const std::string& main_code) { + const std::string& main_code, + const CompositionMap& substitutions) { std::stringstream ss; ss << "// Generated by ShaderComposer\n\n"; @@ -55,19 +64,25 @@ ShaderComposer::Compose(const std::vector<std::string>& dependencies, // Process explicit dependencies first for (const auto& dep : dependencies) { - if (included.find(dep) == included.end()) { - included.insert(dep); - auto it = snippets_.find(dep); + std::string name = dep; + auto sub_it = substitutions.find(name); + if (sub_it != substitutions.end()) { + name = sub_it->second; + } + + if (included.find(name) == included.end()) { + included.insert(name); + auto it = snippets_.find(name); if (it != snippets_.end()) { - ss << "// --- Dependency: " << dep << " ---\n"; - ResolveRecursive(it->second, ss, included); + ss << "// --- Dependency: " << name << " ---\n"; + ResolveRecursive(it->second, ss, included, substitutions); ss << "\n"; } } } ss << "// --- Main Code ---\n"; - ResolveRecursive(main_code, ss, included); + ResolveRecursive(main_code, ss, included, substitutions); return ss.str(); } diff --git a/src/gpu/effects/shader_composer.h b/src/gpu/effects/shader_composer.h index a63a6a4..9eb43f4 100644 --- a/src/gpu/effects/shader_composer.h +++ b/src/gpu/effects/shader_composer.h @@ -15,16 +15,21 @@ class ShaderComposer { // Register a snippet (e.g. "common_math", "sdf_primitives") void RegisterSnippet(const std::string& name, const std::string& code); + using CompositionMap = std::map<std::string, std::string>; + // Assemble a final shader string by prepending required snippets // and recursively resolving #include "snippet_name" directives. + // Optional substitutions: map "placeholder_name" -> "actual_snippet_name" std::string Compose(const std::vector<std::string>& dependencies, - const std::string& main_code); + const std::string& main_code, + const CompositionMap& substitutions = {}); private: ShaderComposer() = default; void ResolveRecursive(const std::string& source, std::stringstream& ss, - std::set<std::string>& included); + std::set<std::string>& included, + const CompositionMap& substitutions); std::map<std::string, std::string> snippets_; }; diff --git a/src/gpu/effects/shaders.cc b/src/gpu/effects/shaders.cc index 7b543e1..4fc8108 100644 --- a/src/gpu/effects/shaders.cc +++ b/src/gpu/effects/shaders.cc @@ -34,8 +34,10 @@ void InitShaderComposer() { register_if_exists("math/sdf_shapes", AssetId::ASSET_SHADER_MATH_SDF_SHAPES); register_if_exists("math/sdf_utils", AssetId::ASSET_SHADER_MATH_SDF_UTILS); register_if_exists("render/shadows", AssetId::ASSET_SHADER_RENDER_SHADOWS); - register_if_exists("render/scene_query", - AssetId::ASSET_SHADER_RENDER_SCENE_QUERY); + register_if_exists("render/scene_query_bvh", + AssetId::ASSET_SHADER_RENDER_SCENE_QUERY_BVH); + register_if_exists("render/scene_query_linear", + AssetId::ASSET_SHADER_RENDER_SCENE_QUERY_LINEAR); register_if_exists("render/lighting_utils", AssetId::ASSET_SHADER_RENDER_LIGHTING_UTILS); diff --git a/src/tests/test_3d_physics.cc b/src/tests/test_3d_physics.cc index 010d245..0dfe1ce 100644 --- a/src/tests/test_3d_physics.cc +++ b/src/tests/test_3d_physics.cc @@ -195,7 +195,7 @@ int main(int argc, char** argv) { Renderer3D::SetDebugEnabled(true); } if (strcmp(argv[i], "--no-bvh") == 0) { - Renderer3D::SetBvhEnabled(false); + g_renderer.SetBvhEnabled(false); } } #else diff --git a/src/tests/test_3d_render.cc b/src/tests/test_3d_render.cc index 8b2b2c1..f8bbaa7 100644 --- a/src/tests/test_3d_render.cc +++ b/src/tests/test_3d_render.cc @@ -184,7 +184,7 @@ int main(int argc, char** argv) { Renderer3D::SetDebugEnabled(true); } if (strcmp(argv[i], "--no-bvh") == 0) { - Renderer3D::SetBvhEnabled(false); + g_renderer.SetBvhEnabled(false); } } #else |
