summaryrefslogtreecommitdiff
path: root/src/gpu/sequence_v2.cc
diff options
context:
space:
mode:
Diffstat (limited to 'src/gpu/sequence_v2.cc')
-rw-r--r--src/gpu/sequence_v2.cc207
1 files changed, 207 insertions, 0 deletions
diff --git a/src/gpu/sequence_v2.cc b/src/gpu/sequence_v2.cc
new file mode 100644
index 0000000..c3f9aea
--- /dev/null
+++ b/src/gpu/sequence_v2.cc
@@ -0,0 +1,207 @@
+// Sequence v2 implementation
+
+#include "gpu/sequence_v2.h"
+#include "gpu/effect_v2.h"
+#include "util/fatal_error.h"
+#include <algorithm>
+
+// NodeRegistry implementation
+
+NodeRegistry::NodeRegistry(WGPUDevice device, int default_width,
+ int default_height)
+ : device_(device), default_width_(default_width),
+ default_height_(default_height) {
+ // Reserve source/sink as implicit nodes (managed externally by MainSequence)
+}
+
+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<WGPUTextureView>
+NodeRegistry::get_output_views(const std::vector<std::string>& names) {
+ std::vector<WGPUTextureView> 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::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<unsigned int>(node.width),
+ static_cast<unsigned int>(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<float>(width_), static_cast<float>(height_)};
+ params_.aspect_ratio =
+ static_cast<float>(width_) / static_cast<float>(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);
+ }
+}