diff options
Diffstat (limited to 'src/gpu/effect.cc')
| -rw-r--r-- | src/gpu/effect.cc | 573 |
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) */ |
