summaryrefslogtreecommitdiff
path: root/src/gpu/effect.cc
diff options
context:
space:
mode:
Diffstat (limited to 'src/gpu/effect.cc')
-rw-r--r--src/gpu/effect.cc573
1 files changed, 0 insertions, 573 deletions
diff --git a/src/gpu/effect.cc b/src/gpu/effect.cc
deleted file mode 100644
index ca98ebd..0000000
--- a/src/gpu/effect.cc
+++ /dev/null
@@ -1,573 +0,0 @@
-// This file is part of the 64k demo project.
-// It implements the Sequence management logic.
-
-#include "effect.h"
-#include "audio/tracker.h"
-#include "gpu/demo_effects.h"
-#include "gpu/gpu.h"
-#include "util/fatal_error.h"
-#include <algorithm>
-#include <cstdio>
-#include <typeinfo>
-#include <vector>
-
-// --- PostProcessEffect ---
-void PostProcessEffect::render(WGPURenderPassEncoder pass,
- const CommonPostProcessUniforms& uniforms) {
- (void)uniforms; // Base implementation doesn't use uniforms
- if (pipeline_ && bind_group_) {
- wgpuRenderPassEncoderSetPipeline(pass, pipeline_);
- wgpuRenderPassEncoderSetBindGroup(pass, 0, bind_group_, 0, nullptr);
- wgpuRenderPassEncoderDraw(pass, 3, 1, 0, 0); // Fullscreen triangle
- }
-}
-
-// --- Sequence Implementation ---
-void Sequence::resize(int width, int height) {
- for (SequenceItem& item : items_) {
- item.effect->resize(width, height);
- }
-}
-
-void Sequence::init(MainSequence* demo) {
- for (SequenceItem& item : items_) {
- if (!item.effect->is_initialized) {
- item.effect->init(demo);
- item.effect->is_initialized = true;
- }
- }
-}
-
-void Sequence::add_effect(std::shared_ptr<Effect> effect, float start_time,
- float end_time, int priority) {
- items_.push_back({effect, start_time, end_time, priority, false});
- is_sorted_ = false;
-}
-
-void Sequence::sort_items() {
- if (is_sorted_)
- return;
- std::sort(items_.begin(), items_.end(),
- [](const SequenceItem& a, const SequenceItem& b) {
- return a.priority < b.priority;
- });
- is_sorted_ = true;
-}
-
-void Sequence::update_active_list(float seq_time) {
- // Check if sequence has ended (if explicit end time is set)
- const bool sequence_ended = (end_time_ >= 0.0f && seq_time >= end_time_);
-
- for (SequenceItem& item : items_) {
- bool should_be_active = !sequence_ended && (seq_time >= item.start_time &&
- seq_time < item.end_time);
-
- if (should_be_active && !item.active) {
-#if !defined(STRIP_ALL)
- Effect* effect_ptr = item.effect.get();
- const char* effect_name = typeid(*effect_ptr).name();
- printf(" [EFFECT START] <%s> (priority=%d, time=%.2f-%.2f)\n",
- effect_name, item.priority, item.start_time, item.end_time);
-#endif
- item.effect->start();
- item.active = true;
- } else if (!should_be_active && item.active) {
-#if !defined(STRIP_ALL)
- Effect* effect_ptr = item.effect.get();
- const char* effect_name = typeid(*effect_ptr).name();
- printf(" [EFFECT END] <%s> (priority=%d)\n", effect_name, item.priority);
-#endif
- item.effect->end();
- item.active = false;
- }
- }
-}
-
-void Sequence::collect_active_effects(
- std::vector<SequenceItem*>& scene_effects,
- std::vector<SequenceItem*>& post_effects) {
- sort_items();
- for (SequenceItem& item : items_) {
- if (item.active) {
- if (item.effect->is_post_process()) {
- post_effects.push_back(&item);
- } else {
- scene_effects.push_back(&item);
- }
- }
- }
-}
-
-void Sequence::reset() {
- for (SequenceItem& item : items_) {
- if (item.active) {
- item.effect->end();
- item.active = false;
- }
- }
-}
-
-// --- MainSequence Implementation ---
-
-MainSequence::MainSequence() = default;
-MainSequence::~MainSequence() = default;
-
-void MainSequence::create_framebuffers(int width, int height) {
- // In test mode, this would be skipped or mocked.
- // For now, it will only be called by the real init.
- WGPUTextureDescriptor desc = {};
- desc.usage =
- WGPUTextureUsage_RenderAttachment | WGPUTextureUsage_TextureBinding;
- desc.dimension = WGPUTextureDimension_2D;
- desc.size = {(uint32_t)width, (uint32_t)height, 1};
- desc.format = gpu_ctx.format;
- desc.mipLevelCount = 1;
- desc.sampleCount = 1;
-
- framebuffer_a_ = wgpuDeviceCreateTexture(gpu_ctx.device, &desc);
- framebuffer_b_ = wgpuDeviceCreateTexture(gpu_ctx.device, &desc);
-
- framebuffer_view_a_ =
- gpu_create_texture_view_2d(framebuffer_a_, gpu_ctx.format);
- framebuffer_view_b_ =
- gpu_create_texture_view_2d(framebuffer_b_, gpu_ctx.format);
-
- // Depth Buffer
- WGPUTextureDescriptor depth_desc = {};
- depth_desc.usage = WGPUTextureUsage_RenderAttachment;
- depth_desc.dimension = WGPUTextureDimension_2D;
- depth_desc.size = {(uint32_t)width, (uint32_t)height, 1};
- depth_desc.format = WGPUTextureFormat_Depth24Plus;
- depth_desc.mipLevelCount = 1;
- depth_desc.sampleCount = 1;
- depth_texture_ = wgpuDeviceCreateTexture(gpu_ctx.device, &depth_desc);
-
- WGPUTextureViewDescriptor depth_view_desc = {};
- depth_view_desc.format = WGPUTextureFormat_Depth24Plus;
- depth_view_desc.dimension = WGPUTextureViewDimension_2D;
- depth_view_desc.aspect = WGPUTextureAspect_DepthOnly;
- depth_view_desc.mipLevelCount = 1;
- depth_view_desc.arrayLayerCount = 1;
- depth_view_ = wgpuTextureCreateView(depth_texture_, &depth_view_desc);
-}
-
-void MainSequence::init_test(const GpuContext& ctx) {
- gpu_ctx = ctx;
- // Use sensible defaults for test dimensions
- width_ = 1280;
- height_ = 720;
-
- create_framebuffers(width_, height_);
- passthrough_effect_ = std::make_unique<PassthroughEffect>(gpu_ctx);
- passthrough_effect_->resize(width_, height_);
- // Sequences are added later in the test, so no need to iterate here.
-}
-
-void MainSequence::init(const GpuContext& ctx, int width, int height) {
- gpu_ctx = ctx;
- width_ = width;
- height_ = height;
-
- create_framebuffers(width, height);
- passthrough_effect_ = std::make_unique<PassthroughEffect>(gpu_ctx);
- passthrough_effect_->resize(width, height);
-
- for (ActiveSequence& entry : sequences_) {
- entry.seq->resize(width, height); // Set dimensions FIRST
- entry.seq->init(this); // Then init with correct dimensions
- }
-}
-
-void MainSequence::add_sequence(std::shared_ptr<Sequence> seq, float start_time,
- int priority) {
- sequences_.push_back({seq, start_time, priority});
- // If MainSequence is already initialized, init the new sequence immediately
- if (gpu_ctx.device) {
- seq->resize(width_, height_); // Set dimensions FIRST
- seq->init(this); // Then init with correct dimensions
- }
- std::sort(sequences_.begin(), sequences_.end(),
- [](const ActiveSequence& a, const ActiveSequence& b) {
- return a.priority < b.priority;
- });
-}
-
-void MainSequence::resize(int width, int height) {
- width_ = width;
- height_ = height;
- // Release old resources
- if (framebuffer_view_a_)
- wgpuTextureViewRelease(framebuffer_view_a_);
- if (framebuffer_a_)
- wgpuTextureRelease(framebuffer_a_);
- if (framebuffer_view_b_)
- wgpuTextureViewRelease(framebuffer_view_b_);
- if (framebuffer_b_)
- wgpuTextureRelease(framebuffer_b_);
- if (depth_view_)
- wgpuTextureViewRelease(depth_view_);
- if (depth_texture_)
- wgpuTextureRelease(depth_texture_);
-
- // Recreate with new size
- create_framebuffers(width, height);
-
- if (passthrough_effect_) {
- passthrough_effect_->resize(width, height);
- }
-
- // Propagate to all sequences
- for (ActiveSequence& entry : sequences_) {
- entry.seq->resize(width, height);
- }
-}
-
-void MainSequence::render_frame(float global_time, float beat_time,
- float beat_phase, float peak,
- float aspect_ratio, WGPUSurface surface) {
- WGPUCommandEncoder encoder =
- wgpuDeviceCreateCommandEncoder(gpu_ctx.device, nullptr);
-
- std::vector<SequenceItem*> scene_effects;
- std::vector<SequenceItem*> post_effects;
- for (ActiveSequence& entry : sequences_) {
- if (global_time >= entry.start_time) {
-#if !defined(STRIP_ALL)
- if (!entry.activated) {
- printf("[SEQUENCE START] priority=%d, start_time=%.2f\n",
- entry.priority, entry.start_time);
- entry.activated = true;
- }
-#endif
- float seq_time = global_time - entry.start_time;
- entry.seq->update_active_list(seq_time);
- entry.seq->collect_active_effects(scene_effects, post_effects);
- }
- }
- std::sort(scene_effects.begin(), scene_effects.end(),
- [](const SequenceItem* a, const SequenceItem* b) {
- return a->priority < b->priority;
- });
- std::sort(post_effects.begin(), post_effects.end(),
- [](const SequenceItem* a, const SequenceItem* b) {
- return a->priority < b->priority;
- });
-
- // 1. Compute
- // Construct common uniforms once (reused for all effects)
- CommonPostProcessUniforms base_uniforms = {
- .resolution = {static_cast<float>(width_), static_cast<float>(height_)},
- .aspect_ratio = aspect_ratio,
- .time = 0.0f, // Will be set per-effect
- .beat_time = beat_time,
- .beat_phase = beat_phase,
- .audio_intensity = peak,
- ._pad = 0.0f,
- };
-
- for (const SequenceItem* item : scene_effects) {
- base_uniforms.time = global_time - item->start_time;
- item->effect->compute(encoder, base_uniforms);
- }
-
- // 2. Scene Pass (to A)
- WGPURenderPassColorAttachment scene_attachment = {};
- scene_attachment.view = framebuffer_view_a_;
- scene_attachment.resolveTarget = nullptr;
- scene_attachment.loadOp = WGPULoadOp_Clear;
- scene_attachment.storeOp = WGPUStoreOp_Store;
- scene_attachment.clearValue = {0, 0, 0, 1};
-#if !defined(DEMO_CROSS_COMPILE_WIN32)
- scene_attachment.depthSlice = WGPU_DEPTH_SLICE_UNDEFINED;
-#endif /* !defined(DEMO_CROSS_COMPILE_WIN32) */
-
- WGPURenderPassDepthStencilAttachment depth_attachment = {};
- depth_attachment.view = depth_view_;
- depth_attachment.depthLoadOp = WGPULoadOp_Clear;
- depth_attachment.depthStoreOp = WGPUStoreOp_Store;
- depth_attachment.depthClearValue = 1.0f;
-
- WGPURenderPassDescriptor scene_desc = {.colorAttachmentCount = 1,
- .colorAttachments = &scene_attachment,
- .depthStencilAttachment =
- &depth_attachment};
- WGPURenderPassEncoder scene_pass =
- wgpuCommandEncoderBeginRenderPass(encoder, &scene_desc);
- wgpuRenderPassEncoderSetViewport(scene_pass, 0.0f, 0.0f, (float)width_,
- (float)height_, 0.0f, 1.0f);
- for (const SequenceItem* item : scene_effects) {
- base_uniforms.time = global_time - item->start_time;
- item->effect->render(scene_pass, base_uniforms);
- }
- wgpuRenderPassEncoderEnd(scene_pass);
-
- // 3. Post Chain
-
- // Capture framebuffer ONCE before post-processing chain
- bool needs_capture = false;
- for (const SequenceItem* item : post_effects) {
- PostProcessEffect* pp = (PostProcessEffect*)(item->effect.get());
- if (pp->needs_framebuffer_capture()) {
- needs_capture = true;
- break;
- }
- }
-
- if (needs_capture) {
- WGPUTextureView captured_view = get_auxiliary_view("captured_frame");
- if (captured_view) {
- WGPURenderPassColorAttachment capture_attachment = {};
- capture_attachment.view = captured_view;
- capture_attachment.resolveTarget = nullptr;
- capture_attachment.loadOp = WGPULoadOp_Clear;
- capture_attachment.storeOp = WGPUStoreOp_Store;
- capture_attachment.clearValue = {0.0f, 0.0f, 0.0f, 1.0f};
-#if !defined(DEMO_CROSS_COMPILE_WIN32)
- capture_attachment.depthSlice = WGPU_DEPTH_SLICE_UNDEFINED;
-#endif
- WGPURenderPassDescriptor capture_desc = {
- .colorAttachmentCount = 1, .colorAttachments = &capture_attachment};
- WGPURenderPassEncoder capture_pass =
- wgpuCommandEncoderBeginRenderPass(encoder, &capture_desc);
- wgpuRenderPassEncoderSetViewport(capture_pass, 0.0f, 0.0f, (float)width_,
- (float)height_, 0.0f, 1.0f);
-
- PostProcessEffect* passthrough =
- (PostProcessEffect*)passthrough_effect_.get();
- passthrough->update_bind_group(framebuffer_view_a_);
- base_uniforms.time = 0.0f;
- passthrough->render(capture_pass, base_uniforms);
-
- wgpuRenderPassEncoderEnd(capture_pass);
- }
- }
-
- WGPUSurfaceTexture st = {};
- WGPUTextureView final_view = nullptr;
-
- if (post_effects.empty()) {
- wgpuSurfaceGetCurrentTexture(surface, &st);
- final_view = wgpuTextureCreateView(st.texture, nullptr);
-
- // Safely cast to PostProcessEffect to call update_bind_group
- PostProcessEffect* pp_effect =
- (PostProcessEffect*)passthrough_effect_.get();
- pp_effect->update_bind_group(framebuffer_view_a_);
-
- WGPURenderPassColorAttachment final_attachment = {};
- final_attachment.view = final_view;
- final_attachment.resolveTarget = nullptr;
- final_attachment.loadOp = WGPULoadOp_Load;
- final_attachment.storeOp = WGPUStoreOp_Store;
-#if !defined(DEMO_CROSS_COMPILE_WIN32)
- final_attachment.depthSlice = WGPU_DEPTH_SLICE_UNDEFINED;
-#endif /* !defined(DEMO_CROSS_COMPILE_WIN32) */
- WGPURenderPassDescriptor final_desc = {
- .colorAttachmentCount = 1, .colorAttachments = &final_attachment};
- WGPURenderPassEncoder final_pass =
- wgpuCommandEncoderBeginRenderPass(encoder, &final_desc);
- wgpuRenderPassEncoderSetViewport(final_pass, 0.0f, 0.0f, (float)width_,
- (float)height_, 0.0f, 1.0f);
- base_uniforms.time = 0.0f;
- passthrough_effect_->render(final_pass, base_uniforms);
- wgpuRenderPassEncoderEnd(final_pass);
- } else {
- WGPUTextureView current_input = framebuffer_view_a_;
- for (size_t i = 0; i < post_effects.size(); ++i) {
- bool is_last = (i == post_effects.size() - 1);
- WGPUTextureView current_output = nullptr;
-
- if (is_last) {
- wgpuSurfaceGetCurrentTexture(surface, &st);
- final_view = wgpuTextureCreateView(st.texture, nullptr);
- current_output = final_view;
- } else {
- current_output =
- (current_input == framebuffer_view_a_ ? framebuffer_view_b_
- : framebuffer_view_a_);
- }
-
- PostProcessEffect* pp =
- (PostProcessEffect*)(post_effects[i]->effect.get());
-
- pp->update_bind_group(current_input);
-
- WGPURenderPassColorAttachment pp_attachment = {};
- pp_attachment.view = current_output;
- pp_attachment.resolveTarget = nullptr;
- pp_attachment.loadOp = WGPULoadOp_Load;
- pp_attachment.storeOp = WGPUStoreOp_Store;
-#if !defined(DEMO_CROSS_COMPILE_WIN32)
- pp_attachment.depthSlice = WGPU_DEPTH_SLICE_UNDEFINED;
-#endif /* !defined(DEMO_CROSS_COMPILE_WIN32) */
- WGPURenderPassDescriptor pp_desc = {.colorAttachmentCount = 1,
- .colorAttachments = &pp_attachment};
- WGPURenderPassEncoder pp_pass =
- wgpuCommandEncoderBeginRenderPass(encoder, &pp_desc);
- wgpuRenderPassEncoderSetViewport(pp_pass, 0.0f, 0.0f, (float)width_,
- (float)height_, 0.0f, 1.0f);
- base_uniforms.time = global_time - post_effects[i]->start_time;
- pp->render(pp_pass, base_uniforms);
- wgpuRenderPassEncoderEnd(pp_pass);
- current_input = current_output;
- }
- }
-
- WGPUCommandBuffer commands = wgpuCommandEncoderFinish(encoder, nullptr);
- wgpuQueueSubmit(gpu_ctx.queue, 1, &commands);
-
- if (st.texture) {
- wgpuTextureViewRelease(final_view);
- wgpuSurfacePresent(surface);
- wgpuTextureRelease(st.texture);
- }
-}
-
-void MainSequence::shutdown() {
- if (framebuffer_view_a_)
- wgpuTextureViewRelease(framebuffer_view_a_);
- if (framebuffer_a_)
- wgpuTextureRelease(framebuffer_a_);
- if (framebuffer_view_b_)
- wgpuTextureViewRelease(framebuffer_view_b_);
- if (framebuffer_b_)
- wgpuTextureRelease(framebuffer_b_);
- if (depth_view_)
- wgpuTextureViewRelease(depth_view_);
- if (depth_texture_)
- wgpuTextureRelease(depth_texture_);
- for (auto& [name, aux] : auxiliary_textures_) {
- if (aux.view)
- wgpuTextureViewRelease(aux.view);
- if (aux.texture)
- wgpuTextureRelease(aux.texture);
- }
- auxiliary_textures_.clear();
- for (ActiveSequence& entry : sequences_) {
- entry.seq->reset();
- }
-}
-
-// Register a named auxiliary texture for inter-effect sharing
-void MainSequence::register_auxiliary_texture(const char* name, int width,
- int height) {
- const std::string key(name);
-
- // Check if already exists (silent, idempotent registration is valid)
- auto it = auxiliary_textures_.find(key);
- if (it != auxiliary_textures_.end()) {
- return;
- }
-
- // Create texture
- const WGPUTextureDescriptor desc = {
- .usage =
- WGPUTextureUsage_RenderAttachment | WGPUTextureUsage_TextureBinding,
- .dimension = WGPUTextureDimension_2D,
- .size = {(uint32_t)width, (uint32_t)height, 1},
- .format = gpu_ctx.format,
- .mipLevelCount = 1,
- .sampleCount = 1,
- };
-
- WGPUTexture texture = wgpuDeviceCreateTexture(gpu_ctx.device, &desc);
- FATAL_CHECK(!texture, "Failed to create auxiliary texture: %s\n", name);
-
- // Create view
- WGPUTextureView view = gpu_create_texture_view_2d(texture, gpu_ctx.format);
- FATAL_CHECK(!view, "Failed to create auxiliary texture view: %s\n", name);
-
- // Store in registry
- auxiliary_textures_[key] = {texture, view, width, height};
-
-#if !defined(STRIP_ALL)
- printf("[MainSequence] Registered auxiliary texture '%s' (%dx%d)\n", name,
- width, height);
-#endif /* !defined(STRIP_ALL) */
-}
-
-// Retrieve auxiliary texture view by name
-WGPUTextureView MainSequence::get_auxiliary_view(const char* name) {
- const std::string key(name);
- auto it = auxiliary_textures_.find(key);
- FATAL_CHECK(it == auxiliary_textures_.end(),
- "Auxiliary texture not found: %s\n", name);
- return it->second.view;
-}
-
-// Resize existing auxiliary texture
-void MainSequence::resize_auxiliary_texture(const char* name, int width,
- int height) {
- const std::string key(name);
- auto it = auxiliary_textures_.find(key);
- FATAL_CHECK(it == auxiliary_textures_.end(),
- "Auxiliary texture not found for resize: %s\n", name);
-
- // Release old resources
- if (it->second.view)
- wgpuTextureViewRelease(it->second.view);
- if (it->second.texture)
- wgpuTextureRelease(it->second.texture);
-
- // Create new texture with new dimensions
- const WGPUTextureDescriptor desc = {
- .usage =
- WGPUTextureUsage_RenderAttachment | WGPUTextureUsage_TextureBinding,
- .dimension = WGPUTextureDimension_2D,
- .size = {(uint32_t)width, (uint32_t)height, 1},
- .format = gpu_ctx.format,
- .mipLevelCount = 1,
- .sampleCount = 1,
- };
-
- WGPUTexture texture = wgpuDeviceCreateTexture(gpu_ctx.device, &desc);
- FATAL_CHECK(!texture, "Failed to resize auxiliary texture: %s\n", name);
-
- // Create view
- WGPUTextureView view = gpu_create_texture_view_2d(texture, gpu_ctx.format);
- FATAL_CHECK(!view, "Failed to create resized auxiliary texture view: %s\n",
- name);
-
- // Update registry
- it->second = {texture, view, width, height};
-
-#if !defined(STRIP_ALL)
- printf("[MainSequence] Resized auxiliary texture '%s' to %dx%d\n", name,
- width, height);
-#endif /* !defined(STRIP_ALL) */
-}
-
-#if !defined(STRIP_ALL)
-void MainSequence::simulate_until(float target_time, float step_rate,
- float bpm) {
- const float aspect_ratio = 16.0f / 9.0f;
- for (float t = 0.0f; t < target_time; t += step_rate) {
- WGPUCommandEncoder encoder =
- wgpuDeviceCreateCommandEncoder(gpu_ctx.device, nullptr);
- float absolute_beat_time = t * bpm / 60.0f;
- float beat_phase = fmodf(absolute_beat_time, 1.0f);
- std::vector<SequenceItem*> scene_effects, post_effects;
- for (ActiveSequence& entry : sequences_) {
- if (t >= entry.start_time) {
- entry.seq->update_active_list(t - entry.start_time);
- entry.seq->collect_active_effects(scene_effects, post_effects);
- }
- }
- for (const SequenceItem* item : scene_effects) {
- CommonPostProcessUniforms test_uniforms = {
- .resolution = {static_cast<float>(width_),
- static_cast<float>(height_)},
- .aspect_ratio = aspect_ratio,
- .time = t - item->start_time,
- .beat_time = absolute_beat_time,
- .beat_phase = beat_phase,
- .audio_intensity = 0.0f,
- ._pad = 0.0f,
- };
- item->effect->compute(encoder, test_uniforms);
- }
- WGPUCommandBuffer commands = wgpuCommandEncoderFinish(encoder, nullptr);
- wgpuQueueSubmit(gpu_ctx.queue, 1, &commands);
- }
-}
-#endif /* !defined(STRIP_ALL) */