diff options
Diffstat (limited to 'src/gpu/sequence_v2.cc')
| -rw-r--r-- | src/gpu/sequence_v2.cc | 207 |
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); + } +} |
