From a126caf9e52b8f7c5be8e09b303e741aa9a410cb Mon Sep 17 00:00:00 2001 From: skal Date: Mon, 16 Feb 2026 10:00:30 +0100 Subject: feat(sequence): port particles_effect to v2 - Add ParticlesEffectV2 with compute + render passes - Create particle_compute_v2.wgsl and particle_render_v2.wgsl - Use UniformsSequenceParams for beat-synchronized particles - Update timeline_v2.seq particles sequence (simplified 2-effect chain) - Add shader exports to shaders.{h,cc} - All 36 tests passing handoff(Claude): Particles v2 complete, rotating_cube next --- src/effects/particles_effect_v2.cc | 94 ++++++++++++++++++++++++++++++++++++++ src/effects/particles_effect_v2.h | 35 ++++++++++++++ src/gpu/shaders.cc | 8 ++++ src/gpu/shaders.h | 2 + 4 files changed, 139 insertions(+) create mode 100644 src/effects/particles_effect_v2.cc create mode 100644 src/effects/particles_effect_v2.h (limited to 'src') diff --git a/src/effects/particles_effect_v2.cc b/src/effects/particles_effect_v2.cc new file mode 100644 index 0000000..5a1a07c --- /dev/null +++ b/src/effects/particles_effect_v2.cc @@ -0,0 +1,94 @@ +// This file is part of the 64k demo project. +// It implements the ParticlesEffectV2. + +#include "effects/particles_effect_v2.h" +#include "gpu/gpu.h" +#include "gpu/shaders.h" +#include + +ParticlesEffectV2::ParticlesEffectV2(const GpuContext& ctx, + const std::vector& inputs, + const std::vector& outputs) + : EffectV2(ctx, inputs, outputs) { + // Initialize uniforms + uniforms_.init(ctx_.device); + + // Initialize particles buffer + std::vector init_p(NUM_PARTICLES); + for (int i = 0; i < NUM_PARTICLES; ++i) { + float x = (float)(i % 100) / 50.0f - 1.0f; + float y = (float)(i / 100) / 100.0f * 3.0f - 1.5f; + init_p[i].pos[0] = x; + init_p[i].pos[1] = y; + init_p[i].pos[2] = ((float)i / NUM_PARTICLES) * 0.5f; + init_p[i].pos[3] = 1.0f; + init_p[i].vel[0] = 0.0f; + init_p[i].vel[1] = 0.0f; + init_p[i].vel[2] = 0.0f; + init_p[i].vel[3] = 0.0f; + init_p[i].rot[0] = 0.0f; + init_p[i].rot[1] = ((float)(i % 7) / 7.0f) * 3.14159f; + init_p[i].rot[2] = 0.0f; + init_p[i].rot[3] = 0.0f; + float hue = (float)(i % 100) / 100.0f; + init_p[i].color[0] = 0.5f + 0.5f * hue; + init_p[i].color[1] = 0.3f + 0.7f * (1.0f - hue); + init_p[i].color[2] = 0.8f; + init_p[i].color[3] = 0.8f; + } + + particles_buffer_ = gpu_create_buffer( + ctx_.device, sizeof(Particle) * NUM_PARTICLES, + WGPUBufferUsage_Storage | WGPUBufferUsage_Vertex, init_p.data()); + + // Create compute shader (particle simulation) + ResourceBinding compute_bindings[] = { + {particles_buffer_, WGPUBufferBindingType_Storage}, + {uniforms_.get(), WGPUBufferBindingType_Uniform}}; + compute_pass_ = gpu_create_compute_pass(ctx_.device, particle_compute_v2_wgsl, + compute_bindings, 2); + compute_pass_.workgroup_size_x = (NUM_PARTICLES + 63) / 64; + + // Create render shader (particle rendering) + ResourceBinding render_bindings[] = { + {particles_buffer_, WGPUBufferBindingType_ReadOnlyStorage}, + {uniforms_.get(), WGPUBufferBindingType_Uniform}}; + render_pass_ = gpu_create_render_pass(ctx_.device, ctx_.format, particle_render_v2_wgsl, + render_bindings, 2); + render_pass_.vertex_count = 6; + render_pass_.instance_count = NUM_PARTICLES; +} + +void ParticlesEffectV2::render(WGPUCommandEncoder encoder, + const UniformsSequenceParams& params, + NodeRegistry& nodes) { + // Update uniforms + uniforms_.update(ctx_.queue, params); + + // Run compute pass (particle simulation) + WGPUComputePassEncoder compute = wgpuCommandEncoderBeginComputePass(encoder, nullptr); + wgpuComputePassEncoderSetPipeline(compute, compute_pass_.pipeline); + wgpuComputePassEncoderSetBindGroup(compute, 0, compute_pass_.bind_group, 0, nullptr); + wgpuComputePassEncoderDispatchWorkgroups(compute, compute_pass_.workgroup_size_x, 1, 1); + wgpuComputePassEncoderEnd(compute); + + // Run render pass (draw particles to output) + WGPUTextureView output_view = nodes.get_view(output_nodes_[0]); + + WGPURenderPassColorAttachment color_attachment = { + .view = output_view, + .depthSlice = WGPU_DEPTH_SLICE_UNDEFINED, + .loadOp = WGPULoadOp_Clear, + .storeOp = WGPUStoreOp_Store, + .clearValue = {0.0, 0.0, 0.0, 1.0}}; + + WGPURenderPassDescriptor render_desc = { + .colorAttachmentCount = 1, + .colorAttachments = &color_attachment}; + + WGPURenderPassEncoder pass = wgpuCommandEncoderBeginRenderPass(encoder, &render_desc); + 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); + wgpuRenderPassEncoderEnd(pass); +} diff --git a/src/effects/particles_effect_v2.h b/src/effects/particles_effect_v2.h new file mode 100644 index 0000000..f0f260c --- /dev/null +++ b/src/effects/particles_effect_v2.h @@ -0,0 +1,35 @@ +// This file is part of the 64k demo project. +// It declares the ParticlesEffectV2. + +#pragma once + +#include "gpu/effect_v2.h" +#include "gpu/gpu.h" +#include "gpu/uniform_helper.h" +#include + +// Particle structure +static const int NUM_PARTICLES = 10000; + +struct Particle { + float pos[4]; + float vel[4]; + float rot[4]; + float color[4]; +}; + +class ParticlesEffectV2 : public EffectV2 { + public: + ParticlesEffectV2(const GpuContext& ctx, + const std::vector& inputs, + const std::vector& outputs); + void render(WGPUCommandEncoder encoder, + const UniformsSequenceParams& params, + NodeRegistry& nodes) override; + + private: + ComputePass compute_pass_; + RenderPass render_pass_; + GpuBuffer particles_buffer_; + UniformBuffer uniforms_; +}; diff --git a/src/gpu/shaders.cc b/src/gpu/shaders.cc index 4f24705..bea1eb9 100644 --- a/src/gpu/shaders.cc +++ b/src/gpu/shaders.cc @@ -173,3 +173,11 @@ const char* gaussian_blur_v2_shader_wgsl = const char* heptagon_v2_shader_wgsl = SafeGetAsset(AssetId::ASSET_SHADER_HEPTAGON_V2); + +const char* particle_compute_v2_wgsl = + + SafeGetAsset(AssetId::ASSET_SHADER_PARTICLE_COMPUTE_V2); + +const char* particle_render_v2_wgsl = + + SafeGetAsset(AssetId::ASSET_SHADER_PARTICLE_RENDER_V2); diff --git a/src/gpu/shaders.h b/src/gpu/shaders.h index d1658fa..b7ee226 100644 --- a/src/gpu/shaders.h +++ b/src/gpu/shaders.h @@ -33,3 +33,5 @@ extern const char* gen_mask_compute_wgsl; extern const char* passthrough_v2_shader_wgsl; extern const char* gaussian_blur_v2_shader_wgsl; extern const char* heptagon_v2_shader_wgsl; +extern const char* particle_compute_v2_wgsl; +extern const char* particle_render_v2_wgsl; -- cgit v1.2.3