From 7041babc9e5333d01191f3eb80fd711bd26cd4f7 Mon Sep 17 00:00:00 2001 From: skal Date: Tue, 17 Feb 2026 08:46:01 +0100 Subject: feat: add time-based effect activation with auto-passthrough Effects now accept start/end time parameters and automatically passthrough when inactive. Implements buffer chain integrity via compile-time validation. - Effect base class: dispatch_render() checks time bounds, auto-passthroughs 1:1 input/output effects outside [start, end] interval - seq_compiler.py: validates producer/consumer lifespan constraints for multi-output effects, adds --validate flag, always validates before codegen - Updated all 9 effect classes and test fixtures to pass start/end times - check_all.sh: includes timeline validation step - Tests: 34/34 passing, demo runs successfully Co-Authored-By: Claude Sonnet 4.5 --- src/effects/flash_effect.cc | 8 +++++--- src/effects/flash_effect.h | 3 ++- src/effects/gaussian_blur_effect.cc | 5 +++-- src/effects/gaussian_blur_effect.h | 3 ++- src/effects/heptagon_effect.cc | 5 +++-- src/effects/heptagon_effect.h | 3 ++- src/effects/hybrid3_d_effect.cc | 5 +++-- src/effects/hybrid3_d_effect.h | 3 ++- src/effects/particles_effect.cc | 5 +++-- src/effects/particles_effect.h | 3 ++- src/effects/passthrough_effect.cc | 7 ++++--- src/effects/passthrough_effect.h | 3 ++- src/effects/peak_meter_effect.cc | 5 +++-- src/effects/peak_meter_effect.h | 3 ++- src/effects/placeholder_effect.cc | 5 +++-- src/effects/placeholder_effect.h | 4 ++-- src/effects/rotating_cube_effect.cc | 5 +++-- src/effects/rotating_cube_effect.h | 3 ++- 18 files changed, 48 insertions(+), 30 deletions(-) (limited to 'src/effects') diff --git a/src/effects/flash_effect.cc b/src/effects/flash_effect.cc index ac46562..c8638f0 100644 --- a/src/effects/flash_effect.cc +++ b/src/effects/flash_effect.cc @@ -7,9 +7,11 @@ #include "util/fatal_error.h" Flash::Flash(const GpuContext& ctx, const std::vector& inputs, - const std::vector& outputs) - : Effect(ctx, inputs, outputs), pipeline_(nullptr), bind_group_(nullptr), - sampler_(nullptr), dummy_texture_(nullptr), dummy_texture_view_(nullptr) { + const std::vector& 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) { HEADLESS_RETURN_IF_NULL(ctx_.device); uniforms_buffer_.init(ctx_.device); diff --git a/src/effects/flash_effect.h b/src/effects/flash_effect.h index 08b7709..1117e0a 100644 --- a/src/effects/flash_effect.h +++ b/src/effects/flash_effect.h @@ -8,7 +8,8 @@ class Flash : public Effect { public: Flash(const GpuContext& ctx, const std::vector& inputs, - const std::vector& outputs); + const std::vector& outputs, float start_time, + float end_time); ~Flash() override; void render(WGPUCommandEncoder encoder, const UniformsSequenceParams& params, diff --git a/src/effects/gaussian_blur_effect.cc b/src/effects/gaussian_blur_effect.cc index b2713ae..02cd77e 100644 --- a/src/effects/gaussian_blur_effect.cc +++ b/src/effects/gaussian_blur_effect.cc @@ -7,8 +7,9 @@ GaussianBlur::GaussianBlur(const GpuContext& ctx, const std::vector& inputs, - const std::vector& outputs) - : Effect(ctx, inputs, outputs), pipeline_(nullptr), bind_group_(nullptr), + const std::vector& 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) HEADLESS_RETURN_IF_NULL(ctx_.device); diff --git a/src/effects/gaussian_blur_effect.h b/src/effects/gaussian_blur_effect.h index 4cb55a3..cc9ff56 100644 --- a/src/effects/gaussian_blur_effect.h +++ b/src/effects/gaussian_blur_effect.h @@ -17,7 +17,8 @@ static_assert(sizeof(GaussianBlurParams) == 16, class GaussianBlur : public Effect { public: GaussianBlur(const GpuContext& ctx, const std::vector& inputs, - const std::vector& outputs); + const std::vector& outputs, float start_time, + float end_time); void render(WGPUCommandEncoder encoder, const UniformsSequenceParams& params, NodeRegistry& nodes) override; diff --git a/src/effects/heptagon_effect.cc b/src/effects/heptagon_effect.cc index fd83229..13d7aad 100644 --- a/src/effects/heptagon_effect.cc +++ b/src/effects/heptagon_effect.cc @@ -8,8 +8,9 @@ Heptagon::Heptagon(const GpuContext& ctx, const std::vector& inputs, - const std::vector& outputs) - : Effect(ctx, inputs, outputs), pipeline_(nullptr), bind_group_(nullptr), + const std::vector& 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) HEADLESS_RETURN_IF_NULL(ctx_.device); diff --git a/src/effects/heptagon_effect.h b/src/effects/heptagon_effect.h index f4e4f9e..ef05d79 100644 --- a/src/effects/heptagon_effect.h +++ b/src/effects/heptagon_effect.h @@ -8,7 +8,8 @@ class Heptagon : public Effect { public: Heptagon(const GpuContext& ctx, const std::vector& inputs, - const std::vector& outputs); + const std::vector& outputs, float start_time, + float end_time); ~Heptagon(); void render(WGPUCommandEncoder encoder, const UniformsSequenceParams& params, diff --git a/src/effects/hybrid3_d_effect.cc b/src/effects/hybrid3_d_effect.cc index 027e2a7..1dd1c05 100644 --- a/src/effects/hybrid3_d_effect.cc +++ b/src/effects/hybrid3_d_effect.cc @@ -9,8 +9,9 @@ Hybrid3D::Hybrid3D(const GpuContext& ctx, const std::vector& inputs, - const std::vector& outputs) - : Effect(ctx, inputs, outputs), depth_node_(outputs[0] + "_depth"), + const std::vector& outputs, float start_time, + float end_time) + : Effect(ctx, inputs, outputs, start_time, end_time), depth_node_(outputs[0] + "_depth"), dummy_texture_(nullptr), dummy_texture_view_(nullptr) { // Headless mode: skip GPU resource creation (compiled out in STRIP_ALL) HEADLESS_RETURN_IF_NULL(ctx_.device); diff --git a/src/effects/hybrid3_d_effect.h b/src/effects/hybrid3_d_effect.h index 10bee58..e0aa6da 100644 --- a/src/effects/hybrid3_d_effect.h +++ b/src/effects/hybrid3_d_effect.h @@ -12,7 +12,8 @@ class Hybrid3D : public Effect { public: Hybrid3D(const GpuContext& ctx, const std::vector& inputs, - const std::vector& outputs); + const std::vector& outputs, float start_time, + float end_time); ~Hybrid3D() override; void declare_nodes(NodeRegistry& registry) override; diff --git a/src/effects/particles_effect.cc b/src/effects/particles_effect.cc index 35e9919..22e41e9 100644 --- a/src/effects/particles_effect.cc +++ b/src/effects/particles_effect.cc @@ -9,8 +9,9 @@ Particles::Particles(const GpuContext& ctx, const std::vector& inputs, - const std::vector& outputs) - : Effect(ctx, inputs, outputs) { + const std::vector& 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); diff --git a/src/effects/particles_effect.h b/src/effects/particles_effect.h index 5799690..38c60d7 100644 --- a/src/effects/particles_effect.h +++ b/src/effects/particles_effect.h @@ -21,7 +21,8 @@ struct Particle { class Particles : public Effect { public: Particles(const GpuContext& ctx, const std::vector& inputs, - const std::vector& outputs); + const std::vector& outputs, float start_time, + float end_time); void render(WGPUCommandEncoder encoder, const UniformsSequenceParams& params, NodeRegistry& nodes) override; diff --git a/src/effects/passthrough_effect.cc b/src/effects/passthrough_effect.cc index be53846..392102d 100644 --- a/src/effects/passthrough_effect.cc +++ b/src/effects/passthrough_effect.cc @@ -7,9 +7,10 @@ Passthrough::Passthrough(const GpuContext& ctx, const std::vector& inputs, - const std::vector& outputs) - : Effect(ctx, inputs, outputs), pipeline_(nullptr), bind_group_(nullptr), - sampler_(nullptr) { + const std::vector& 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) HEADLESS_RETURN_IF_NULL(ctx_.device); diff --git a/src/effects/passthrough_effect.h b/src/effects/passthrough_effect.h index 00bf423..f684b9b 100644 --- a/src/effects/passthrough_effect.h +++ b/src/effects/passthrough_effect.h @@ -8,7 +8,8 @@ class Passthrough : public Effect { public: Passthrough(const GpuContext& ctx, const std::vector& inputs, - const std::vector& outputs); + const std::vector& outputs, float start_time, + float end_time); void render(WGPUCommandEncoder encoder, const UniformsSequenceParams& params, NodeRegistry& nodes) override; diff --git a/src/effects/peak_meter_effect.cc b/src/effects/peak_meter_effect.cc index a2cf6fc..63be2c0 100644 --- a/src/effects/peak_meter_effect.cc +++ b/src/effects/peak_meter_effect.cc @@ -7,8 +7,9 @@ PeakMeter::PeakMeter(const GpuContext& ctx, const std::vector& inputs, - const std::vector& outputs) - : Effect(ctx, inputs, outputs), pipeline_(nullptr), bind_group_(nullptr) { + const std::vector& outputs, float start_time, + float end_time) + : Effect(ctx, inputs, outputs, start_time, end_time), pipeline_(nullptr), bind_group_(nullptr) { HEADLESS_RETURN_IF_NULL(ctx_.device); uniforms_buffer_.init(ctx_.device); diff --git a/src/effects/peak_meter_effect.h b/src/effects/peak_meter_effect.h index b563289..e397a71 100644 --- a/src/effects/peak_meter_effect.h +++ b/src/effects/peak_meter_effect.h @@ -8,7 +8,8 @@ class PeakMeter : public Effect { public: PeakMeter(const GpuContext& ctx, const std::vector& inputs, - const std::vector& outputs); + const std::vector& outputs, float start_time, + float end_time); ~PeakMeter() override; void render(WGPUCommandEncoder encoder, const UniformsSequenceParams& params, diff --git a/src/effects/placeholder_effect.cc b/src/effects/placeholder_effect.cc index 1428779..6ba665a 100644 --- a/src/effects/placeholder_effect.cc +++ b/src/effects/placeholder_effect.cc @@ -9,9 +9,10 @@ Placeholder::Placeholder(const GpuContext& ctx, const std::vector& inputs, const std::vector& outputs, + float start_time, float end_time, const char* placeholder_name) - : Effect(ctx, inputs, outputs), pipeline_(nullptr), bind_group_(nullptr), - sampler_(nullptr), name_(placeholder_name) { + : Effect(ctx, inputs, outputs, start_time, end_time), pipeline_(nullptr), + bind_group_(nullptr), sampler_(nullptr), name_(placeholder_name) { // Log once on construction fprintf(stderr, "TODO: %s not yet implemented, using passthrough\n", name_); diff --git a/src/effects/placeholder_effect.h b/src/effects/placeholder_effect.h index 7d8d734..24cf3f4 100644 --- a/src/effects/placeholder_effect.h +++ b/src/effects/placeholder_effect.h @@ -9,8 +9,8 @@ class Placeholder : public Effect { public: Placeholder(const GpuContext& ctx, const std::vector& inputs, - const std::vector& outputs, - const char* placeholder_name = "UnknownEffect"); + const std::vector& outputs, float start_time, + float end_time, const char* placeholder_name = "UnknownEffect"); void render(WGPUCommandEncoder encoder, const UniformsSequenceParams& params, NodeRegistry& nodes) override; diff --git a/src/effects/rotating_cube_effect.cc b/src/effects/rotating_cube_effect.cc index 42a0ba7..0f1781d 100644 --- a/src/effects/rotating_cube_effect.cc +++ b/src/effects/rotating_cube_effect.cc @@ -9,8 +9,9 @@ RotatingCube::RotatingCube(const GpuContext& ctx, const std::vector& inputs, - const std::vector& outputs) - : Effect(ctx, inputs, outputs), depth_node_(outputs[0] + "_depth") { + const std::vector& outputs, float start_time, + float end_time) + : Effect(ctx, inputs, outputs, start_time, end_time), depth_node_(outputs[0] + "_depth") { // Headless mode: skip GPU resource creation (compiled out in STRIP_ALL) HEADLESS_RETURN_IF_NULL(ctx_.device); diff --git a/src/effects/rotating_cube_effect.h b/src/effects/rotating_cube_effect.h index 0c713d2..e773025 100644 --- a/src/effects/rotating_cube_effect.h +++ b/src/effects/rotating_cube_effect.h @@ -11,7 +11,8 @@ class RotatingCube : public Effect { public: RotatingCube(const GpuContext& ctx, const std::vector& inputs, - const std::vector& outputs); + const std::vector& outputs, float start_time, + float end_time); ~RotatingCube() override; void declare_nodes(NodeRegistry& registry) override; -- cgit v1.2.3