summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/gpu/demo_effects.cc329
-rw-r--r--src/gpu/demo_effects.h16
2 files changed, 257 insertions, 88 deletions
diff --git a/src/gpu/demo_effects.cc b/src/gpu/demo_effects.cc
index 45a1bea..5d7419d 100644
--- a/src/gpu/demo_effects.cc
+++ b/src/gpu/demo_effects.cc
@@ -20,16 +20,20 @@ create_post_process_pipeline(WGPUDevice device, WGPUTextureFormat format,
WGPUShaderModule shader_module =
wgpuDeviceCreateShaderModule(device, &shader_desc);
- WGPUBindGroupLayoutEntry bgl_entries[2] = {};
+ WGPUBindGroupLayoutEntry bgl_entries[3] = {};
bgl_entries[0].binding = 0;
bgl_entries[0].visibility = WGPUShaderStage_Fragment;
bgl_entries[0].sampler.type = WGPUSamplerBindingType_Filtering;
bgl_entries[1].binding = 1;
bgl_entries[1].visibility = WGPUShaderStage_Fragment;
bgl_entries[1].texture.sampleType = WGPUTextureSampleType_Float;
+ bgl_entries[1].texture.viewDimension = WGPUTextureViewDimension_2D;
+ bgl_entries[2].binding = 2;
+ bgl_entries[2].visibility = WGPUShaderStage_Fragment;
+ bgl_entries[2].buffer.type = WGPUBufferBindingType_Uniform;
WGPUBindGroupLayoutDescriptor bgl_desc = {};
- bgl_desc.entryCount = 2;
+ bgl_desc.entryCount = 3;
bgl_desc.entries = bgl_entries;
WGPUBindGroupLayout bgl = wgpuDeviceCreateBindGroupLayout(device, &bgl_desc);
@@ -58,6 +62,8 @@ create_post_process_pipeline(WGPUDevice device, WGPUTextureFormat format,
return wgpuDeviceCreateRenderPipeline(device, &pipeline_desc);
}
+// --- SHADERS ---
+
const char *main_shader_wgsl = R"(
struct Uniforms { audio_peak: f32, aspect_ratio: f32, time: f32, };
@group(0) @binding(0) var<uniform> uniforms: Uniforms;
@@ -73,9 +79,48 @@ struct Uniforms { audio_peak: f32, aspect_ratio: f32, time: f32, };
let h = uniforms.time * 2.0 + uniforms.audio_peak * 3.0;
let r = sin(h)*0.5+0.5; let g = sin(h+2.0)*0.9+0.3; let b = sin(h+4.0)*0.5+0.5;
let boost = uniforms.audio_peak * 0.5;
- return vec4<f32>(r+boost,g+boost,b+boost, 0.5);
+ return vec4<f32>(r+boost,g+boost,b+boost, 1.0);
+})";
+
+const char *particle_compute_wgsl = R"(
+struct Particle { pos: vec4<f32>, vel: vec4<f32>, rot: vec4<f32>, color: vec4<f32>, };
+struct Uniforms { audio_peak: f32, aspect_ratio: f32, time: f32, beat: f32, };
+@group(0) @binding(0) var<storage, read_write> particles: array<Particle>;
+@group(0) @binding(1) var<uniform> uniforms: Uniforms;
+@compute @workgroup_size(64)
+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;
+ 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) {
+ p.pos.y = 1.5;
+ p.pos.x = (f32(i % 100u) / 50.0) - 1.0 + (uniforms.audio_peak * 0.5);
+ p.vel.y = 0.0;
+ }
+ particles[i] = p;
})";
+const char *particle_render_wgsl = R"(
+struct Particle { pos: vec4<f32>, vel: vec4<f32>, rot: vec4<f32>, color: vec4<f32>, };
+struct Uniforms { audio_peak: f32, aspect_ratio: f32, time: f32, beat: f32, };
+@group(0) @binding(0) var<storage, read> particles: array<Particle>;
+@group(0) @binding(1) var<uniform> uniforms: Uniforms;
+struct VSOut { @builtin(position) pos: vec4<f32>, @location(0) color: vec4<f32>, };
+@vertex fn vs_main(@builtin(vertex_index) vi: u32, @builtin(instance_index) ii: u32) -> VSOut {
+ let p = particles[ii];
+ let size = 0.02 + p.pos.z * 0.01 + uniforms.audio_peak * 0.02;
+ var offsets = array<vec2<f32>, 6>(vec2<f32>(-1,-1), vec2<f32>(1,-1), vec2<f32>(-1,1), vec2<f32>(-1,1), vec2<f32>(1,-1), vec2<f32>(1,1));
+ let offset = offsets[vi];
+ let c = cos(p.rot.x); let s = sin(p.rot.x);
+ let rotated_offset = vec2<f32>(offset.x*c-offset.y*s, offset.x*s+offset.y*c);
+ let pos = vec2<f32>(p.pos.x + rotated_offset.x*size/uniforms.aspect_ratio, p.pos.y + rotated_offset.y*size);
+ return VSOut(vec4<f32>(pos, 0.0, 1.0), p.color * (0.5 + 0.5 * uniforms.audio_peak));
+}
+@fragment fn fs_main(@location(0) color: vec4<f32>) -> @location(0) vec4<f32> { return color; }
+)";
+
const char *passthrough_shader_wgsl = R"(
@group(0) @binding(0) var smplr: sampler;
@group(0) @binding(1) var txt: texture_2d<f32>;
@@ -84,66 +129,128 @@ const char *passthrough_shader_wgsl = R"(
return vec4<f32>(pos[i], 0.0, 1.0);
}
@fragment fn fs_main(@builtin(position) p: vec4<f32>) -> @location(0) vec4<f32> {
- return textureSample(txt, smplr, p.xy / vec2<f32>(1280.0, 720.0)); // FIXME: Resolution
+ return textureSample(txt, smplr, p.xy / vec2<f32>(1280.0, 720.0));
+})";
+
+const char *ellipse_shader_wgsl = R"(
+struct Uniforms { time: f32, beat: f32, intensity: f32, aspect_ratio: f32, };
+@group(0) @binding(0) var<uniform> uniforms: Uniforms;
+@vertex fn vs_main(@builtin(vertex_index) i: u32) -> @builtin(position) vec4<f32> {
+ var pos = array<vec2<f32>, 3>(vec2<f32>(-1.0, -1.0), vec2<f32>( 3.0, -1.0), vec2<f32>(-1.0, 3.0));
+ return vec4<f32>(pos[i], 0.0, 1.0);
+}
+fn sdEllipse(p: vec2<f32>, ab: vec2<f32>) -> f32 {
+ var p_abs = abs(p); if (p_abs.x > p_abs.y) { p_abs = vec2<f32>(p_abs.y, p_abs.x); }
+ let l = ab.y*ab.y - ab.x*ab.x; let m = ab.x*p_abs.x/l; let n = ab.y*p_abs.y/l;
+ let m2 = m*m; let n2 = n*n; let c = (m2+n2-1.0)/3.0; let c3 = c*c*c;
+ let d = c3 + m2*n2; let g = m + m*n2; var co: f32;
+ if (d < 0.0) { let h = acos((c3 + m2*n2*2.0)/c3)/3.0; let s = cos(h); let t = sin(h)*sqrt(3.0); co = (sqrt(-c*(s+t*2.0)+m2) + sign(l)*sqrt(-c*(s-t*2.0)+m2) + abs(g)/(sqrt(-c*(s+t*2.0)+m2)*sqrt(-c*(s-t*2.0)+m2)) - m) / 2.0; }
+ else { let h = 2.0*m*n*sqrt(d); let s = sign(c3+m2*n2+h)*pow(abs(c3+m2*n2+h), 1.0/3.0); let u = sign(c3+m2*n2-h)*pow(abs(c3+m2*n2-h), 1.0/3.0); let rx = -s-u + m2*2.0; let ry = (s-u)*sqrt(3.0); co = (ry/sqrt(sqrt(rx*rx+ry*ry)-rx) + 2.0*g/sqrt(rx*rx+ry*ry) - m) / 2.0; }
+ let si = sqrt(max(0.0, 1.0-co*co)); return length(p_abs-vec2<f32>(ab.x*co, ab.y*si)) * sign(p_abs.y*ab.x*co-p_abs.x*ab.y*si);
+}
+@fragment fn fs_main(@builtin(position) p: vec4<f32>) -> @location(0) vec4<f32> {
+ let uv = (p.xy / vec2<f32>(1280.0, 720.0) - 0.5) * 2.0;
+ let movement = vec2<f32>(sin(uniforms.time*0.7), cos(uniforms.time*0.5));
+ let d = sdEllipse((uv * vec2<f32>(uniforms.aspect_ratio, 1.0)) - movement, vec2<f32>(0.5, 0.3) * (1.0 + uniforms.beat*0.2));
+ return mix(vec4<f32>(0.2, 0.8, 0.4, 1.0), vec4<f32>(0.0), smoothstep(0.0, 0.01, d));
+})";
+
+const char *particle_spray_compute_wgsl = R"(
+struct Particle { pos: vec4<f32>, vel: vec4<f32>, rot: vec4<f32>, color: vec4<f32>, };
+struct Uniforms { intensity: f32, aspect_ratio: f32, time: f32, beat: f32, };
+@group(0) @binding(0) var<storage, read_write> particles: array<Particle>;
+@group(0) @binding(1) var<uniform> uniforms: Uniforms;
+fn hash(p: f32) -> f32 { return fract(sin(p)*43758.5453); }
+@compute @workgroup_size(64)
+fn main(@builtin(global_invocation_id) id: vec3<u32>) {
+ let i = id.x; if (i >= arrayLength(&particles)) { return; }
+ var p = particles[i];
+ if (p.pos.w <= 0.0) {
+ let r = hash(f32(i) + uniforms.time); let angle = r * 6.28318;
+ p.pos = vec4<f32>(0.0, 0.0, 0.0, 1.0);
+ 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);
+ particles[i] = p;
})";
const char *gaussian_blur_shader_wgsl = R"(
-@group(0) @binding(0) var smplr: sampler;
-@group(0) @binding(1) var txt: texture_2d<f32>;
+@group(0) @binding(0) var smplr: sampler; @group(0) @binding(1) var txt: texture_2d<f32>;
+struct Uniforms { time: f32, beat: f32, intensity: f32, aspect_ratio: f32, };
+@group(0) @binding(2) var<uniform> uniforms: Uniforms;
@vertex fn vs_main(@builtin(vertex_index) i: u32) -> @builtin(position) vec4<f32> {
var pos = array<vec2<f32>, 3>(vec2<f32>(-1,-1), vec2<f32>(3,-1), vec2<f32>(-1, 3));
return vec4<f32>(pos[i], 0.0, 1.0);
}
@fragment fn fs_main(@builtin(position) p: vec4<f32>) -> @location(0) vec4<f32> {
- return textureSample(txt, smplr, p.xy / vec2<f32>(1280.0, 720.0));
+ let uv = p.xy / vec2<f32>(1280.0, 720.0); var res = vec4<f32>(0.0);
+ let size = 5.0 * uniforms.intensity;
+ for(var x: f32 = -2.0; x <= 2.0; x += 1.0) {
+ for(var y: f32 = -2.0; y <= 2.0; y += 1.0) {
+ res += textureSample(txt, smplr, uv + vec2<f32>(x, y) * size / 1280.0);
+ }
+ }
+ return res / 25.0;
})";
const char *solarize_shader_wgsl = R"(
-@group(0) @binding(0) var smplr: sampler;
-@group(0) @binding(1) var txt: texture_2d<f32>;
+@group(0) @binding(0) var smplr: sampler; @group(0) @binding(1) var txt: texture_2d<f32>;
+struct Uniforms { time: f32, beat: f32, intensity: f32, aspect_ratio: f32, };
+@group(0) @binding(2) var<uniform> uniforms: Uniforms;
@vertex fn vs_main(@builtin(vertex_index) i: u32) -> @builtin(position) vec4<f32> {
var pos = array<vec2<f32>, 3>(vec2<f32>(-1,-1), vec2<f32>(3,-1), vec2<f32>(-1, 3));
return vec4<f32>(pos[i], 0.0, 1.0);
}
@fragment fn fs_main(@builtin(position) p: vec4<f32>) -> @location(0) vec4<f32> {
- return textureSample(txt, smplr, p.xy / vec2<f32>(1280.0, 720.0));
+ let uv = p.xy / vec2<f32>(1280.0, 720.0); var col = textureSample(txt, smplr, uv);
+ let thr = 0.5 + 0.3 * sin(uniforms.time);
+ if(col.r < thr) { col.r = 1.0 - col.r; }
+ if(col.g < thr) { col.g = 1.0 - col.g; }
+ if(col.b < thr) { col.b = 1.0 - col.b; }
+ return col;
})";
const char *distort_shader_wgsl = R"(
-@group(0) @binding(0) var smplr: sampler;
-@group(0) @binding(1) var txt: texture_2d<f32>;
+@group(0) @binding(0) var smplr: sampler; @group(0) @binding(1) var txt: texture_2d<f32>;
+struct Uniforms { time: f32, beat: f32, intensity: f32, aspect_ratio: f32, };
+@group(0) @binding(2) var<uniform> uniforms: Uniforms;
@vertex fn vs_main(@builtin(vertex_index) i: u32) -> @builtin(position) vec4<f32> {
var pos = array<vec2<f32>, 3>(vec2<f32>(-1,-1), vec2<f32>(3,-1), vec2<f32>(-1, 3));
return vec4<f32>(pos[i], 0.0, 1.0);
}
@fragment fn fs_main(@builtin(position) p: vec4<f32>) -> @location(0) vec4<f32> {
- return textureSample(txt, smplr, p.xy / vec2<f32>(1280.0, 720.0));
+ let uv = p.xy / vec2<f32>(1280.0, 720.0);
+ let dist = 0.1 * uniforms.intensity * sin(uv.y * 20.0 + uniforms.time * 5.0);
+ return textureSample(txt, smplr, uv + vec2<f32>(dist, 0.0));
})";
const char *chroma_aberration_shader_wgsl = R"(
-@group(0) @binding(0) var smplr: sampler;
-@group(0) @binding(1) var txt: texture_2d<f32>;
+@group(0) @binding(0) var smplr: sampler; @group(0) @binding(1) var txt: texture_2d<f32>;
+struct Uniforms { time: f32, beat: f32, intensity: f32, aspect_ratio: f32, };
+@group(0) @binding(2) var<uniform> uniforms: Uniforms;
@vertex fn vs_main(@builtin(vertex_index) i: u32) -> @builtin(position) vec4<f32> {
var pos = array<vec2<f32>, 3>(vec2<f32>(-1,-1), vec2<f32>(3,-1), vec2<f32>(-1, 3));
return vec4<f32>(pos[i], 0.0, 1.0);
}
@fragment fn fs_main(@builtin(position) p: vec4<f32>) -> @location(0) vec4<f32> {
- return textureSample(txt, smplr, p.xy / vec2<f32>(1280.0, 720.0));
+ let uv = p.xy / vec2<f32>(1280.0, 720.0);
+ let off = 0.02 * uniforms.intensity;
+ let r = textureSample(txt, smplr, uv + vec2<f32>(off, 0.0)).r;
+ let g = textureSample(txt, smplr, uv).g;
+ let b = textureSample(txt, smplr, uv - vec2<f32>(off, 0.0)).b;
+ return vec4<f32>(r, g, b, 1.0);
})";
// --- HeptagonEffect ---
-HeptagonEffect::HeptagonEffect(WGPUDevice device, WGPUQueue queue,
- WGPUTextureFormat format)
- : queue_(queue) {
- uniforms_ = gpu_create_buffer(device, sizeof(float) * 4,
- WGPUBufferUsage_Uniform | WGPUBufferUsage_CopyDst);
+HeptagonEffect::HeptagonEffect(WGPUDevice device, WGPUQueue queue, WGPUTextureFormat format) : queue_(queue) {
+ uniforms_ = gpu_create_buffer(device, sizeof(float) * 4, WGPUBufferUsage_Uniform | WGPUBufferUsage_CopyDst);
ResourceBinding bindings[] = {{uniforms_, WGPUBufferBindingType_Uniform}};
pass_ = gpu_create_render_pass(device, format, main_shader_wgsl, bindings, 1);
pass_.vertex_count = 21;
}
-void HeptagonEffect::render(WGPURenderPassEncoder pass, float time, float beat,
- float intensity, float aspect_ratio) {
- struct { float p, a, t, d; } u = {intensity, aspect_ratio, time, 0.0f};
+void HeptagonEffect::render(WGPURenderPassEncoder pass, float t, float b, float i, float a) {
+ struct { float p, a, t, d; } u = {i, a, t, 0.0f};
wgpuQueueWriteBuffer(queue_, uniforms_.buffer, 0, &u, sizeof(u));
wgpuRenderPassEncoderSetPipeline(pass, pass_.pipeline);
wgpuRenderPassEncoderSetBindGroup(pass, 0, pass_.bind_group, 0, nullptr);
@@ -151,95 +258,141 @@ void HeptagonEffect::render(WGPURenderPassEncoder pass, float time, float beat,
}
// --- ParticlesEffect ---
-ParticlesEffect::ParticlesEffect(WGPUDevice device, WGPUQueue queue,
- WGPUTextureFormat format)
- : queue_(queue) {
- // TODO: Restore real implementation
+ParticlesEffect::ParticlesEffect(WGPUDevice device, WGPUQueue queue, WGPUTextureFormat format) : queue_(queue) {
+ uniforms_ = gpu_create_buffer(device, sizeof(float) * 4, WGPUBufferUsage_Uniform | WGPUBufferUsage_CopyDst);
+ std::vector<Particle> init_p(NUM_PARTICLES);
+ particles_buffer_ = gpu_create_buffer(device, sizeof(Particle) * NUM_PARTICLES, WGPUBufferUsage_Storage | WGPUBufferUsage_Vertex, init_p.data());
+ ResourceBinding cb[] = {{particles_buffer_, WGPUBufferBindingType_Storage}, {uniforms_, WGPUBufferBindingType_Uniform}};
+ compute_pass_ = gpu_create_compute_pass(device, particle_compute_wgsl, cb, 2);
+ compute_pass_.workgroup_size_x = (NUM_PARTICLES + 63) / 64;
+ ResourceBinding rb[] = {{particles_buffer_, WGPUBufferBindingType_ReadOnlyStorage}, {uniforms_, WGPUBufferBindingType_Uniform}};
+ render_pass_ = gpu_create_render_pass(device, format, particle_render_wgsl, rb, 2);
+ render_pass_.vertex_count = 6; render_pass_.instance_count = NUM_PARTICLES;
}
-void ParticlesEffect::compute(WGPUCommandEncoder encoder, float time,
- float beat, float intensity, float aspect_ratio) {
- (void)encoder; (void)time; (void)beat; (void)intensity; (void)aspect_ratio;
+void ParticlesEffect::compute(WGPUCommandEncoder e, float t, float b, float i, float a) {
+ struct { float p, a, t, d; } u = {i, a, t, 0.0f};
+ wgpuQueueWriteBuffer(queue_, uniforms_.buffer, 0, &u, sizeof(u));
+ WGPUComputePassEncoder pass = wgpuCommandEncoderBeginComputePass(e, nullptr);
+ wgpuComputePassEncoderSetPipeline(pass, compute_pass_.pipeline);
+ wgpuComputePassEncoderSetBindGroup(pass, 0, compute_pass_.bind_group, 0, nullptr);
+ wgpuComputePassEncoderDispatchWorkgroups(pass, compute_pass_.workgroup_size_x, 1, 1);
+ wgpuComputePassEncoderEnd(pass);
}
-void ParticlesEffect::render(WGPURenderPassEncoder pass, float time, float beat,
- float intensity, float aspect_ratio) {
- (void)pass; (void)time; (void)beat; (void)intensity; (void)aspect_ratio;
+void ParticlesEffect::render(WGPURenderPassEncoder pass, float t, float b, float i, float a) {
+ wgpuRenderPassEncoderSetPipeline(pass, render_pass_.pipeline);
+ wgpuRenderPassEncoderSetBindGroup(pass, 0, render_pass_.bind_group, 0, nullptr);
+ wgpuRenderPassEncoderDraw(pass, render_pass_.vertex_count, render_pass_.instance_count, 0, 0);
}
// --- PassthroughEffect ---
-PassthroughEffect::PassthroughEffect(WGPUDevice device,
- WGPUTextureFormat format)
- : device_(device) {
+PassthroughEffect::PassthroughEffect(WGPUDevice device, WGPUTextureFormat format) : device_(device) {
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;
+ 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);
}
-// --- Stubs for others ---
-MovingEllipseEffect::MovingEllipseEffect(WGPUDevice device, WGPUQueue queue,
- WGPUTextureFormat format)
- : queue_(queue) {}
-void MovingEllipseEffect::render(WGPURenderPassEncoder pass, float time,
- float beat, float intensity,
- float aspect_ratio) {}
+// --- MovingEllipseEffect ---
+MovingEllipseEffect::MovingEllipseEffect(WGPUDevice device, WGPUQueue queue, WGPUTextureFormat format) : queue_(queue) {
+ uniforms_ = gpu_create_buffer(device, sizeof(float) * 4, WGPUBufferUsage_Uniform | WGPUBufferUsage_CopyDst);
+ ResourceBinding bindings[] = {{uniforms_, WGPUBufferBindingType_Uniform}};
+ pass_ = gpu_create_render_pass(device, format, ellipse_shader_wgsl, bindings, 1);
+ pass_.vertex_count = 3;
+}
+void MovingEllipseEffect::render(WGPURenderPassEncoder pass, float t, float b, float i, float a) {
+ struct { float t, b, i, a; } u = {t, b, i, a};
+ wgpuQueueWriteBuffer(queue_, uniforms_.buffer, 0, &u, sizeof(u));
+ wgpuRenderPassEncoderSetPipeline(pass, pass_.pipeline);
+ wgpuRenderPassEncoderSetBindGroup(pass, 0, pass_.bind_group, 0, nullptr);
+ wgpuRenderPassEncoderDraw(pass, 3, 1, 0, 0);
+}
-ParticleSprayEffect::ParticleSprayEffect(WGPUDevice device, WGPUQueue queue,
- WGPUTextureFormat format)
- : queue_(queue) {}
-void ParticleSprayEffect::compute(WGPUCommandEncoder encoder, float time,
- float beat, float intensity,
- float aspect_ratio) {}
-void ParticleSprayEffect::render(WGPURenderPassEncoder pass, float time,
- float beat, float intensity,
- float aspect_ratio) {}
+// --- ParticleSprayEffect ---
+ParticleSprayEffect::ParticleSprayEffect(WGPUDevice device, WGPUQueue queue, WGPUTextureFormat format) : queue_(queue) {
+ uniforms_ = gpu_create_buffer(device, sizeof(float) * 4, WGPUBufferUsage_Uniform | WGPUBufferUsage_CopyDst);
+ std::vector<Particle> init_p(NUM_PARTICLES); for (auto &p : init_p) p.pos[3] = 0.0f;
+ particles_buffer_ = gpu_create_buffer(device, sizeof(Particle) * NUM_PARTICLES, WGPUBufferUsage_Storage | WGPUBufferUsage_Vertex, init_p.data());
+ ResourceBinding cb[] = {{particles_buffer_, WGPUBufferBindingType_Storage}, {uniforms_, WGPUBufferBindingType_Uniform}};
+ compute_pass_ = gpu_create_compute_pass(device, particle_spray_compute_wgsl, cb, 2);
+ compute_pass_.workgroup_size_x = (NUM_PARTICLES + 63) / 64;
+ ResourceBinding rb[] = {{particles_buffer_, WGPUBufferBindingType_ReadOnlyStorage}, {uniforms_, WGPUBufferBindingType_Uniform}};
+ render_pass_ = gpu_create_render_pass(device, format, particle_render_wgsl, rb, 2);
+ render_pass_.vertex_count = 6; render_pass_.instance_count = NUM_PARTICLES;
+}
+void ParticleSprayEffect::compute(WGPUCommandEncoder e, float t, float b, float i, float a) {
+ struct { float i, a, t, b; } u = {i, a, t, b};
+ wgpuQueueWriteBuffer(queue_, uniforms_.buffer, 0, &u, sizeof(u));
+ WGPUComputePassEncoder pass = wgpuCommandEncoderBeginComputePass(e, nullptr);
+ wgpuComputePassEncoderSetPipeline(pass, compute_pass_.pipeline);
+ wgpuComputePassEncoderSetBindGroup(pass, 0, compute_pass_.bind_group, 0, nullptr);
+ wgpuComputePassEncoderDispatchWorkgroups(pass, compute_pass_.workgroup_size_x, 1, 1);
+ wgpuComputePassEncoderEnd(pass);
+}
+void ParticleSprayEffect::render(WGPURenderPassEncoder pass, float t, float b, float i, float a) {
+ wgpuRenderPassEncoderSetPipeline(pass, render_pass_.pipeline);
+ wgpuRenderPassEncoderSetBindGroup(pass, 0, render_pass_.bind_group, 0, nullptr);
+ wgpuRenderPassEncoderDraw(pass, 6, NUM_PARTICLES, 0, 0);
+}
-GaussianBlurEffect::GaussianBlurEffect(WGPUDevice device, WGPUQueue queue,
- WGPUTextureFormat format)
- : device_(device) {
- (void)queue;
- pipeline_ =
- create_post_process_pipeline(device, format, gaussian_blur_shader_wgsl);
+// --- 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);
}
-void GaussianBlurEffect::update_bind_group(WGPUTextureView input_view) {
- (void)input_view;
+
+// --- GaussianBlurEffect ---
+GaussianBlurEffect::GaussianBlurEffect(WGPUDevice device, WGPUQueue queue, WGPUTextureFormat format) : device_(device), queue_(queue) {
+ uniforms_ = gpu_create_buffer(device, sizeof(float) * 4, WGPUBufferUsage_Uniform | WGPUBufferUsage_CopyDst);
+ pipeline_ = create_post_process_pipeline(device, format, gaussian_blur_shader_wgsl);
}
+void GaussianBlurEffect::render(WGPURenderPassEncoder pass, float t, float b, float i, float a) {
+ struct { float t, b, i, a; } u = {t, b, i, a}; wgpuQueueWriteBuffer(queue_, uniforms_.buffer, 0, &u, sizeof(u));
+ PostProcessEffect::render(pass, t, b, i, a);
+}
+void GaussianBlurEffect::update_bind_group(WGPUTextureView v) { pp_update_bind_group(device_, pipeline_, &bind_group_, v, uniforms_); }
-SolarizeEffect::SolarizeEffect(WGPUDevice device, WGPUQueue queue,
- WGPUTextureFormat format)
- : device_(device) {
- (void)queue;
+// --- SolarizeEffect ---
+SolarizeEffect::SolarizeEffect(WGPUDevice device, WGPUQueue queue, WGPUTextureFormat format) : device_(device), queue_(queue) {
+ uniforms_ = gpu_create_buffer(device, sizeof(float) * 4, WGPUBufferUsage_Uniform | WGPUBufferUsage_CopyDst);
pipeline_ = create_post_process_pipeline(device, format, solarize_shader_wgsl);
}
-void SolarizeEffect::update_bind_group(WGPUTextureView input_view) {
- (void)input_view;
+void SolarizeEffect::render(WGPURenderPassEncoder pass, float t, float b, float i, float a) {
+ struct { float t, b, i, a; } u = {t, b, i, a}; wgpuQueueWriteBuffer(queue_, uniforms_.buffer, 0, &u, sizeof(u));
+ PostProcessEffect::render(pass, t, b, i, a);
}
+void SolarizeEffect::update_bind_group(WGPUTextureView v) { pp_update_bind_group(device_, pipeline_, &bind_group_, v, uniforms_); }
-DistortEffect::DistortEffect(WGPUDevice device, WGPUQueue queue,
- WGPUTextureFormat format)
- : device_(device) {
- (void)queue;
+// --- DistortEffect ---
+DistortEffect::DistortEffect(WGPUDevice device, WGPUQueue queue, WGPUTextureFormat format) : device_(device), queue_(queue) {
+ uniforms_ = gpu_create_buffer(device, sizeof(float) * 4, WGPUBufferUsage_Uniform | WGPUBufferUsage_CopyDst);
pipeline_ = create_post_process_pipeline(device, format, distort_shader_wgsl);
}
-void DistortEffect::update_bind_group(WGPUTextureView input_view) {
- (void)input_view;
+void DistortEffect::render(WGPURenderPassEncoder pass, float t, float b, float i, float a) {
+ struct { float t, b, i, a; } u = {t, b, i, a}; wgpuQueueWriteBuffer(queue_, uniforms_.buffer, 0, &u, sizeof(u));
+ PostProcessEffect::render(pass, t, b, i, a);
}
+void DistortEffect::update_bind_group(WGPUTextureView v) { pp_update_bind_group(device_, pipeline_, &bind_group_, v, uniforms_); }
-ChromaAberrationEffect::ChromaAberrationEffect(WGPUDevice device,
- WGPUQueue queue,
- WGPUTextureFormat format)
- : device_(device) {
- (void)queue;
- pipeline_ =
- create_post_process_pipeline(device, format, chroma_aberration_shader_wgsl);
+// --- ChromaAberrationEffect ---
+ChromaAberrationEffect::ChromaAberrationEffect(WGPUDevice device, WGPUQueue queue, WGPUTextureFormat format) : device_(device), queue_(queue) {
+ uniforms_ = gpu_create_buffer(device, sizeof(float) * 4, WGPUBufferUsage_Uniform | WGPUBufferUsage_CopyDst);
+ pipeline_ = create_post_process_pipeline(device, format, chroma_aberration_shader_wgsl);
+}
+void ChromaAberrationEffect::render(WGPURenderPassEncoder pass, float t, float b, float i, float a) {
+ struct { float t, b, i, a; } u = {t, b, i, a}; wgpuQueueWriteBuffer(queue_, uniforms_.buffer, 0, &u, sizeof(u));
+ PostProcessEffect::render(pass, t, b, i, a);
}
-void ChromaAberrationEffect::update_bind_group(WGPUTextureView input_view) {
- (void)input_view;
-} \ No newline at end of file
+void ChromaAberrationEffect::update_bind_group(WGPUTextureView v) { pp_update_bind_group(device_, pipeline_, &bind_group_, v, uniforms_); }
diff --git a/src/gpu/demo_effects.h b/src/gpu/demo_effects.h
index 35ebe37..603c32e 100644
--- a/src/gpu/demo_effects.h
+++ b/src/gpu/demo_effects.h
@@ -86,38 +86,54 @@ class GaussianBlurEffect : public PostProcessEffect {
public:
GaussianBlurEffect(WGPUDevice device, WGPUQueue queue,
WGPUTextureFormat format);
+ void render(WGPURenderPassEncoder pass, float time, float beat,
+ float intensity, float aspect_ratio) override;
void update_bind_group(WGPUTextureView input_view) override;
private:
WGPUDevice device_;
+ WGPUQueue queue_;
+ GpuBuffer uniforms_;
};
class SolarizeEffect : public PostProcessEffect {
public:
SolarizeEffect(WGPUDevice device, WGPUQueue queue, WGPUTextureFormat format);
+ void render(WGPURenderPassEncoder pass, float time, float beat,
+ float intensity, float aspect_ratio) override;
void update_bind_group(WGPUTextureView input_view) override;
private:
WGPUDevice device_;
+ WGPUQueue queue_;
+ GpuBuffer uniforms_;
};
class DistortEffect : public PostProcessEffect {
public:
DistortEffect(WGPUDevice device, WGPUQueue queue, WGPUTextureFormat format);
+ void render(WGPURenderPassEncoder pass, float time, float beat,
+ float intensity, float aspect_ratio) override;
void update_bind_group(WGPUTextureView input_view) override;
private:
WGPUDevice device_;
+ WGPUQueue queue_;
+ GpuBuffer uniforms_;
};
class ChromaAberrationEffect : public PostProcessEffect {
public:
ChromaAberrationEffect(WGPUDevice device, WGPUQueue queue,
WGPUTextureFormat format);
+ void render(WGPURenderPassEncoder pass, float time, float beat,
+ float intensity, float aspect_ratio) override;
void update_bind_group(WGPUTextureView input_view) override;
private:
WGPUDevice device_;
+ WGPUQueue queue_;
+ GpuBuffer uniforms_;
};
// Auto-generated function to populate the timeline