summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--cmake/DemoSourceLists.cmake1
-rw-r--r--src/app/test_demo.cc89
-rw-r--r--src/effects/peak_meter_effect.cc98
-rw-r--r--src/effects/peak_meter_effect.h23
-rw-r--r--src/gpu/demo_effects.h1
-rw-r--r--src/tests/gpu/test_demo_effects.cc4
-rw-r--r--workspaces/test/timeline.seq3
7 files changed, 129 insertions, 90 deletions
diff --git a/cmake/DemoSourceLists.cmake b/cmake/DemoSourceLists.cmake
index a532a9a..3c162e9 100644
--- a/cmake/DemoSourceLists.cmake
+++ b/cmake/DemoSourceLists.cmake
@@ -38,6 +38,7 @@ set(COMMON_GPU_EFFECTS
src/effects/rotating_cube_effect.cc
src/effects/hybrid3_d_effect.cc
src/effects/flash_effect.cc
+ src/effects/peak_meter_effect.cc
# TODO: Port CNN effects to v2 (complex v1 dependencies)
# cnn_v1/src/cnn_v1_effect.cc
# cnn_v2/src/cnn_v2_effect.cc
diff --git a/src/app/test_demo.cc b/src/app/test_demo.cc
index 83e6f8b..6031aef 100644
--- a/src/app/test_demo.cc
+++ b/src/app/test_demo.cc
@@ -19,89 +19,6 @@
#include "generated/test_timeline.h"
extern float GetDemoDuration();
-// TODO: Port PeakMeterEffect and CNN effects to v2
-// #include "../../cnn_v1/src/cnn_v1_effect.h"
-// #include "../../cnn_v2/src/cnn_v2_effect.h"
-// #include "gpu/post_process_helper.h"
-// #include "gpu/shader_composer.h"
-
-#if 0 // Disabled: needs v2 port
-class PeakMeterEffect : public PostProcessEffectV2 {
- public:
- PeakMeterEffect(const GpuContext& ctx) : PostProcessEffect(ctx) {
- // Use ShaderComposer to include CommonUniforms from common_uniforms.wgsl
- const char* shader_main = R"(
- struct VertexOutput {
- @builtin(position) position: vec4<f32>,
- @location(0) uv: vec2<f32>,
- };
-
- struct EffectParams {
- unused: f32,
- };
-
- @group(0) @binding(0) var inputSampler: sampler;
- @group(0) @binding(1) var inputTexture: texture_2d<f32>;
- @group(0) @binding(2) var<uniform> uniforms: CommonUniforms;
- @group(0) @binding(3) var<uniform> params: EffectParams;
-
- @vertex
- fn vs_main(@builtin(vertex_index) vertexIndex: u32) -> VertexOutput {
- var output: VertexOutput;
- // Full-screen triangle (required for post-process pass-through)
- var pos = array<vec2<f32>, 3>(
- vec2<f32>(-1.0, -1.0),
- vec2<f32>(3.0, -1.0),
- vec2<f32>(-1.0, 3.0)
- );
- output.position = vec4<f32>(pos[vertexIndex], 0.0, 1.0);
- output.uv = pos[vertexIndex] * 0.5 + 0.5;
- return output;
- }
-
- @fragment
- fn fs_main(input: VertexOutput) -> @location(0) vec4<f32> {
- // Bar dimensions
- let bar_y_min = 0.005;
- let bar_y_max = 0.015;
- let bar_x_min = 0.015;
- let bar_x_max = 0.250;
- let in_bar_y = input.uv.y >= bar_y_min && input.uv.y <= bar_y_max;
- let in_bar_x = input.uv.x >= bar_x_min && input.uv.x <= bar_x_max;
-
- // Optimization: Return bar color early (avoids texture sampling for ~5% of pixels)
- if (in_bar_y && in_bar_x) {
- let uv_x = (input.uv.x - bar_x_min) / (bar_x_max - bar_x_min);
- let factor = step(uv_x, uniforms.audio_intensity);
- return mix(vec4<f32>(0.0, 0.0, 0.0, 1.0), vec4<f32>(1.0, 0.0, 0.0,1.0), factor);
- }
-
- // Pass through input texture for rest of screen
- return textureSample(inputTexture, inputSampler, input.uv);
- }
- )";
-
- // Compose shader with common_uniforms to get CommonUniforms definition
- std::string shader_code =
- ShaderComposer::Get().Compose({"common_uniforms"}, shader_main);
-
- pipeline_ = create_post_process_pipeline(ctx_.device, ctx_.format,
- shader_code.c_str());
- }
-
- void update_bind_group(WGPUTextureView input_view) override {
- pp_update_bind_group(ctx_.device, pipeline_, &bind_group_, input_view,
- uniforms_.get(), {});
- }
-
- void render(WGPURenderPassEncoder pass,
- const UniformsSequenceParams& uniforms) override {
- uniforms_.update(ctx_.queue, uniforms);
- PostProcessEffect::render(pass, uniforms);
- }
-};
-#endif // Disabled: needs v2 port
-
static int g_cnn_version = 2; // Default to v2
#if !defined(STRIP_ALL)
@@ -228,12 +145,6 @@ int main(int argc, char** argv) {
// Initialize v2 timeline from test_demo.seq
InitializeSequences(*gpu_get_context(), width, height);
-#if !defined(STRIP_ALL)
- // TODO: Port CNN and peak meter effects to v2
- // const GpuContext* gpu_ctx = gpu_get_context();
- // (v1 gpu_add_custom_effect not available in v2)
-#endif
-
audio_init();
static AudioEngine g_audio_engine;
diff --git a/src/effects/peak_meter_effect.cc b/src/effects/peak_meter_effect.cc
new file mode 100644
index 0000000..692d851
--- /dev/null
+++ b/src/effects/peak_meter_effect.cc
@@ -0,0 +1,98 @@
+// Peak meter overlay for audio debugging
+
+#include "effects/peak_meter_effect.h"
+#include "gpu/post_process_helper.h"
+#include "gpu/shader_composer.h"
+#include "util/fatal_error.h"
+
+PeakMeterEffect::PeakMeterEffect(const GpuContext& ctx,
+ const std::vector<std::string>& inputs,
+ const std::vector<std::string>& outputs)
+ : Effect(ctx, inputs, outputs), pipeline_(nullptr), bind_group_(nullptr) {
+ HEADLESS_RETURN_IF_NULL(ctx_.device);
+
+ uniforms_buffer_.init(ctx_.device);
+
+ const char* shader_main = R"(
+ struct VertexOutput {
+ @builtin(position) position: vec4<f32>,
+ @location(0) uv: vec2<f32>,
+ };
+
+ @group(0) @binding(0) var inputSampler: sampler;
+ @group(0) @binding(1) var inputTexture: texture_2d<f32>;
+ @group(0) @binding(2) var<uniform> uniforms: CommonUniforms;
+
+ @vertex
+ fn vs_main(@builtin(vertex_index) vertexIndex: u32) -> VertexOutput {
+ var output: VertexOutput;
+ var pos = array<vec2<f32>, 3>(
+ vec2<f32>(-1.0, -1.0),
+ vec2<f32>(3.0, -1.0),
+ vec2<f32>(-1.0, 3.0)
+ );
+ output.position = vec4<f32>(pos[vertexIndex], 0.0, 1.0);
+ output.uv = pos[vertexIndex] * 0.5 + 0.5;
+ return output;
+ }
+
+ @fragment
+ fn fs_main(input: VertexOutput) -> @location(0) vec4<f32> {
+ let bar_y_min = 0.005;
+ let bar_y_max = 0.015;
+ let bar_x_min = 0.015;
+ let bar_x_max = 0.250;
+ let in_bar_y = input.uv.y >= bar_y_min && input.uv.y <= bar_y_max;
+ let in_bar_x = input.uv.x >= bar_x_min && input.uv.x <= bar_x_max;
+
+ if (in_bar_y && in_bar_x) {
+ let uv_x = (input.uv.x - bar_x_min) / (bar_x_max - bar_x_min);
+ let factor = step(uv_x, uniforms.audio_intensity);
+ return mix(vec4<f32>(0.0, 0.0, 0.0, 1.0), vec4<f32>(1.0, 0.0, 0.0, 1.0), factor);
+ }
+
+ return textureSample(inputTexture, inputSampler, input.uv);
+ }
+ )";
+
+ std::string shader_code =
+ ShaderComposer::Get().Compose({"common_uniforms"}, shader_main);
+
+ pipeline_ = create_post_process_pipeline(ctx_.device,
+ WGPUTextureFormat_RGBA8Unorm,
+ shader_code.c_str());
+}
+
+PeakMeterEffect::~PeakMeterEffect() {
+ if (bind_group_) wgpuBindGroupRelease(bind_group_);
+ if (pipeline_) wgpuRenderPipelineRelease(pipeline_);
+}
+
+void PeakMeterEffect::render(WGPUCommandEncoder encoder,
+ const UniformsSequenceParams& params,
+ NodeRegistry& nodes) {
+ WGPUTextureView input_view = nodes.get_view(input_nodes_[0]);
+ WGPUTextureView output_view = nodes.get_view(output_nodes_[0]);
+
+ uniforms_buffer_.update(ctx_.queue, params);
+ pp_update_bind_group(ctx_.device, pipeline_, &bind_group_, input_view,
+ uniforms_buffer_.get(), {nullptr, 0});
+
+ WGPURenderPassColorAttachment color_attachment = {};
+ color_attachment.view = output_view;
+ color_attachment.depthSlice = WGPU_DEPTH_SLICE_UNDEFINED;
+ 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;
+ pass_desc.colorAttachments = &color_attachment;
+
+ WGPURenderPassEncoder pass = wgpuCommandEncoderBeginRenderPass(encoder, &pass_desc);
+ wgpuRenderPassEncoderSetPipeline(pass, pipeline_);
+ wgpuRenderPassEncoderSetBindGroup(pass, 0, bind_group_, 0, nullptr);
+ wgpuRenderPassEncoderDraw(pass, 3, 1, 0, 0);
+ wgpuRenderPassEncoderEnd(pass);
+ wgpuRenderPassEncoderRelease(pass);
+}
diff --git a/src/effects/peak_meter_effect.h b/src/effects/peak_meter_effect.h
new file mode 100644
index 0000000..905bcda
--- /dev/null
+++ b/src/effects/peak_meter_effect.h
@@ -0,0 +1,23 @@
+// Peak meter overlay for audio debugging
+// Renders red horizontal bar showing audio_intensity
+
+#pragma once
+#include "gpu/effect.h"
+#include "gpu/uniform_helper.h"
+
+class PeakMeterEffect : public Effect {
+ public:
+ PeakMeterEffect(const GpuContext& ctx,
+ const std::vector<std::string>& inputs,
+ const std::vector<std::string>& outputs);
+ ~PeakMeterEffect() override;
+
+ void render(WGPUCommandEncoder encoder,
+ const UniformsSequenceParams& params,
+ NodeRegistry& nodes) override;
+
+ private:
+ WGPURenderPipeline pipeline_;
+ WGPUBindGroup bind_group_;
+ UniformBuffer<UniformsSequenceParams> uniforms_buffer_;
+};
diff --git a/src/gpu/demo_effects.h b/src/gpu/demo_effects.h
index 0151b80..999deb1 100644
--- a/src/gpu/demo_effects.h
+++ b/src/gpu/demo_effects.h
@@ -22,6 +22,7 @@
#include "effects/hybrid3_d_effect.h"
#include "effects/particles_effect.h"
#include "effects/passthrough_effect.h"
+#include "effects/peak_meter_effect.h"
#include "effects/placeholder_effect.h"
#include "effects/rotating_cube_effect.h"
#include "effects/flash_effect.h"
diff --git a/src/tests/gpu/test_demo_effects.cc b/src/tests/gpu/test_demo_effects.cc
index 0c461b5..cebf4a6 100644
--- a/src/tests/gpu/test_demo_effects.cc
+++ b/src/tests/gpu/test_demo_effects.cc
@@ -69,6 +69,10 @@ static void test_effects() {
std::make_shared<FlashEffect>(
fixture.ctx(), std::vector<std::string>{"source"},
std::vector<std::string>{"sink"})},
+ {"PeakMeterEffect",
+ std::make_shared<PeakMeterEffect>(
+ fixture.ctx(), std::vector<std::string>{"source"},
+ std::vector<std::string>{"sink"})},
};
int passed = 0;
diff --git a/workspaces/test/timeline.seq b/workspaces/test/timeline.seq
index 8907014..aa97580 100644
--- a/workspaces/test/timeline.seq
+++ b/workspaces/test/timeline.seq
@@ -3,6 +3,7 @@
# BPM 120 (set in test_demo.track)
SEQUENCE 0.0 0 "MainLoop"
-EFFECT + FlashEffect source -> sink 0.0 16.0
+EFFECT + FlashEffect source -> flash_out 0.0 16.0
+EFFECT + PeakMeterEffect flash_out -> sink 0.0 16.0
END_DEMO 32.0