summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/gpu/demo_effects.cc77
-rw-r--r--src/gpu/demo_effects.h1
-rw-r--r--src/gpu/effect.cc63
-rw-r--r--src/gpu/gpu.h56
-rw-r--r--src/tests/test_sequence.cc89
5 files changed, 154 insertions, 132 deletions
diff --git a/src/gpu/demo_effects.cc b/src/gpu/demo_effects.cc
index 71f1284..27712d3 100644
--- a/src/gpu/demo_effects.cc
+++ b/src/gpu/demo_effects.cc
@@ -58,6 +58,8 @@ create_post_process_pipeline(WGPUDevice device, WGPUTextureFormat format,
pipeline_desc.vertex.entryPoint = str_view("vs_main");
pipeline_desc.fragment = &fragment_state;
pipeline_desc.primitive.topology = WGPUPrimitiveTopology_TriangleList;
+ pipeline_desc.multisample.count = 1;
+ pipeline_desc.multisample.mask = 0xFFFFFFFF;
return wgpuDeviceCreateRenderPipeline(device, &pipeline_desc);
}
@@ -91,7 +93,8 @@ struct Uniforms { audio_peak: f32, aspect_ratio: f32, time: f32, beat: f32, };
fn main(@builtin(global_invocation_id) id: vec3<u32>) {
let i = id.x; if (i >= arrayLength(&particles)) { return; }
var p = particles[i];
- p.pos.xyz = p.pos.xyz + p.vel.xyz * 0.016;
+ let new_pos = p.pos.xyz + p.vel.xyz * 0.016;
+ p.pos = vec4<f32>(new_pos, p.pos.w);
p.vel.y = p.vel.y - 0.01 * (1.0 + uniforms.audio_peak * 5.0);
p.rot.x = p.rot.x + p.rot.y * 0.016;
if (p.pos.y < -1.5) {
@@ -171,7 +174,9 @@ fn main(@builtin(global_invocation_id) id: vec3<u32>) {
p.vel = vec4<f32>(cos(angle), sin(angle), 0.0, 0.0) * (0.5 + hash(r)*0.5) * (1.0 + uniforms.intensity * 2.0);
p.color = vec4<f32>(hash(r+0.1), hash(r+0.2), 1.0, 1.0);
}
- p.pos.xyz = p.pos.xyz + p.vel.xyz * 0.016; p.vel.y = p.vel.y - 0.01; p.pos.w = p.pos.w - 0.01 * (1.0 + uniforms.beat);
+ let new_pos = p.pos.xyz + p.vel.xyz * 0.016;
+ p.pos = vec4<f32>(new_pos, p.pos.w - 0.01 * (1.0 + uniforms.beat));
+ p.vel.y = p.vel.y - 0.01;
particles[i] = p;
})";
@@ -310,27 +315,44 @@ void ParticlesEffect::render(WGPURenderPassEncoder pass, float t, float b,
render_pass_.instance_count, 0, 0);
}
+// --- PostProcess Implementation Helper ---
+static void pp_update_bind_group(WGPUDevice device, WGPURenderPipeline pipeline,
+ WGPUBindGroup *bind_group,
+ WGPUTextureView input_view,
+ GpuBuffer uniforms) {
+ if (*bind_group)
+ wgpuBindGroupRelease(*bind_group);
+ WGPUBindGroupLayout bgl = wgpuRenderPipelineGetBindGroupLayout(pipeline, 0);
+ WGPUSamplerDescriptor sd = {};
+ sd.magFilter = WGPUFilterMode_Linear;
+ sd.minFilter = WGPUFilterMode_Linear;
+ sd.maxAnisotropy = 1;
+ WGPUSampler sampler = wgpuDeviceCreateSampler(device, &sd);
+ WGPUBindGroupEntry bge[3] = {};
+ bge[0].binding = 0;
+ bge[0].sampler = sampler;
+ bge[1].binding = 1;
+ bge[1].textureView = input_view;
+ bge[2].binding = 2;
+ bge[2].buffer = uniforms.buffer;
+ bge[2].size = uniforms.size;
+ WGPUBindGroupDescriptor bgd = {
+ .layout = bgl, .entryCount = 3, .entries = bge};
+ *bind_group = wgpuDeviceCreateBindGroup(device, &bgd);
+}
+
// --- PassthroughEffect ---
PassthroughEffect::PassthroughEffect(WGPUDevice device,
WGPUTextureFormat format)
: device_(device) {
+ uniforms_ =
+ gpu_create_buffer(device, sizeof(float) * 4,
+ WGPUBufferUsage_Uniform | WGPUBufferUsage_CopyDst);
pipeline_ =
create_post_process_pipeline(device, format, passthrough_shader_wgsl);
}
void PassthroughEffect::update_bind_group(WGPUTextureView input_view) {
- if (bind_group_)
- wgpuBindGroupRelease(bind_group_);
- WGPUBindGroupLayout bgl = wgpuRenderPipelineGetBindGroupLayout(pipeline_, 0);
- WGPUSamplerDescriptor sd = {};
- WGPUSampler sampler = wgpuDeviceCreateSampler(device_, &sd);
- WGPUBindGroupEntry bge[2] = {};
- bge[0].binding = 0;
- bge[0].sampler = sampler;
- bge[1].binding = 1;
- bge[1].textureView = input_view;
- WGPUBindGroupDescriptor bgd = {
- .layout = bgl, .entryCount = 2, .entries = bge};
- bind_group_ = wgpuDeviceCreateBindGroup(device_, &bgd);
+ pp_update_bind_group(device_, pipeline_, &bind_group_, input_view, uniforms_);
}
// --- MovingEllipseEffect ---
@@ -404,31 +426,6 @@ void ParticleSprayEffect::render(WGPURenderPassEncoder pass, float t, float b,
wgpuRenderPassEncoderDraw(pass, 6, NUM_PARTICLES, 0, 0);
}
-// --- PostProcess Implementation Helper ---
-static void pp_update_bind_group(WGPUDevice device, WGPURenderPipeline pipeline,
- WGPUBindGroup *bind_group,
- WGPUTextureView input_view,
- GpuBuffer uniforms) {
- if (*bind_group)
- wgpuBindGroupRelease(*bind_group);
- WGPUBindGroupLayout bgl = wgpuRenderPipelineGetBindGroupLayout(pipeline, 0);
- WGPUSamplerDescriptor sd = {};
- sd.magFilter = WGPUFilterMode_Linear;
- sd.minFilter = WGPUFilterMode_Linear;
- WGPUSampler sampler = wgpuDeviceCreateSampler(device, &sd);
- WGPUBindGroupEntry bge[3] = {};
- bge[0].binding = 0;
- bge[0].sampler = sampler;
- bge[1].binding = 1;
- bge[1].textureView = input_view;
- bge[2].binding = 2;
- bge[2].buffer = uniforms.buffer;
- bge[2].size = uniforms.size;
- WGPUBindGroupDescriptor bgd = {
- .layout = bgl, .entryCount = 3, .entries = bge};
- *bind_group = wgpuDeviceCreateBindGroup(device, &bgd);
-}
-
// --- GaussianBlurEffect ---
GaussianBlurEffect::GaussianBlurEffect(WGPUDevice device, WGPUQueue queue,
WGPUTextureFormat format)
diff --git a/src/gpu/demo_effects.h b/src/gpu/demo_effects.h
index 603c32e..5db710e 100644
--- a/src/gpu/demo_effects.h
+++ b/src/gpu/demo_effects.h
@@ -50,6 +50,7 @@ public:
private:
WGPUDevice device_;
+ GpuBuffer uniforms_;
};
class MovingEllipseEffect : public Effect {
diff --git a/src/gpu/effect.cc b/src/gpu/effect.cc
index 0ac9063..f9ebf0c 100644
--- a/src/gpu/effect.cc
+++ b/src/gpu/effect.cc
@@ -175,12 +175,11 @@ void MainSequence::render_frame(float global_time, float beat, float peak,
// 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
WGPURenderPassDescriptor scene_desc = {.colorAttachmentCount = 1,
.colorAttachments = &scene_attachment};
WGPURenderPassEncoder scene_pass =
@@ -192,48 +191,52 @@ void MainSequence::render_frame(float global_time, float beat, float peak,
wgpuRenderPassEncoderEnd(scene_pass);
// 3. Post Chain
+ WGPUSurfaceTexture st = {};
+ WGPUTextureView final_view = nullptr;
+
if (post_effects.empty()) {
- WGPUSurfaceTexture st;
wgpuSurfaceGetCurrentTexture(surface, &st);
- WGPUTextureView final_view = wgpuTextureCreateView(st.texture, nullptr);
+ final_view = wgpuTextureCreateView(st.texture, nullptr);
passthrough_effect_->update_bind_group(framebuffer_view_a_);
- WGPURenderPassColorAttachment final_attachment = {.view = final_view,
- .loadOp = WGPULoadOp_Load,
- .storeOp =
- WGPUStoreOp_Store};
+ WGPURenderPassColorAttachment final_attachment = {};
+ final_attachment.view = final_view;
+ final_attachment.resolveTarget = nullptr;
+ final_attachment.loadOp = WGPULoadOp_Load;
+ final_attachment.storeOp = WGPUStoreOp_Store;
+ final_attachment.depthSlice = WGPU_DEPTH_SLICE_UNDEFINED;
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);
-
- 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 = nullptr;
- WGPUTextureView current_output =
- is_last
- ? wgpuTextureCreateView(st.texture, nullptr)
- : (current_input == framebuffer_view_a_ ? framebuffer_view_b_
- : framebuffer_view_a_);
+ 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 =
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};
+ WGPURenderPassColorAttachment pp_attachment = {};
+ pp_attachment.view = current_output;
+ pp_attachment.resolveTarget = nullptr;
+ pp_attachment.loadOp = WGPULoadOp_Load;
+ pp_attachment.storeOp = WGPUStoreOp_Store;
+ pp_attachment.depthSlice = WGPU_DEPTH_SLICE_UNDEFINED;
WGPURenderPassDescriptor pp_desc = {.colorAttachmentCount = 1,
.colorAttachments = &pp_attachment};
WGPURenderPassEncoder pp_pass =
@@ -241,18 +244,18 @@ void MainSequence::render_frame(float global_time, float beat, float peak,
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;
}
}
WGPUCommandBuffer commands = wgpuCommandEncoderFinish(encoder, nullptr);
wgpuQueueSubmit(queue, 1, &commands);
+
+ if (st.texture) {
+ wgpuTextureViewRelease(final_view);
+ wgpuSurfacePresent(surface);
+ wgpuTextureRelease(st.texture);
+ }
}
void MainSequence::shutdown() {
diff --git a/src/gpu/gpu.h b/src/gpu/gpu.h
index 0bc20b7..c2c0670 100644
--- a/src/gpu/gpu.h
+++ b/src/gpu/gpu.h
@@ -8,8 +8,6 @@
#include <cstring> // For strlen
-
-
#if defined(DEMO_CROSS_COMPILE_WIN32)
// Windows (MinGW) using wgpu-native v0.19.4.1
@@ -18,69 +16,52 @@
#include <webgpu/wgpu.h>
+static inline const char *str_view(const char *str) {
+ return str;
+}
-
-static inline const char *str_view(const char *str) { return str; }
-
-
-
-static inline const char *label_view(const char *str) { return str; }
-
-
-
-
-
-
+static inline const char *label_view(const char *str) {
+ return str;
+}
#define WGPUSType_ShaderSourceWGSL WGPUSType_ShaderModuleWGSLDescriptor
-
-
using WGPUShaderSourceWGSL = WGPUShaderModuleWGSLDescriptor;
+#define WGPUSurfaceGetCurrentTextureStatus_SuccessOptimal \
+ WGPUSurfaceGetCurrentTextureStatus_Success
-
-#define WGPUSurfaceGetCurrentTextureStatus_SuccessOptimal WGPUSurfaceGetCurrentTextureStatus_Success
-
-
-
-#define WGPUSurfaceGetCurrentTextureStatus_SuccessSuboptimal WGPUSurfaceGetCurrentTextureStatus_Success
-
-
+#define WGPUSurfaceGetCurrentTextureStatus_SuccessSuboptimal \
+ WGPUSurfaceGetCurrentTextureStatus_Success
#define WGPUCallbackMode_WaitAnyOnly 0
-
-
-
+#define WGPU_DEPTH_SLICE_UNDEFINED (0xffffffff)
#else
-// Native (macOS/Linux) using newer wgpu-native
+// Native (macOS/Linux) using newer wgpu-native
#include <webgpu.h>
#include <wgpu.h>
-
-
static inline WGPUStringView str_view(const char *str) {
- if (!str) return {nullptr, 0};
+ if (!str)
+ return {nullptr, 0};
return {str, strlen(str)};
-
}
-
-
static inline WGPUStringView label_view(const char *str) {
#ifndef STRIP_ALL
- if (!str) return {nullptr, 0};
+ if (!str)
+ return {nullptr, 0};
return {str, strlen(str)};
@@ -91,15 +72,10 @@ static inline WGPUStringView label_view(const char *str) {
return {nullptr, 0};
#endif
-
}
-
-
#endif
-
-
struct GLFWwindow;
// Basic wrapper for WebGPU buffers
diff --git a/src/tests/test_sequence.cc b/src/tests/test_sequence.cc
index 48ae436..db66b8a 100644
--- a/src/tests/test_sequence.cc
+++ b/src/tests/test_sequence.cc
@@ -1,8 +1,8 @@
// This file is part of the 64k demo project.
// It tests the Sequence and Effect management system.
-#include "gpu/effect.h"
#include "gpu/demo_effects.h"
+#include "gpu/effect.h"
#include "gpu/gpu.h"
#include <assert.h>
#include <stdio.h>
@@ -14,7 +14,8 @@ static WGPUQueue dummy_queue = (WGPUQueue)1;
static WGPUTextureFormat dummy_format = (WGPUTextureFormat)1;
static WGPUSurface dummy_surface = (WGPUSurface)1;
static WGPUCommandEncoder dummy_encoder = (WGPUCommandEncoder)1;
-static WGPURenderPassEncoder dummy_render_pass_encoder = (WGPURenderPassEncoder)1;
+static WGPURenderPassEncoder dummy_render_pass_encoder =
+ (WGPURenderPassEncoder)1;
// --- Dummy Effect for Tracking ---
class DummyEffect : public Effect {
@@ -25,14 +26,39 @@ public:
int end_calls = 0;
bool is_pp = false;
- DummyEffect(bool post_process = false) : is_pp(post_process) {}
+ DummyEffect(bool post_process = false) : is_pp(post_process) {
+ }
- void init(MainSequence *demo) override { init_calls++; (void)demo; }
- void start() override { start_calls++; }
- void render(WGPURenderPassEncoder pass, float time, float beat, float intensity, float aspect_ratio) override { render_calls++; (void)pass; (void)time; (void)beat; (void)intensity; (void)aspect_ratio; }
- void compute(WGPUCommandEncoder encoder, float time, float beat, float intensity, float aspect_ratio) override { (void)encoder; (void)time; (void)beat; (void)intensity; (void)aspect_ratio; }
- void end() override { end_calls++; }
- bool is_post_process() const override { return is_pp; }
+ void init(MainSequence *demo) override {
+ init_calls++;
+ (void)demo;
+ }
+ void start() override {
+ start_calls++;
+ }
+ void render(WGPURenderPassEncoder pass, float time, float beat,
+ float intensity, float aspect_ratio) override {
+ render_calls++;
+ (void)pass;
+ (void)time;
+ (void)beat;
+ (void)intensity;
+ (void)aspect_ratio;
+ }
+ void compute(WGPUCommandEncoder encoder, float time, float beat,
+ float intensity, float aspect_ratio) override {
+ (void)encoder;
+ (void)time;
+ (void)beat;
+ (void)intensity;
+ (void)aspect_ratio;
+ }
+ void end() override {
+ end_calls++;
+ }
+ bool is_post_process() const override {
+ return is_pp;
+ }
};
// --- Dummy PostProcessEffect for Tracking (unused in simplified tests) ---
@@ -42,14 +68,30 @@ public:
int render_calls = 0;
int update_bind_group_calls = 0;
- DummyPostProcessEffect(WGPUDevice device, WGPUTextureFormat format) { (void)device; (void)format; }
+ DummyPostProcessEffect(WGPUDevice device, WGPUTextureFormat format) {
+ (void)device;
+ (void)format;
+ }
- void init(MainSequence *demo) override { init_calls++; (void)demo; }
- void render(WGPURenderPassEncoder pass, float time, float beat, float intensity, float aspect_ratio) override { render_calls++; (void)pass; (void)time; (void)beat; (void)intensity; (void)aspect_ratio; }
- void update_bind_group(WGPUTextureView input_view) override { update_bind_group_calls++; (void)input_view; }
+ void init(MainSequence *demo) override {
+ init_calls++;
+ (void)demo;
+ }
+ void render(WGPURenderPassEncoder pass, float time, float beat,
+ float intensity, float aspect_ratio) override {
+ render_calls++;
+ (void)pass;
+ (void)time;
+ (void)beat;
+ (void)intensity;
+ (void)aspect_ratio;
+ }
+ void update_bind_group(WGPUTextureView input_view) override {
+ update_bind_group_calls++;
+ (void)input_view;
+ }
};
-
// --- Test Cases ---
void test_effect_lifecycle() {
@@ -63,7 +105,9 @@ void test_effect_lifecycle() {
main_seq.add_sequence(seq1, 0.0f, 0);
// Before effect starts
- main_seq.render_frame(0.5f, 0, 0, 1.0f, dummy_surface); // This will still call real render, but test counts only init
+ main_seq.render_frame(0.5f, 0, 0, 1.0f,
+ dummy_surface); // This will still call real render, but
+ // test counts only init
assert(effect1->init_calls == 1);
assert(effect1->start_calls == 0);
assert(effect1->render_calls == 0);
@@ -72,7 +116,8 @@ void test_effect_lifecycle() {
// Effect starts
main_seq.render_frame(1.0f, 0, 0, 1.0f, dummy_surface);
assert(effect1->start_calls == 1);
- // assert(effect1->render_calls == 1); // No longer checking render calls directly from here
+ // assert(effect1->render_calls == 1); // No longer checking render calls
+ // directly from here
assert(effect1->end_calls == 0);
// During effect
@@ -109,7 +154,8 @@ void test_simulate_until() {
assert(effect1->init_calls == 1);
assert(effect1->start_calls == 1);
- assert(effect1->render_calls == 0); // Render should not be called in simulate_until
+ assert(effect1->render_calls ==
+ 0); // Render should not be called in simulate_until
assert(effect1->end_calls == 0);
main_seq.simulate_until(3.5f, 1.0f / 60.0f);
@@ -126,18 +172,17 @@ int main() {
printf("Running Sequence/Effect System tests...\n");
- // TODO: Re-enable and fix test_effect_lifecycle once GPU resource mocking is robust.
+ // TODO: Re-enable and fix test_effect_lifecycle once GPU resource mocking is
+ // robust.
// test_effect_lifecycle();
- // TODO: Re-enable and fix test_simulate_until once GPU resource mocking is robust.
+ // TODO: Re-enable and fix test_simulate_until once GPU resource mocking is
+ // robust.
// test_simulate_until();
printf("Sequence/Effect System tests PASSED\n");
return 0;
-
}
-
-