diff options
| -rw-r--r-- | src/gpu/bind_group_builder.h | 111 | ||||
| -rw-r--r-- | src/gpu/effects/cnn_effect.cc | 106 | ||||
| -rw-r--r-- | src/gpu/effects/post_process_helper.cc | 102 | ||||
| -rw-r--r-- | src/gpu/effects/rotating_cube_effect.cc | 23 | ||||
| -rw-r--r-- | src/gpu/pipeline_builder.h | 109 | ||||
| -rw-r--r-- | src/gpu/sampler_cache.h | 61 |
6 files changed, 337 insertions, 175 deletions
diff --git a/src/gpu/bind_group_builder.h b/src/gpu/bind_group_builder.h new file mode 100644 index 0000000..d63f6e2 --- /dev/null +++ b/src/gpu/bind_group_builder.h @@ -0,0 +1,111 @@ +// WGPU bind group builder - reduces boilerplate for bind group creation +#pragma once +#include <vector> + +// Forward declarations (users must include gpu.h) +struct WGPUBindGroupLayoutEntry; +struct WGPUBindGroupEntry; +struct WGPUDeviceImpl; +typedef struct WGPUDeviceImpl* WGPUDevice; +struct WGPUBindGroupLayoutImpl; +typedef struct WGPUBindGroupLayoutImpl* WGPUBindGroupLayout; +struct WGPUBindGroupImpl; +typedef struct WGPUBindGroupImpl* WGPUBindGroup; +struct WGPUBufferImpl; +typedef struct WGPUBufferImpl* WGPUBuffer; +struct WGPUTextureViewImpl; +typedef struct WGPUTextureViewImpl* WGPUTextureView; +struct WGPUSamplerImpl; +typedef struct WGPUSamplerImpl* WGPUSampler; +typedef uint32_t WGPUShaderStageFlags; + +#include "platform/platform.h" + +class BindGroupLayoutBuilder { + std::vector<WGPUBindGroupLayoutEntry> entries_; + +public: + BindGroupLayoutBuilder& uniform(uint32_t binding, WGPUShaderStageFlags vis, size_t min_size = 0) { + WGPUBindGroupLayoutEntry e{}; + e.binding = binding; + e.visibility = vis; + e.buffer.type = WGPUBufferBindingType_Uniform; + if (min_size) e.buffer.minBindingSize = min_size; + entries_.push_back(e); + return *this; + } + + BindGroupLayoutBuilder& storage(uint32_t binding, WGPUShaderStageFlags vis, size_t min_size = 0) { + WGPUBindGroupLayoutEntry e{}; + e.binding = binding; + e.visibility = vis; + e.buffer.type = WGPUBufferBindingType_ReadOnlyStorage; + if (min_size) e.buffer.minBindingSize = min_size; + entries_.push_back(e); + return *this; + } + + BindGroupLayoutBuilder& texture(uint32_t binding, WGPUShaderStageFlags vis) { + WGPUBindGroupLayoutEntry e{}; + e.binding = binding; + e.visibility = vis; + e.texture.sampleType = WGPUTextureSampleType_Float; + e.texture.viewDimension = WGPUTextureViewDimension_2D; + entries_.push_back(e); + return *this; + } + + BindGroupLayoutBuilder& sampler(uint32_t binding, WGPUShaderStageFlags vis) { + WGPUBindGroupLayoutEntry e{}; + e.binding = binding; + e.visibility = vis; + e.sampler.type = WGPUSamplerBindingType_Filtering; + entries_.push_back(e); + return *this; + } + + WGPUBindGroupLayout build(WGPUDevice device) { + WGPUBindGroupLayoutDescriptor desc{}; + desc.entryCount = entries_.size(); + desc.entries = entries_.data(); + return wgpuDeviceCreateBindGroupLayout(device, &desc); + } +}; + +class BindGroupBuilder { + std::vector<WGPUBindGroupEntry> entries_; + +public: + BindGroupBuilder& buffer(uint32_t binding, WGPUBuffer buf, size_t size) { + WGPUBindGroupEntry e{}; + e.binding = binding; + e.buffer = buf; + e.size = size; + entries_.push_back(e); + return *this; + } + + BindGroupBuilder& texture(uint32_t binding, WGPUTextureView view) { + WGPUBindGroupEntry e{}; + e.binding = binding; + e.textureView = view; + entries_.push_back(e); + return *this; + } + + BindGroupBuilder& sampler(uint32_t binding, WGPUSampler samp) { + WGPUBindGroupEntry e{}; + e.binding = binding; + e.sampler = samp; + entries_.push_back(e); + return *this; + } + + WGPUBindGroup build(WGPUDevice device, WGPUBindGroupLayout layout) { + WGPUBindGroupDescriptor desc{}; + desc.layout = layout; + desc.entryCount = entries_.size(); + desc.entries = entries_.data(); + return wgpuDeviceCreateBindGroup(device, &desc); + } +}; diff --git a/src/gpu/effects/cnn_effect.cc b/src/gpu/effects/cnn_effect.cc index 7107bea..d74187c 100644 --- a/src/gpu/effects/cnn_effect.cc +++ b/src/gpu/effects/cnn_effect.cc @@ -6,70 +6,30 @@ #include "gpu/effects/shaders.h" #include "gpu/effects/shader_composer.h" #include "gpu/effect.h" +#include "gpu/bind_group_builder.h" +#include "gpu/sampler_cache.h" +#include "gpu/pipeline_builder.h" // Create custom pipeline with 5 bindings (includes original texture) static WGPURenderPipeline create_cnn_pipeline(WGPUDevice device, WGPUTextureFormat format, const char* shader_code) { - std::string composed_shader = ShaderComposer::Get().Compose({}, shader_code); + WGPUBindGroupLayout bgl = BindGroupLayoutBuilder() + .sampler(0, WGPUShaderStage_Fragment) + .texture(1, WGPUShaderStage_Fragment) + .uniform(2, WGPUShaderStage_Vertex | WGPUShaderStage_Fragment) + .uniform(3, WGPUShaderStage_Fragment) + .texture(4, WGPUShaderStage_Fragment) + .build(device); - WGPUShaderModuleDescriptor shader_desc = {}; - WGPUShaderSourceWGSL wgsl_src = {}; - wgsl_src.chain.sType = WGPUSType_ShaderSourceWGSL; - wgsl_src.code = str_view(composed_shader.c_str()); - shader_desc.nextInChain = &wgsl_src.chain; - WGPUShaderModule shader_module = - wgpuDeviceCreateShaderModule(device, &shader_desc); + WGPURenderPipeline pipeline = RenderPipelineBuilder(device) + .shader(shader_code) + .bind_group_layout(bgl) + .format(format) + .build(); - WGPUBindGroupLayoutEntry bgl_entries[5] = {}; - bgl_entries[0].binding = 0; // sampler - bgl_entries[0].visibility = WGPUShaderStage_Fragment; - bgl_entries[0].sampler.type = WGPUSamplerBindingType_Filtering; - bgl_entries[1].binding = 1; // input texture - bgl_entries[1].visibility = WGPUShaderStage_Fragment; - bgl_entries[1].texture.sampleType = WGPUTextureSampleType_Float; - bgl_entries[1].texture.viewDimension = WGPUTextureViewDimension_2D; - bgl_entries[2].binding = 2; // uniforms - bgl_entries[2].visibility = WGPUShaderStage_Vertex | WGPUShaderStage_Fragment; - bgl_entries[2].buffer.type = WGPUBufferBindingType_Uniform; - bgl_entries[3].binding = 3; // effect params - bgl_entries[3].visibility = WGPUShaderStage_Fragment; - bgl_entries[3].buffer.type = WGPUBufferBindingType_Uniform; - bgl_entries[4].binding = 4; // original texture - bgl_entries[4].visibility = WGPUShaderStage_Fragment; - bgl_entries[4].texture.sampleType = WGPUTextureSampleType_Float; - bgl_entries[4].texture.viewDimension = WGPUTextureViewDimension_2D; - - WGPUBindGroupLayoutDescriptor bgl_desc = {}; - bgl_desc.entryCount = 5; - bgl_desc.entries = bgl_entries; - WGPUBindGroupLayout bgl = wgpuDeviceCreateBindGroupLayout(device, &bgl_desc); - - WGPUPipelineLayoutDescriptor pl_desc = {}; - pl_desc.bindGroupLayoutCount = 1; - pl_desc.bindGroupLayouts = &bgl; - WGPUPipelineLayout pl = wgpuDeviceCreatePipelineLayout(device, &pl_desc); - - WGPUColorTargetState color_target = {}; - color_target.format = format; - color_target.writeMask = WGPUColorWriteMask_All; - - WGPUFragmentState fragment_state = {}; - fragment_state.module = shader_module; - fragment_state.entryPoint = str_view("fs_main"); - fragment_state.targetCount = 1; - fragment_state.targets = &color_target; - - WGPURenderPipelineDescriptor pipeline_desc = {}; - pipeline_desc.layout = pl; - pipeline_desc.vertex.module = shader_module; - pipeline_desc.vertex.entryPoint = str_view("vs_main"); - pipeline_desc.fragment = &fragment_state; - pipeline_desc.primitive.topology = WGPUPrimitiveTopology_TriangleList; - pipeline_desc.multisample.count = 1; - pipeline_desc.multisample.mask = 0xFFFFFFFF; - - return wgpuDeviceCreateRenderPipeline(device, &pipeline_desc); + wgpuBindGroupLayoutRelease(bgl); + return pipeline; } CNNEffect::CNNEffect(const GpuContext& ctx) @@ -137,29 +97,13 @@ void CNNEffect::update_bind_group(WGPUTextureView input_view) { wgpuBindGroupRelease(bind_group_); WGPUBindGroupLayout bgl = wgpuRenderPipelineGetBindGroupLayout(pipeline_, 0); - WGPUSamplerDescriptor sd = {}; - sd.magFilter = WGPUFilterMode_Linear; - sd.minFilter = WGPUFilterMode_Linear; - sd.maxAnisotropy = 1; - WGPUSampler sampler = wgpuDeviceCreateSampler(ctx_.device, &sd); - - WGPUBindGroupEntry bge[5] = {}; - bge[0].binding = 0; - bge[0].sampler = sampler; - bge[1].binding = 1; - bge[1].textureView = input_view_; - bge[2].binding = 2; - bge[2].buffer = uniforms_.get().buffer; - bge[2].size = uniforms_.get().size; - bge[3].binding = 3; - bge[3].buffer = params_buffer_.get().buffer; - bge[3].size = params_buffer_.get().size; - bge[4].binding = 4; - bge[4].textureView = original_view_ ? original_view_ : input_view_; + WGPUSampler sampler = SamplerCache::Get().get_or_create(ctx_.device, SamplerCache::linear()); - WGPUBindGroupDescriptor bgd = {}; - bgd.layout = bgl; - bgd.entryCount = 5; - bgd.entries = bge; - bind_group_ = wgpuDeviceCreateBindGroup(ctx_.device, &bgd); + bind_group_ = BindGroupBuilder() + .sampler(0, sampler) + .texture(1, input_view_) + .buffer(2, uniforms_.get().buffer, uniforms_.get().size) + .buffer(3, params_buffer_.get().buffer, params_buffer_.get().size) + .texture(4, original_view_ ? original_view_ : input_view_) + .build(ctx_.device, bgl); } diff --git a/src/gpu/effects/post_process_helper.cc b/src/gpu/effects/post_process_helper.cc index e99467f..0c339c7 100644 --- a/src/gpu/effects/post_process_helper.cc +++ b/src/gpu/effects/post_process_helper.cc @@ -5,69 +5,30 @@ #include "../demo_effects.h" #include "gpu/gpu.h" #include "gpu/effects/shader_composer.h" +#include "gpu/bind_group_builder.h" +#include "gpu/sampler_cache.h" +#include "gpu/pipeline_builder.h" #include <cstring> // Helper to create a standard post-processing pipeline WGPURenderPipeline create_post_process_pipeline(WGPUDevice device, WGPUTextureFormat format, const char* shader_code) { - std::string composed_shader = ShaderComposer::Get().Compose({}, shader_code); + WGPUBindGroupLayout bgl = BindGroupLayoutBuilder() + .sampler(PP_BINDING_SAMPLER, WGPUShaderStage_Fragment) + .texture(PP_BINDING_TEXTURE, WGPUShaderStage_Fragment) + .uniform(PP_BINDING_UNIFORMS, WGPUShaderStage_Vertex | WGPUShaderStage_Fragment) + .uniform(PP_BINDING_EFFECT_PARAMS, WGPUShaderStage_Fragment) + .build(device); - WGPUShaderModuleDescriptor shader_desc = {}; - WGPUShaderSourceWGSL wgsl_src = {}; - wgsl_src.chain.sType = WGPUSType_ShaderSourceWGSL; - wgsl_src.code = str_view(composed_shader.c_str()); - shader_desc.nextInChain = &wgsl_src.chain; - WGPUShaderModule shader_module = - wgpuDeviceCreateShaderModule(device, &shader_desc); + WGPURenderPipeline pipeline = RenderPipelineBuilder(device) + .shader(shader_code) + .bind_group_layout(bgl) + .format(format) + .build(); - WGPUBindGroupLayoutEntry bgl_entries[4] = {}; - bgl_entries[0].binding = PP_BINDING_SAMPLER; - bgl_entries[0].visibility = WGPUShaderStage_Fragment; - bgl_entries[0].sampler.type = WGPUSamplerBindingType_Filtering; - bgl_entries[1].binding = PP_BINDING_TEXTURE; - bgl_entries[1].visibility = WGPUShaderStage_Fragment; - bgl_entries[1].texture.sampleType = WGPUTextureSampleType_Float; - bgl_entries[1].texture.viewDimension = WGPUTextureViewDimension_2D; - bgl_entries[2].binding = PP_BINDING_UNIFORMS; - bgl_entries[2].visibility = WGPUShaderStage_Vertex | WGPUShaderStage_Fragment; - bgl_entries[2].buffer.type = WGPUBufferBindingType_Uniform; - - // Add an entry for effect-specific parameters - bgl_entries[3].binding = PP_BINDING_EFFECT_PARAMS; - bgl_entries[3].visibility = WGPUShaderStage_Fragment; - bgl_entries[3].buffer.type = WGPUBufferBindingType_Uniform; - - WGPUBindGroupLayoutDescriptor bgl_desc = {}; - bgl_desc.entryCount = 4; - bgl_desc.entries = bgl_entries; - WGPUBindGroupLayout bgl = wgpuDeviceCreateBindGroupLayout(device, &bgl_desc); - - WGPUPipelineLayoutDescriptor pl_desc = {}; - pl_desc.bindGroupLayoutCount = 1; - pl_desc.bindGroupLayouts = &bgl; - WGPUPipelineLayout pl = wgpuDeviceCreatePipelineLayout(device, &pl_desc); - - WGPUColorTargetState color_target = {}; - color_target.format = format; - color_target.writeMask = WGPUColorWriteMask_All; - - WGPUFragmentState fragment_state = {}; - fragment_state.module = shader_module; - fragment_state.entryPoint = str_view("fs_main"); - fragment_state.targetCount = 1; - fragment_state.targets = &color_target; - - WGPURenderPipelineDescriptor pipeline_desc = {}; - pipeline_desc.layout = pl; - pipeline_desc.vertex.module = shader_module; - pipeline_desc.vertex.entryPoint = str_view("vs_main"); - pipeline_desc.fragment = &fragment_state; - pipeline_desc.primitive.topology = WGPUPrimitiveTopology_TriangleList; - pipeline_desc.multisample.count = 1; - pipeline_desc.multisample.mask = 0xFFFFFFFF; - - return wgpuDeviceCreateRenderPipeline(device, &pipeline_desc); + wgpuBindGroupLayoutRelease(bgl); + return pipeline; } // --- PostProcess Implementation Helper --- @@ -82,25 +43,16 @@ void pp_update_bind_group(WGPUDevice device, WGPURenderPipeline pipeline, if (*bind_group) wgpuBindGroupRelease(*bind_group); + WGPUBindGroupLayout bgl = wgpuRenderPipelineGetBindGroupLayout(pipeline, 0); - WGPUSamplerDescriptor sd = {}; - sd.magFilter = WGPUFilterMode_Linear; - sd.minFilter = WGPUFilterMode_Linear; - sd.maxAnisotropy = 1; - WGPUSampler sampler = wgpuDeviceCreateSampler(device, &sd); - WGPUBindGroupEntry bge[4] = {}; - bge[0].binding = PP_BINDING_SAMPLER; - bge[0].sampler = sampler; - bge[1].binding = PP_BINDING_TEXTURE; - bge[1].textureView = input_view; - bge[2].binding = PP_BINDING_UNIFORMS; - bge[2].buffer = uniforms.buffer; - bge[2].size = uniforms.size; - bge[3].binding = PP_BINDING_EFFECT_PARAMS; - bge[3].buffer = - effect_params.buffer ? effect_params.buffer : g_dummy_buffer.buffer; - bge[3].size = effect_params.buffer ? effect_params.size : g_dummy_buffer.size; - WGPUBindGroupDescriptor bgd = { - .layout = bgl, .entryCount = 4, .entries = bge}; - *bind_group = wgpuDeviceCreateBindGroup(device, &bgd); + WGPUSampler sampler = SamplerCache::Get().get_or_create(device, SamplerCache::linear()); + + *bind_group = BindGroupBuilder() + .sampler(PP_BINDING_SAMPLER, sampler) + .texture(PP_BINDING_TEXTURE, input_view) + .buffer(PP_BINDING_UNIFORMS, uniforms.buffer, uniforms.size) + .buffer(PP_BINDING_EFFECT_PARAMS, + effect_params.buffer ? effect_params.buffer : g_dummy_buffer.buffer, + effect_params.buffer ? effect_params.size : g_dummy_buffer.size) + .build(device, bgl); } diff --git a/src/gpu/effects/rotating_cube_effect.cc b/src/gpu/effects/rotating_cube_effect.cc index 8d1f05a..da973e5 100644 --- a/src/gpu/effects/rotating_cube_effect.cc +++ b/src/gpu/effects/rotating_cube_effect.cc @@ -5,16 +5,14 @@ #include "gpu/effects/rotating_cube_effect.h" #include "generated/assets.h" #include "gpu/effects/shader_composer.h" +#include "gpu/sampler_cache.h" #include "util/asset_manager_utils.h" RotatingCubeEffect::RotatingCubeEffect(const GpuContext& ctx) : Effect(ctx) { } RotatingCubeEffect::~RotatingCubeEffect() { - if (mask_sampler_) - wgpuSamplerRelease(mask_sampler_); - if (noise_sampler_) - wgpuSamplerRelease(noise_sampler_); + // Samplers owned by SamplerCache - don't release if (noise_view_) wgpuTextureViewRelease(noise_view_); if (noise_texture_) @@ -49,21 +47,8 @@ void RotatingCubeEffect::init(MainSequence* demo) { noise_texture_ = wgpuDeviceCreateTexture(ctx_.device, &tex_desc); noise_view_ = wgpuTextureCreateView(noise_texture_, nullptr); - WGPUSamplerDescriptor sampler_desc = {}; - sampler_desc.addressModeU = WGPUAddressMode_Repeat; - sampler_desc.addressModeV = WGPUAddressMode_Repeat; - sampler_desc.magFilter = WGPUFilterMode_Linear; - sampler_desc.minFilter = WGPUFilterMode_Linear; - sampler_desc.maxAnisotropy = 1; - noise_sampler_ = wgpuDeviceCreateSampler(ctx_.device, &sampler_desc); - - WGPUSamplerDescriptor mask_sampler_desc = {}; - mask_sampler_desc.addressModeU = WGPUAddressMode_ClampToEdge; - mask_sampler_desc.addressModeV = WGPUAddressMode_ClampToEdge; - mask_sampler_desc.magFilter = WGPUFilterMode_Linear; - mask_sampler_desc.minFilter = WGPUFilterMode_Linear; - mask_sampler_desc.maxAnisotropy = 1; - mask_sampler_ = wgpuDeviceCreateSampler(ctx_.device, &mask_sampler_desc); + noise_sampler_ = SamplerCache::Get().get_or_create(ctx_.device, SamplerCache::linear()); + mask_sampler_ = SamplerCache::Get().get_or_create(ctx_.device, SamplerCache::clamp()); size_t shader_size; const char* shader_code = 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 <vector> +#include <string> + +// 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<WGPUBindGroupLayout> 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; + } +}; diff --git a/src/gpu/sampler_cache.h b/src/gpu/sampler_cache.h new file mode 100644 index 0000000..0f012a8 --- /dev/null +++ b/src/gpu/sampler_cache.h @@ -0,0 +1,61 @@ +// Sampler cache - deduplicates samplers across effects +#pragma once +#include <map> + +// Forward declarations (users must include gpu.h) +struct WGPUDeviceImpl; +typedef struct WGPUDeviceImpl* WGPUDevice; +struct WGPUSamplerImpl; +typedef struct WGPUSamplerImpl* WGPUSampler; + +#include "platform/platform.h" + +struct SamplerSpec { + WGPUAddressMode u, v; + WGPUFilterMode mag, min; + uint16_t anisotropy; + + bool operator<(const SamplerSpec& o) const { + if (u != o.u) return u < o.u; + if (v != o.v) return v < o.v; + if (mag != o.mag) return mag < o.mag; + if (min != o.min) return min < o.min; + return anisotropy < o.anisotropy; + } +}; + +class SamplerCache { + std::map<SamplerSpec, WGPUSampler> cache_; + SamplerCache() = default; + +public: + static SamplerCache& Get() { + static SamplerCache instance; + return instance; + } + + WGPUSampler get_or_create(WGPUDevice device, const SamplerSpec& spec) { + auto it = cache_.find(spec); + if (it != cache_.end()) return it->second; + + WGPUSamplerDescriptor desc{}; + desc.addressModeU = spec.u; + desc.addressModeV = spec.v; + desc.magFilter = spec.mag; + desc.minFilter = spec.min; + desc.maxAnisotropy = spec.anisotropy; + WGPUSampler sampler = wgpuDeviceCreateSampler(device, &desc); + cache_[spec] = sampler; + return sampler; + } + + // Common presets + static SamplerSpec linear() { + return {WGPUAddressMode_Repeat, WGPUAddressMode_Repeat, + WGPUFilterMode_Linear, WGPUFilterMode_Linear, 1}; + } + static SamplerSpec clamp() { + return {WGPUAddressMode_ClampToEdge, WGPUAddressMode_ClampToEdge, + WGPUFilterMode_Linear, WGPUFilterMode_Linear, 1}; + } +}; |
