diff options
| author | skal <pascal.massimino@gmail.com> | 2026-02-16 11:54:46 +0100 |
|---|---|---|
| committer | skal <pascal.massimino@gmail.com> | 2026-02-16 11:54:46 +0100 |
| commit | af5d0e4c3a6cb773a4fb51ac32f4c33a7f8d8224 (patch) | |
| tree | a76464ca40a43d6042ed5431547008cfbe746c34 /tools | |
| parent | 8eeadaf0d5653c21b948103e4d328f634b739a17 (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-x | tools/seq_compiler_v2.py | 166 | ||||
| -rw-r--r-- | tools/test_demo.seq | 9 |
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 |
