From 75bdd2f8c17169422d175ed2d9dfceb9acb0fe77 Mon Sep 17 00:00:00 2001 From: skal Date: Fri, 6 Feb 2026 02:14:30 +0100 Subject: 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. --- assets/final/demo_assets.txt | 3 +- assets/final/shaders/render/scene_query_bvh.wgsl | 55 ++++++ .../final/shaders/render/scene_query_linear.wgsl | 26 +++ assets/final/shaders/renderer_3d.wgsl | 12 +- src/3d/renderer.cc | 195 ++++++++++++++------- src/3d/renderer.h | 23 +-- src/gpu/effects/shader_composer.cc | 33 +++- src/gpu/effects/shader_composer.h | 9 +- src/gpu/effects/shaders.cc | 6 +- src/tests/test_3d_physics.cc | 2 +- src/tests/test_3d_render.cc | 2 +- 11 files changed, 265 insertions(+), 101 deletions(-) create mode 100644 assets/final/shaders/render/scene_query_bvh.wgsl create mode 100644 assets/final/shaders/render/scene_query_linear.wgsl diff --git a/assets/final/demo_assets.txt b/assets/final/demo_assets.txt index 5ba2ec2..c38da51 100644 --- a/assets/final/demo_assets.txt +++ b/assets/final/demo_assets.txt @@ -44,5 +44,6 @@ SHADER_SKYBOX, NONE, shaders/skybox.wgsl, "Skybox background shader" SHADER_MATH_SDF_SHAPES, NONE, shaders/math/sdf_shapes.wgsl, "SDF Shapes Snippet" SHADER_MATH_SDF_UTILS, NONE, shaders/math/sdf_utils.wgsl, "SDF Utils Snippet" SHADER_RENDER_SHADOWS, NONE, shaders/render/shadows.wgsl, "Shadows Snippet" -SHADER_RENDER_SCENE_QUERY, NONE, shaders/render/scene_query.wgsl, "Scene Query Snippet" +SHADER_RENDER_SCENE_QUERY_BVH, NONE, shaders/render/scene_query_bvh.wgsl, "Scene Query Snippet (BVH)" +SHADER_RENDER_SCENE_QUERY_LINEAR, NONE, shaders/render/scene_query_linear.wgsl, "Scene Query Snippet (Linear)" SHADER_RENDER_LIGHTING_UTILS, NONE, shaders/render/lighting_utils.wgsl, "Lighting Utils Snippet" \ No newline at end of file diff --git a/assets/final/shaders/render/scene_query_bvh.wgsl b/assets/final/shaders/render/scene_query_bvh.wgsl new file mode 100644 index 0000000..c7dfdf4 --- /dev/null +++ b/assets/final/shaders/render/scene_query_bvh.wgsl @@ -0,0 +1,55 @@ +#include "math/sdf_shapes" +#include "math/sdf_utils" + +struct BVHNode { + min: vec3, + left_idx: i32, + max: vec3, + obj_idx_or_right: i32, +}; + +@group(0) @binding(2) var bvh_nodes: array; + +fn get_dist(p: vec3, obj_type: f32) -> f32 { + if (obj_type == 1.0) { return length(p) - 1.0; } // Unit Sphere + if (obj_type == 2.0) { return sdBox(p, vec3(1.0)); } // Unit Box + if (obj_type == 3.0) { return sdTorus(p, vec2(1.0, 0.4)); } // Unit Torus + if (obj_type == 4.0) { return sdPlane(p, vec3(0.0, 1.0, 0.0), 0.0); } + return 100.0; +} + +fn map_scene(p: vec3, skip_idx: u32) -> f32 { + var d = 1000.0; + var stack: array; + var stack_ptr = 0; + + if (arrayLength(&bvh_nodes) > 0u) { + stack[stack_ptr] = 0; + stack_ptr++; + } + + while (stack_ptr > 0) { + stack_ptr--; + let node_idx = stack[stack_ptr]; + let node = bvh_nodes[node_idx]; + + if (aabb_sdf(p, node.min, node.max) < d) { + if (node.left_idx < 0) { // Leaf + let obj_idx = u32(node.obj_idx_or_right); + if (obj_idx == skip_idx) { continue; } + let obj = object_data.objects[obj_idx]; + let q = (obj.inv_model * vec4(p, 1.0)).xyz; + let s = min(length(obj.model[0].xyz), min(length(obj.model[1].xyz), length(obj.model[2].xyz))); + d = min(d, get_dist(q, obj.params.x) * s); + } else { // Internal + if (stack_ptr < 31) { + stack[stack_ptr] = node.left_idx; + stack_ptr++; + stack[stack_ptr] = node.obj_idx_or_right; + stack_ptr++; + } + } + } + } + return d; +} diff --git a/assets/final/shaders/render/scene_query_linear.wgsl b/assets/final/shaders/render/scene_query_linear.wgsl new file mode 100644 index 0000000..7bcd96f --- /dev/null +++ b/assets/final/shaders/render/scene_query_linear.wgsl @@ -0,0 +1,26 @@ +#include "math/sdf_shapes" + +fn get_dist(p: vec3, obj_type: f32) -> f32 { + if (obj_type == 1.0) { return length(p) - 1.0; } // Unit Sphere + if (obj_type == 2.0) { return sdBox(p, vec3(1.0)); } // Unit Box + if (obj_type == 3.0) { return sdTorus(p, vec2(1.0, 0.4)); } // Unit Torus + if (obj_type == 4.0) { return sdPlane(p, vec3(0.0, 1.0, 0.0), 0.0); } + return 100.0; +} + +fn map_scene(p: vec3, skip_idx: u32) -> f32 { + var d = 1000.0; + let count = u32(globals.params.x); + + for (var i = 0u; i < count; i = i + 1u) { + if (i == skip_idx) { continue; } + let obj = object_data.objects[i]; + let obj_type = obj.params.x; + if (obj_type <= 0.0) { continue; } + + let q = (obj.inv_model * vec4(p, 1.0)).xyz; + let s = min(length(obj.model[0].xyz), min(length(obj.model[1].xyz), length(obj.model[2].xyz))); + d = min(d, get_dist(q, obj_type) * s); + } + return d; +} diff --git a/assets/final/shaders/renderer_3d.wgsl b/assets/final/shaders/renderer_3d.wgsl index 723aeb0..f855052 100644 --- a/assets/final/shaders/renderer_3d.wgsl +++ b/assets/final/shaders/renderer_3d.wgsl @@ -3,13 +3,7 @@ @group(0) @binding(0) var globals: GlobalUniforms; @group(0) @binding(1) var object_data: ObjectsBuffer; -struct BVHNode { - min: vec3, - left_idx: i32, - max: vec3, - obj_idx_or_right: i32, -}; -@group(0) @binding(2) var bvh_nodes: array; +// Binding 2 is reserved for BVH buffer when enabled @group(0) @binding(3) var noise_tex: texture_2d; @group(0) @binding(4) var noise_sampler: sampler; @@ -65,7 +59,7 @@ fn vs_main(@builtin(vertex_index) vertex_index: u32, return out; } -#include "render/scene_query" +#include "render/scene_query_mode" #include "render/shadows" #include "render/lighting_utils" #include "ray_box" @@ -175,4 +169,4 @@ fn fs_main(in: VertexOutput) -> @location(0) vec4 { let shadow = calc_shadow(p, light_dir, 0.05, 20.0, in.instance_index); let lit_color = calculate_lighting(base_color, normal, p, shadow); return vec4(lit_color, 1.0); -} +} \ No newline at end of file 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 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 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& included) { + std::set& 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& 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& 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; + // 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& 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& included); + std::set& included, + const CompositionMap& substitutions); std::map 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 -- cgit v1.2.3