diff options
Diffstat (limited to 'src')
30 files changed, 236 insertions, 314 deletions
diff --git a/src/3d/scene_loader.cc b/src/3d/scene_loader.cc index 2c29bc3..1ff2cc2 100644 --- a/src/3d/scene_loader.cc +++ b/src/3d/scene_loader.cc @@ -9,7 +9,9 @@ #include <new> // For std::nothrow #include <vector> -bool SceneLoader::LoadScene(Scene& scene, const uint8_t* data, size_t size) { +namespace SceneLoader { + +bool LoadScene(Scene& scene, const uint8_t* data, size_t size) { if (!data || size < 16) { // Header size check printf("SceneLoader: Data too small\n"); return false; @@ -166,4 +168,5 @@ bool SceneLoader::LoadScene(Scene& scene, const uint8_t* data, size_t size) { } return true; -}
\ No newline at end of file +} +} // namespace SceneLoader
\ No newline at end of file diff --git a/src/3d/scene_loader.h b/src/3d/scene_loader.h index eabfb7a..c4fb9e9 100644 --- a/src/3d/scene_loader.h +++ b/src/3d/scene_loader.h @@ -4,12 +4,11 @@ #include <cstddef> #include <cstdint> -// SceneLoader handles parsing of binary scene files (.bin) exported from -// Blender. It populates a Scene object with objects, lights, and cameras. -class SceneLoader { - public: - // Loads a scene from a binary buffer. - // Returns true on success, false on failure (e.g., invalid magic, version - // mismatch). - static bool LoadScene(Scene& scene, const uint8_t* data, size_t size); -}; +namespace SceneLoader { + +// Loads a scene from a binary buffer. +// Returns true on success, false on failure (e.g., invalid magic, version +// mismatch). +bool LoadScene(Scene& scene, const uint8_t* data, size_t size); + +} // namespace SceneLoader diff --git a/src/effects/flash_effect.cc b/src/effects/flash_effect.cc index c8638f0..93ff4dd 100644 --- a/src/effects/flash_effect.cc +++ b/src/effects/flash_effect.cc @@ -9,47 +9,15 @@ Flash::Flash(const GpuContext& ctx, const std::vector<std::string>& inputs, const std::vector<std::string>& outputs, float start_time, float end_time) - : Effect(ctx, inputs, outputs, start_time, end_time), pipeline_(nullptr), - bind_group_(nullptr), sampler_(nullptr), dummy_texture_(nullptr), - dummy_texture_view_(nullptr) { + : Effect(ctx, inputs, outputs, start_time, end_time) { HEADLESS_RETURN_IF_NULL(ctx_.device); - uniforms_buffer_.init(ctx_.device); - pipeline_ = create_post_process_pipeline( - ctx_.device, WGPUTextureFormat_RGBA8Unorm, flash_shader_wgsl); + init_uniforms_buffer(); + create_nearest_sampler(); + create_dummy_scene_texture(); - // Create dummy sampler (scene effects don't use texture input) - WGPUSamplerDescriptor sampler_desc = {}; - sampler_desc.addressModeU = WGPUAddressMode_ClampToEdge; - sampler_desc.addressModeV = WGPUAddressMode_ClampToEdge; - sampler_desc.magFilter = WGPUFilterMode_Nearest; - sampler_desc.minFilter = WGPUFilterMode_Nearest; - sampler_desc.maxAnisotropy = 1; - sampler_ = wgpuDeviceCreateSampler(ctx_.device, &sampler_desc); - - // Create 1×1 dummy texture - WGPUTextureDescriptor tex_desc = {}; - tex_desc.size = {1, 1, 1}; - tex_desc.format = WGPUTextureFormat_RGBA8Unorm; - tex_desc.usage = WGPUTextureUsage_TextureBinding; - tex_desc.dimension = WGPUTextureDimension_2D; - tex_desc.mipLevelCount = 1; - tex_desc.sampleCount = 1; - dummy_texture_ = wgpuDeviceCreateTexture(ctx_.device, &tex_desc); - dummy_texture_view_ = wgpuTextureCreateView(dummy_texture_, nullptr); -} - -Flash::~Flash() { - if (bind_group_) - wgpuBindGroupRelease(bind_group_); - if (pipeline_) - wgpuRenderPipelineRelease(pipeline_); - if (sampler_) - wgpuSamplerRelease(sampler_); - if (dummy_texture_view_) - wgpuTextureViewRelease(dummy_texture_view_); - if (dummy_texture_) - wgpuTextureRelease(dummy_texture_); + pipeline_.set(create_post_process_pipeline( + ctx_.device, WGPUTextureFormat_RGBA8Unorm, flash_shader_wgsl)); } void Flash::render(WGPUCommandEncoder encoder, @@ -61,17 +29,13 @@ void Flash::render(WGPUCommandEncoder encoder, uniforms_buffer_.update(ctx_.queue, params); // Update bind group (use dummy texture for scene effect) - pp_update_bind_group(ctx_.device, pipeline_, &bind_group_, - dummy_texture_view_, uniforms_buffer_.get(), + pp_update_bind_group(ctx_.device, pipeline_.get(), bind_group_.get_address(), + dummy_texture_view_.get(), uniforms_buffer_.get(), {nullptr, 0}); // Render pass WGPURenderPassColorAttachment color_attachment = {}; - color_attachment.view = output_view; - color_attachment.depthSlice = WGPU_DEPTH_SLICE_UNDEFINED; - color_attachment.loadOp = WGPULoadOp_Clear; - color_attachment.storeOp = WGPUStoreOp_Store; - color_attachment.clearValue = {0.0, 0.0, 0.0, 1.0}; + gpu_init_color_attachment(color_attachment, output_view); WGPURenderPassDescriptor pass_desc = {}; pass_desc.colorAttachmentCount = 1; @@ -79,8 +43,8 @@ void Flash::render(WGPUCommandEncoder encoder, WGPURenderPassEncoder pass = wgpuCommandEncoderBeginRenderPass(encoder, &pass_desc); - wgpuRenderPassEncoderSetPipeline(pass, pipeline_); - wgpuRenderPassEncoderSetBindGroup(pass, 0, bind_group_, 0, nullptr); + wgpuRenderPassEncoderSetPipeline(pass, pipeline_.get()); + wgpuRenderPassEncoderSetBindGroup(pass, 0, bind_group_.get(), 0, nullptr); wgpuRenderPassEncoderDraw(pass, 3, 1, 0, 0); wgpuRenderPassEncoderEnd(pass); wgpuRenderPassEncoderRelease(pass); diff --git a/src/effects/flash_effect.h b/src/effects/flash_effect.h index 1117e0a..052957d 100644 --- a/src/effects/flash_effect.h +++ b/src/effects/flash_effect.h @@ -4,22 +4,18 @@ #pragma once #include "gpu/effect.h" #include "gpu/uniform_helper.h" +#include "gpu/wgpu_resource.h" class Flash : public Effect { public: Flash(const GpuContext& ctx, const std::vector<std::string>& inputs, const std::vector<std::string>& outputs, float start_time, float end_time); - ~Flash() override; void render(WGPUCommandEncoder encoder, const UniformsSequenceParams& params, NodeRegistry& nodes) override; private: - WGPURenderPipeline pipeline_; - WGPUBindGroup bind_group_; - WGPUSampler sampler_; - WGPUTexture dummy_texture_; - WGPUTextureView dummy_texture_view_; - UniformBuffer<UniformsSequenceParams> uniforms_buffer_; + RenderPipeline pipeline_; + BindGroup bind_group_; }; diff --git a/src/effects/gaussian_blur_effect.cc b/src/effects/gaussian_blur_effect.cc index 02cd77e..0548b4a 100644 --- a/src/effects/gaussian_blur_effect.cc +++ b/src/effects/gaussian_blur_effect.cc @@ -9,27 +9,15 @@ GaussianBlur::GaussianBlur(const GpuContext& ctx, const std::vector<std::string>& inputs, const std::vector<std::string>& outputs, float start_time, float end_time) - : Effect(ctx, inputs, outputs, start_time, end_time), pipeline_(nullptr), bind_group_(nullptr), - sampler_(nullptr) { - // Headless mode: skip GPU resource creation (compiled out in STRIP_ALL) + : Effect(ctx, inputs, outputs, start_time, end_time), pipeline_(nullptr), bind_group_(nullptr) { HEADLESS_RETURN_IF_NULL(ctx_.device); - // Create pipeline + init_uniforms_buffer(); + create_linear_sampler(); + params_buffer_.init(ctx_.device); + pipeline_ = create_post_process_pipeline( ctx_.device, WGPUTextureFormat_RGBA8Unorm, gaussian_blur_shader_wgsl); - - // Create sampler - WGPUSamplerDescriptor sampler_desc = {}; - sampler_desc.addressModeU = WGPUAddressMode_ClampToEdge; - sampler_desc.addressModeV = WGPUAddressMode_ClampToEdge; - sampler_desc.magFilter = WGPUFilterMode_Linear; - sampler_desc.minFilter = WGPUFilterMode_Linear; - sampler_desc.maxAnisotropy = 1; - sampler_ = wgpuDeviceCreateSampler(ctx_.device, &sampler_desc); - - // Init uniform buffers - params_buffer_.init(ctx_.device); - uniforms_buffer_.init(ctx_.device); } void GaussianBlur::render(WGPUCommandEncoder encoder, @@ -46,7 +34,7 @@ void GaussianBlur::render(WGPUCommandEncoder encoder, // Update bind group WGPUBindGroupEntry entries[4] = {}; entries[0].binding = PP_BINDING_SAMPLER; - entries[0].sampler = sampler_; + entries[0].sampler = sampler_.get(); entries[1].binding = PP_BINDING_TEXTURE; entries[1].textureView = input_view; entries[2].binding = PP_BINDING_UNIFORMS; @@ -68,11 +56,7 @@ void GaussianBlur::render(WGPUCommandEncoder encoder, // Render pass WGPURenderPassColorAttachment color_attachment = {}; - color_attachment.view = output_view; - color_attachment.depthSlice = WGPU_DEPTH_SLICE_UNDEFINED; - color_attachment.loadOp = WGPULoadOp_Clear; - color_attachment.storeOp = WGPUStoreOp_Store; - color_attachment.clearValue = {0.0, 0.0, 0.0, 1.0}; + gpu_init_color_attachment(color_attachment, output_view); WGPURenderPassDescriptor pass_desc = {}; pass_desc.colorAttachmentCount = 1; diff --git a/src/effects/gaussian_blur_effect.h b/src/effects/gaussian_blur_effect.h index cc9ff56..f4b8fcf 100644 --- a/src/effects/gaussian_blur_effect.h +++ b/src/effects/gaussian_blur_effect.h @@ -26,8 +26,6 @@ class GaussianBlur : public Effect { private: WGPURenderPipeline pipeline_; WGPUBindGroup bind_group_; - WGPUSampler sampler_; GaussianBlurParams blur_params_; UniformBuffer<GaussianBlurParams> params_buffer_; - UniformBuffer<UniformsSequenceParams> uniforms_buffer_; }; diff --git a/src/effects/heptagon_effect.cc b/src/effects/heptagon_effect.cc index 13d7aad..c9ec17c 100644 --- a/src/effects/heptagon_effect.cc +++ b/src/effects/heptagon_effect.cc @@ -10,50 +10,15 @@ Heptagon::Heptagon(const GpuContext& ctx, const std::vector<std::string>& inputs, const std::vector<std::string>& outputs, float start_time, float end_time) - : Effect(ctx, inputs, outputs, start_time, end_time), pipeline_(nullptr), bind_group_(nullptr), - sampler_(nullptr) { - // Headless mode: skip GPU resource creation (compiled out in STRIP_ALL) + : Effect(ctx, inputs, outputs, start_time, end_time) { HEADLESS_RETURN_IF_NULL(ctx_.device); - // Init uniforms - uniforms_buffer_.init(ctx_.device); + init_uniforms_buffer(); + create_nearest_sampler(); + create_dummy_scene_texture(); - // Create pipeline (standard post-process, no depth) - pipeline_ = create_post_process_pipeline( - ctx_.device, WGPUTextureFormat_RGBA8Unorm, heptagon_shader_wgsl); - - // Create dummy sampler (scene effects don't use texture input) - WGPUSamplerDescriptor sampler_desc = {}; - sampler_desc.addressModeU = WGPUAddressMode_ClampToEdge; - sampler_desc.addressModeV = WGPUAddressMode_ClampToEdge; - sampler_desc.magFilter = WGPUFilterMode_Nearest; - sampler_desc.minFilter = WGPUFilterMode_Nearest; - sampler_desc.maxAnisotropy = 1; - sampler_ = wgpuDeviceCreateSampler(ctx_.device, &sampler_desc); - - // Create 1×1 dummy texture - WGPUTextureDescriptor tex_desc = {}; - tex_desc.size = {1, 1, 1}; - tex_desc.format = WGPUTextureFormat_RGBA8Unorm; - tex_desc.usage = WGPUTextureUsage_TextureBinding; - tex_desc.dimension = WGPUTextureDimension_2D; - tex_desc.mipLevelCount = 1; - tex_desc.sampleCount = 1; - dummy_texture_ = wgpuDeviceCreateTexture(ctx_.device, &tex_desc); - dummy_texture_view_ = wgpuTextureCreateView(dummy_texture_, nullptr); -} - -Heptagon::~Heptagon() { - if (bind_group_) - wgpuBindGroupRelease(bind_group_); - if (pipeline_) - wgpuRenderPipelineRelease(pipeline_); - if (sampler_) - wgpuSamplerRelease(sampler_); - if (dummy_texture_view_) - wgpuTextureViewRelease(dummy_texture_view_); - if (dummy_texture_) - wgpuTextureRelease(dummy_texture_); + pipeline_.set(create_post_process_pipeline( + ctx_.device, WGPUTextureFormat_RGBA8Unorm, heptagon_shader_wgsl)); } void Heptagon::render(WGPUCommandEncoder encoder, @@ -66,17 +31,13 @@ void Heptagon::render(WGPUCommandEncoder encoder, uniforms_buffer_.update(ctx_.queue, params); // Create bind group (use dummy texture for scene effect) - pp_update_bind_group(ctx_.device, pipeline_, &bind_group_, - dummy_texture_view_, uniforms_buffer_.get(), + pp_update_bind_group(ctx_.device, pipeline_.get(), bind_group_.get_address(), + dummy_texture_view_.get(), uniforms_buffer_.get(), {nullptr, 0}); // Render pass WGPURenderPassColorAttachment color_attachment = {}; - color_attachment.view = output_view; - color_attachment.depthSlice = WGPU_DEPTH_SLICE_UNDEFINED; - color_attachment.loadOp = WGPULoadOp_Clear; - color_attachment.storeOp = WGPUStoreOp_Store; - color_attachment.clearValue = {0.0, 0.0, 0.0, 1.0}; + gpu_init_color_attachment(color_attachment, output_view); WGPURenderPassDescriptor pass_desc = {}; pass_desc.colorAttachmentCount = 1; @@ -84,8 +45,8 @@ void Heptagon::render(WGPUCommandEncoder encoder, WGPURenderPassEncoder pass = wgpuCommandEncoderBeginRenderPass(encoder, &pass_desc); - wgpuRenderPassEncoderSetPipeline(pass, pipeline_); - wgpuRenderPassEncoderSetBindGroup(pass, 0, bind_group_, 0, nullptr); + wgpuRenderPassEncoderSetPipeline(pass, pipeline_.get()); + wgpuRenderPassEncoderSetBindGroup(pass, 0, bind_group_.get(), 0, nullptr); wgpuRenderPassEncoderDraw(pass, 3, 1, 0, 0); // Fullscreen triangle wgpuRenderPassEncoderEnd(pass); wgpuRenderPassEncoderRelease(pass); diff --git a/src/effects/heptagon_effect.h b/src/effects/heptagon_effect.h index ef05d79..4563e47 100644 --- a/src/effects/heptagon_effect.h +++ b/src/effects/heptagon_effect.h @@ -4,22 +4,18 @@ #include "gpu/effect.h" #include "gpu/uniform_helper.h" +#include "gpu/wgpu_resource.h" class Heptagon : public Effect { public: Heptagon(const GpuContext& ctx, const std::vector<std::string>& inputs, const std::vector<std::string>& outputs, float start_time, float end_time); - ~Heptagon(); void render(WGPUCommandEncoder encoder, const UniformsSequenceParams& params, NodeRegistry& nodes) override; private: - WGPURenderPipeline pipeline_; - WGPUBindGroup bind_group_; - WGPUSampler sampler_; - WGPUTexture dummy_texture_; - WGPUTextureView dummy_texture_view_; - UniformBuffer<UniformsSequenceParams> uniforms_buffer_; + RenderPipeline pipeline_; + BindGroup bind_group_; }; diff --git a/src/effects/hybrid3_d_effect.cc b/src/effects/hybrid3_d_effect.cc index 1dd1c05..5832b57 100644 --- a/src/effects/hybrid3_d_effect.cc +++ b/src/effects/hybrid3_d_effect.cc @@ -106,12 +106,7 @@ void Hybrid3D::render(WGPUCommandEncoder encoder, // Render 3D scene using sequence encoder WGPURenderPassColorAttachment color_attachment = {}; -#if !defined(DEMO_CROSS_COMPILE_WIN32) - color_attachment.depthSlice = WGPU_DEPTH_SLICE_UNDEFINED; -#endif - color_attachment.view = color_view; - color_attachment.loadOp = WGPULoadOp_Clear; - color_attachment.storeOp = WGPUStoreOp_Store; + gpu_init_color_attachment(color_attachment, color_view); color_attachment.clearValue = {0.05f, 0.05f, 0.05f, 1.0f}; WGPURenderPassDepthStencilAttachment depth_attachment = {}; diff --git a/src/effects/particles_effect.cc b/src/effects/particles_effect.cc index 22e41e9..3c9feb7 100644 --- a/src/effects/particles_effect.cc +++ b/src/effects/particles_effect.cc @@ -12,11 +12,9 @@ Particles::Particles(const GpuContext& ctx, const std::vector<std::string>& outputs, float start_time, float end_time) : Effect(ctx, inputs, outputs, start_time, end_time) { - // Headless mode: skip GPU resource creation (compiled out in STRIP_ALL) HEADLESS_RETURN_IF_NULL(ctx_.device); - // Initialize uniforms - uniforms_.init(ctx_.device); + init_uniforms_buffer(); // Initialize particles buffer std::vector<Particle> init_p(NUM_PARTICLES); @@ -49,7 +47,7 @@ Particles::Particles(const GpuContext& ctx, // Create compute shader (particle simulation) ResourceBinding compute_bindings[] = { {particles_buffer_, WGPUBufferBindingType_Storage}, - {uniforms_.get(), WGPUBufferBindingType_Uniform}}; + {uniforms_buffer_.get(), WGPUBufferBindingType_Uniform}}; compute_pass_ = gpu_create_compute_pass(ctx_.device, particle_compute_wgsl, compute_bindings, 2); compute_pass_.workgroup_size_x = (NUM_PARTICLES + 63) / 64; @@ -57,7 +55,7 @@ Particles::Particles(const GpuContext& ctx, // Create render shader (particle rendering) ResourceBinding render_bindings[] = { {particles_buffer_, WGPUBufferBindingType_ReadOnlyStorage}, - {uniforms_.get(), WGPUBufferBindingType_Uniform}}; + {uniforms_buffer_.get(), WGPUBufferBindingType_Uniform}}; render_pass_ = gpu_create_render_pass(ctx_.device, WGPUTextureFormat_RGBA8Unorm, particle_render_wgsl, render_bindings, 2); @@ -69,7 +67,7 @@ void Particles::render(WGPUCommandEncoder encoder, const UniformsSequenceParams& params, NodeRegistry& nodes) { // Update uniforms - uniforms_.update(ctx_.queue, params); + uniforms_buffer_.update(ctx_.queue, params); // Run compute pass (particle simulation) WGPUComputePassEncoder compute = @@ -84,12 +82,8 @@ void Particles::render(WGPUCommandEncoder encoder, // Run render pass (draw particles to output) WGPUTextureView output_view = nodes.get_view(output_nodes_[0]); - WGPURenderPassColorAttachment color_attachment = { - .view = output_view, - .depthSlice = WGPU_DEPTH_SLICE_UNDEFINED, - .loadOp = WGPULoadOp_Clear, - .storeOp = WGPUStoreOp_Store, - .clearValue = {0.0, 0.0, 0.0, 1.0}}; + WGPURenderPassColorAttachment color_attachment = {}; + gpu_init_color_attachment(color_attachment, output_view); WGPURenderPassDescriptor render_desc = { .colorAttachmentCount = 1, .colorAttachments = &color_attachment}; diff --git a/src/effects/particles_effect.h b/src/effects/particles_effect.h index 38c60d7..e855b7b 100644 --- a/src/effects/particles_effect.h +++ b/src/effects/particles_effect.h @@ -30,5 +30,4 @@ class Particles : public Effect { ComputePass compute_pass_; RenderPass render_pass_; GpuBuffer particles_buffer_; - UniformBuffer<UniformsSequenceParams> uniforms_; }; diff --git a/src/effects/passthrough_effect.cc b/src/effects/passthrough_effect.cc index 392102d..24eefca 100644 --- a/src/effects/passthrough_effect.cc +++ b/src/effects/passthrough_effect.cc @@ -9,27 +9,14 @@ Passthrough::Passthrough(const GpuContext& ctx, const std::vector<std::string>& inputs, const std::vector<std::string>& outputs, float start_time, float end_time) - : Effect(ctx, inputs, outputs, start_time, end_time), pipeline_(nullptr), - bind_group_(nullptr), sampler_(nullptr) { - // Headless mode: skip GPU resource creation (compiled out in STRIP_ALL) + : Effect(ctx, inputs, outputs, start_time, end_time) { HEADLESS_RETURN_IF_NULL(ctx_.device); - // Init uniform buffer - uniforms_buffer_.init(ctx_.device); - // Create pipeline (simple version without effect params) - pipeline_ = create_post_process_pipeline_simple( - ctx_.device, WGPUTextureFormat_RGBA8Unorm, passthrough_shader_wgsl); + init_uniforms_buffer(); + create_linear_sampler(); - // Create sampler - WGPUSamplerDescriptor sampler_desc = {}; - sampler_desc.addressModeU = WGPUAddressMode_ClampToEdge; - sampler_desc.addressModeV = WGPUAddressMode_ClampToEdge; - sampler_desc.addressModeW = WGPUAddressMode_ClampToEdge; - sampler_desc.magFilter = WGPUFilterMode_Linear; - sampler_desc.minFilter = WGPUFilterMode_Linear; - sampler_desc.mipmapFilter = WGPUMipmapFilterMode_Nearest; - sampler_desc.maxAnisotropy = 1; - sampler_ = wgpuDeviceCreateSampler(ctx_.device, &sampler_desc); + pipeline_.set(create_post_process_pipeline_simple( + ctx_.device, WGPUTextureFormat_RGBA8Unorm, passthrough_shader_wgsl)); } void Passthrough::render(WGPUCommandEncoder encoder, @@ -45,7 +32,7 @@ void Passthrough::render(WGPUCommandEncoder encoder, // Manually create bind group with only 3 entries (no effect params needed) WGPUBindGroupEntry entries[3] = {}; entries[0].binding = PP_BINDING_SAMPLER; - entries[0].sampler = sampler_; + entries[0].sampler = sampler_.get(); entries[1].binding = PP_BINDING_TEXTURE; entries[1].textureView = input_view; entries[2].binding = PP_BINDING_UNIFORMS; @@ -53,22 +40,15 @@ void Passthrough::render(WGPUCommandEncoder encoder, entries[2].size = sizeof(UniformsSequenceParams); WGPUBindGroupDescriptor bg_desc = {}; - bg_desc.layout = wgpuRenderPipelineGetBindGroupLayout(pipeline_, 0); + bg_desc.layout = wgpuRenderPipelineGetBindGroupLayout(pipeline_.get(), 0); bg_desc.entryCount = 3; bg_desc.entries = entries; - if (bind_group_) { - wgpuBindGroupRelease(bind_group_); - } - bind_group_ = wgpuDeviceCreateBindGroup(ctx_.device, &bg_desc); + bind_group_.replace(wgpuDeviceCreateBindGroup(ctx_.device, &bg_desc)); // Render pass - WGPURenderPassColorAttachment color_attachment = { - .view = output_view, - .depthSlice = WGPU_DEPTH_SLICE_UNDEFINED, - .loadOp = WGPULoadOp_Clear, - .storeOp = WGPUStoreOp_Store, - .clearValue = {0.0, 0.0, 0.0, 1.0}}; + WGPURenderPassColorAttachment color_attachment = {}; + gpu_init_color_attachment(color_attachment, output_view); WGPURenderPassDescriptor pass_desc = {}; pass_desc.colorAttachmentCount = 1; @@ -76,8 +56,8 @@ void Passthrough::render(WGPUCommandEncoder encoder, WGPURenderPassEncoder pass = wgpuCommandEncoderBeginRenderPass(encoder, &pass_desc); - wgpuRenderPassEncoderSetPipeline(pass, pipeline_); - wgpuRenderPassEncoderSetBindGroup(pass, 0, bind_group_, 0, nullptr); + wgpuRenderPassEncoderSetPipeline(pass, pipeline_.get()); + wgpuRenderPassEncoderSetBindGroup(pass, 0, bind_group_.get(), 0, nullptr); wgpuRenderPassEncoderDraw(pass, 3, 1, 0, 0); // Fullscreen triangle wgpuRenderPassEncoderEnd(pass); wgpuRenderPassEncoderRelease(pass); diff --git a/src/effects/passthrough_effect.h b/src/effects/passthrough_effect.h index f684b9b..1c60e02 100644 --- a/src/effects/passthrough_effect.h +++ b/src/effects/passthrough_effect.h @@ -4,6 +4,7 @@ #include "gpu/effect.h" #include "gpu/uniform_helper.h" +#include "gpu/wgpu_resource.h" class Passthrough : public Effect { public: @@ -15,8 +16,6 @@ class Passthrough : public Effect { NodeRegistry& nodes) override; private: - WGPURenderPipeline pipeline_; - WGPUBindGroup bind_group_; - WGPUSampler sampler_; - UniformBuffer<UniformsSequenceParams> uniforms_buffer_; + RenderPipeline pipeline_; + BindGroup bind_group_; }; diff --git a/src/effects/peak_meter_effect.cc b/src/effects/peak_meter_effect.cc index 63be2c0..d077302 100644 --- a/src/effects/peak_meter_effect.cc +++ b/src/effects/peak_meter_effect.cc @@ -12,7 +12,7 @@ PeakMeter::PeakMeter(const GpuContext& ctx, : Effect(ctx, inputs, outputs, start_time, end_time), pipeline_(nullptr), bind_group_(nullptr) { HEADLESS_RETURN_IF_NULL(ctx_.device); - uniforms_buffer_.init(ctx_.device); + init_uniforms_buffer(); const char* shader_main = R"( struct VertexOutput { @@ -81,11 +81,8 @@ void PeakMeter::render(WGPUCommandEncoder encoder, uniforms_buffer_.get(), {nullptr, 0}); WGPURenderPassColorAttachment color_attachment = {}; - color_attachment.view = output_view; - color_attachment.depthSlice = WGPU_DEPTH_SLICE_UNDEFINED; + gpu_init_color_attachment(color_attachment, output_view); color_attachment.loadOp = WGPULoadOp_Load; - color_attachment.storeOp = WGPUStoreOp_Store; - color_attachment.clearValue = {0.0, 0.0, 0.0, 1.0}; WGPURenderPassDescriptor pass_desc = {}; pass_desc.colorAttachmentCount = 1; diff --git a/src/effects/peak_meter_effect.h b/src/effects/peak_meter_effect.h index e397a71..1786522 100644 --- a/src/effects/peak_meter_effect.h +++ b/src/effects/peak_meter_effect.h @@ -18,5 +18,4 @@ class PeakMeter : public Effect { private: WGPURenderPipeline pipeline_; WGPUBindGroup bind_group_; - UniformBuffer<UniformsSequenceParams> uniforms_buffer_; }; diff --git a/src/effects/placeholder_effect.cc b/src/effects/placeholder_effect.cc index 6ba665a..e024a6b 100644 --- a/src/effects/placeholder_effect.cc +++ b/src/effects/placeholder_effect.cc @@ -12,26 +12,16 @@ Placeholder::Placeholder(const GpuContext& ctx, float start_time, float end_time, const char* placeholder_name) : Effect(ctx, inputs, outputs, start_time, end_time), pipeline_(nullptr), - bind_group_(nullptr), sampler_(nullptr), name_(placeholder_name) { - // Log once on construction + bind_group_(nullptr), name_(placeholder_name) { fprintf(stderr, "TODO: %s not yet implemented, using passthrough\n", name_); - // Headless mode: skip GPU resource creation (compiled out in STRIP_ALL) HEADLESS_RETURN_IF_NULL(ctx_.device); - uniforms_buffer_.init(ctx_.device); + init_uniforms_buffer(); + create_linear_sampler(); + pipeline_ = create_post_process_pipeline( ctx_.device, WGPUTextureFormat_RGBA8Unorm, passthrough_shader_wgsl); - - WGPUSamplerDescriptor sampler_desc = {}; - sampler_desc.addressModeU = WGPUAddressMode_ClampToEdge; - sampler_desc.addressModeV = WGPUAddressMode_ClampToEdge; - sampler_desc.addressModeW = WGPUAddressMode_ClampToEdge; - sampler_desc.magFilter = WGPUFilterMode_Linear; - sampler_desc.minFilter = WGPUFilterMode_Linear; - sampler_desc.mipmapFilter = WGPUMipmapFilterMode_Nearest; - sampler_desc.maxAnisotropy = 1; - sampler_ = wgpuDeviceCreateSampler(ctx_.device, &sampler_desc); } void Placeholder::render(WGPUCommandEncoder encoder, @@ -45,12 +35,8 @@ void Placeholder::render(WGPUCommandEncoder encoder, pp_update_bind_group(ctx_.device, pipeline_, &bind_group_, input_view, uniforms_buffer_.get(), {nullptr, 0}); - WGPURenderPassColorAttachment color_attachment = { - .view = output_view, - .depthSlice = WGPU_DEPTH_SLICE_UNDEFINED, - .loadOp = WGPULoadOp_Clear, - .storeOp = WGPUStoreOp_Store, - .clearValue = {0.0, 0.0, 0.0, 1.0}}; + WGPURenderPassColorAttachment color_attachment = {}; + gpu_init_color_attachment(color_attachment, output_view); WGPURenderPassDescriptor pass_desc = {}; pass_desc.colorAttachmentCount = 1; diff --git a/src/effects/placeholder_effect.h b/src/effects/placeholder_effect.h index 24cf3f4..72b156f 100644 --- a/src/effects/placeholder_effect.h +++ b/src/effects/placeholder_effect.h @@ -18,7 +18,5 @@ class Placeholder : public Effect { private: WGPURenderPipeline pipeline_; WGPUBindGroup bind_group_; - WGPUSampler sampler_; - UniformBuffer<UniformsSequenceParams> uniforms_buffer_; const char* name_; }; diff --git a/src/effects/rotating_cube_effect.cc b/src/effects/rotating_cube_effect.cc index 0f1781d..6c350a7 100644 --- a/src/effects/rotating_cube_effect.cc +++ b/src/effects/rotating_cube_effect.cc @@ -163,9 +163,9 @@ void RotatingCube::render(WGPUCommandEncoder encoder, WGPUTexture input_tex = nodes.get_texture(input_nodes_[0]); WGPUTexture output_tex = nodes.get_texture(output_nodes_[0]); if (input_tex && output_tex) { - WGPUTexelCopyTextureInfo src = { + GpuTextureCopyInfo src = { .texture = input_tex, .mipLevel = 0, .aspect = WGPUTextureAspect_All}; - WGPUTexelCopyTextureInfo dst = {.texture = output_tex, + GpuTextureCopyInfo dst = {.texture = output_tex, .mipLevel = 0, .aspect = WGPUTextureAspect_All}; WGPUExtent3D copy_size = {(uint32_t)params.resolution.x, @@ -181,7 +181,9 @@ void RotatingCube::render(WGPUCommandEncoder encoder, // Render pass with depth WGPURenderPassColorAttachment color_attachment = { .view = color_view, + #if !defined(DEMO_CROSS_COMPILE_WIN32) .depthSlice = WGPU_DEPTH_SLICE_UNDEFINED, +#endif // .loadOp = WGPULoadOp_Clear, .loadOp = WGPULoadOp_Load, .storeOp = WGPUStoreOp_Store, diff --git a/src/gpu/demo_effects.cc b/src/gpu/demo_effects.cc index d9c5964..5114e3a 100644 --- a/src/gpu/demo_effects.cc +++ b/src/gpu/demo_effects.cc @@ -1,6 +1,4 @@ // This file is part of the 64k demo project. // Central include for all effect implementations. -// Individual effects are in src/effects/*_v2.{h,cc} -// Timeline management is in src/generated/timeline.cc (v2) #include "gpu/demo_effects.h" diff --git a/src/gpu/demo_effects.h b/src/gpu/demo_effects.h index 85299ca..2825891 100644 --- a/src/gpu/demo_effects.h +++ b/src/gpu/demo_effects.h @@ -8,7 +8,7 @@ #include "3d/renderer.h" #include "3d/scene.h" -// Base effect classes (v2) +// Base effect classes #include "effects/shaders.h" #include "gpu/effect.h" #include "gpu/post_process_helper.h" @@ -16,7 +16,7 @@ #include "gpu/texture_manager.h" #include "gpu/uniform_helper.h" -// Individual Effect Headers (v2) +// Individual Effect Headers #include "effects/flash_effect.h" #include "effects/gaussian_blur_effect.h" #include "effects/heptagon_effect.h" @@ -26,14 +26,8 @@ #include "effects/peak_meter_effect.h" #include "effects/placeholder_effect.h" #include "effects/rotating_cube_effect.h" -// TODO: Port CNN effects to v2 +// TODO: Port CNN effects // #include "../../cnn_v1/src/cnn_v1_effect.h" // #include "../../cnn_v2/src/cnn_v2_effect.h" #include <memory> - -// Auto-generated functions from sequence compiler v2 -// See generated/timeline.h for: -// - InitializeV2Sequences() -// - GetActiveV2Sequence() -// - RenderV2Timeline() diff --git a/src/gpu/effect.cc b/src/gpu/effect.cc index 752dd84..d0693ec 100644 --- a/src/gpu/effect.cc +++ b/src/gpu/effect.cc @@ -14,6 +14,7 @@ Effect::Effect(const GpuContext& ctx, const std::vector<std::string>& inputs, FATAL_CHECK(!outputs.empty(), "Effect must have at least one output\n"); FATAL_CHECK(start_time <= end_time, "Invalid time range: %f > %f\n", start_time, end_time); + HEADLESS_RETURN_IF_NULL(ctx_.device); } void Effect::dispatch_render(WGPUCommandEncoder encoder, @@ -55,3 +56,21 @@ void Effect::blit_input_to_output(WGPUCommandEncoder encoder, wgpuCommandEncoderCopyTextureToTexture(encoder, &src_copy, &dst_copy, &extent); } + +void Effect::init_uniforms_buffer() { + uniforms_buffer_.init(ctx_.device); +} + +void Effect::create_linear_sampler() { + sampler_.set(gpu_create_linear_sampler(ctx_.device)); +} + +void Effect::create_nearest_sampler() { + sampler_.set(gpu_create_nearest_sampler(ctx_.device)); +} + +void Effect::create_dummy_scene_texture() { + TextureWithView dummy = gpu_create_dummy_scene_texture(ctx_.device); + dummy_texture_.set(dummy.texture); + dummy_texture_view_.set(dummy.view); +} diff --git a/src/gpu/effect.h b/src/gpu/effect.h index 0d7e35e..d47a8b7 100644 --- a/src/gpu/effect.h +++ b/src/gpu/effect.h @@ -6,6 +6,8 @@ #include "gpu/gpu.h" #include "gpu/sequence.h" +#include "gpu/uniform_helper.h" +#include "gpu/wgpu_resource.h" #include <string> #include <vector> @@ -23,17 +25,15 @@ class Effect { (void)registry; } - // Dispatch render with automatic passthrough outside [start, end] + // Automatic passthrough outside [start, end] void dispatch_render(WGPUCommandEncoder encoder, const UniformsSequenceParams& params, NodeRegistry& nodes); - // Render effect (multi-input/multi-output) - override in derived classes virtual void render(WGPUCommandEncoder encoder, const UniformsSequenceParams& params, NodeRegistry& nodes) = 0; - // Resize notification virtual void resize(int width, int height) { width_ = width; height_ = height; @@ -53,6 +53,24 @@ class Effect { int width_ = 1280; int height_ = 720; + // Common resources for most effects + UniformBuffer<UniformsSequenceParams> uniforms_buffer_; + Sampler sampler_; + Texture dummy_texture_; + TextureView dummy_texture_view_; + + // Helper: Initialize uniforms buffer (call in subclass constructor) + void init_uniforms_buffer(); + + // Helper: Create linear sampler (call in subclass constructor) + void create_linear_sampler(); + + // Helper: Create nearest sampler (call in subclass constructor) + void create_nearest_sampler(); + + // Helper: Create dummy texture for scene effects (call in subclass constructor) + void create_dummy_scene_texture(); + private: float start_time_; float end_time_; diff --git a/src/gpu/gpu.cc b/src/gpu/gpu.cc index 2226889..e743bf7 100644 --- a/src/gpu/gpu.cc +++ b/src/gpu/gpu.cc @@ -124,6 +124,41 @@ WGPUTextureView gpu_create_texture_view_2d(WGPUTexture texture, return wgpuTextureCreateView(texture, &view_desc); } +WGPUSampler gpu_create_linear_sampler(WGPUDevice device) { + WGPUSamplerDescriptor desc = {}; + desc.addressModeU = WGPUAddressMode_ClampToEdge; + desc.addressModeV = WGPUAddressMode_ClampToEdge; + desc.addressModeW = WGPUAddressMode_ClampToEdge; + desc.magFilter = WGPUFilterMode_Linear; + desc.minFilter = WGPUFilterMode_Linear; + desc.mipmapFilter = WGPUMipmapFilterMode_Nearest; + desc.maxAnisotropy = 1; + return wgpuDeviceCreateSampler(device, &desc); +} + +WGPUSampler gpu_create_nearest_sampler(WGPUDevice device) { + WGPUSamplerDescriptor desc = {}; + desc.addressModeU = WGPUAddressMode_ClampToEdge; + desc.addressModeV = WGPUAddressMode_ClampToEdge; + desc.magFilter = WGPUFilterMode_Nearest; + desc.minFilter = WGPUFilterMode_Nearest; + desc.maxAnisotropy = 1; + return wgpuDeviceCreateSampler(device, &desc); +} + +TextureWithView gpu_create_dummy_scene_texture(WGPUDevice device) { + WGPUTextureDescriptor desc = {}; + desc.size = {1, 1, 1}; + desc.format = WGPUTextureFormat_RGBA8Unorm; + desc.usage = WGPUTextureUsage_TextureBinding; + desc.dimension = WGPUTextureDimension_2D; + desc.mipLevelCount = 1; + desc.sampleCount = 1; + WGPUTexture texture = wgpuDeviceCreateTexture(device, &desc); + WGPUTextureView view = wgpuTextureCreateView(texture, nullptr); + return {texture, view}; +} + RenderPass gpu_create_render_pass(WGPUDevice device, WGPUTextureFormat format, const char* shader_code, ResourceBinding* bindings, int num_bindings) { diff --git a/src/gpu/gpu.h b/src/gpu/gpu.h index 5e928b2..d6c0255 100644 --- a/src/gpu/gpu.h +++ b/src/gpu/gpu.h @@ -1,27 +1,23 @@ // This file is part of the 64k demo project. -// It defines the public interface for the GPU rendering system. -// Coordinates WebGPU lifecycle and draw calls. +// GPU rendering system interface. #pragma once #include "platform/platform.h" -struct PlatformState; // Forward declaration +struct PlatformState; -// GPU context bundling device, queue, and surface format struct GpuContext { WGPUDevice device; WGPUQueue queue; WGPUTextureFormat format; }; -// Basic wrapper for WebGPU buffers struct GpuBuffer { WGPUBuffer buffer; size_t size; }; -// Encapsulates a compute operation struct ComputePass { WGPUComputePipeline pipeline; WGPUBindGroup bind_group; @@ -30,7 +26,6 @@ struct ComputePass { uint32_t workgroup_size_z; }; -// Encapsulates a render operation struct RenderPass { WGPURenderPipeline pipeline; WGPUBindGroup bind_group; @@ -48,34 +43,27 @@ WGPUSurface gpu_get_surface(); const GpuContext* gpu_get_context(); -// Placeholder for GPU performance capture. -// This define can be controlled via CMake to conditionally enable profiling -// code. #define ENABLE_GPU_PERF_CAPTURE - -// Helper functions (exposed for internal/future use) struct ResourceBinding { GpuBuffer buffer; - WGPUBufferBindingType type; // e.g., WGPUBufferBindingType_Uniform, - // WGPUBufferBindingType_Storage + WGPUBufferBindingType type; }; -// Cross-platform helper for color attachment initialization inline void gpu_init_color_attachment(WGPURenderPassColorAttachment& attachment, WGPUTextureView view) { attachment.view = view; attachment.loadOp = WGPULoadOp_Clear; attachment.storeOp = WGPUStoreOp_Store; attachment.clearValue = {0.0f, 0.0f, 0.0f, 1.0f}; +#if !defined(DEMO_CROSS_COMPILE_WIN32) attachment.depthSlice = WGPU_DEPTH_SLICE_UNDEFINED; +#endif } -// Texture creation helper struct TextureWithView { WGPUTexture texture; WGPUTextureView view; }; -// Platform-abstracted texture copy types #if defined(DEMO_CROSS_COMPILE_WIN32) using GpuTextureCopyInfo = WGPUImageCopyTexture; using GpuTextureDataLayout = WGPUTextureDataLayout; @@ -109,4 +97,11 @@ RenderPass gpu_create_render_pass(WGPUDevice device, WGPUTextureFormat format, // Needed for render pipeline const char* shader_code, ResourceBinding* bindings, - int num_bindings);
\ No newline at end of file + int num_bindings); + +// Common sampler configurations +WGPUSampler gpu_create_linear_sampler(WGPUDevice device); +WGPUSampler gpu_create_nearest_sampler(WGPUDevice device); + +// Dummy 1x1 texture for scene effects (don't need texture input) +TextureWithView gpu_create_dummy_scene_texture(WGPUDevice device);
\ No newline at end of file diff --git a/src/gpu/post_process_helper.h b/src/gpu/post_process_helper.h index 9a71046..811d0e4 100644 --- a/src/gpu/post_process_helper.h +++ b/src/gpu/post_process_helper.h @@ -7,27 +7,21 @@ #include "gpu/sequence.h" #include "util/mini_math.h" -// Use UniformsSequenceParams (defined in sequence.h) for post-process effects - // Standard post-process bind group layout (group 0): -#define PP_BINDING_SAMPLER 0 // Sampler for input texture -#define PP_BINDING_TEXTURE 1 // Input texture (previous render pass) -#define PP_BINDING_UNIFORMS 2 // Custom uniforms buffer -#define PP_BINDING_EFFECT_PARAMS 3 // Effect-specific parameters +#define PP_BINDING_SAMPLER 0 +#define PP_BINDING_TEXTURE 1 +#define PP_BINDING_UNIFORMS 2 +#define PP_BINDING_EFFECT_PARAMS 3 -// Helper to create a standard post-processing pipeline -// Uniforms are accessible to both vertex and fragment shaders WGPURenderPipeline create_post_process_pipeline(WGPUDevice device, WGPUTextureFormat format, const char* shader_code); -// Helper to create a simple post-processing pipeline (no effect params, 3 -// bindings only) +// No effect params, 3 bindings only WGPURenderPipeline create_post_process_pipeline_simple(WGPUDevice device, WGPUTextureFormat format, const char* shader_code); -// Helper to update bind group for post-processing effects void pp_update_bind_group(WGPUDevice device, WGPURenderPipeline pipeline, WGPUBindGroup* bind_group, WGPUTextureView input_view, GpuBuffer uniforms, GpuBuffer effect_params); diff --git a/src/gpu/sdf_effect.h b/src/gpu/sdf_effect.h index aacea69..12ea556 100644 --- a/src/gpu/sdf_effect.h +++ b/src/gpu/sdf_effect.h @@ -1,5 +1,5 @@ // This file is part of the 64k demo project. -// It defines SDFEffect base class for raymarching effects. +// SDF raymarching effect base class. #pragma once @@ -8,11 +8,8 @@ #include "gpu/effect.h" #include "gpu/uniform_helper.h" -// Base class for SDF raymarching effects -// Provides CameraParams uniform buffer and helper methods -// // Binding convention: -// @group(0) @binding(2): UniformsSequenceParams (from Effect base) +// @group(0) @binding(2): UniformsSequenceParams // @group(0) @binding(3): CameraParams // @group(0) @binding(4+): Per-effect custom parameters class SDFEffect : public Effect { @@ -23,7 +20,6 @@ class SDFEffect : public Effect { virtual ~SDFEffect() = default; - // Populate camera parameters from Camera object void update_camera(const Camera& camera, float aspect_ratio) { CameraParams params; params.inv_view = camera.get_view_matrix().inverse(); @@ -34,7 +30,6 @@ class SDFEffect : public Effect { camera_params_.update(ctx_.queue, params); } - // Populate camera parameters with custom values void update_camera(const vec3& position, const vec3& target, const vec3& up, float fov, float near_plane, float far_plane, float aspect_ratio) { diff --git a/src/gpu/sequence.h b/src/gpu/sequence.h index 0f448ce..3bb770f 100644 --- a/src/gpu/sequence.h +++ b/src/gpu/sequence.h @@ -1,5 +1,4 @@ -// Sequence: Explicit node system with DAG effect routing -// DAG-based effect routing with ping-pong optimization +// Sequence: DAG-based effect routing with ping-pong optimization #ifndef SEQUENCE_H #define SEQUENCE_H @@ -16,11 +15,11 @@ class Effect; enum class NodeType { - U8X4_NORM, // RGBAu8 normalized (0-1) - default Source/Sink - F32X4, // RGBA float32 - F16X8, // 8-channel float16 - DEPTH24, // Depth buffer - COMPUTE_F32, // Compute buffer + U8X4_NORM, // RGBAu8 (default Source/Sink) + F32X4, + F16X8, + DEPTH24, + COMPUTE_F32, }; struct Node { @@ -49,29 +48,22 @@ class NodeRegistry { NodeRegistry(WGPUDevice device, int default_width, int default_height); ~NodeRegistry(); - // Declare new node with explicit type/dimensions void declare_node(const std::string& name, NodeType type, int width, int height); - // Declare aliased node (ping-pong optimization) void declare_aliased_node(const std::string& name, const std::string& alias_of); - // Retrieve views WGPUTextureView get_view(const std::string& name); std::vector<WGPUTextureView> get_output_views(const std::vector<std::string>& names); - // Retrieve texture (for blits) WGPUTexture get_texture(const std::string& name); - // Resize all nodes void resize(int width, int height); - // Check if node exists bool has_node(const std::string& name) const; - // Register external view (for source/sink managed externally) void set_external_view(const std::string& name, WGPUTextureView view); private: @@ -79,7 +71,7 @@ class NodeRegistry { int default_width_; int default_height_; std::map<std::string, Node> nodes_; - std::map<std::string, std::string> aliases_; // name -> backing node name + std::map<std::string, std::string> aliases_; void create_texture(Node& node); }; @@ -88,7 +80,7 @@ struct EffectDAGNode { std::shared_ptr<Effect> effect; std::vector<std::string> input_nodes; std::vector<std::string> output_nodes; - int execution_order; // Topologically sorted + int execution_order; }; class Sequence { @@ -96,7 +88,6 @@ class Sequence { Sequence(const GpuContext& ctx, int width, int height); virtual ~Sequence() = default; - // Virtual methods (most sequences use defaults) virtual void preprocess(float seq_time, float beat_time, float beat_phase, float audio_intensity); virtual void postprocess(WGPUCommandEncoder encoder); @@ -104,20 +95,16 @@ class Sequence { void resize(int width, int height); - // Initialize effect nodes (call at end of subclass constructor) void init_effect_nodes(); - // Set surface texture view for rendering (sink node) void set_sink_view(WGPUTextureView view) { nodes_.set_external_view("sink", view); } - // Set source texture view (input framebuffer) void set_source_view(WGPUTextureView view) { nodes_.set_external_view("source", view); } - // Test accessor const std::vector<EffectDAGNode>& get_effect_dag() const { return effect_dag_; } diff --git a/src/gpu/shader_composer.h b/src/gpu/shader_composer.h index d0972f2..90e1ec1 100644 --- a/src/gpu/shader_composer.h +++ b/src/gpu/shader_composer.h @@ -12,19 +12,16 @@ class ShaderComposer { public: static ShaderComposer& Get(); - // Register a snippet (e.g. "common_math", "sdf_primitives") void RegisterSnippet(const std::string& name, const std::string& code); using CompositionMap = std::map<std::string, std::string>; - // Assemble a final shader string by prepending required snippets - // and recursively resolving #include "snippet_name" directives. - // Optional substitutions: map "placeholder_name" -> "actual_snippet_name" + // Resolves #include directives recursively + // substitutions: map "placeholder_name" -> "actual_snippet_name" std::string Compose(const std::vector<std::string>& dependencies, const std::string& main_code, const CompositionMap& substitutions = {}); - // Verify all #include directives reference registered snippets void VerifyIncludes() const; private: diff --git a/src/gpu/uniform_helper.h b/src/gpu/uniform_helper.h index 8556c98..b2f3406 100644 --- a/src/gpu/uniform_helper.h +++ b/src/gpu/uniform_helper.h @@ -1,12 +1,10 @@ // This file is part of the 64k demo project. -// It provides a generic uniform buffer helper to reduce boilerplate. -// Templated on uniform struct type for type safety and automatic sizing. +// Generic uniform buffer helper. #pragma once #include "gpu/gpu.h" -// Generic uniform buffer helper // Usage: // UniformBuffer<MyUniforms> uniforms_; // uniforms_.init(device); @@ -15,18 +13,15 @@ template <typename T> class UniformBuffer { public: UniformBuffer() = default; - // Initialize the uniform buffer with the device void init(WGPUDevice device) { buffer_ = gpu_create_buffer( device, sizeof(T), WGPUBufferUsage_Uniform | WGPUBufferUsage_CopyDst); } - // Update the uniform buffer with new data void update(WGPUQueue queue, const T& data) { wgpuQueueWriteBuffer(queue, buffer_.buffer, 0, &data, sizeof(T)); } - // Get the underlying GpuBuffer (for bind group creation) GpuBuffer& get() { return buffer_; } diff --git a/src/gpu/wgpu_resource.h b/src/gpu/wgpu_resource.h new file mode 100644 index 0000000..e448b18 --- /dev/null +++ b/src/gpu/wgpu_resource.h @@ -0,0 +1,45 @@ +// WGPU Resource RAII Wrapper +// Automatic release on destruction +// +// IMPORTANT: Field ordering matters for destruction order. +// Members are destroyed in reverse declaration order. +// Declare dependencies before dependents: +// RenderPipeline pipeline_; // Destroyed last +// BindGroup bind_group_; // Destroyed first (may reference pipeline) + +#pragma once +#include "platform/platform.h" +#include "util/fatal_error.h" + +template<typename T, void(*Release)(T)> +class WGPUResource { + public: + WGPUResource() : ptr_(nullptr) {} + ~WGPUResource() { if (ptr_) Release(ptr_); } + + void set(T ptr) { + FATAL_ASSERT(ptr_ == nullptr); + ptr_ = ptr; + } + + void replace(T ptr) { + if (ptr_) Release(ptr_); + ptr_ = ptr; + } + + T get() const { return ptr_; } + T* get_address() { return &ptr_; } + + private: + T ptr_; + WGPUResource(const WGPUResource&) = delete; + WGPUResource& operator=(const WGPUResource&) = delete; +}; + +using BindGroup = WGPUResource<WGPUBindGroup, wgpuBindGroupRelease>; +using RenderPipeline = WGPUResource<WGPURenderPipeline, wgpuRenderPipelineRelease>; +using ComputePipeline = WGPUResource<WGPUComputePipeline, wgpuComputePipelineRelease>; +using Sampler = WGPUResource<WGPUSampler, wgpuSamplerRelease>; +using Texture = WGPUResource<WGPUTexture, wgpuTextureRelease>; +using TextureView = WGPUResource<WGPUTextureView, wgpuTextureViewRelease>; +using Buffer = WGPUResource<WGPUBuffer, wgpuBufferRelease>; |
