summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/effects/flash_effect.cc52
-rw-r--r--src/effects/flash_effect.h10
-rw-r--r--src/effects/gaussian_blur_effect.cc24
-rw-r--r--src/effects/gaussian_blur_effect.h2
-rw-r--r--src/effects/heptagon_effect.cc55
-rw-r--r--src/effects/heptagon_effect.h10
-rw-r--r--src/effects/particles_effect.cc10
-rw-r--r--src/effects/particles_effect.h1
-rw-r--r--src/effects/passthrough_effect.cc36
-rw-r--r--src/effects/passthrough_effect.h7
-rw-r--r--src/effects/peak_meter_effect.cc2
-rw-r--r--src/effects/peak_meter_effect.h1
-rw-r--r--src/effects/placeholder_effect.cc18
-rw-r--r--src/effects/placeholder_effect.h2
-rw-r--r--src/gpu/effect.cc19
-rw-r--r--src/gpu/effect.h20
-rw-r--r--src/gpu/gpu.cc35
-rw-r--r--src/gpu/gpu.h9
-rw-r--r--src/gpu/wgpu_resource.h45
19 files changed, 181 insertions, 177 deletions
diff --git a/src/effects/flash_effect.cc b/src/effects/flash_effect.cc
index 7baf6a2..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,8 +29,8 @@ 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
@@ -75,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 7304321..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;
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 c472f2f..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,8 +31,8 @@ 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
@@ -80,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/particles_effect.cc b/src/effects/particles_effect.cc
index d83f303..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 =
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 b9d9337..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,14 +40,11 @@ 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 = {};
@@ -72,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 d823e20..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 {
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 3367f1c..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,
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/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 f5cdf2d..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>
@@ -51,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 9a3fd38..d6c0255 100644
--- a/src/gpu/gpu.h
+++ b/src/gpu/gpu.h
@@ -97,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/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>;