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.cc302
1 files changed, 168 insertions, 134 deletions
diff --git a/src/gpu/effect.cc b/src/gpu/effect.cc
index 040b523..0ab476b 100644
--- a/src/gpu/effect.cc
+++ b/src/gpu/effect.cc
@@ -2,11 +2,23 @@
// It implements the Sequence management logic.
#include "effect.h"
+#include "gpu/demo_effects.h"
+#include "gpu/gpu.h"
#include <algorithm>
#include <cstdio>
+#include <vector>
-// --- Sequence Implementation ---
+// --- PostProcessEffect ---
+void PostProcessEffect::render(WGPURenderPassEncoder pass, float, float, float,
+ float) {
+ 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::init(MainSequence *demo) {
for (auto &item : items_) {
if (!item.effect->is_initialized) {
@@ -23,8 +35,8 @@ void Sequence::add_effect(std::shared_ptr<Effect> effect, float start_time,
}
void Sequence::sort_items() {
- if (is_sorted_) return;
- // Sort by priority ascending (0 draws first, 100 draws on top)
+ if (is_sorted_)
+ return;
std::sort(items_.begin(), items_.end(),
[](const SequenceItem &a, const SequenceItem &b) {
return a.priority < b.priority;
@@ -37,40 +49,27 @@ void Sequence::update_active_list(float seq_time) {
bool should_be_active =
(seq_time >= item.start_time && seq_time < item.end_time);
- if (should_be_active) {
- if (!item.active) {
- item.effect->start();
- item.active = true;
- }
- } else {
- if (item.active) {
- item.effect->end();
- item.active = false;
- }
+ if (should_be_active && !item.active) {
+ item.effect->start();
+ item.active = true;
+ } else if (!should_be_active && item.active) {
+ item.effect->end();
+ item.active = false;
}
}
}
-void Sequence::dispatch_compute(WGPUCommandEncoder encoder, float seq_time,
- float beat, float intensity,
- float aspect_ratio) {
+void Sequence::collect_active_effects(
+ std::vector<SequenceItem *> &scene_effects,
+ std::vector<SequenceItem *> &post_effects) {
sort_items();
for (auto &item : items_) {
if (item.active) {
- item.effect->compute(encoder, seq_time - item.start_time, beat, intensity,
- aspect_ratio);
- }
- }
-}
-
-void Sequence::dispatch_render(WGPURenderPassEncoder pass, float seq_time,
- float beat, float intensity,
- float aspect_ratio) {
- sort_items(); // Should be sorted already but safe to check
- for (auto &item : items_) {
- if (item.active) {
- item.effect->render(pass, seq_time - item.start_time, beat, intensity,
- aspect_ratio);
+ if (item.effect->is_post_process()) {
+ post_effects.push_back(&item);
+ } else {
+ scene_effects.push_back(&item);
+ }
}
}
}
@@ -86,19 +85,49 @@ void Sequence::reset() {
// --- MainSequence Implementation ---
-void MainSequence::init(WGPUDevice d, WGPUQueue q, WGPUTextureFormat f) {
+MainSequence::MainSequence() = default;
+MainSequence::~MainSequence() = default;
+
+void MainSequence::create_framebuffers(int width, int height) {
+ WGPUTextureDescriptor desc = {};
+ desc.usage =
+ WGPUTextureUsage_RenderAttachment | WGPUTextureUsage_TextureBinding;
+ desc.dimension = WGPUTextureDimension_2D;
+ desc.size = {(uint32_t)width, (uint32_t)height, 1};
+ desc.format = format;
+ desc.mipLevelCount = 1;
+ desc.sampleCount = 1;
+
+ framebuffer_a_ = wgpuDeviceCreateTexture(device, &desc);
+ framebuffer_b_ = wgpuDeviceCreateTexture(device, &desc);
+
+ WGPUTextureViewDescriptor view_desc = {};
+ view_desc.dimension = WGPUTextureViewDimension_2D;
+ view_desc.format = format;
+ view_desc.mipLevelCount = 1;
+ view_desc.arrayLayerCount = 1;
+
+ framebuffer_view_a_ = wgpuTextureCreateView(framebuffer_a_, &view_desc);
+ framebuffer_view_b_ = wgpuTextureCreateView(framebuffer_b_, &view_desc);
+}
+
+void MainSequence::init(WGPUDevice d, WGPUQueue q, WGPUTextureFormat f,
+ int width, int height) {
device = d;
queue = q;
format = f;
-
+
+ create_framebuffers(width, height);
+ passthrough_effect_ = std::make_unique<PassthroughEffect>(device, format);
+
for (auto &entry : sequences_) {
entry.seq->init(this);
}
}
-void MainSequence::add_sequence(std::shared_ptr<Sequence> seq, float start_time, int priority) {
+void MainSequence::add_sequence(std::shared_ptr<Sequence> seq, float start_time,
+ int priority) {
sequences_.push_back({seq, start_time, priority});
- // Sort sequences by priority
std::sort(sequences_.begin(), sequences_.end(),
[](const ActiveSequence &a, const ActiveSequence &b) {
return a.priority < b.priority;
@@ -107,135 +136,140 @@ void MainSequence::add_sequence(std::shared_ptr<Sequence> seq, float start_time,
void MainSequence::render_frame(float global_time, float beat, float peak,
float aspect_ratio, WGPUSurface surface) {
- WGPUSurfaceTexture surface_texture;
- wgpuSurfaceGetCurrentTexture(surface, &surface_texture);
-
-#if defined(DEMO_CROSS_COMPILE_WIN32)
- #define STATUS_OPTIMAL WGPUSurfaceGetCurrentTextureStatus_Success
- #define STATUS_SUBOPTIMAL WGPUSurfaceGetCurrentTextureStatus_Success
-#else
- #define STATUS_OPTIMAL WGPUSurfaceGetCurrentTextureStatus_SuccessOptimal
- #define STATUS_SUBOPTIMAL WGPUSurfaceGetCurrentTextureStatus_SuccessSuboptimal
-#endif
-
- if (surface_texture.status != STATUS_OPTIMAL &&
- surface_texture.status != STATUS_SUBOPTIMAL) {
- return;
- }
+ WGPUCommandEncoder encoder = wgpuDeviceCreateCommandEncoder(device, nullptr);
- WGPUTextureView view = wgpuTextureCreateView(surface_texture.texture, nullptr);
-
- WGPUCommandEncoderDescriptor encoder_desc = {};
- WGPUCommandEncoder encoder = wgpuDeviceCreateCommandEncoder(device, &encoder_desc);
-
- // 1. Update & Compute Phase
+ std::vector<SequenceItem *> scene_effects;
+ std::vector<SequenceItem *> post_effects;
for (auto &entry : sequences_) {
- // Check if sequence is active (start_time <= global_time)
- // We assume sequences run until end of demo or have internal end?
- // User said "Sequence ... overlap". Implicitly they might have duration but here we just check start.
- // Let's assume they are active if time >= start.
- // Effects inside sequence handle duration.
if (global_time >= entry.start_time) {
- float seq_time = global_time - entry.start_time;
- entry.seq->update_active_list(seq_time);
-
- // Pass generic aspect ratio 16:9 for compute?
- // Or wait for render. Particles compute uses it.
- // We can get it from surface texture size if we want?
- // Let's pass 1.777f for now or fetch.
- // gpu_draw used to pass it. We need it here.
- // Wait, render_frame doesn't take aspect_ratio. gpu_draw did.
- // I should add aspect_ratio to render_frame or calculate it from surface.
+ float seq_time = global_time - entry.start_time;
+ entry.seq->update_active_list(seq_time);
+ entry.seq->collect_active_effects(scene_effects, post_effects);
}
}
-
- for (auto &entry : sequences_) {
- if (global_time >= entry.start_time) {
- entry.seq->dispatch_compute(encoder, global_time - entry.start_time, beat, peak, aspect_ratio);
- }
+ 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
+ for (const auto &item : scene_effects) {
+ item->effect->compute(encoder, global_time - item->start_time, beat, peak,
+ aspect_ratio);
}
- // 2. Render Phase
- {
- WGPURenderPassColorAttachment color_attachment = {};
- color_attachment.view = view;
- color_attachment.loadOp = WGPULoadOp_Clear;
- color_attachment.storeOp = WGPUStoreOp_Store;
-
- // Clear color logic could be dynamic or part of a "BackgroundEffect"?
- // For now hardcode.
- float flash = peak * 0.2f;
- color_attachment.clearValue = {0.05 + flash, 0.1 + flash, 0.2 + flash, 1.0};
-
+ // 2. Scene Pass (to A)
+ WGPURenderPassColorAttachment scene_attachment = {};
+ scene_attachment.view = framebuffer_view_a_;
+ scene_attachment.loadOp = WGPULoadOp_Clear;
+ scene_attachment.storeOp = WGPUStoreOp_Store;
+ scene_attachment.clearValue = {0, 0, 0, 1};
#if !defined(DEMO_CROSS_COMPILE_WIN32)
- color_attachment.depthSlice = WGPU_DEPTH_SLICE_UNDEFINED;
+ scene_attachment.depthSlice = WGPU_DEPTH_SLICE_UNDEFINED;
#endif
+ WGPURenderPassDescriptor scene_desc = {.colorAttachmentCount = 1,
+ .colorAttachments = &scene_attachment};
+ WGPURenderPassEncoder scene_pass =
+ wgpuCommandEncoderBeginRenderPass(encoder, &scene_desc);
+ for (const auto &item : scene_effects) {
+ item->effect->render(scene_pass, global_time - item->start_time, beat, peak,
+ aspect_ratio);
+ }
+ wgpuRenderPassEncoderEnd(scene_pass);
- WGPURenderPassDescriptor render_pass_desc = {};
- render_pass_desc.colorAttachmentCount = 1;
- render_pass_desc.colorAttachments = &color_attachment;
+ // 3. Post Chain
+ if (post_effects.empty()) {
+ WGPUSurfaceTexture st;
+ wgpuSurfaceGetCurrentTexture(surface, &st);
+ WGPUTextureView final_view = wgpuTextureCreateView(st.texture, nullptr);
+ passthrough_effect_->update_bind_group(framebuffer_view_a_);
- WGPURenderPassEncoder pass = wgpuCommandEncoderBeginRenderPass(encoder, &render_pass_desc);
+ WGPURenderPassColorAttachment final_attachment = {
+ .view = final_view, .loadOp = WGPULoadOp_Load, .storeOp = WGPUStoreOp_Store};
+ WGPURenderPassDescriptor final_desc = {.colorAttachmentCount = 1,
+ .colorAttachments =
+ &final_attachment};
+ WGPURenderPassEncoder final_pass =
+ wgpuCommandEncoderBeginRenderPass(encoder, &final_desc);
+ passthrough_effect_->render(final_pass, 0, 0, 0, aspect_ratio);
+ wgpuRenderPassEncoderEnd(final_pass);
- for (auto &entry : sequences_) {
- if (global_time >= entry.start_time) {
- entry.seq->dispatch_render(pass, global_time - entry.start_time, beat, peak, aspect_ratio);
- }
- }
+ wgpuTextureViewRelease(final_view);
+ wgpuSurfacePresent(surface);
+ wgpuTextureRelease(st.texture);
+ } 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);
+ WGPUSurfaceTexture st;
+ if (is_last)
+ wgpuSurfaceGetCurrentTexture(surface, &st);
+
+ WGPUTextureView current_output =
+ is_last ? wgpuTextureCreateView(st.texture, nullptr)
+ : (current_input == framebuffer_view_a_ ? framebuffer_view_b_
+ : framebuffer_view_a_);
- wgpuRenderPassEncoderEnd(pass);
+ PostProcessEffect *pp =
+ static_cast<PostProcessEffect *>(post_effects[i]->effect.get());
+ pp->update_bind_group(current_input);
+
+ WGPURenderPassColorAttachment pp_attachment = {
+ .view = current_output, .loadOp = WGPULoadOp_Load, .storeOp = WGPUStoreOp_Store};
+ WGPURenderPassDescriptor pp_desc = {.colorAttachmentCount = 1,
+ .colorAttachments = &pp_attachment};
+ WGPURenderPassEncoder pp_pass =
+ wgpuCommandEncoderBeginRenderPass(encoder, &pp_desc);
+ pp->render(pp_pass, global_time - post_effects[i]->start_time, beat, peak,
+ aspect_ratio);
+ wgpuRenderPassEncoderEnd(pp_pass);
+
+ if (is_last) {
+ wgpuTextureViewRelease(current_output);
+ wgpuSurfacePresent(surface);
+ wgpuTextureRelease(st.texture);
+ }
+ current_input = current_output;
+ }
}
- WGPUCommandBufferDescriptor cmd_desc = {};
- WGPUCommandBuffer commands = wgpuCommandEncoderFinish(encoder, &cmd_desc);
+ WGPUCommandBuffer commands = wgpuCommandEncoderFinish(encoder, nullptr);
wgpuQueueSubmit(queue, 1, &commands);
- wgpuSurfacePresent(surface);
+}
- wgpuTextureViewRelease(view);
- wgpuTextureRelease(surface_texture.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_);
+ for (auto &entry : sequences_) {
+ entry.seq->reset();
+ }
}
-void MainSequence::simulate_until(float target_time, float step_rate) {
#ifndef STRIP_ALL
- // Assuming 128 BPM as per main.cc.
- // Ideally this should be passed in or shared.
+void MainSequence::simulate_until(float target_time, float step_rate) {
const float bpm = 128.0f;
- const float aspect_ratio = 16.0f / 9.0f; // Dummy aspect
-
+ const float aspect_ratio = 16.0f / 9.0f;
for (float t = 0.0f; t < target_time; t += step_rate) {
- WGPUCommandEncoderDescriptor encoder_desc = {};
- WGPUCommandEncoder encoder = wgpuDeviceCreateCommandEncoder(device, &encoder_desc);
-
+ WGPUCommandEncoder encoder = wgpuDeviceCreateCommandEncoder(device, nullptr);
float beat = fmodf(t * bpm / 60.0f, 1.0f);
-
- // Update active lists
+ std::vector<SequenceItem *> scene_effects, post_effects;
for (auto &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);
}
}
-
- // Dispatch compute
- for (auto &entry : sequences_) {
- if (t >= entry.start_time) {
- // peak = 0.0f during simulation (no audio analysis)
- entry.seq->dispatch_compute(encoder, t - entry.start_time, beat, 0.0f, aspect_ratio);
- }
+ for (const auto &item : scene_effects) {
+ item->effect->compute(encoder, t - item->start_time, beat, 0.0f, aspect_ratio);
}
-
- WGPUCommandBufferDescriptor cmd_desc = {};
- WGPUCommandBuffer commands = wgpuCommandEncoderFinish(encoder, &cmd_desc);
+ WGPUCommandBuffer commands = wgpuCommandEncoderFinish(encoder, nullptr);
wgpuQueueSubmit(queue, 1, &commands);
}
-#else
- (void)target_time;
- (void)step_rate;
-#endif
}
-
-void MainSequence::shutdown() {
- for (auto &entry : sequences_) {
- entry.seq->reset();
- }
- sequences_.clear();
-} \ No newline at end of file
+#endif