summaryrefslogtreecommitdiff
path: root/tools
diff options
context:
space:
mode:
authorskal <pascal.massimino@gmail.com>2026-02-16 11:54:46 +0100
committerskal <pascal.massimino@gmail.com>2026-02-16 11:54:46 +0100
commitaf5d0e4c3a6cb773a4fb51ac32f4c33a7f8d8224 (patch)
treea76464ca40a43d6042ed5431547008cfbe746c34 /tools
parent8eeadaf0d5653c21b948103e4d328f634b739a17 (diff)
feat(sequence): complete v2 migration with DAG-based routing
Phase 4 complete: V1 system removed, v2 fully operational. Architecture Changes: - Explicit Node system with typed buffers (u8x4_norm, f32x4, depth24) - DAG effect routing with multi-input/multi-output support - Python compiler (seq_compiler_v2.py) with topological sort and ping-pong optimization - Compile-time node aliasing for framebuffer reuse V1 Removal (~4KB): - Deleted effect.h/cc base classes (1.4KB) - Deleted 19 v1 effect pairs: heptagon, particles, passthrough, gaussian_blur, solarize, scene1, chroma_aberration, vignette, hybrid_3d, flash_cube, theme_modulation, fade, flash, circle_mask, rotating_cube, sdf_test, distort, moving_ellipse, particle_spray (2.7KB) V2 Effects Ported: - PassthroughEffectV2, PlaceholderEffectV2 - GaussianBlurEffectV2 (multi-pass with temp nodes) - HeptagonEffectV2 (scene effect with dummy texture) - ParticlesEffectV2 (compute + render, format fixed) - RotatingCubeEffectV2 (3D with depth node) - Hybrid3DEffectV2 (Renderer3D integration, dummy textures for noise/sky) Compiler Features: - DAG validation (cycle detection, connectivity checks) - Topological sort for execution order - Ping-pong optimization (aliased node detection) - Surface-based and encoder-based RenderV2Timeline generation - init_effect_nodes() automatic generation Fixes Applied: - WebGPU binding layout validation (standard v2 post-process layout) - Surface format mismatch (ctx.format for blit, RGBA8Unorm for framebuffers) - Depth attachment compatibility (removed forced depth from gpu_create_render_pass) - Renderer3D texture initialization (created dummy 1x1 white textures) - ParticlesEffectV2 format (changed from ctx.format to RGBA8Unorm) - Encoder-based RenderV2Timeline (added missing preprocess() call) Testing: - 34/36 tests passing (2 v1-dependent tests disabled) - demo64k runs successfully (no crashes) - All seek positions work (--seek 12, --seek 15 validated) Documentation: - Updated PROJECT_CONTEXT.md (v2 status, reference to SEQUENCE_v2.md) - Added completion entry to COMPLETED.md TODO (Future): - Port CNN effects to v2 - Implement flatten mode (--flatten code generation) - Port remaining 10+ effects - Update HTML timeline editor for v2 (deferred) handoff(Claude): Sequence v2 migration complete, v1 removed, system operational. Phase 5 (editor) deferred per user preference. Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
Diffstat (limited to 'tools')
-rwxr-xr-xtools/seq_compiler_v2.py166
-rw-r--r--tools/test_demo.seq9
2 files changed, 152 insertions, 23 deletions
diff --git a/tools/seq_compiler_v2.py b/tools/seq_compiler_v2.py
index 9918a92..f835295 100755
--- a/tools/seq_compiler_v2.py
+++ b/tools/seq_compiler_v2.py
@@ -432,7 +432,8 @@ class {class_name} : public SequenceV2 {{
}});
'''
- cpp += ''' }
+ cpp += ''' init_effect_nodes();
+ }
};
'''
@@ -482,7 +483,6 @@ def main():
# Generate sequence registry and accessors
all_cpp += '''
// V2 Sequence Registry
-#include "gpu/effect.h"
#include <vector>
#include <memory>
@@ -524,28 +524,160 @@ void RenderV2Timeline(WGPUCommandEncoder encoder, float time, int width, int hei
float beat_time, float audio_intensity) {
SequenceV2* seq = GetActiveV2Sequence(time);
if (seq) {
- UniformsSequenceParams params = {};
- params.resolution = {(float)width, (float)height};
- params.aspect_ratio = (float)width / (float)height;
- params.time = time;
- params.beat_time = beat_time;
- params.beat_phase = 0.0f;
- params.audio_intensity = audio_intensity;
-
+ seq->preprocess(time, beat_time, 0.0f, audio_intensity);
seq->render_effects(encoder);
}
}
-// V1 compatibility stub
-void LoadTimeline(MainSequence& main_seq, const GpuContext& ctx) {
- // V1 stub - v2 sequences initialized separately
- (void)main_seq;
- (void)ctx;
-}
-
float GetDemoDuration() {
return 40.0f; // TODO: Calculate from v2 sequences
}
+
+// Surface-based rendering with framebuffers
+#include "gpu/post_process_helper.h"
+#include "gpu/shaders.h"
+
+static WGPUTexture g_source_texture = nullptr;
+static WGPUTextureView g_source_view = nullptr;
+static WGPUTexture g_sink_texture = nullptr;
+static WGPUTextureView g_sink_view = nullptr;
+static int g_fb_width = 0;
+static int g_fb_height = 0;
+static UniformBuffer<UniformsSequenceParams> g_blit_uniforms;
+
+static void ensure_framebuffers(WGPUDevice device, int width, int height) {
+ if (g_source_texture && g_fb_width == width && g_fb_height == height) {
+ return;
+ }
+
+ // Release old
+ if (g_source_view) wgpuTextureViewRelease(g_source_view);
+ if (g_source_texture) wgpuTextureRelease(g_source_texture);
+ if (g_sink_view) wgpuTextureViewRelease(g_sink_view);
+ if (g_sink_texture) wgpuTextureRelease(g_sink_texture);
+
+ // Create new
+ WGPUTextureDescriptor tex_desc = {};
+ tex_desc.size = {(uint32_t)width, (uint32_t)height, 1};
+ tex_desc.format = WGPUTextureFormat_RGBA8Unorm;
+ tex_desc.usage = WGPUTextureUsage_RenderAttachment | WGPUTextureUsage_TextureBinding;
+ tex_desc.dimension = WGPUTextureDimension_2D;
+ tex_desc.mipLevelCount = 1;
+ tex_desc.sampleCount = 1;
+
+ g_source_texture = wgpuDeviceCreateTexture(device, &tex_desc);
+ g_source_view = wgpuTextureCreateView(g_source_texture, nullptr);
+ g_sink_texture = wgpuDeviceCreateTexture(device, &tex_desc);
+ g_sink_view = wgpuTextureCreateView(g_sink_texture, nullptr);
+
+ g_fb_width = width;
+ g_fb_height = height;
+}
+
+void RenderV2Timeline(WGPUSurface surface, float time, int width, int height,
+ float beat_time, float audio_intensity) {
+ SequenceV2* seq = GetActiveV2Sequence(time);
+ if (!seq) return;
+
+ const GpuContext* ctx = gpu_get_context();
+ ensure_framebuffers(ctx->device, width, height);
+
+ // Initialize blit uniforms buffer if needed
+ if (!g_blit_uniforms.get().buffer) {
+ g_blit_uniforms.init(ctx->device);
+ }
+
+ // Bind source/sink views to sequence
+ seq->set_source_view(g_source_view);
+ seq->set_sink_view(g_sink_view);
+
+ // Update uniforms via preprocess
+ seq->preprocess(time, beat_time, 0.0f, audio_intensity);
+
+ WGPUCommandEncoder encoder = wgpuDeviceCreateCommandEncoder(ctx->device, nullptr);
+
+ // Clear source
+ WGPURenderPassColorAttachment clear_attach = {};
+ clear_attach.view = g_source_view;
+#if !defined(DEMO_CROSS_COMPILE_WIN32)
+ clear_attach.depthSlice = WGPU_DEPTH_SLICE_UNDEFINED;
+#endif
+ clear_attach.loadOp = WGPULoadOp_Clear;
+ clear_attach.storeOp = WGPUStoreOp_Store;
+ clear_attach.clearValue = {0.0, 0.0, 0.0, 1.0};
+
+ WGPURenderPassDescriptor clear_desc = {};
+ clear_desc.colorAttachmentCount = 1;
+ clear_desc.colorAttachments = &clear_attach;
+
+ WGPURenderPassEncoder clear_pass = wgpuCommandEncoderBeginRenderPass(encoder, &clear_desc);
+ wgpuRenderPassEncoderEnd(clear_pass);
+ wgpuRenderPassEncoderRelease(clear_pass);
+
+ // Render effects
+ seq->render_effects(encoder);
+
+ // Blit sink to surface
+ WGPUSurfaceTexture surface_texture;
+ wgpuSurfaceGetCurrentTexture(surface, &surface_texture);
+
+ if (surface_texture.status == WGPUSurfaceGetCurrentTextureStatus_SuccessOptimal) {
+ WGPURenderPassColorAttachment blit_attach = {};
+ blit_attach.view = surface_texture.texture
+ ? wgpuTextureCreateView(surface_texture.texture, nullptr)
+ : nullptr;
+#if !defined(DEMO_CROSS_COMPILE_WIN32)
+ blit_attach.depthSlice = WGPU_DEPTH_SLICE_UNDEFINED;
+#endif
+ blit_attach.loadOp = WGPULoadOp_Clear;
+ blit_attach.storeOp = WGPUStoreOp_Store;
+ blit_attach.clearValue = {0.0, 0.0, 0.0, 1.0};
+
+ WGPURenderPassDescriptor blit_desc = {};
+ blit_desc.colorAttachmentCount = 1;
+ blit_desc.colorAttachments = &blit_attach;
+
+ static WGPURenderPipeline blit_pipeline = nullptr;
+ static WGPUBindGroup blit_bind_group = nullptr;
+
+ if (!blit_pipeline) {
+ blit_pipeline = create_post_process_pipeline(ctx->device,
+ ctx->format, passthrough_v2_shader_wgsl);
+ }
+
+ // Update blit uniforms
+ UniformsSequenceParams blit_params = {};
+ blit_params.resolution = {(float)width, (float)height};
+ blit_params.aspect_ratio = (float)width / (float)height;
+ blit_params.time = time;
+ blit_params.beat_time = beat_time;
+ blit_params.beat_phase = 0.0f;
+ blit_params.audio_intensity = audio_intensity;
+ g_blit_uniforms.update(ctx->queue, blit_params);
+
+ pp_update_bind_group(ctx->device, blit_pipeline, &blit_bind_group,
+ g_sink_view, g_blit_uniforms.get(), {nullptr, 0});
+
+ WGPURenderPassEncoder blit_pass = wgpuCommandEncoderBeginRenderPass(encoder, &blit_desc);
+ wgpuRenderPassEncoderSetPipeline(blit_pass, blit_pipeline);
+ wgpuRenderPassEncoderSetBindGroup(blit_pass, 0, blit_bind_group, 0, nullptr);
+ wgpuRenderPassEncoderDraw(blit_pass, 3, 1, 0, 0);
+ wgpuRenderPassEncoderEnd(blit_pass);
+ wgpuRenderPassEncoderRelease(blit_pass);
+
+ if (blit_attach.view) wgpuTextureViewRelease(blit_attach.view);
+ }
+
+ WGPUCommandBuffer commands = wgpuCommandEncoderFinish(encoder, nullptr);
+ wgpuQueueSubmit(ctx->queue, 1, &commands);
+ wgpuCommandBufferRelease(commands);
+ wgpuCommandEncoderRelease(encoder);
+
+ wgpuSurfacePresent(surface);
+ if (surface_texture.texture) {
+ wgpuTextureRelease(surface_texture.texture);
+ }
+}
'''
# Write output
diff --git a/tools/test_demo.seq b/tools/test_demo.seq
index ae0301f..fa4dae8 100644
--- a/tools/test_demo.seq
+++ b/tools/test_demo.seq
@@ -1,8 +1,5 @@
-# Minimal timeline for audio/visual sync testing
+# Minimal timeline for audio/visual sync testing (v2)
# BPM 120 (set in test_demo.track)
-SEQUENCE 0.0 0 "Main Loop"
- EFFECT + Scene1Effect 0.0 16.0
- EFFECT + FlashEffect 0.0 16.0
-
-END_DEMO 32b
+SEQUENCE 0.0 0 "test_loop"
+ EFFECT + HeptagonEffectV2 source -> sink 0.0 16.0