// 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; } };