From 16c2cdce6ad1d89d3c537f2c2cff743449925125 Mon Sep 17 00:00:00 2001 From: skal Date: Sun, 8 Feb 2026 08:33:55 +0100 Subject: feat(platform): Centralize platform-specific WebGPU code and improve shader composition --- src/3d/renderer.cc | 250 ++++++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 249 insertions(+), 1 deletion(-) (limited to 'src/3d/renderer.cc') diff --git a/src/3d/renderer.cc b/src/3d/renderer.cc index e08a0d0..0bc8812 100644 --- a/src/3d/renderer.cc +++ b/src/3d/renderer.cc @@ -5,6 +5,7 @@ #include "generated/assets.h" #include "gpu/effects/shader_composer.h" #include "util/asset_manager.h" +#include "util/mesh_utils.h" #include #include #include @@ -124,7 +125,254 @@ void Renderer3D::create_default_resources() { WGPUBufferUsage_Storage | WGPUBufferUsage_CopyDst, nullptr) .buffer; } +void Renderer3D::create_pipeline() { + // Shader Modules + std::string sdf_shader_source = + ShaderComposer::Get().Compose(std::vector{}, AssetId::ASSET_SHADER_RENDER_SCENE_QUERY_BVH); + WGPUShaderModuleDescriptor sdf_shader_desc = + platform_create_shader_module_descriptor(sdf_shader_source.c_str()); + WGPUShaderModule sdf_shader_module = + wgpuDeviceCreateShaderModule(device_, &sdf_shader_desc); + + std::string sdf_no_bvh_shader_source = + ShaderComposer::Get().Compose(std::vector{}, AssetId::ASSET_SHADER_RENDER_SCENE_QUERY_LINEAR); + WGPUShaderModuleDescriptor sdf_no_bvh_shader_desc = + platform_create_shader_module_descriptor( + sdf_no_bvh_shader_source.c_str()); + WGPUShaderModule sdf_no_bvh_shader_module = + wgpuDeviceCreateShaderModule(device_, &sdf_no_bvh_shader_desc); + + // Mesh Shader Module + std::string mesh_shader_source = + ShaderComposer::Get().Compose(std::vector{}, AssetId::ASSET_SHADER_RENDER_SHADOWS); // Use SHADOWS shader + WGPUShaderModuleDescriptor mesh_shader_desc = + platform_create_shader_module_descriptor(mesh_shader_source.c_str()); + WGPUShaderModule mesh_shader_module = + wgpuDeviceCreateShaderModule(device_, &mesh_shader_desc); + + // Bind Group Layouts and Pipelines (SDF) + std::vector bgl_entries; + // Uniforms + bgl_entries.push_back({.binding = 0, + .visibility = WGPUShaderStage_Vertex | + WGPUShaderStage_Fragment, + .buffer = {.type = WGPUBufferBindingType_Uniform, + .hasDynamicOffset = false, + .minBindingSize = sizeof(GlobalUniforms)}}); + // Object Storage + bgl_entries.push_back( + {.binding = 1, + .visibility = WGPUShaderStage_Vertex | WGPUShaderStage_Fragment, + .buffer = {.type = WGPUBufferBindingType_ReadOnlyStorage, + .hasDynamicOffset = false, + .minBindingSize = sizeof(ObjectData) * kMaxObjects}}); + + // BVH Storage (Conditional for BVH pipeline) + bgl_entries.push_back( + {.binding = 2, + .visibility = WGPUShaderStage_Vertex | WGPUShaderStage_Fragment, + .buffer = {.type = WGPUBufferBindingType_ReadOnlyStorage, + .hasDynamicOffset = false, + .minBindingSize = sizeof(BVHNode) * kMaxObjects * 2}}); + + // Noise Texture + bgl_entries.push_back({.binding = 3, + .visibility = WGPUShaderStage_Fragment, + .texture = {.sampleType = WGPUTextureSampleType_Float, + .viewDimension = WGPUTextureViewDimension_2D, + .multisampled = false}}); + // Default Sampler + bgl_entries.push_back({.binding = 4, + .visibility = WGPUShaderStage_Fragment, + .sampler = {.type = WGPUSamplerBindingType_Filtering}}); + // Skybox Texture + bgl_entries.push_back({.binding = 5, + .visibility = WGPUShaderStage_Fragment, + .texture = {.sampleType = WGPUTextureSampleType_Float, + .viewDimension = WGPUTextureViewDimension_2D, + .multisampled = false}}); + + WGPUBindGroupLayoutDescriptor bgl_desc = {}; + bgl_desc.entryCount = (uint32_t)bgl_entries.size(); + bgl_desc.entries = bgl_entries.data(); + + WGPUBindGroupLayout bind_group_layout = + wgpuDeviceCreateBindGroupLayout(device_, &bgl_desc); + + WGPUPipelineLayoutDescriptor pl_desc = {}; + pl_desc.bindGroupLayoutCount = 1; + pl_desc.bindGroupLayouts = &bind_group_layout; + WGPUPipelineLayout pipeline_layout = + wgpuDeviceCreatePipelineLayout(device_, &pl_desc); + + // Depth Stencil State + WGPUDepthStencilState depth_stencil = {}; + depth_stencil.format = WGPUTextureFormat_Depth24Plus; + depth_stencil.depthWriteEnabled = WGPUOptionalBool_True; + depth_stencil.depthCompare = WGPUCompareFunction_Less; + + // Configure common color target state + WGPUColorTargetState color_target = {}; + color_target.format = format_; + color_target.writeMask = WGPUColorWriteMask_All; + // Alpha blending for meshes/particles, but not SDFs + color_target.blend = nullptr; // Explicitly set to nullptr + + WGPUFragmentState fragment_state = {}; + fragment_state.module = sdf_shader_module; + fragment_state.entryPoint = str_view("fs_main"); + fragment_state.targetCount = 1; + fragment_state.targets = &color_target; + + // Create SDF pipeline (with BVH) + WGPURenderPipelineDescriptor pipeline_desc = {}; + pipeline_desc.layout = pipeline_layout; + pipeline_desc.vertex.module = sdf_shader_module; + pipeline_desc.vertex.entryPoint = str_view("vs_main"); + pipeline_desc.primitive.topology = WGPUPrimitiveTopology_TriangleList; + pipeline_desc.multisample.count = 1; + pipeline_desc.multisample.mask = 0xFFFFFFFF; + pipeline_desc.fragment = &fragment_state; + PLATFORM_SET_PIPELINE_DEPTH_STENCIL(pipeline_desc, &depth_stencil); + pipeline_ = wgpuDeviceCreateRenderPipeline(device_, &pipeline_desc); + + // Create SDF pipeline (without BVH) + fragment_state.module = sdf_no_bvh_shader_module; // Use linear shader + WGPURenderPipelineDescriptor pipeline_no_bvh_desc = pipeline_desc; + pipeline_no_bvh_desc.vertex.module = sdf_no_bvh_shader_module; + pipeline_no_bvh_desc.fragment = &fragment_state; + PLATFORM_SET_PIPELINE_DEPTH_STENCIL(pipeline_no_bvh_desc, &depth_stencil); + pipeline_no_bvh_ = wgpuDeviceCreateRenderPipeline(device_, &pipeline_no_bvh_desc); + + // Configure blend state for mesh/particle pipeline + WGPUBlendState mesh_blend_state = {}; + mesh_blend_state.color.srcFactor = WGPUBlendFactor_SrcAlpha; + mesh_blend_state.color.dstFactor = WGPUBlendFactor_OneMinusSrcAlpha; + mesh_blend_state.color.operation = WGPUBlendOperation_Add; + mesh_blend_state.alpha.srcFactor = WGPUBlendFactor_One; + mesh_blend_state.alpha.dstFactor = WGPUBlendFactor_OneMinusSrcAlpha; + mesh_blend_state.alpha.operation = WGPUBlendOperation_Add; + color_target.blend = &mesh_blend_state; // Enable blending for meshes + + // Create Mesh pipeline + WGPURenderPipelineDescriptor mesh_pipeline_desc = {}; + mesh_pipeline_desc.layout = pipeline_layout; + mesh_pipeline_desc.vertex.module = mesh_shader_module; + mesh_pipeline_desc.vertex.entryPoint = str_view("vs_main"); + mesh_pipeline_desc.primitive.topology = WGPUPrimitiveTopology_TriangleList; + mesh_pipeline_desc.multisample.count = 1; + mesh_pipeline_desc.multisample.mask = 0xFFFFFFFF; + mesh_pipeline_desc.fragment = &fragment_state; // Reuse fragment state + // Override fragment module to mesh_shader_module + fragment_state.module = mesh_shader_module; + mesh_pipeline_desc.fragment = &fragment_state; + PLATFORM_SET_PIPELINE_DEPTH_STENCIL(mesh_pipeline_desc, &depth_stencil); + + // Vertex attributes for mesh + WGPUVertexAttribute vert_attrs[3] = {}; + // Position + vert_attrs[0].shaderLocation = 0; + vert_attrs[0].offset = 0; + vert_attrs[0].format = WGPUVertexFormat_Float32x3; + // Normal + vert_attrs[1].shaderLocation = 1; + vert_attrs[1].offset = sizeof(vec3); + vert_attrs[1].format = WGPUVertexFormat_Float32x3; + // UV + vert_attrs[2].shaderLocation = 2; + vert_attrs[2].offset = sizeof(vec3) * 2; + vert_attrs[2].format = WGPUVertexFormat_Float32x2; + + WGPUVertexBufferLayout vert_buffer_layout = {}; + vert_buffer_layout.arrayStride = sizeof(MeshVertex); + vert_buffer_layout.stepMode = WGPUVertexStepMode_Vertex; + vert_buffer_layout.attributeCount = 3; + vert_buffer_layout.attributes = vert_attrs; + + mesh_pipeline_desc.vertex.bufferCount = 1; + mesh_pipeline_desc.vertex.buffers = &vert_buffer_layout; + + mesh_pipeline_ = wgpuDeviceCreateRenderPipeline(device_, &mesh_pipeline_desc); + + // Release shader modules as they are now part of the pipelines + wgpuShaderModuleRelease(sdf_shader_module); + wgpuShaderModuleRelease(sdf_no_bvh_shader_module); + wgpuShaderModuleRelease(mesh_shader_module); + wgpuPipelineLayoutRelease(pipeline_layout); + wgpuBindGroupLayoutRelease(bind_group_layout); +} +void Renderer3D::create_skybox_pipeline() { + std::string skybox_shader_source = + ShaderComposer::Get().Compose(std::vector{}, AssetId::ASSET_SHADER_RENDER_SHADOWS); // Use SHADOWS shader + WGPUShaderModuleDescriptor skybox_shader_desc = + platform_create_shader_module_descriptor(skybox_shader_source.c_str()); + WGPUShaderModule skybox_shader_module = + wgpuDeviceCreateShaderModule(device_, &skybox_shader_desc); + + WGPUBindGroupLayoutEntry bgl_entries[3] = {}; + // Skybox Texture + bgl_entries[0].binding = 0; + bgl_entries[0].visibility = WGPUShaderStage_Fragment; + bgl_entries[0].texture = {.sampleType = WGPUTextureSampleType_Float, + .viewDimension = WGPUTextureViewDimension_2D, + .multisampled = false}; + // Sampler + bgl_entries[1].binding = 1; + bgl_entries[1].visibility = WGPUShaderStage_Fragment; + bgl_entries[1].sampler = {.type = WGPUSamplerBindingType_Filtering}; + // Global Uniforms + bgl_entries[2].binding = 2; + bgl_entries[2].visibility = WGPUShaderStage_Vertex; + bgl_entries[2].buffer = {.type = WGPUBufferBindingType_Uniform, + .hasDynamicOffset = false, + .minBindingSize = sizeof(GlobalUniforms)}; + + WGPUBindGroupLayoutDescriptor bgl_desc = {}; + bgl_desc.entryCount = 3; + bgl_desc.entries = bgl_entries; + WGPUBindGroupLayout bind_group_layout = + wgpuDeviceCreateBindGroupLayout(device_, &bgl_desc); + + WGPUPipelineLayoutDescriptor pl_desc = {}; + pl_desc.bindGroupLayoutCount = 1; + pl_desc.bindGroupLayouts = &bind_group_layout; + WGPUPipelineLayout pipeline_layout = + wgpuDeviceCreatePipelineLayout(device_, &pl_desc); + + WGPUColorTargetState color_target = {}; + color_target.format = format_; + color_target.writeMask = WGPUColorWriteMask_All; + color_target.blend = nullptr; // No blending for skybox + + WGPUFragmentState fragment_state = {}; + fragment_state.module = skybox_shader_module; + fragment_state.entryPoint = str_view("fs_main"); + fragment_state.targetCount = 1; + fragment_state.targets = &color_target; + + WGPURenderPipelineDescriptor pipeline_desc = {}; + pipeline_desc.layout = pipeline_layout; + pipeline_desc.vertex.module = skybox_shader_module; + pipeline_desc.vertex.entryPoint = str_view("vs_main"); + pipeline_desc.primitive.topology = WGPUPrimitiveTopology_TriangleList; + pipeline_desc.multisample.count = 1; + pipeline_desc.multisample.mask = 0xFFFFFFFF; + pipeline_desc.fragment = &fragment_state; + + // Skybox depth state (clear and write depth for objects) + WGPUDepthStencilState depth_stencil = {}; + depth_stencil.format = WGPUTextureFormat_Depth24Plus; + depth_stencil.depthWriteEnabled = WGPUOptionalBool_False; + depth_stencil.depthCompare = WGPUCompareFunction_Always; + PLATFORM_SET_PIPELINE_DEPTH_STENCIL(pipeline_desc, &depth_stencil); + + skybox_pipeline_ = wgpuDeviceCreateRenderPipeline(device_, &pipeline_desc); + + wgpuShaderModuleRelease(skybox_shader_module); + wgpuPipelineLayoutRelease(pipeline_layout); + wgpuBindGroupLayoutRelease(bind_group_layout); +} void Renderer3D::set_noise_texture(WGPUTextureView noise_view) { noise_texture_view_ = noise_view; } @@ -450,4 +698,4 @@ void Renderer3D::render(const Scene& scene, const Camera& camera, float time, wgpuCommandBufferRelease(commands); wgpuCommandEncoderRelease(encoder); -} \ No newline at end of file +} -- cgit v1.2.3