From e7cd4d65f9f55ccc14045cbcac9d61358ba0c2bf Mon Sep 17 00:00:00 2001 From: skal Date: Tue, 10 Feb 2026 17:27:34 +0100 Subject: refactor: Factor WGPU boilerplate into builder pattern helpers MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add BindGroupLayoutBuilder, BindGroupBuilder, RenderPipelineBuilder, and SamplerCache to reduce repetitive WGPU code. Refactor post_process_helper, cnn_effect, and rotating_cube_effect. Changes: - Bind group creation: 19 instances, 14→4 lines each - Pipeline creation: 30-50→8 lines - Sampler deduplication: 6 instances → cached - Total boilerplate reduction: -122 lines across 3 files Builder pattern prevents binding index errors and consolidates platform-specific #ifdef in fewer locations. Binary size unchanged (6.3M debug). Tests pass. Co-Authored-By: Claude Sonnet 4.5 --- src/gpu/pipeline_builder.h | 109 +++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 109 insertions(+) create mode 100644 src/gpu/pipeline_builder.h (limited to 'src/gpu/pipeline_builder.h') diff --git a/src/gpu/pipeline_builder.h b/src/gpu/pipeline_builder.h new file mode 100644 index 0000000..06b4ceb --- /dev/null +++ b/src/gpu/pipeline_builder.h @@ -0,0 +1,109 @@ +// WGPU render pipeline builder - reduces pipeline creation boilerplate +#pragma once +#include +#include + +// Forward declarations (users must include gpu.h and shader_composer.h) +struct WGPUDeviceImpl; +typedef struct WGPUDeviceImpl* WGPUDevice; +struct WGPUBindGroupLayoutImpl; +typedef struct WGPUBindGroupLayoutImpl* WGPUBindGroupLayout; +struct WGPURenderPipelineImpl; +typedef struct WGPURenderPipelineImpl* WGPURenderPipeline; +struct WGPUShaderModuleImpl; +typedef struct WGPUShaderModuleImpl* WGPUShaderModule; + +#include "platform/platform.h" +#include "gpu/effects/shader_composer.h" + +class RenderPipelineBuilder { + WGPUDevice device_; + WGPURenderPipelineDescriptor desc_{}; + WGPUColorTargetState color_{}; + WGPUBlendState blend_{}; + WGPUDepthStencilState depth_{}; + std::vector layouts_; + std::string shader_text_; + WGPUShaderModule shader_module_ = nullptr; + bool has_blend_ = false; + bool has_depth_ = false; + +public: + explicit RenderPipelineBuilder(WGPUDevice device) : device_(device) { + desc_.primitive.topology = WGPUPrimitiveTopology_TriangleList; + desc_.primitive.cullMode = WGPUCullMode_None; + desc_.multisample.count = 1; + desc_.multisample.mask = 0xFFFFFFFF; + } + + RenderPipelineBuilder& shader(const char* wgsl, bool compose = true) { + shader_text_ = compose ? ShaderComposer::Get().Compose({}, wgsl) : wgsl; + WGPUShaderSourceWGSL wgsl_src{}; + wgsl_src.chain.sType = WGPUSType_ShaderSourceWGSL; + wgsl_src.code = str_view(shader_text_.c_str()); + WGPUShaderModuleDescriptor shader_desc{}; + shader_desc.nextInChain = &wgsl_src.chain; + shader_module_ = wgpuDeviceCreateShaderModule(device_, &shader_desc); + desc_.vertex.module = shader_module_; + desc_.vertex.entryPoint = str_view("vs_main"); + return *this; + } + + RenderPipelineBuilder& bind_group_layout(WGPUBindGroupLayout layout) { + layouts_.push_back(layout); + return *this; + } + + RenderPipelineBuilder& format(WGPUTextureFormat fmt) { + color_.format = fmt; + return *this; + } + + RenderPipelineBuilder& blend_alpha() { + has_blend_ = true; + blend_.color.operation = WGPUBlendOperation_Add; + blend_.color.srcFactor = WGPUBlendFactor_SrcAlpha; + blend_.color.dstFactor = WGPUBlendFactor_OneMinusSrcAlpha; + blend_.alpha.operation = WGPUBlendOperation_Add; + blend_.alpha.srcFactor = WGPUBlendFactor_One; + blend_.alpha.dstFactor = WGPUBlendFactor_OneMinusSrcAlpha; + return *this; + } + + RenderPipelineBuilder& depth(WGPUTextureFormat depth_fmt = WGPUTextureFormat_Depth24Plus) { + has_depth_ = true; + depth_.format = depth_fmt; + depth_.depthWriteEnabled = WGPUOptionalBool_True; + depth_.depthCompare = WGPUCompareFunction_Less; + return *this; + } + + RenderPipelineBuilder& cull_back() { + desc_.primitive.cullMode = WGPUCullMode_Back; + return *this; + } + + WGPURenderPipeline build() { + color_.writeMask = WGPUColorWriteMask_All; + if (has_blend_) color_.blend = &blend_; + + WGPUFragmentState fragment{}; + fragment.module = shader_module_; + fragment.entryPoint = str_view("fs_main"); + fragment.targetCount = 1; + fragment.targets = &color_; + + WGPUPipelineLayoutDescriptor pl_desc{}; + pl_desc.bindGroupLayoutCount = layouts_.size(); + pl_desc.bindGroupLayouts = layouts_.data(); + WGPUPipelineLayout layout = wgpuDeviceCreatePipelineLayout(device_, &pl_desc); + + desc_.layout = layout; + desc_.fragment = &fragment; + if (has_depth_) desc_.depthStencil = &depth_; + + WGPURenderPipeline pipeline = wgpuDeviceCreateRenderPipeline(device_, &desc_); + wgpuPipelineLayoutRelease(layout); + return pipeline; + } +}; -- cgit v1.2.3