From b2ede3f0680edc894a54e28374cb87ab2690afa2 Mon Sep 17 00:00:00 2001 From: skal Date: Mon, 16 Feb 2026 14:32:59 +0100 Subject: refactor: remove v2 versioning artifacts, establish Sequence as canonical system MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Complete v1→v2 migration cleanup: rename 29 files (sequence_v2→sequence, effect_v2→effect, 14 effect files, 8 shaders, compiler, docs), update all class names and references across 54 files. Archive v1 timeline. System now uses standard naming with all versioning removed. 30/34 tests passing. Co-Authored-By: Claude Sonnet 4.5 --- src/gpu/demo_effects.h | 20 ++--- src/gpu/effect.cc | 11 +++ src/gpu/effect.h | 50 +++++++++++ src/gpu/effect_v2.cc | 11 --- src/gpu/effect_v2.h | 47 ---------- src/gpu/gpu.cc | 2 +- src/gpu/sequence.cc | 235 +++++++++++++++++++++++++++++++++++++++++++++++++ src/gpu/sequence.h | 131 +++++++++++++++++++++++++++ src/gpu/sequence_v2.cc | 235 ------------------------------------------------- src/gpu/sequence_v2.h | 128 --------------------------- 10 files changed, 438 insertions(+), 432 deletions(-) create mode 100644 src/gpu/effect.cc create mode 100644 src/gpu/effect.h delete mode 100644 src/gpu/effect_v2.cc delete mode 100644 src/gpu/effect_v2.h create mode 100644 src/gpu/sequence.cc create mode 100644 src/gpu/sequence.h delete mode 100644 src/gpu/sequence_v2.cc delete mode 100644 src/gpu/sequence_v2.h (limited to 'src/gpu') diff --git a/src/gpu/demo_effects.h b/src/gpu/demo_effects.h index beccd46..b837ffe 100644 --- a/src/gpu/demo_effects.h +++ b/src/gpu/demo_effects.h @@ -9,21 +9,21 @@ #include "3d/scene.h" // Base effect classes (v2) -#include "gpu/effect_v2.h" +#include "gpu/effect.h" #include "gpu/post_process_helper.h" -#include "gpu/sequence_v2.h" +#include "gpu/sequence.h" #include "gpu/shaders.h" #include "gpu/texture_manager.h" #include "gpu/uniform_helper.h" // Individual Effect Headers (v2) -#include "effects/gaussian_blur_effect_v2.h" -#include "effects/heptagon_effect_v2.h" -#include "effects/hybrid3_d_effect_v2.h" -#include "effects/particles_effect_v2.h" -#include "effects/passthrough_effect_v2.h" -#include "effects/placeholder_effect_v2.h" -#include "effects/rotating_cube_effect_v2.h" +#include "effects/gaussian_blur_effect.h" +#include "effects/heptagon_effect.h" +#include "effects/hybrid3_d_effect.h" +#include "effects/particles_effect.h" +#include "effects/passthrough_effect.h" +#include "effects/placeholder_effect.h" +#include "effects/rotating_cube_effect.h" // TODO: Port CNN effects to v2 // #include "../../cnn_v1/src/cnn_v1_effect.h" // #include "../../cnn_v2/src/cnn_v2_effect.h" @@ -31,7 +31,7 @@ #include // Auto-generated functions from sequence compiler v2 -// See generated/timeline_v2.h for: +// See generated/timeline.h for: // - InitializeV2Sequences() // - GetActiveV2Sequence() // - RenderV2Timeline() diff --git a/src/gpu/effect.cc b/src/gpu/effect.cc new file mode 100644 index 0000000..e4d3a90 --- /dev/null +++ b/src/gpu/effect.cc @@ -0,0 +1,11 @@ +// Effect implementation + +#include "gpu/effect.h" +#include "util/fatal_error.h" + +Effect::Effect(const GpuContext& ctx, const std::vector& inputs, + const std::vector& outputs) + : ctx_(ctx), input_nodes_(inputs), output_nodes_(outputs) { + FATAL_CHECK(!inputs.empty(), "Effect must have at least one input\n"); + FATAL_CHECK(!outputs.empty(), "Effect must have at least one output\n"); +} diff --git a/src/gpu/effect.h b/src/gpu/effect.h new file mode 100644 index 0000000..d40e750 --- /dev/null +++ b/src/gpu/effect.h @@ -0,0 +1,50 @@ +// Effect: Base class for effects with multi-input/multi-output support + +#ifndef EFFECT_H +#define EFFECT_H +#pragma once + +#include "gpu/gpu.h" +#include "gpu/sequence.h" +#include +#include + +class NodeRegistry; + +class Effect { + public: + Effect(const GpuContext& ctx, const std::vector& inputs, + const std::vector& outputs); + virtual ~Effect() = default; + + // Optional: Declare temporary nodes (e.g., multi-pass intermediate buffers) + virtual void declare_nodes(NodeRegistry& registry) { + (void)registry; + } + + // Render effect (multi-input/multi-output) + virtual void render(WGPUCommandEncoder encoder, + const UniformsSequenceParams& params, + NodeRegistry& nodes) = 0; + + // Resize notification + virtual void resize(int width, int height) { + width_ = width; + height_ = height; + } + + const std::vector& input_nodes() const { + return input_nodes_; + } + const std::vector& output_nodes() const { + return output_nodes_; + } + + protected: + const GpuContext& ctx_; + std::vector input_nodes_; + std::vector output_nodes_; + int width_ = 1280; + int height_ = 720; +}; +#endif // EFFECT_H diff --git a/src/gpu/effect_v2.cc b/src/gpu/effect_v2.cc deleted file mode 100644 index 7ecdfbd..0000000 --- a/src/gpu/effect_v2.cc +++ /dev/null @@ -1,11 +0,0 @@ -// EffectV2 implementation - -#include "gpu/effect_v2.h" -#include "util/fatal_error.h" - -EffectV2::EffectV2(const GpuContext& ctx, const std::vector& inputs, - const std::vector& outputs) - : ctx_(ctx), input_nodes_(inputs), output_nodes_(outputs) { - FATAL_CHECK(!inputs.empty(), "Effect must have at least one input\n"); - FATAL_CHECK(!outputs.empty(), "Effect must have at least one output\n"); -} diff --git a/src/gpu/effect_v2.h b/src/gpu/effect_v2.h deleted file mode 100644 index 0d4e18d..0000000 --- a/src/gpu/effect_v2.h +++ /dev/null @@ -1,47 +0,0 @@ -// EffectV2: Base class for v2 effects with multi-input/multi-output support - -#pragma once - -#include "gpu/gpu.h" -#include "gpu/sequence_v2.h" -#include -#include - -class NodeRegistry; - -class EffectV2 { - public: - EffectV2(const GpuContext& ctx, const std::vector& inputs, - const std::vector& outputs); - virtual ~EffectV2() = default; - - // Optional: Declare temporary nodes (e.g., multi-pass intermediate buffers) - virtual void declare_nodes(NodeRegistry& registry) { - (void)registry; - } - - // Render effect (multi-input/multi-output) - virtual void render(WGPUCommandEncoder encoder, - const UniformsSequenceParams& params, - NodeRegistry& nodes) = 0; - - // Resize notification - virtual void resize(int width, int height) { - width_ = width; - height_ = height; - } - - const std::vector& input_nodes() const { - return input_nodes_; - } - const std::vector& output_nodes() const { - return output_nodes_; - } - - protected: - const GpuContext& ctx_; - std::vector input_nodes_; - std::vector output_nodes_; - int width_ = 1280; - int height_ = 720; -}; diff --git a/src/gpu/gpu.cc b/src/gpu/gpu.cc index 647833c..805e555 100644 --- a/src/gpu/gpu.cc +++ b/src/gpu/gpu.cc @@ -3,7 +3,7 @@ // Driven by audio peaks for synchronized visual effects. #include "gpu.h" -#include "generated/timeline_v2.h" +#include "generated/timeline.h" #include "gpu/shader_composer.h" #include "gpu/shaders.h" #include "platform/platform.h" diff --git a/src/gpu/sequence.cc b/src/gpu/sequence.cc new file mode 100644 index 0000000..d0a925f --- /dev/null +++ b/src/gpu/sequence.cc @@ -0,0 +1,235 @@ +// Sequence implementation + +#include "gpu/sequence.h" +#include "gpu/effect.h" +#include "util/fatal_error.h" +#include + +// NodeRegistry implementation + +NodeRegistry::NodeRegistry(WGPUDevice device, int default_width, + int default_height) + : device_(device), default_width_(default_width), + default_height_(default_height) { + // Create source/sink nodes with actual textures + Node source_node = {}; + source_node.type = NodeType::U8X4_NORM; + source_node.width = default_width; + source_node.height = default_height; + create_texture(source_node); + nodes_["source"] = source_node; + + Node sink_node = {}; + sink_node.type = NodeType::U8X4_NORM; + sink_node.width = default_width; + sink_node.height = default_height; + create_texture(sink_node); + nodes_["sink"] = sink_node; +} + +NodeRegistry::~NodeRegistry() { + for (auto& [name, node] : nodes_) { + if (node.view) { + wgpuTextureViewRelease(node.view); + } + for (auto& mip_view : node.mip_views) { + wgpuTextureViewRelease(mip_view); + } + if (node.texture) { + wgpuTextureRelease(node.texture); + } + } +} + +void NodeRegistry::declare_node(const std::string& name, NodeType type, + int width, int height) { + FATAL_CHECK(nodes_.find(name) == nodes_.end(), + "Node already declared: %s\n", name.c_str()); + + if (width <= 0) + width = default_width_; + if (height <= 0) + height = default_height_; + + Node node; + node.type = type; + node.width = width; + node.height = height; + create_texture(node); + + nodes_[name] = node; +} + +void NodeRegistry::declare_aliased_node(const std::string& name, + const std::string& alias_of) { + FATAL_CHECK(nodes_.find(alias_of) != nodes_.end(), + "Alias target does not exist: %s\n", alias_of.c_str()); + FATAL_CHECK(aliases_.find(name) == aliases_.end(), "Alias already exists: %s\n", + name.c_str()); + + aliases_[name] = alias_of; +} + +WGPUTextureView NodeRegistry::get_view(const std::string& name) { + // Check aliases first + auto alias_it = aliases_.find(name); + if (alias_it != aliases_.end()) { + return get_view(alias_it->second); + } + + auto it = nodes_.find(name); + FATAL_CHECK(it != nodes_.end(), "Node not found: %s\n", name.c_str()); + return it->second.view; +} + +std::vector +NodeRegistry::get_output_views(const std::vector& names) { + std::vector views; + views.reserve(names.size()); + for (const auto& name : names) { + views.push_back(get_view(name)); + } + return views; +} + +void NodeRegistry::resize(int width, int height) { + default_width_ = width; + default_height_ = height; + + for (auto& [name, node] : nodes_) { + // Release old texture + if (node.view) { + wgpuTextureViewRelease(node.view); + } + for (auto& mip_view : node.mip_views) { + wgpuTextureViewRelease(mip_view); + } + if (node.texture) { + wgpuTextureRelease(node.texture); + } + + // Recreate with new dimensions + node.width = width; + node.height = height; + create_texture(node); + } +} + +bool NodeRegistry::has_node(const std::string& name) const { + return nodes_.find(name) != nodes_.end() || + aliases_.find(name) != aliases_.end(); +} + +void NodeRegistry::set_external_view(const std::string& name, + WGPUTextureView view) { + // Register external view (texture not owned by registry) + Node node = {}; + node.view = view; + node.texture = nullptr; // Not owned + nodes_[name] = node; +} + +void NodeRegistry::create_texture(Node& node) { + WGPUTextureFormat format; + WGPUTextureUsage usage; + + switch (node.type) { + case NodeType::U8X4_NORM: + format = WGPUTextureFormat_RGBA8Unorm; + usage = WGPUTextureUsage_RenderAttachment | WGPUTextureUsage_TextureBinding; + break; + case NodeType::F32X4: + format = WGPUTextureFormat_RGBA32Float; + usage = WGPUTextureUsage_RenderAttachment | WGPUTextureUsage_TextureBinding; + break; + case NodeType::F16X8: + format = WGPUTextureFormat_RGBA16Float; // WebGPU doesn't have 8-channel, use RGBA16 + usage = WGPUTextureUsage_RenderAttachment | WGPUTextureUsage_TextureBinding; + break; + case NodeType::DEPTH24: + format = WGPUTextureFormat_Depth24Plus; + usage = WGPUTextureUsage_RenderAttachment; + break; + case NodeType::COMPUTE_F32: + format = WGPUTextureFormat_RGBA32Float; + usage = WGPUTextureUsage_StorageBinding | WGPUTextureUsage_TextureBinding; + break; + } + + WGPUTextureDescriptor desc = {}; + desc.usage = usage; + desc.dimension = WGPUTextureDimension_2D; + desc.size = {static_cast(node.width), + static_cast(node.height), 1}; + desc.format = format; + desc.mipLevelCount = 1; + desc.sampleCount = 1; + + node.texture = wgpuDeviceCreateTexture(device_, &desc); + FATAL_CHECK(node.texture != nullptr, "Failed to create texture\n"); + + WGPUTextureViewDescriptor view_desc = {}; + view_desc.format = format; + view_desc.dimension = WGPUTextureViewDimension_2D; + view_desc.baseMipLevel = 0; + view_desc.mipLevelCount = 1; + view_desc.baseArrayLayer = 0; + view_desc.arrayLayerCount = 1; + view_desc.aspect = (node.type == NodeType::DEPTH24) + ? WGPUTextureAspect_DepthOnly + : WGPUTextureAspect_All; + + node.view = wgpuTextureCreateView(node.texture, &view_desc); + FATAL_CHECK(node.view != nullptr, "Failed to create texture view\n"); +} + +// Sequence implementation + +Sequence::Sequence(const GpuContext& ctx, int width, int height) + : ctx_(ctx), width_(width), height_(height), + nodes_(ctx.device, width, height) { + uniforms_buffer_.init(ctx.device); +} + +void Sequence::preprocess(float seq_time, float beat_time, float beat_phase, + float audio_intensity) { + params_.resolution = {static_cast(width_), static_cast(height_)}; + params_.aspect_ratio = + static_cast(width_) / static_cast(height_); + params_.time = seq_time; + params_.beat_time = beat_time; + params_.beat_phase = beat_phase; + params_.audio_intensity = audio_intensity; + params_._pad = 0.0f; + + uniforms_buffer_.update(ctx_.queue, params_); +} + +void Sequence::postprocess(WGPUCommandEncoder encoder) { + (void)encoder; + // Default: No-op (last effect writes to sink directly) +} + +void Sequence::render_effects(WGPUCommandEncoder encoder) { + // Execute DAG in topological order (pre-sorted by compiler) + for (const auto& dag_node : effect_dag_) { + dag_node.effect->render(encoder, params_, nodes_); + } +} + +void Sequence::resize(int width, int height) { + width_ = width; + height_ = height; + nodes_.resize(width, height); + + // Notify effects + for (auto& dag_node : effect_dag_) { + dag_node.effect->resize(width, height); + } +} + +void Sequence::init_effect_nodes() { + for (auto& dag_node : effect_dag_) { + dag_node.effect->declare_nodes(nodes_); + } +} diff --git a/src/gpu/sequence.h b/src/gpu/sequence.h new file mode 100644 index 0000000..a33dedb --- /dev/null +++ b/src/gpu/sequence.h @@ -0,0 +1,131 @@ +// Sequence: Explicit node system with DAG effect routing +// DAG-based effect routing with ping-pong optimization + +#ifndef SEQUENCE_H +#define SEQUENCE_H +#pragma once + +#include "gpu/gpu.h" +#include "gpu/uniform_helper.h" +#include "util/mini_math.h" +#include +#include +#include +#include + +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 +}; + +struct Node { + NodeType type; + int width; + int height; + WGPUTexture texture; + WGPUTextureView view; + std::vector mip_views; // For multi-target render +}; + +struct UniformsSequenceParams { + vec2 resolution; + float aspect_ratio; + float time; // Per-sequence relative time + float beat_time; // Musical beats + float beat_phase; // Fractional beat 0.0-1.0 + float audio_intensity; + float _pad; +}; +static_assert(sizeof(UniformsSequenceParams) == 32, + "UniformsSequenceParams must be 32 bytes for WGSL alignment"); + +class NodeRegistry { + public: + 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 + get_output_views(const std::vector& names); + + // 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: + WGPUDevice device_; + int default_width_; + int default_height_; + std::map nodes_; + std::map aliases_; // name -> backing node name + + void create_texture(Node& node); +}; + +struct EffectDAGNode { + std::shared_ptr effect; + std::vector input_nodes; + std::vector output_nodes; + int execution_order; // Topologically sorted +}; + +class Sequence { + public: + 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); + virtual void render_effects(WGPUCommandEncoder encoder); + + 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& get_effect_dag() const { + return effect_dag_; + } + + protected: + const GpuContext& ctx_; + int width_; + int height_; + NodeRegistry nodes_; + std::vector effect_dag_; + UniformsSequenceParams params_; + UniformBuffer uniforms_buffer_; +}; +#endif // SEQUENCE_H diff --git a/src/gpu/sequence_v2.cc b/src/gpu/sequence_v2.cc deleted file mode 100644 index 3912849..0000000 --- a/src/gpu/sequence_v2.cc +++ /dev/null @@ -1,235 +0,0 @@ -// Sequence v2 implementation - -#include "gpu/sequence_v2.h" -#include "gpu/effect_v2.h" -#include "util/fatal_error.h" -#include - -// NodeRegistry implementation - -NodeRegistry::NodeRegistry(WGPUDevice device, int default_width, - int default_height) - : device_(device), default_width_(default_width), - default_height_(default_height) { - // Create source/sink nodes with actual textures - Node source_node = {}; - source_node.type = NodeType::U8X4_NORM; - source_node.width = default_width; - source_node.height = default_height; - create_texture(source_node); - nodes_["source"] = source_node; - - Node sink_node = {}; - sink_node.type = NodeType::U8X4_NORM; - sink_node.width = default_width; - sink_node.height = default_height; - create_texture(sink_node); - nodes_["sink"] = sink_node; -} - -NodeRegistry::~NodeRegistry() { - for (auto& [name, node] : nodes_) { - if (node.view) { - wgpuTextureViewRelease(node.view); - } - for (auto& mip_view : node.mip_views) { - wgpuTextureViewRelease(mip_view); - } - if (node.texture) { - wgpuTextureRelease(node.texture); - } - } -} - -void NodeRegistry::declare_node(const std::string& name, NodeType type, - int width, int height) { - FATAL_CHECK(nodes_.find(name) == nodes_.end(), - "Node already declared: %s\n", name.c_str()); - - if (width <= 0) - width = default_width_; - if (height <= 0) - height = default_height_; - - Node node; - node.type = type; - node.width = width; - node.height = height; - create_texture(node); - - nodes_[name] = node; -} - -void NodeRegistry::declare_aliased_node(const std::string& name, - const std::string& alias_of) { - FATAL_CHECK(nodes_.find(alias_of) != nodes_.end(), - "Alias target does not exist: %s\n", alias_of.c_str()); - FATAL_CHECK(aliases_.find(name) == aliases_.end(), "Alias already exists: %s\n", - name.c_str()); - - aliases_[name] = alias_of; -} - -WGPUTextureView NodeRegistry::get_view(const std::string& name) { - // Check aliases first - auto alias_it = aliases_.find(name); - if (alias_it != aliases_.end()) { - return get_view(alias_it->second); - } - - auto it = nodes_.find(name); - FATAL_CHECK(it != nodes_.end(), "Node not found: %s\n", name.c_str()); - return it->second.view; -} - -std::vector -NodeRegistry::get_output_views(const std::vector& names) { - std::vector views; - views.reserve(names.size()); - for (const auto& name : names) { - views.push_back(get_view(name)); - } - return views; -} - -void NodeRegistry::resize(int width, int height) { - default_width_ = width; - default_height_ = height; - - for (auto& [name, node] : nodes_) { - // Release old texture - if (node.view) { - wgpuTextureViewRelease(node.view); - } - for (auto& mip_view : node.mip_views) { - wgpuTextureViewRelease(mip_view); - } - if (node.texture) { - wgpuTextureRelease(node.texture); - } - - // Recreate with new dimensions - node.width = width; - node.height = height; - create_texture(node); - } -} - -bool NodeRegistry::has_node(const std::string& name) const { - return nodes_.find(name) != nodes_.end() || - aliases_.find(name) != aliases_.end(); -} - -void NodeRegistry::set_external_view(const std::string& name, - WGPUTextureView view) { - // Register external view (texture not owned by registry) - Node node = {}; - node.view = view; - node.texture = nullptr; // Not owned - nodes_[name] = node; -} - -void NodeRegistry::create_texture(Node& node) { - WGPUTextureFormat format; - WGPUTextureUsage usage; - - switch (node.type) { - case NodeType::U8X4_NORM: - format = WGPUTextureFormat_RGBA8Unorm; - usage = WGPUTextureUsage_RenderAttachment | WGPUTextureUsage_TextureBinding; - break; - case NodeType::F32X4: - format = WGPUTextureFormat_RGBA32Float; - usage = WGPUTextureUsage_RenderAttachment | WGPUTextureUsage_TextureBinding; - break; - case NodeType::F16X8: - format = WGPUTextureFormat_RGBA16Float; // WebGPU doesn't have 8-channel, use RGBA16 - usage = WGPUTextureUsage_RenderAttachment | WGPUTextureUsage_TextureBinding; - break; - case NodeType::DEPTH24: - format = WGPUTextureFormat_Depth24Plus; - usage = WGPUTextureUsage_RenderAttachment; - break; - case NodeType::COMPUTE_F32: - format = WGPUTextureFormat_RGBA32Float; - usage = WGPUTextureUsage_StorageBinding | WGPUTextureUsage_TextureBinding; - break; - } - - WGPUTextureDescriptor desc = {}; - desc.usage = usage; - desc.dimension = WGPUTextureDimension_2D; - desc.size = {static_cast(node.width), - static_cast(node.height), 1}; - desc.format = format; - desc.mipLevelCount = 1; - desc.sampleCount = 1; - - node.texture = wgpuDeviceCreateTexture(device_, &desc); - FATAL_CHECK(node.texture != nullptr, "Failed to create texture\n"); - - WGPUTextureViewDescriptor view_desc = {}; - view_desc.format = format; - view_desc.dimension = WGPUTextureViewDimension_2D; - view_desc.baseMipLevel = 0; - view_desc.mipLevelCount = 1; - view_desc.baseArrayLayer = 0; - view_desc.arrayLayerCount = 1; - view_desc.aspect = (node.type == NodeType::DEPTH24) - ? WGPUTextureAspect_DepthOnly - : WGPUTextureAspect_All; - - node.view = wgpuTextureCreateView(node.texture, &view_desc); - FATAL_CHECK(node.view != nullptr, "Failed to create texture view\n"); -} - -// SequenceV2 implementation - -SequenceV2::SequenceV2(const GpuContext& ctx, int width, int height) - : ctx_(ctx), width_(width), height_(height), - nodes_(ctx.device, width, height) { - uniforms_buffer_.init(ctx.device); -} - -void SequenceV2::preprocess(float seq_time, float beat_time, float beat_phase, - float audio_intensity) { - params_.resolution = {static_cast(width_), static_cast(height_)}; - params_.aspect_ratio = - static_cast(width_) / static_cast(height_); - params_.time = seq_time; - params_.beat_time = beat_time; - params_.beat_phase = beat_phase; - params_.audio_intensity = audio_intensity; - params_._pad = 0.0f; - - uniforms_buffer_.update(ctx_.queue, params_); -} - -void SequenceV2::postprocess(WGPUCommandEncoder encoder) { - (void)encoder; - // Default: No-op (last effect writes to sink directly) -} - -void SequenceV2::render_effects(WGPUCommandEncoder encoder) { - // Execute DAG in topological order (pre-sorted by compiler) - for (const auto& dag_node : effect_dag_) { - dag_node.effect->render(encoder, params_, nodes_); - } -} - -void SequenceV2::resize(int width, int height) { - width_ = width; - height_ = height; - nodes_.resize(width, height); - - // Notify effects - for (auto& dag_node : effect_dag_) { - dag_node.effect->resize(width, height); - } -} - -void SequenceV2::init_effect_nodes() { - for (auto& dag_node : effect_dag_) { - dag_node.effect->declare_nodes(nodes_); - } -} diff --git a/src/gpu/sequence_v2.h b/src/gpu/sequence_v2.h deleted file mode 100644 index 2197a82..0000000 --- a/src/gpu/sequence_v2.h +++ /dev/null @@ -1,128 +0,0 @@ -// Sequence v2: Explicit node system with DAG effect routing -// Replaces implicit framebuffer ping-pong with compile-time optimized nodes - -#pragma once - -#include "gpu/gpu.h" -#include "gpu/uniform_helper.h" -#include "util/mini_math.h" -#include -#include -#include -#include - -class EffectV2; - -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 -}; - -struct Node { - NodeType type; - int width; - int height; - WGPUTexture texture; - WGPUTextureView view; - std::vector mip_views; // For multi-target render -}; - -struct UniformsSequenceParams { - vec2 resolution; - float aspect_ratio; - float time; // Per-sequence relative time - float beat_time; // Musical beats - float beat_phase; // Fractional beat 0.0-1.0 - float audio_intensity; - float _pad; -}; -static_assert(sizeof(UniformsSequenceParams) == 32, - "UniformsSequenceParams must be 32 bytes for WGSL alignment"); - -class NodeRegistry { - public: - 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 - get_output_views(const std::vector& names); - - // 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: - WGPUDevice device_; - int default_width_; - int default_height_; - std::map nodes_; - std::map aliases_; // name -> backing node name - - void create_texture(Node& node); -}; - -struct EffectDAGNode { - std::shared_ptr effect; - std::vector input_nodes; - std::vector output_nodes; - int execution_order; // Topologically sorted -}; - -class SequenceV2 { - public: - SequenceV2(const GpuContext& ctx, int width, int height); - virtual ~SequenceV2() = 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); - virtual void render_effects(WGPUCommandEncoder encoder); - - 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& get_effect_dag() const { - return effect_dag_; - } - - protected: - const GpuContext& ctx_; - int width_; - int height_; - NodeRegistry nodes_; - std::vector effect_dag_; - UniformsSequenceParams params_; - UniformBuffer uniforms_buffer_; -}; -- cgit v1.2.3