From 4c0907bf71150794f7b235f1c2abfab48e728df3 Mon Sep 17 00:00:00 2001 From: skal Date: Thu, 19 Mar 2026 20:57:15 +0100 Subject: docs: archive stale/completed docs, compact active refs (-1300 lines) - Archive WORKSPACE_SYSTEM.md (completed); replace with 36-line operational ref - Archive SHADER_REUSE_INVESTIGATION.md (implemented Feb 2026) - Archive GPU_PROCEDURAL_PHASE4.md (completed feature) - Archive GEOM_BUFFER.md (ideation only, never implemented) - Archive SPECTRAL_BRUSH_EDITOR.md (v1 DCT approach, superseded by MQ v2) - Update CLAUDE.md Tier 3 refs; point Audio to SPECTRAL_BRUSH_2.md - Update TODO.md Task #5 design link to SPECTRAL_BRUSH_2.md - Update COMPLETED.md archive index handoff(Claude): doc cleanup done, 30 active docs (was 34), -1300 lines --- CLAUDE.md | 6 +- TODO.md | 2 +- doc/COMPLETED.md | 5 + doc/GEOM_BUFFER.md | 229 ------------ doc/GPU_PROCEDURAL_PHASE4.md | 70 ---- doc/SHADER_REUSE_INVESTIGATION.md | 241 ------------- doc/SPECTRAL_BRUSH_EDITOR.md | 195 ---------- doc/WORKSPACE_SYSTEM.md | 582 ++---------------------------- doc/archive/GEOM_BUFFER.md | 229 ++++++++++++ doc/archive/GPU_PROCEDURAL_PHASE4.md | 70 ++++ doc/archive/SHADER_REUSE_INVESTIGATION.md | 241 +++++++++++++ doc/archive/SPECTRAL_BRUSH_EDITOR.md | 195 ++++++++++ doc/archive/WORKSPACE_SYSTEM.md | 576 +++++++++++++++++++++++++++++ 13 files changed, 1341 insertions(+), 1300 deletions(-) delete mode 100644 doc/GEOM_BUFFER.md delete mode 100644 doc/GPU_PROCEDURAL_PHASE4.md delete mode 100644 doc/SHADER_REUSE_INVESTIGATION.md delete mode 100644 doc/SPECTRAL_BRUSH_EDITOR.md create mode 100644 doc/archive/GEOM_BUFFER.md create mode 100644 doc/archive/GPU_PROCEDURAL_PHASE4.md create mode 100644 doc/archive/SHADER_REUSE_INVESTIGATION.md create mode 100644 doc/archive/SPECTRAL_BRUSH_EDITOR.md create mode 100644 doc/archive/WORKSPACE_SYSTEM.md diff --git a/CLAUDE.md b/CLAUDE.md index e525708..5d4eb58 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -17,12 +17,12 @@ # TIER 3: DESIGN DOCS (Load On-Demand by Subsystem) # ============================================ # -# Audio: @doc/SPECTRAL_BRUSH_EDITOR.md, @doc/TRACKER.md, @doc/BEAT_TIMING.md +# Audio: @doc/SPECTRAL_BRUSH_2.md, @doc/TRACKER.md, @doc/BEAT_TIMING.md # CNN: @cnn_v1/docs/CNN_V1_EFFECT.md, @cnn_v2/docs/CNN_V2.md, @cnn_v2/docs/CNN_V2_BINARY_FORMAT.md -# 3D/Graphics: @doc/3D.md, @doc/GPU_PROCEDURAL_PHASE4.md, @doc/MASKING_SYSTEM.md, @doc/SDF_EFFECT_GUIDE.md +# 3D/Graphics: @doc/3D.md, @doc/MASKING_SYSTEM.md, @doc/SDF_EFFECT_GUIDE.md # Scene: @doc/SCENE_FORMAT.md, @doc/SEQUENCE.md, @doc/WORKSPACE_SYSTEM.md # Build: @doc/ASSET_SYSTEM.md, @doc/BUILD.md, @doc/CMAKE_MODULES.md, @doc/SIZE_MEASUREMENT.md -# Rendering: @doc/GEOM_BUFFER.md, @doc/SHADER_REUSE_INVESTIGATION.md, @doc/UNIFORM_BUFFER_GUIDELINES.md, @doc/WGPU_HELPERS.md, @doc/AUXILIARY_TEXTURE_INIT.md +# Rendering: @doc/UNIFORM_BUFFER_GUIDELINES.md, @doc/WGPU_HELPERS.md, @doc/AUXILIARY_TEXTURE_INIT.md # Tools: @doc/test_demo_README.md, @doc/HOT_RELOAD.md, @doc/HEADLESS_MODE.md, @doc/RECIPE.md, @doc/TOOLS_REFERENCE.md # Arch: @doc/ARCHITECTURE.md, @doc/CODING_STYLE.md, @doc/BACKLOG.md, @doc/CONTEXT_MAINTENANCE.md diff --git a/TODO.md b/TODO.md index b154926..aca1257 100644 --- a/TODO.md +++ b/TODO.md @@ -8,7 +8,7 @@ Procedural spectrogram tool: 50-100× compression (5 KB .spec → ~100 bytes C++). -**Design:** `doc/SPECTRAL_BRUSH_EDITOR.md` +**Design:** `doc/SPECTRAL_BRUSH_2.md` (MQ-based v2) --- diff --git a/doc/COMPLETED.md b/doc/COMPLETED.md index 9299a93..42e4bc6 100644 --- a/doc/COMPLETED.md +++ b/doc/COMPLETED.md @@ -11,6 +11,11 @@ Completed task archive. See `doc/archive/` for detailed historical documents. - AUDIO_LIFECYCLE_REFACTOR.md, AUDIO_TIMING_ARCHITECTURE.md - BUILD_OPTIMIZATION_PROPOSAL.md, BUILD_OPTIMIZATION_PROPOSAL_V2.md - PLATFORM_ANALYSIS.md, SHADER_PARAMETRIZATION_PLAN.md +- SHADER_REUSE_INVESTIGATION.md (shared `src/shaders/` approach implemented Feb 2026) +- GPU_PROCEDURAL_PHASE4.md (multi-input composite shaders with sampler cache, ~830 bytes) +- GEOM_BUFFER.md (G-buffer ideation, not yet implemented — see BACKLOG) +- WORKSPACE_SYSTEM.md (full design; compacted to operational reference Feb 2026) +- SPECTRAL_BRUSH_EDITOR.md (v1 DCT approach; superseded by MQ-based v2 in SPECTRAL_BRUSH_2.md) **Handoffs & Summaries:** - HANDOFF.md, HANDOFF_2026-02-04.md, HANDOFF_2026-02-08.md, HANDOFF_CLAUDE.md diff --git a/doc/GEOM_BUFFER.md b/doc/GEOM_BUFFER.md deleted file mode 100644 index 0188125..0000000 --- a/doc/GEOM_BUFFER.md +++ /dev/null @@ -1,229 +0,0 @@ -# Geometry Buffer Design [IN PROGRESS] - -**Status:** Ideation phase -**Goal:** Efficient G-buffer for deferred rendering in 64k demo - ---- - -## Overview - -Replace direct rendering with geometry buffer accumulation for advanced post-processing and lighting. - -**Target:** 8-10 bytes/pixel, 16-bit precision - ---- - -## Buffer Elements - -### Core Attributes - -| Attribute | Channels | Precision | Source | -|-----------|----------|-----------|--------| -| Albedo (RGB) | 3 | f16 | Material/procedural | -| Roughness | 1 | u8/u16 | PBR material property | -| Metallic | 1 | u8/u16 | PBR material property | -| Normal (XYZ) | 2 | f16 | Octahedral encoding | -| Depth | 1 | f16/f32 | 1/z for precision | -| Object/Material ID | 1 | u16 | Rasterization/SDF | -| Transparency | 1 | u8/u16 | Alpha channel | - -### Optional/Derived - -| Attribute | Storage | Notes | -|-----------|---------|-------| -| Depth gradient | On-demand | Compute from depth (Sobel) | -| Laplacian | On-demand | Second derivative of depth | -| Motion vectors | 2×f16 | Screen-space XY | -| AO | 1×f16 | Ambient occlusion | - -**Key insight:** Depth derivatives cheaper to compute than store (2-4 bytes/pixel saved). - ---- - -## Packing Strategies - -### Traditional Multi-Render-Target (MRT) - -``` -RT0 (RGBA16): Albedo.rgb + Roughness (packed with metallic) -RT1 (RG16): Octahedral normal (2 channels encode XYZ) -RT2 (R32F): 1/z depth (or use hardware depth buffer) -RT3 (RG16): Motion vectors XY -RT4 (R16UI): Object/Material ID -``` - -**Total:** 4-5 render targets = 8-10 bytes/pixel - -### Compute Shader + Storage Buffer (RECOMMENDED) - -**Advantages:** -- Custom bit-packing (not bound to RGBA formats) -- Compute derivatives in-pass (depth gradient, Laplacian) -- Cache-optimized tiling (Morton order) -- No MRT limits (store 20+ attributes) - -**Tradeoffs:** -- No hardware depth/early-Z during G-buffer generation -- Manual atomics if pixel overdraw -- Lose ROPs hardware optimizations - -**Struct Layout:** -```cpp -struct GBufferPixel { - u32 packed_normal; // Octahedral 16+16 - u32 rgba_rough; // RGBA8 + Roughness8 + Metallic8 (26 bits used) - f16 inv_z; // 1/z depth - u16 material_id; // Object/material - // Total: 12 bytes/pixel -}; - -// Compressed variant (8 bytes): -struct CompactGBuffer { - u32 normal_depth; // Oct16 normal + u16 quantized depth - u32 rgba_params; // RGB565 + Rough4 + Metal4 + Flags4 -}; -``` - -**Access Pattern:** -```wgsl -@group(0) @binding(0) var g_buffer: array; - -fn write_gbuffer(pixel_id: u32, data: SurfaceData) { - g_buffer[pixel_id].packed_normal = pack_octahedral(data.normal); - g_buffer[pixel_id].rgba_rough = pack_rgba8(data.albedo) | (u32(data.roughness * 255.0) << 24); - g_buffer[pixel_id].inv_z = f16(1.0 / data.depth); - g_buffer[pixel_id].material_id = data.id; -} -``` - ---- - -## Normal Encoding - -**Octahedral mapping** (most efficient for 2-channel storage): -- Encodes unit sphere normal to 2D square -- 16-bit per channel = good precision -- Fast encode/decode (no trig) - -```cpp -vec2 octahedral_encode(vec3 n) { - n /= (abs(n.x) + abs(n.y) + abs(n.z)); - vec2 p = n.z >= 0.0 ? n.xy : (1.0 - abs(n.yx)) * sign(n.xy); - return p * 0.5 + 0.5; // [0, 1] -} - -vec3 octahedral_decode(vec2 p) { - p = p * 2.0 - 1.0; // [-1, 1] - vec3 n = vec3(p.x, p.y, 1.0 - abs(p.x) - abs(p.y)); - float t = max(-n.z, 0.0); - n.x += n.x >= 0.0 ? -t : t; - n.y += n.y >= 0.0 ? -t : t; - return normalize(n); -} -``` - ---- - -## Depth Storage - -**1/z (inverse depth):** -- Better precision distribution (more bits near camera) -- Linear in screen space -- Matches perspective projection - -**Alternatives:** -- Logarithmic depth (even better precision) -- Hardware depth buffer (R32F, free with render targets) - ---- - -## Material Properties - -**Roughness/Metallic are NOT geometry:** -- **Source:** Texture lookups, procedural noise, or constants -- **Not bump-mapping:** Bump/normal maps perturb normals (geometry) -- **PBR properties:** Control light interaction (0=smooth/dielectric, 1=rough/metal) - -**Demoscene approach:** Procedural generation or baked constants (avoid textures). - ---- - -## Post-Processing Derivatives - -**Compute on-demand** (cheaper than storing): - -```wgsl -// Depth gradient (Sobel filter) -fn depth_gradient(uv: vec2f) -> vec2f { - let dx = textureLoad(depth, uv + vec2(1,0)) - textureLoad(depth, uv - vec2(1,0)); - let dy = textureLoad(depth, uv + vec2(0,1)) - textureLoad(depth, uv - vec2(0,1)); - return vec2(dx, dy) * 0.5; -} - -// Laplacian (edge detection) -fn laplacian(uv: vec2f) -> f32 { - let c = textureLoad(depth, uv); - let n = textureLoad(depth, uv + vec2(0,1)); - let s = textureLoad(depth, uv - vec2(0,1)); - let e = textureLoad(depth, uv + vec2(1,0)); - let w = textureLoad(depth, uv - vec2(1,0)); - return (n + s + e + w) - 4.0 * c; -} -``` - ---- - -## Integration with Hybrid Renderer - -**Current:** Hybrid SDF raymarching + rasterized proxy geometry -**Future:** Both write to unified G-buffer - -```cpp -// Rasterization pass -void rasterize_geometry() { - // Vertex shader → fragment shader - // Write to G-buffer (compute or MRT) -} - -// SDF raymarching pass (compute) -void raymarch_sdf() { - // Per-pixel ray march - // Write to same G-buffer at hit points -} - -// Deferred lighting pass -void deferred_lighting() { - // Read G-buffer - // Apply PBR lighting, shadows, etc. -} -``` - -**Atomics handling:** Use depth test or tile-based sorting to avoid conflicts. - ---- - -## Size Budget - -**Target:** 1920×1080 @ 8 bytes/pixel = **16 MB** -**Compressed:** 1920×1080 @ 6 bytes/pixel = **12 MB** - -**Acceptable for 64k demo:** RAM usage OK, not binary size. - ---- - -## Next Steps - -1. Prototype compute shader G-buffer writer -2. Implement octahedral normal encoding -3. Test SDF + raster unified writes -4. Add deferred lighting pass -5. Validate depth derivative quality (gradient/Laplacian) -6. Optimize packing (aim for 6-8 bytes/pixel) - ---- - -## References - -- Octahedral mapping: "Survey of Efficient Representations for Independent Unit Vectors" (Meyer et al.) -- PBR theory: "Physically Based Rendering" (Pharr, Jakob, Humphreys) -- G-buffer design: "Deferred Rendering in Killzone 2" (Valient, 2007) diff --git a/doc/GPU_PROCEDURAL_PHASE4.md b/doc/GPU_PROCEDURAL_PHASE4.md deleted file mode 100644 index 4cfc271..0000000 --- a/doc/GPU_PROCEDURAL_PHASE4.md +++ /dev/null @@ -1,70 +0,0 @@ -# GPU Procedural Phase 4: Texture Composition - -**Status:** ✅ Complete - -## Implementation - -Multi-input composite shaders with configurable sampler support. - -### API - -```cpp -enum class SamplerType { - LinearClamp, LinearRepeat, NearestClamp, NearestRepeat -}; - -void create_gpu_composite_texture( - const std::string& name, - const std::string& shader_func, - const char* shader_code, - const void* uniform_data, - size_t uniform_size, - int width, int height, - const std::vector& input_names, - SamplerType sampler = SamplerType::LinearClamp); -``` - -### Shaders - -**gen_blend.wgsl** - Blend two textures with lerp factor: -- Bindings: output (0), uniform (1), input_a (2), input_b (3), sampler (4) -- Uniform: `{u32 width, height; f32 blend_factor, _pad0}` - -**gen_mask.wgsl** - Multiply textures (masking): -- Bindings: output (0), uniform (1), input_a (2), input_b (3), sampler (4) -- Uniform: `{u32 width, height}` - -### Usage - -```cpp -extern const char* gen_blend_compute_wgsl; - -struct { uint32_t width, height; float blend_factor, _pad0; } uni = {256, 256, 0.5f, 0.0f}; - -tex_mgr.create_gpu_composite_texture( - "blended", "gen_blend", gen_blend_compute_wgsl, - &uni, sizeof(uni), 256, 256, - {"noise_a", "noise_b"}, - SamplerType::LinearClamp); -``` - -### Features - -- **Dynamic bind groups:** N input textures + 1 sampler -- **Lazy sampler creation:** Map-based cache, 4 preset types -- **Multi-stage composition:** Composite of composites supported -- **Guarded with `#if !defined(STRIP_GPU_COMPOSITE)`** - -### Size Impact - -- Code: ~460 lines added -- Compressed: ~830 bytes (2 shaders + dispatch logic) - -### Tests - -`test_gpu_composite.cc`: -- Blend two noise textures -- Mask noise with grid -- Multi-stage composite (composite of composites) - -All 35 tests passing. diff --git a/doc/SHADER_REUSE_INVESTIGATION.md b/doc/SHADER_REUSE_INVESTIGATION.md deleted file mode 100644 index e840126..0000000 --- a/doc/SHADER_REUSE_INVESTIGATION.md +++ /dev/null @@ -1,241 +0,0 @@ -# Shader Code Reuse Investigation - -## ✅ Implementation Status - -**Date:** February 13, 2026 -**Solution:** Option 1 - Shared Common Directory -**Status:** IMPLEMENTED - -**Results:** -- Created `src/shaders/` with 20 shared shader files (moved from `common/shaders/` 2026-02-28) -- Eliminated 36 duplicate files across workspaces -- Asset references use `../../src/shaders/...` -- Enhanced asset_packer with filesystem path normalization -- Build passes, all tests pass - -See `doc/FILE_HIERARCHY_CLEANUP_2026-02-13.md` for full details. - ---- - -## Investigation (Historical) - -### Current State - -### Duplication Analysis -- **36 duplicate shader files** between main and test workspaces -- Common files include: math utilities, render helpers, compute shaders, uniforms -- Files are byte-identical duplicates (verified) - -### Current System Architecture -1. **ShaderComposer**: Runtime shader composition via `#include` directives -2. **Asset System**: Shaders loaded from workspace `assets.txt` -3. **Registration**: `InitShaderComposer()` registers snippets with paths like: - - `common_uniforms` - - `math/sdf_shapes`, `math/noise`, `math/common_utils` - - `render/shadows`, `render/scene_query_bvh`, `render/lighting_utils` - -### Duplicate Common Shaders -``` -Common across both workspaces: -- common_uniforms.wgsl -- math/{common_utils, noise, sdf_shapes, sdf_utils}.wgsl -- render/{lighting_utils, scene_query_bvh, scene_query_linear, shadows}.wgsl -- compute/{gen_blend, gen_grid, gen_mask, gen_noise, gen_perlin}.wgsl -- Various effect shaders (passthrough, lighting, ray_box, etc.) -``` - ---- - -## Proposed Approaches - -### Option 1: Shared Common Directory (Original Design) -**Structure:** -``` -/common/ - /shaders/ - /math/ # Shared math utilities - /render/ # Shared rendering helpers - /compute/ # Shared compute shaders - common_uniforms.wgsl - -/workspaces/ - /main/ - /shaders/ # Workspace-specific only - /music/ - /weights/ - /obj/ -``` - -**Pros:** -- Single source of truth for common code -- No duplication -- Clear separation: common vs workspace-specific -- Matches original design doc intent - -**Cons:** -- Breaks workspace isolation (workspaces depend on external directory) -- Harder to version control per-workspace -- Need to update asset packer to handle cross-workspace references -- Complicates workspace portability - ---- - -### Option 2: Symbolic Links -**Structure:** -``` -/workspaces/ - /main/ - /shaders/ - /common@ -> ../../common/shaders/ # Symlink - custom_effect.wgsl -``` - -**Pros:** -- Maintains single source -- Works with existing asset system -- Simple filesystem solution - -**Cons:** -- Windows symlink issues (requires admin or dev mode) -- Git symlink handling varies -- Still breaks workspace isolation -- Can confuse developers - ---- - -### Option 3: Build-Time Copy/Sync -**Structure:** -Workspaces keep duplicates, but sync from canonical source at build time. - -``` -/common/shaders/ # Canonical source -/workspaces/*/shaders/ # Build-synced copies -``` - -**Implementation:** -- CMake script copies common/ → workspaces/ before asset packing -- Or: Pre-commit hook validates consistency - -**Pros:** -- Workspaces remain self-contained -- No runtime dependencies -- Works across all platforms -- Git shows actual files - -**Cons:** -- Easy to forget sync -- Merge conflicts if edited in workspace -- Need tooling to maintain consistency - ---- - -### Option 4: Asset System Extension -**Enhance asset packer to support cross-workspace includes:** - -`workspaces/main/assets.txt`: -``` -SHADER_COMMON_UTILS, NONE, @common/shaders/math/common_utils.wgsl -SHADER_CUSTOM, NONE, shaders/custom_effect.wgsl -``` - -**Pros:** -- Clean at asset definition level -- ShaderComposer unchanged -- Explicit common vs local - -**Cons:** -- Asset packer modification needed -- New syntax to learn -- Breaks workspace portability - ---- - -### Option 5: Status Quo + Documentation -**Keep duplicates, document as intentional.** - -Add `common/shaders/` as reference templates that workspaces copy. - -**Pros:** -- Workspaces fully independent -- Can diverge per-workspace if needed -- No build system changes -- Simple mental model - -**Cons:** -- Ongoing duplication -- Manual sync required -- Disk space (minimal impact) -- Bug fixes need multi-workspace updates - ---- - -## Recommendation Matrix - -| Criteria | Opt 1 | Opt 2 | Opt 3 | Opt 4 | Opt 5 | -|----------|-------|-------|-------|-------|-------| -| No duplication | ✓ | ✓ | ✗ | ✓ | ✗ | -| Workspace isolation | ✗ | ✗ | ✓ | ✗ | ✓ | -| Cross-platform | ✓ | ✗ | ✓ | ✓ | ✓ | -| Low complexity | ✗ | ✓ | ✗ | ✗ | ✓ | -| Easy maintenance | ✓ | ✓ | ✗ | ✓ | ✗ | - ---- - -## Current Implementation Details - -### How Shaders Are Currently Included - -**In WGSL files:** -```wgsl -#include "common_uniforms" -#include "math/sdf_shapes" -#include "render/shadows" -``` - -**In C++ (shaders.cc):** -```cpp -void InitShaderComposer() { - auto& sc = ShaderComposer::Get(); - sc.RegisterSnippet("common_uniforms", ...); - sc.RegisterSnippet("math/sdf_shapes", ...); - sc.RegisterSnippet("render/shadows", ...); -} -``` - -**Asset Registration (assets.txt):** -``` -SHADER_MATH_SDF_SHAPES, NONE, shaders/math/sdf_shapes.wgsl -SHADER_RENDER_SHADOWS, NONE, shaders/render/shadows.wgsl -``` - -### ShaderComposer Flow -1. Assets loaded from workspace `assets.txt` -2. Snippets registered in `InitShaderComposer()` -3. Effects call `Compose()` with dependencies -4. `#include` directives recursively resolved -5. Final shader string assembled - ---- - -## Next Steps - -1. **Decide on approach** based on project priorities: - - Simplicity → Option 5 - - True reuse → Option 1 or 4 - - Compatibility → Option 3 - -2. **If Option 1 chosen:** - - Create `/common/shaders/` with canonical sources - - Update `workspace.cfg` to reference common - - Update asset packer to resolve `../common/` paths - - Update docs - -3. **If Option 3 chosen:** - - Create sync script in `scripts/` - - Add to build process - - Document workflow - -4. **If Option 5 chosen:** - - Create `/common/shaders/` as templates - - Add `doc/SHADER_COMMON.md` explaining pattern - - Accept duplication as intentional diff --git a/doc/SPECTRAL_BRUSH_EDITOR.md b/doc/SPECTRAL_BRUSH_EDITOR.md deleted file mode 100644 index a7d0e3a..0000000 --- a/doc/SPECTRAL_BRUSH_EDITOR.md +++ /dev/null @@ -1,195 +0,0 @@ -# Spectral Brush Editor (Task #5) - -## Concept - -Replace large `.spec` assets with procedural C++ code (50-100× compression). - -**Before:** 5 KB binary `.spec` file -**After:** ~100 bytes C++ code calling `draw_bezier_curve()` - -**Workflow:** -``` -.wav → Load in editor → Trace with Bezier curves → Export procedural_params.txt + C++ code -``` - ---- - -## Core Primitive: "Spectral Brush" - -### 1. Central Curve (Bezier) -Traces time-frequency path: `{freq_bin, amplitude} = bezier(frame_number)` - -**Example control points:** -```javascript -[ - {frame: 0, freq_hz: 200.0, amplitude: 0.9}, // Attack - {frame: 20, freq_hz: 80.0, amplitude: 0.7}, // Sustain - {frame: 100, freq_hz: 50.0, amplitude: 0.0} // Decay -] -``` - -### 2. Vertical Profile -Shapes "brush stroke" around curve at each frame. - -**Profile Types:** -- **Gaussian**: `exp(-(dist² / σ²))` - Musical tones, bass -- **Decaying Sinusoid**: `exp(-decay * dist) * cos(ω * dist)` - Metallic sounds -- **Noise**: `random(seed, dist) * amplitude` - Hi-hats, cymbals -- **Composite**: Combine multiple profiles (add/subtract/multiply) - ---- - -## File Formats - -### A. `procedural_params.txt` (Human-readable) -```text -METADATA dct_size=512 num_frames=100 sample_rate=32000 - -CURVE bezier - CONTROL_POINT 0 200.0 0.9 - CONTROL_POINT 20 80.0 0.7 - CONTROL_POINT 100 50.0 0.0 - PROFILE gaussian sigma=30.0 -END_CURVE -``` - -### B. C++ Code (Ready to compile) -```cpp -#include "audio/spectral_brush.h" - -void gen_kick_procedural(float* spec, int dct_size, int num_frames) { - const float frames[] = {0.0f, 20.0f, 100.0f}; - const float freqs[] = {200.0f, 80.0f, 50.0f}; - const float amps[] = {0.9f, 0.7f, 0.0f}; - - draw_bezier_curve(spec, dct_size, num_frames, - frames, freqs, amps, 3, - PROFILE_GAUSSIAN, 30.0f); -} -``` - ---- - -## C++ Runtime API - -### Files: `src/audio/spectral_brush.{h,cc}` - -**Key functions:** -```cpp -enum ProfileType { - PROFILE_GAUSSIAN, - PROFILE_DECAYING_SINUSOID, - PROFILE_NOISE -}; - -void draw_bezier_curve(float* spectrogram, int dct_size, int num_frames, - const float* control_frames, - const float* control_freqs_hz, - const float* control_amps, - int n_control_points, - ProfileType profile_type, - float profile_param1, - float profile_param2 = 0.0f); - -float evaluate_bezier_linear(const float* control_frames, - const float* control_values, - int n_points, - float frame); - -float evaluate_profile(ProfileType type, float distance, - float param1, float param2); -``` - ---- - -## Editor UI - -### Technology Stack -- HTML5 Canvas (visualization) -- Web Audio API (playback) -- Pure JavaScript (no dependencies) -- Reuse `dct.js` from existing editor - -### Key Features -- Dual-layer canvas (reference + procedural spectrograms) -- Drag control points to adjust curves -- Real-time spectrogram rendering -- Audio playback (keys 1/2 for procedural/original) -- Undo/Redo (Ctrl+Z, Ctrl+Shift+Z) -- Load .wav/.spec, save params, generate C++ code - -### Keyboard Shortcuts -| Key | Action | -|-----|--------| -| 1 | Play procedural | -| 2 | Play original | -| Space | Play/pause | -| Delete | Remove control point | -| Ctrl+Z | Undo | -| Ctrl+S | Save params | - ---- - -## Implementation Phases - -### Phase 1: C++ Runtime -**Files:** `src/audio/spectral_brush.{h,cc}`, `src/tests/test_spectral_brush.cc` - -**Tasks:** -- Define API (ProfileType, draw_bezier_curve, evaluate_profile) -- Implement linear Bezier interpolation -- Implement Gaussian profile -- Add unit tests -- **Deliverable:** Compiles, tests pass - -### Phase 2: Editor Core -**Files:** `tools/spectral_editor/{index.html, script.js, style.css, dct.js}` - -**Tasks:** -- HTML structure (canvas, controls, file input) -- Bezier curve editor (place/drag/delete control points) -- Dual-layer canvas rendering -- Real-time spectrogram generation -- Audio playback (IDCT → Web Audio API) -- Undo/Redo system -- **Deliverable:** Interactive editor, can trace .wav files - -### Phase 3: File I/O -**Tasks:** -- Load .wav (decode, STFT → spectrogram) -- Load .spec (binary parser) -- Save procedural_params.txt (text format) -- Generate C++ code (template) -- Load procedural_params.txt (re-editing) -- **Deliverable:** Full save/load cycle - ---- - -## Design Decisions - -- **Bezier:** Linear interpolation (Phase 1), cubic later -- **Profiles:** Gaussian only (Phase 1), others later -- **Parameters:** Soft UI limits, no enforced bounds -- **RNG:** Home-brew deterministic (small, repeatable) -- **Code gen:** Single function per sound (generic loader later) - ---- - -## Size Impact - -**Example: Kick drum** - -**Before (Binary):** -- 512 bins × 100 frames × 4 bytes = 200 KB uncompressed -- ~5 KB compressed (zlib) - -**After (Procedural):** -- 4 control points × 3 arrays × 4 floats = ~48 bytes data -- Function call overhead = ~20 bytes -- **Total: ~100 bytes** (50-100× reduction) - -**Trade-off:** Runtime CPU cost, acceptable for 64k demo. - ---- - -*See TODO.md for detailed implementation tasks.* diff --git a/doc/WORKSPACE_SYSTEM.md b/doc/WORKSPACE_SYSTEM.md index 6b4319d..f0981fd 100644 --- a/doc/WORKSPACE_SYSTEM.md +++ b/doc/WORKSPACE_SYSTEM.md @@ -1,576 +1,36 @@ -# Workspace System (Task #77) [COMPLETED] +# Workspace System -## Status +**Status:** ✅ Complete (Feb 9, 2026) -**Implemented:** Feb 9, 2026 +Self-contained demo workspaces. Select with `cmake -B build -DDEMO_WORKSPACE=`. -Workspace system is now active. Use `cmake -B build -DDEMO_WORKSPACE=` to select workspace. +## Directory Layout -## Overview - -Self-contained demo workspaces for parallel development and clean content separation. - -**Motivation:** Current structure scatters demo content across multiple locations: -- `assets/demo.seq` - Timeline -- `assets/music.track` - Audio -- `assets/final/demo_assets.txt` - Asset list -- `assets/final/music/*.spec` - Audio samples -- `assets/shaders/*.wgsl` - Shaders (some demo-specific, some shared) - -This makes it hard to: -- Work on multiple demos simultaneously -- Understand what files belong to which demo -- Share common resources cleanly -- Onboard new developers - -## Problem Statement - -**Current structure (flat):** ``` -/assets/ - demo.seq # Main demo timeline - music.track # Main demo music - test_demo.seq # Test demo timeline - test_demo.track # Test demo music - /final/ - demo_assets.txt # Main demo assets - /music/*.spec # Audio samples (mixed usage) - /shaders/ - *.wgsl # Mixed: common + demo-specific -``` - -**Issues:** -1. No clear ownership of files -2. Can't easily switch between demos -3. Assets intermingled (demo vs test) -4. Shaders hard to categorize (shared vs demo-specific) -5. Adding new demos requires scattered changes - -## Current Structure (Implemented) - -### Workspace Directory Layout - -``` -/workspaces/ - /main/ # Main production demo - workspace.cfg # Workspace metadata - timeline.seq # Visual effects - music.track # Audio patterns - assets.txt # Asset list - /music/*.spec # Audio samples - /weights/*.bin # CNN binary weights - /obj/*.obj # 3D models - /shaders/ # Workspace-specific shaders only - custom_effect.wgsl - - /test/ # Test/validation demo - workspace.cfg - timeline.seq - music.track - assets.txt - /music/ - /weights/ - /obj/ - /shaders/ - -/common/ # Shared resources - /shaders/ - /math/ # Shared math utilities - common_utils.wgsl - noise.wgsl - sdf_shapes.wgsl - sdf_utils.wgsl - /render/ # Shared rendering helpers - lighting_utils.wgsl - scene_query_bvh.wgsl - scene_query_linear.wgsl - shadows.wgsl - /compute/ # Shared compute shaders - gen_blend.wgsl - gen_grid.wgsl - gen_mask.wgsl - gen_noise.wgsl - gen_perlin.wgsl - common_uniforms.wgsl - lighting.wgsl - passthrough.wgsl - ray_box.wgsl - ray_triangle.wgsl - sdf_primitives.wgsl - skybox.wgsl - -/tools/ - originals/ # Source audio files (.wav, .aif) -``` - -### Workspace Configuration - -**`workspace.cfg` format:** -```ini -[workspace] -name = "Main Demo" -description = "Production 64k demo" -version = "1.0" +workspaces/ + main/ # Main production demo + timeline.seq # Visual effects + music.track # Audio patterns + assets.txt # Asset list + music/*.spec # Audio samples + weights/*.bin # CNN weights + obj/*.obj # 3D models + shaders/ # Workspace-specific shaders only + test/ # Test demo (same structure) -[build] -target = "demo64k" -timeline = "timeline.seq" -music = "music.track" -assets = "assets.txt" -asset_dirs = ["music/", "weights/", "obj/"] -shader_dirs = ["shaders/"] - -[build] -# Output binary name -target = "demo64k" - -# Include paths (relative to workspace root) -timeline = "timeline.seq" -music = "music.track" -assets = "assets.txt" - -# Asset directories -asset_dirs = ["assets/", "../common/audio/"] - -# Shader directories (order matters: workspace-specific first) -shader_dirs = ["shaders/", "../src/shaders/"] - -[options] -# Default resolution -width = 1280 -height = 720 - -# Duration (seconds, -1 = auto-detect) -duration = -1 - -# BPM for timeline -bpm = 120 +src/shaders/ # Shared common shaders (math/, render/, compute/) ``` -## Architecture - -### Build System Integration - -**CMakeLists.txt changes:** -```cmake -# Select active workspace (default: main) -set(DEMO_WORKSPACE "main" CACHE STRING "Active workspace") +## Usage -# Parse workspace config -set(WORKSPACE_DIR "${CMAKE_SOURCE_DIR}/workspaces/${DEMO_WORKSPACE}") -set(WORKSPACE_CFG "${WORKSPACE_DIR}/workspace.cfg") - -# Read config and set variables -parse_workspace_config(${WORKSPACE_CFG} - TIMELINE_PATH - MUSIC_PATH - ASSETS_PATH - ASSET_DIRS - SHADER_DIRS - TARGET_NAME -) - -# Generate assets from workspace -add_custom_command( - OUTPUT ${GEN_ASSETS_H} - COMMAND asset_packer ${WORKSPACE_DIR}/${ASSETS_PATH} ... - DEPENDS ${WORKSPACE_DIR}/${ASSETS_PATH} -) - -# Generate timeline from workspace -add_custom_command( - OUTPUT ${GEN_TIMELINE_CC} - COMMAND seq_compiler ${WORKSPACE_DIR}/${TIMELINE_PATH} ... - DEPENDS ${WORKSPACE_DIR}/${TIMELINE_PATH} -) - -# Add shader include paths from workspace -foreach(SHADER_DIR ${SHADER_DIRS}) - list(APPEND SHADER_INCLUDE_DIRS "${WORKSPACE_DIR}/${SHADER_DIR}") -endforeach() -``` - -**Building specific workspace:** ```bash -# Default (main workspace) -cmake -S . -B build -cmake --build build -j4 - -# Specific workspace -cmake -S . -B build_test -DDEMO_WORKSPACE=test -cmake --build build_test -j4 - -# Experimental workspace -cmake -S . -B build_exp -DDEMO_WORKSPACE=experiments/demo2024_revision -cmake --build build_exp -j4 -``` - -### Asset Packer Changes - -**Support workspace-relative paths:** -```cpp -// asset_packer.cc -void PackerContext::resolve_asset_path(const char* rel_path) { - // Try workspace-local first - std::string workspace_path = workspace_dir + "/" + rel_path; - if (file_exists(workspace_path)) { - return workspace_path; - } - - // Try common assets - std::string common_path = "assets/common/" + rel_path; - if (file_exists(common_path)) { - return common_path; - } - - error("Asset not found: %s", rel_path); -} -``` - -### Shader Composer Changes - -**Support multiple include paths:** -```cpp -// shader_composer.cc -class ShaderComposer { - std::vector include_paths_; - - void add_include_path(const char* path) { - include_paths_.push_back(path); - } - - std::string resolve_include(const char* filename) { - for (const auto& base : include_paths_) { - std::string full = base + "/" + filename; - if (file_exists(full)) { - return full; - } - } - error("Shader include not found: %s", filename); - } -}; -``` - -**Shader includes with search paths:** -```wgsl -// workspace-specific shader -#include "custom_uniforms.wgsl" // From workspaces/main/shaders/ -#include "math/common_utils.wgsl" // From src/shaders/ -``` - -### CLI Tool - -**`scripts/workspace.sh` - Workspace management:** -```bash -#!/bin/bash -# workspace.sh - Manage demo workspaces - -case "$1" in - list) - # List all workspaces - ls -1 workspaces/ - ;; - - create) - # Create new workspace from template - NAME=$2 - mkdir -p workspaces/$NAME/{assets,shaders} - cp templates/workspace.cfg workspaces/$NAME/ - echo "Created workspace: $NAME" - ;; - - build) - # Build specific workspace - NAME=$2 - cmake -S . -B build_$NAME -DDEMO_WORKSPACE=$NAME - cmake --build build_$NAME -j4 - ;; - - info) - # Show workspace details - NAME=$2 - cat workspaces/$NAME/workspace.cfg - ;; -esac -``` - -**Usage:** -```bash -./scripts/workspace.sh list -./scripts/workspace.sh create my_demo -./scripts/workspace.sh build main -./scripts/workspace.sh info test -``` - -## Implementation Plan - -### Phase 1: Create Workspace Structure - -**1.1 Create directory tree:** -```bash -mkdir -p workspaces/{main,test}/assets -mkdir -p workspaces/{main,test}/shaders -mkdir -p assets/common/{shaders,audio} -``` - -**1.2 Move existing files:** -```bash -# Main demo -mv assets/demo.seq workspaces/main/timeline.seq -mv assets/music.track workspaces/main/music.track -mv assets/final/demo_assets.txt workspaces/main/assets.txt -mv assets/final/music workspaces/main/assets/ +# Main demo (default) +cmake -S . -B build -DDEMO_WORKSPACE=main && cmake --build build -j4 # Test demo -mv assets/test_demo.seq workspaces/test/timeline.seq -mv assets/test_demo.track workspaces/test/music.track -cp assets/final/demo_assets.txt workspaces/test/assets.txt # Or create minimal version - -# Common shaders -mv assets/shaders/math assets/common/shaders/ -mv assets/shaders/common_uniforms assets/common/shaders/ -``` - -**1.3 Create workspace configs:** -```bash -# Generate workspace.cfg for main and test -# See format above -``` - -### Phase 2: Update Build System - -**2.1 Add workspace parsing:** -- Create `cmake/ParseWorkspace.cmake` -- Parse INI-style config -- Set CMake variables - -**2.2 Update CMakeLists.txt:** -- Add `DEMO_WORKSPACE` option -- Use workspace paths for assets/timeline/music -- Pass shader include paths to ShaderComposer - -**2.3 Update compiler tools:** -- `asset_packer`: Accept workspace root dir -- `seq_compiler`: Accept workspace root dir -- `tracker_compiler`: Accept workspace root dir - -### Phase 3: Update Code - -**3.1 ShaderComposer:** -- Add multi-path include resolution -- Search workspace shaders first, then common - -**3.2 Asset loading:** -- Update paths to match new structure -- Ensure backward compatibility during transition - -**3.3 Generated code:** -- Verify generated assets.h/timeline.cc still work -- Update include paths if needed - -### Phase 4: Documentation & Tools - -**4.1 Update docs:** -- README.md: Explain workspace structure -- HOWTO.md: Document workspace build commands -- CONTRIBUTING.md: Workspace best practices - -**4.2 Create helper scripts:** -- `scripts/workspace.sh`: Workspace management CLI -- `templates/workspace.cfg`: Template for new workspaces - -**4.3 Update CI:** -- Build both main and test workspaces -- Validate workspace isolation - -### Phase 5: Migration & Validation - -**5.1 Test builds:** -```bash -# Build both workspaces -cmake -S . -B build_main -DDEMO_WORKSPACE=main -cmake --build build_main -j4 - -cmake -S . -B build_test -DDEMO_WORKSPACE=test -cmake --build build_test -j4 - -# Run tests -cd build_main && ctest -cd build_test && ctest +cmake -S . -B build_test -DDEMO_WORKSPACE=test && cmake --build build_test -j4 ``` -**5.2 Verify isolation:** -- Modify workspace-specific shader → only that workspace affected -- Modify common shader → both workspaces affected - -**5.3 Update .gitignore:** -``` -/build_*/ -/workspaces/*/generated/ -``` - -## Benefits - -### 1. Clear Ownership -Each workspace is self-contained: -```bash -workspaces/main/ -├── workspace.cfg # Configuration -├── timeline.seq # Timeline -├── music.track # Music -├── assets.txt # Assets -├── assets/ # Local assets -└── shaders/ # Local shaders -``` - -### 2. Parallel Development -Multiple developers can work on different demos without conflicts: -```bash -# Developer A: Main demo -cd workspaces/main -vim timeline.seq - -# Developer B: Experimental demo -cd workspaces/experiments/christmas_demo -vim timeline.seq -``` - -### 3. Easy Switching -```bash -# Build main demo -cmake -B build -DDEMO_WORKSPACE=main -./build/demo64k - -# Build test demo -cmake -B build -DDEMO_WORKSPACE=test -./build/test_demo -``` - -### 4. Clean Sharing -Common resources in one place: -``` -assets/common/ -├── shaders/ -│ ├── math/ # Shared by all -│ └── common_uniforms/ -└── audio/ - └── standard_drums.spec -``` - -### 5. Scalability -Easy to add new demos: -```bash -./scripts/workspace.sh create party2025 -cd workspaces/party2025 -# Start developing... -``` - -## Migration Path - -### Backward Compatibility - -During transition, support both structures: -```cmake -if(EXISTS "${CMAKE_SOURCE_DIR}/workspaces/${DEMO_WORKSPACE}") - # New workspace structure - set(USING_WORKSPACES TRUE) -else() - # Legacy flat structure - set(USING_WORKSPACES FALSE) -endif() -``` - -### Gradual Migration - -1. **Phase 1:** Create workspace structure alongside existing -2. **Phase 2:** Update build system to support both -3. **Phase 3:** Move files to workspaces -4. **Phase 4:** Remove legacy paths -5. **Phase 5:** Update all documentation - -**Timeline:** 1-2 weeks for full migration - -## Trade-offs - -### Pros -- Clear project organization -- Parallel demo development -- Clean resource sharing -- Better onboarding for new devs -- Scales to many demos - -### Cons -- Initial migration effort -- Build system complexity increases -- More directories to navigate -- Learning curve for contributors - -### Alternative Approaches - -**1. Monorepo with subprojects:** -- Separate CMake projects per demo -- More isolated but harder to share code - -**2. Configuration files only:** -- Keep flat structure, use config to select files -- Less clear, harder to understand ownership - -**3. Git branches per demo:** -- Each demo is a branch -- Conflicts when merging shared code - -**Chosen:** Workspace system (best balance) - -## Implementation Effort - -**Estimated time: 12-16 hours** - -- Phase 1: Create structure (2-3 hours) -- Phase 2: Build system (4-5 hours) -- Phase 3: Code updates (3-4 hours) -- Phase 4: Documentation (2-3 hours) -- Phase 5: Validation (1-2 hours) - -**Priority:** Medium (improves workflow, not blocking) - -## Success Criteria - -1. Both `main` and `test` workspaces build successfully -2. Switching workspaces requires only CMake flag change -3. Workspace-specific changes don't affect other workspaces -4. Common shader changes affect all workspaces -5. New workspace creation takes < 5 minutes -6. All tests pass for both workspaces -7. Documentation clear for new contributors - -## Related Files - -**New files:** -- `workspaces/main/workspace.cfg` - Main demo config -- `workspaces/test/workspace.cfg` - Test demo config -- `cmake/ParseWorkspace.cmake` - Config parser -- `scripts/workspace.sh` - Workspace CLI tool -- `templates/workspace.cfg` - New workspace template - -**Modified files:** -- `CMakeLists.txt` - Workspace support -- `tools/asset_packer.cc` - Multi-path resolution -- `src/gpu/shader_composer.cc` - Multi-path includes -- `README.md` - Workspace documentation -- `doc/HOWTO.md` - Build commands -- `doc/CONTRIBUTING.md` - Workspace guidelines - -**Moved files:** -- `assets/demo.seq` → `workspaces/main/timeline.seq` -- `assets/music.track` → `workspaces/main/music.track` -- `assets/final/demo_assets.txt` → `workspaces/main/assets.txt` -- `assets/test_demo.*` → `workspaces/test/` -- `assets/shaders/math/` → `assets/common/shaders/math/` - -## Notes +Shared shaders in `src/shaders/` are referenced as `../../src/shaders/...` in `assets.txt`. -- This is a **structural refactor**, not a feature -- No runtime behavior changes -- Enables future scalability -- Makes project more professional -- Good foundation for demoscene releases (multiple demos per repo) +See `doc/archive/WORKSPACE_SYSTEM.md` for full design and implementation history. diff --git a/doc/archive/GEOM_BUFFER.md b/doc/archive/GEOM_BUFFER.md new file mode 100644 index 0000000..0188125 --- /dev/null +++ b/doc/archive/GEOM_BUFFER.md @@ -0,0 +1,229 @@ +# Geometry Buffer Design [IN PROGRESS] + +**Status:** Ideation phase +**Goal:** Efficient G-buffer for deferred rendering in 64k demo + +--- + +## Overview + +Replace direct rendering with geometry buffer accumulation for advanced post-processing and lighting. + +**Target:** 8-10 bytes/pixel, 16-bit precision + +--- + +## Buffer Elements + +### Core Attributes + +| Attribute | Channels | Precision | Source | +|-----------|----------|-----------|--------| +| Albedo (RGB) | 3 | f16 | Material/procedural | +| Roughness | 1 | u8/u16 | PBR material property | +| Metallic | 1 | u8/u16 | PBR material property | +| Normal (XYZ) | 2 | f16 | Octahedral encoding | +| Depth | 1 | f16/f32 | 1/z for precision | +| Object/Material ID | 1 | u16 | Rasterization/SDF | +| Transparency | 1 | u8/u16 | Alpha channel | + +### Optional/Derived + +| Attribute | Storage | Notes | +|-----------|---------|-------| +| Depth gradient | On-demand | Compute from depth (Sobel) | +| Laplacian | On-demand | Second derivative of depth | +| Motion vectors | 2×f16 | Screen-space XY | +| AO | 1×f16 | Ambient occlusion | + +**Key insight:** Depth derivatives cheaper to compute than store (2-4 bytes/pixel saved). + +--- + +## Packing Strategies + +### Traditional Multi-Render-Target (MRT) + +``` +RT0 (RGBA16): Albedo.rgb + Roughness (packed with metallic) +RT1 (RG16): Octahedral normal (2 channels encode XYZ) +RT2 (R32F): 1/z depth (or use hardware depth buffer) +RT3 (RG16): Motion vectors XY +RT4 (R16UI): Object/Material ID +``` + +**Total:** 4-5 render targets = 8-10 bytes/pixel + +### Compute Shader + Storage Buffer (RECOMMENDED) + +**Advantages:** +- Custom bit-packing (not bound to RGBA formats) +- Compute derivatives in-pass (depth gradient, Laplacian) +- Cache-optimized tiling (Morton order) +- No MRT limits (store 20+ attributes) + +**Tradeoffs:** +- No hardware depth/early-Z during G-buffer generation +- Manual atomics if pixel overdraw +- Lose ROPs hardware optimizations + +**Struct Layout:** +```cpp +struct GBufferPixel { + u32 packed_normal; // Octahedral 16+16 + u32 rgba_rough; // RGBA8 + Roughness8 + Metallic8 (26 bits used) + f16 inv_z; // 1/z depth + u16 material_id; // Object/material + // Total: 12 bytes/pixel +}; + +// Compressed variant (8 bytes): +struct CompactGBuffer { + u32 normal_depth; // Oct16 normal + u16 quantized depth + u32 rgba_params; // RGB565 + Rough4 + Metal4 + Flags4 +}; +``` + +**Access Pattern:** +```wgsl +@group(0) @binding(0) var g_buffer: array; + +fn write_gbuffer(pixel_id: u32, data: SurfaceData) { + g_buffer[pixel_id].packed_normal = pack_octahedral(data.normal); + g_buffer[pixel_id].rgba_rough = pack_rgba8(data.albedo) | (u32(data.roughness * 255.0) << 24); + g_buffer[pixel_id].inv_z = f16(1.0 / data.depth); + g_buffer[pixel_id].material_id = data.id; +} +``` + +--- + +## Normal Encoding + +**Octahedral mapping** (most efficient for 2-channel storage): +- Encodes unit sphere normal to 2D square +- 16-bit per channel = good precision +- Fast encode/decode (no trig) + +```cpp +vec2 octahedral_encode(vec3 n) { + n /= (abs(n.x) + abs(n.y) + abs(n.z)); + vec2 p = n.z >= 0.0 ? n.xy : (1.0 - abs(n.yx)) * sign(n.xy); + return p * 0.5 + 0.5; // [0, 1] +} + +vec3 octahedral_decode(vec2 p) { + p = p * 2.0 - 1.0; // [-1, 1] + vec3 n = vec3(p.x, p.y, 1.0 - abs(p.x) - abs(p.y)); + float t = max(-n.z, 0.0); + n.x += n.x >= 0.0 ? -t : t; + n.y += n.y >= 0.0 ? -t : t; + return normalize(n); +} +``` + +--- + +## Depth Storage + +**1/z (inverse depth):** +- Better precision distribution (more bits near camera) +- Linear in screen space +- Matches perspective projection + +**Alternatives:** +- Logarithmic depth (even better precision) +- Hardware depth buffer (R32F, free with render targets) + +--- + +## Material Properties + +**Roughness/Metallic are NOT geometry:** +- **Source:** Texture lookups, procedural noise, or constants +- **Not bump-mapping:** Bump/normal maps perturb normals (geometry) +- **PBR properties:** Control light interaction (0=smooth/dielectric, 1=rough/metal) + +**Demoscene approach:** Procedural generation or baked constants (avoid textures). + +--- + +## Post-Processing Derivatives + +**Compute on-demand** (cheaper than storing): + +```wgsl +// Depth gradient (Sobel filter) +fn depth_gradient(uv: vec2f) -> vec2f { + let dx = textureLoad(depth, uv + vec2(1,0)) - textureLoad(depth, uv - vec2(1,0)); + let dy = textureLoad(depth, uv + vec2(0,1)) - textureLoad(depth, uv - vec2(0,1)); + return vec2(dx, dy) * 0.5; +} + +// Laplacian (edge detection) +fn laplacian(uv: vec2f) -> f32 { + let c = textureLoad(depth, uv); + let n = textureLoad(depth, uv + vec2(0,1)); + let s = textureLoad(depth, uv - vec2(0,1)); + let e = textureLoad(depth, uv + vec2(1,0)); + let w = textureLoad(depth, uv - vec2(1,0)); + return (n + s + e + w) - 4.0 * c; +} +``` + +--- + +## Integration with Hybrid Renderer + +**Current:** Hybrid SDF raymarching + rasterized proxy geometry +**Future:** Both write to unified G-buffer + +```cpp +// Rasterization pass +void rasterize_geometry() { + // Vertex shader → fragment shader + // Write to G-buffer (compute or MRT) +} + +// SDF raymarching pass (compute) +void raymarch_sdf() { + // Per-pixel ray march + // Write to same G-buffer at hit points +} + +// Deferred lighting pass +void deferred_lighting() { + // Read G-buffer + // Apply PBR lighting, shadows, etc. +} +``` + +**Atomics handling:** Use depth test or tile-based sorting to avoid conflicts. + +--- + +## Size Budget + +**Target:** 1920×1080 @ 8 bytes/pixel = **16 MB** +**Compressed:** 1920×1080 @ 6 bytes/pixel = **12 MB** + +**Acceptable for 64k demo:** RAM usage OK, not binary size. + +--- + +## Next Steps + +1. Prototype compute shader G-buffer writer +2. Implement octahedral normal encoding +3. Test SDF + raster unified writes +4. Add deferred lighting pass +5. Validate depth derivative quality (gradient/Laplacian) +6. Optimize packing (aim for 6-8 bytes/pixel) + +--- + +## References + +- Octahedral mapping: "Survey of Efficient Representations for Independent Unit Vectors" (Meyer et al.) +- PBR theory: "Physically Based Rendering" (Pharr, Jakob, Humphreys) +- G-buffer design: "Deferred Rendering in Killzone 2" (Valient, 2007) diff --git a/doc/archive/GPU_PROCEDURAL_PHASE4.md b/doc/archive/GPU_PROCEDURAL_PHASE4.md new file mode 100644 index 0000000..4cfc271 --- /dev/null +++ b/doc/archive/GPU_PROCEDURAL_PHASE4.md @@ -0,0 +1,70 @@ +# GPU Procedural Phase 4: Texture Composition + +**Status:** ✅ Complete + +## Implementation + +Multi-input composite shaders with configurable sampler support. + +### API + +```cpp +enum class SamplerType { + LinearClamp, LinearRepeat, NearestClamp, NearestRepeat +}; + +void create_gpu_composite_texture( + const std::string& name, + const std::string& shader_func, + const char* shader_code, + const void* uniform_data, + size_t uniform_size, + int width, int height, + const std::vector& input_names, + SamplerType sampler = SamplerType::LinearClamp); +``` + +### Shaders + +**gen_blend.wgsl** - Blend two textures with lerp factor: +- Bindings: output (0), uniform (1), input_a (2), input_b (3), sampler (4) +- Uniform: `{u32 width, height; f32 blend_factor, _pad0}` + +**gen_mask.wgsl** - Multiply textures (masking): +- Bindings: output (0), uniform (1), input_a (2), input_b (3), sampler (4) +- Uniform: `{u32 width, height}` + +### Usage + +```cpp +extern const char* gen_blend_compute_wgsl; + +struct { uint32_t width, height; float blend_factor, _pad0; } uni = {256, 256, 0.5f, 0.0f}; + +tex_mgr.create_gpu_composite_texture( + "blended", "gen_blend", gen_blend_compute_wgsl, + &uni, sizeof(uni), 256, 256, + {"noise_a", "noise_b"}, + SamplerType::LinearClamp); +``` + +### Features + +- **Dynamic bind groups:** N input textures + 1 sampler +- **Lazy sampler creation:** Map-based cache, 4 preset types +- **Multi-stage composition:** Composite of composites supported +- **Guarded with `#if !defined(STRIP_GPU_COMPOSITE)`** + +### Size Impact + +- Code: ~460 lines added +- Compressed: ~830 bytes (2 shaders + dispatch logic) + +### Tests + +`test_gpu_composite.cc`: +- Blend two noise textures +- Mask noise with grid +- Multi-stage composite (composite of composites) + +All 35 tests passing. diff --git a/doc/archive/SHADER_REUSE_INVESTIGATION.md b/doc/archive/SHADER_REUSE_INVESTIGATION.md new file mode 100644 index 0000000..e840126 --- /dev/null +++ b/doc/archive/SHADER_REUSE_INVESTIGATION.md @@ -0,0 +1,241 @@ +# Shader Code Reuse Investigation + +## ✅ Implementation Status + +**Date:** February 13, 2026 +**Solution:** Option 1 - Shared Common Directory +**Status:** IMPLEMENTED + +**Results:** +- Created `src/shaders/` with 20 shared shader files (moved from `common/shaders/` 2026-02-28) +- Eliminated 36 duplicate files across workspaces +- Asset references use `../../src/shaders/...` +- Enhanced asset_packer with filesystem path normalization +- Build passes, all tests pass + +See `doc/FILE_HIERARCHY_CLEANUP_2026-02-13.md` for full details. + +--- + +## Investigation (Historical) + +### Current State + +### Duplication Analysis +- **36 duplicate shader files** between main and test workspaces +- Common files include: math utilities, render helpers, compute shaders, uniforms +- Files are byte-identical duplicates (verified) + +### Current System Architecture +1. **ShaderComposer**: Runtime shader composition via `#include` directives +2. **Asset System**: Shaders loaded from workspace `assets.txt` +3. **Registration**: `InitShaderComposer()` registers snippets with paths like: + - `common_uniforms` + - `math/sdf_shapes`, `math/noise`, `math/common_utils` + - `render/shadows`, `render/scene_query_bvh`, `render/lighting_utils` + +### Duplicate Common Shaders +``` +Common across both workspaces: +- common_uniforms.wgsl +- math/{common_utils, noise, sdf_shapes, sdf_utils}.wgsl +- render/{lighting_utils, scene_query_bvh, scene_query_linear, shadows}.wgsl +- compute/{gen_blend, gen_grid, gen_mask, gen_noise, gen_perlin}.wgsl +- Various effect shaders (passthrough, lighting, ray_box, etc.) +``` + +--- + +## Proposed Approaches + +### Option 1: Shared Common Directory (Original Design) +**Structure:** +``` +/common/ + /shaders/ + /math/ # Shared math utilities + /render/ # Shared rendering helpers + /compute/ # Shared compute shaders + common_uniforms.wgsl + +/workspaces/ + /main/ + /shaders/ # Workspace-specific only + /music/ + /weights/ + /obj/ +``` + +**Pros:** +- Single source of truth for common code +- No duplication +- Clear separation: common vs workspace-specific +- Matches original design doc intent + +**Cons:** +- Breaks workspace isolation (workspaces depend on external directory) +- Harder to version control per-workspace +- Need to update asset packer to handle cross-workspace references +- Complicates workspace portability + +--- + +### Option 2: Symbolic Links +**Structure:** +``` +/workspaces/ + /main/ + /shaders/ + /common@ -> ../../common/shaders/ # Symlink + custom_effect.wgsl +``` + +**Pros:** +- Maintains single source +- Works with existing asset system +- Simple filesystem solution + +**Cons:** +- Windows symlink issues (requires admin or dev mode) +- Git symlink handling varies +- Still breaks workspace isolation +- Can confuse developers + +--- + +### Option 3: Build-Time Copy/Sync +**Structure:** +Workspaces keep duplicates, but sync from canonical source at build time. + +``` +/common/shaders/ # Canonical source +/workspaces/*/shaders/ # Build-synced copies +``` + +**Implementation:** +- CMake script copies common/ → workspaces/ before asset packing +- Or: Pre-commit hook validates consistency + +**Pros:** +- Workspaces remain self-contained +- No runtime dependencies +- Works across all platforms +- Git shows actual files + +**Cons:** +- Easy to forget sync +- Merge conflicts if edited in workspace +- Need tooling to maintain consistency + +--- + +### Option 4: Asset System Extension +**Enhance asset packer to support cross-workspace includes:** + +`workspaces/main/assets.txt`: +``` +SHADER_COMMON_UTILS, NONE, @common/shaders/math/common_utils.wgsl +SHADER_CUSTOM, NONE, shaders/custom_effect.wgsl +``` + +**Pros:** +- Clean at asset definition level +- ShaderComposer unchanged +- Explicit common vs local + +**Cons:** +- Asset packer modification needed +- New syntax to learn +- Breaks workspace portability + +--- + +### Option 5: Status Quo + Documentation +**Keep duplicates, document as intentional.** + +Add `common/shaders/` as reference templates that workspaces copy. + +**Pros:** +- Workspaces fully independent +- Can diverge per-workspace if needed +- No build system changes +- Simple mental model + +**Cons:** +- Ongoing duplication +- Manual sync required +- Disk space (minimal impact) +- Bug fixes need multi-workspace updates + +--- + +## Recommendation Matrix + +| Criteria | Opt 1 | Opt 2 | Opt 3 | Opt 4 | Opt 5 | +|----------|-------|-------|-------|-------|-------| +| No duplication | ✓ | ✓ | ✗ | ✓ | ✗ | +| Workspace isolation | ✗ | ✗ | ✓ | ✗ | ✓ | +| Cross-platform | ✓ | ✗ | ✓ | ✓ | ✓ | +| Low complexity | ✗ | ✓ | ✗ | ✗ | ✓ | +| Easy maintenance | ✓ | ✓ | ✗ | ✓ | ✗ | + +--- + +## Current Implementation Details + +### How Shaders Are Currently Included + +**In WGSL files:** +```wgsl +#include "common_uniforms" +#include "math/sdf_shapes" +#include "render/shadows" +``` + +**In C++ (shaders.cc):** +```cpp +void InitShaderComposer() { + auto& sc = ShaderComposer::Get(); + sc.RegisterSnippet("common_uniforms", ...); + sc.RegisterSnippet("math/sdf_shapes", ...); + sc.RegisterSnippet("render/shadows", ...); +} +``` + +**Asset Registration (assets.txt):** +``` +SHADER_MATH_SDF_SHAPES, NONE, shaders/math/sdf_shapes.wgsl +SHADER_RENDER_SHADOWS, NONE, shaders/render/shadows.wgsl +``` + +### ShaderComposer Flow +1. Assets loaded from workspace `assets.txt` +2. Snippets registered in `InitShaderComposer()` +3. Effects call `Compose()` with dependencies +4. `#include` directives recursively resolved +5. Final shader string assembled + +--- + +## Next Steps + +1. **Decide on approach** based on project priorities: + - Simplicity → Option 5 + - True reuse → Option 1 or 4 + - Compatibility → Option 3 + +2. **If Option 1 chosen:** + - Create `/common/shaders/` with canonical sources + - Update `workspace.cfg` to reference common + - Update asset packer to resolve `../common/` paths + - Update docs + +3. **If Option 3 chosen:** + - Create sync script in `scripts/` + - Add to build process + - Document workflow + +4. **If Option 5 chosen:** + - Create `/common/shaders/` as templates + - Add `doc/SHADER_COMMON.md` explaining pattern + - Accept duplication as intentional diff --git a/doc/archive/SPECTRAL_BRUSH_EDITOR.md b/doc/archive/SPECTRAL_BRUSH_EDITOR.md new file mode 100644 index 0000000..a7d0e3a --- /dev/null +++ b/doc/archive/SPECTRAL_BRUSH_EDITOR.md @@ -0,0 +1,195 @@ +# Spectral Brush Editor (Task #5) + +## Concept + +Replace large `.spec` assets with procedural C++ code (50-100× compression). + +**Before:** 5 KB binary `.spec` file +**After:** ~100 bytes C++ code calling `draw_bezier_curve()` + +**Workflow:** +``` +.wav → Load in editor → Trace with Bezier curves → Export procedural_params.txt + C++ code +``` + +--- + +## Core Primitive: "Spectral Brush" + +### 1. Central Curve (Bezier) +Traces time-frequency path: `{freq_bin, amplitude} = bezier(frame_number)` + +**Example control points:** +```javascript +[ + {frame: 0, freq_hz: 200.0, amplitude: 0.9}, // Attack + {frame: 20, freq_hz: 80.0, amplitude: 0.7}, // Sustain + {frame: 100, freq_hz: 50.0, amplitude: 0.0} // Decay +] +``` + +### 2. Vertical Profile +Shapes "brush stroke" around curve at each frame. + +**Profile Types:** +- **Gaussian**: `exp(-(dist² / σ²))` - Musical tones, bass +- **Decaying Sinusoid**: `exp(-decay * dist) * cos(ω * dist)` - Metallic sounds +- **Noise**: `random(seed, dist) * amplitude` - Hi-hats, cymbals +- **Composite**: Combine multiple profiles (add/subtract/multiply) + +--- + +## File Formats + +### A. `procedural_params.txt` (Human-readable) +```text +METADATA dct_size=512 num_frames=100 sample_rate=32000 + +CURVE bezier + CONTROL_POINT 0 200.0 0.9 + CONTROL_POINT 20 80.0 0.7 + CONTROL_POINT 100 50.0 0.0 + PROFILE gaussian sigma=30.0 +END_CURVE +``` + +### B. C++ Code (Ready to compile) +```cpp +#include "audio/spectral_brush.h" + +void gen_kick_procedural(float* spec, int dct_size, int num_frames) { + const float frames[] = {0.0f, 20.0f, 100.0f}; + const float freqs[] = {200.0f, 80.0f, 50.0f}; + const float amps[] = {0.9f, 0.7f, 0.0f}; + + draw_bezier_curve(spec, dct_size, num_frames, + frames, freqs, amps, 3, + PROFILE_GAUSSIAN, 30.0f); +} +``` + +--- + +## C++ Runtime API + +### Files: `src/audio/spectral_brush.{h,cc}` + +**Key functions:** +```cpp +enum ProfileType { + PROFILE_GAUSSIAN, + PROFILE_DECAYING_SINUSOID, + PROFILE_NOISE +}; + +void draw_bezier_curve(float* spectrogram, int dct_size, int num_frames, + const float* control_frames, + const float* control_freqs_hz, + const float* control_amps, + int n_control_points, + ProfileType profile_type, + float profile_param1, + float profile_param2 = 0.0f); + +float evaluate_bezier_linear(const float* control_frames, + const float* control_values, + int n_points, + float frame); + +float evaluate_profile(ProfileType type, float distance, + float param1, float param2); +``` + +--- + +## Editor UI + +### Technology Stack +- HTML5 Canvas (visualization) +- Web Audio API (playback) +- Pure JavaScript (no dependencies) +- Reuse `dct.js` from existing editor + +### Key Features +- Dual-layer canvas (reference + procedural spectrograms) +- Drag control points to adjust curves +- Real-time spectrogram rendering +- Audio playback (keys 1/2 for procedural/original) +- Undo/Redo (Ctrl+Z, Ctrl+Shift+Z) +- Load .wav/.spec, save params, generate C++ code + +### Keyboard Shortcuts +| Key | Action | +|-----|--------| +| 1 | Play procedural | +| 2 | Play original | +| Space | Play/pause | +| Delete | Remove control point | +| Ctrl+Z | Undo | +| Ctrl+S | Save params | + +--- + +## Implementation Phases + +### Phase 1: C++ Runtime +**Files:** `src/audio/spectral_brush.{h,cc}`, `src/tests/test_spectral_brush.cc` + +**Tasks:** +- Define API (ProfileType, draw_bezier_curve, evaluate_profile) +- Implement linear Bezier interpolation +- Implement Gaussian profile +- Add unit tests +- **Deliverable:** Compiles, tests pass + +### Phase 2: Editor Core +**Files:** `tools/spectral_editor/{index.html, script.js, style.css, dct.js}` + +**Tasks:** +- HTML structure (canvas, controls, file input) +- Bezier curve editor (place/drag/delete control points) +- Dual-layer canvas rendering +- Real-time spectrogram generation +- Audio playback (IDCT → Web Audio API) +- Undo/Redo system +- **Deliverable:** Interactive editor, can trace .wav files + +### Phase 3: File I/O +**Tasks:** +- Load .wav (decode, STFT → spectrogram) +- Load .spec (binary parser) +- Save procedural_params.txt (text format) +- Generate C++ code (template) +- Load procedural_params.txt (re-editing) +- **Deliverable:** Full save/load cycle + +--- + +## Design Decisions + +- **Bezier:** Linear interpolation (Phase 1), cubic later +- **Profiles:** Gaussian only (Phase 1), others later +- **Parameters:** Soft UI limits, no enforced bounds +- **RNG:** Home-brew deterministic (small, repeatable) +- **Code gen:** Single function per sound (generic loader later) + +--- + +## Size Impact + +**Example: Kick drum** + +**Before (Binary):** +- 512 bins × 100 frames × 4 bytes = 200 KB uncompressed +- ~5 KB compressed (zlib) + +**After (Procedural):** +- 4 control points × 3 arrays × 4 floats = ~48 bytes data +- Function call overhead = ~20 bytes +- **Total: ~100 bytes** (50-100× reduction) + +**Trade-off:** Runtime CPU cost, acceptable for 64k demo. + +--- + +*See TODO.md for detailed implementation tasks.* diff --git a/doc/archive/WORKSPACE_SYSTEM.md b/doc/archive/WORKSPACE_SYSTEM.md new file mode 100644 index 0000000..6b4319d --- /dev/null +++ b/doc/archive/WORKSPACE_SYSTEM.md @@ -0,0 +1,576 @@ +# Workspace System (Task #77) [COMPLETED] + +## Status + +**Implemented:** Feb 9, 2026 + +Workspace system is now active. Use `cmake -B build -DDEMO_WORKSPACE=` to select workspace. + +## Overview + +Self-contained demo workspaces for parallel development and clean content separation. + +**Motivation:** Current structure scatters demo content across multiple locations: +- `assets/demo.seq` - Timeline +- `assets/music.track` - Audio +- `assets/final/demo_assets.txt` - Asset list +- `assets/final/music/*.spec` - Audio samples +- `assets/shaders/*.wgsl` - Shaders (some demo-specific, some shared) + +This makes it hard to: +- Work on multiple demos simultaneously +- Understand what files belong to which demo +- Share common resources cleanly +- Onboard new developers + +## Problem Statement + +**Current structure (flat):** +``` +/assets/ + demo.seq # Main demo timeline + music.track # Main demo music + test_demo.seq # Test demo timeline + test_demo.track # Test demo music + /final/ + demo_assets.txt # Main demo assets + /music/*.spec # Audio samples (mixed usage) + /shaders/ + *.wgsl # Mixed: common + demo-specific +``` + +**Issues:** +1. No clear ownership of files +2. Can't easily switch between demos +3. Assets intermingled (demo vs test) +4. Shaders hard to categorize (shared vs demo-specific) +5. Adding new demos requires scattered changes + +## Current Structure (Implemented) + +### Workspace Directory Layout + +``` +/workspaces/ + /main/ # Main production demo + workspace.cfg # Workspace metadata + timeline.seq # Visual effects + music.track # Audio patterns + assets.txt # Asset list + /music/*.spec # Audio samples + /weights/*.bin # CNN binary weights + /obj/*.obj # 3D models + /shaders/ # Workspace-specific shaders only + custom_effect.wgsl + + /test/ # Test/validation demo + workspace.cfg + timeline.seq + music.track + assets.txt + /music/ + /weights/ + /obj/ + /shaders/ + +/common/ # Shared resources + /shaders/ + /math/ # Shared math utilities + common_utils.wgsl + noise.wgsl + sdf_shapes.wgsl + sdf_utils.wgsl + /render/ # Shared rendering helpers + lighting_utils.wgsl + scene_query_bvh.wgsl + scene_query_linear.wgsl + shadows.wgsl + /compute/ # Shared compute shaders + gen_blend.wgsl + gen_grid.wgsl + gen_mask.wgsl + gen_noise.wgsl + gen_perlin.wgsl + common_uniforms.wgsl + lighting.wgsl + passthrough.wgsl + ray_box.wgsl + ray_triangle.wgsl + sdf_primitives.wgsl + skybox.wgsl + +/tools/ + originals/ # Source audio files (.wav, .aif) +``` + +### Workspace Configuration + +**`workspace.cfg` format:** +```ini +[workspace] +name = "Main Demo" +description = "Production 64k demo" +version = "1.0" + +[build] +target = "demo64k" +timeline = "timeline.seq" +music = "music.track" +assets = "assets.txt" +asset_dirs = ["music/", "weights/", "obj/"] +shader_dirs = ["shaders/"] + +[build] +# Output binary name +target = "demo64k" + +# Include paths (relative to workspace root) +timeline = "timeline.seq" +music = "music.track" +assets = "assets.txt" + +# Asset directories +asset_dirs = ["assets/", "../common/audio/"] + +# Shader directories (order matters: workspace-specific first) +shader_dirs = ["shaders/", "../src/shaders/"] + +[options] +# Default resolution +width = 1280 +height = 720 + +# Duration (seconds, -1 = auto-detect) +duration = -1 + +# BPM for timeline +bpm = 120 +``` + +## Architecture + +### Build System Integration + +**CMakeLists.txt changes:** +```cmake +# Select active workspace (default: main) +set(DEMO_WORKSPACE "main" CACHE STRING "Active workspace") + +# Parse workspace config +set(WORKSPACE_DIR "${CMAKE_SOURCE_DIR}/workspaces/${DEMO_WORKSPACE}") +set(WORKSPACE_CFG "${WORKSPACE_DIR}/workspace.cfg") + +# Read config and set variables +parse_workspace_config(${WORKSPACE_CFG} + TIMELINE_PATH + MUSIC_PATH + ASSETS_PATH + ASSET_DIRS + SHADER_DIRS + TARGET_NAME +) + +# Generate assets from workspace +add_custom_command( + OUTPUT ${GEN_ASSETS_H} + COMMAND asset_packer ${WORKSPACE_DIR}/${ASSETS_PATH} ... + DEPENDS ${WORKSPACE_DIR}/${ASSETS_PATH} +) + +# Generate timeline from workspace +add_custom_command( + OUTPUT ${GEN_TIMELINE_CC} + COMMAND seq_compiler ${WORKSPACE_DIR}/${TIMELINE_PATH} ... + DEPENDS ${WORKSPACE_DIR}/${TIMELINE_PATH} +) + +# Add shader include paths from workspace +foreach(SHADER_DIR ${SHADER_DIRS}) + list(APPEND SHADER_INCLUDE_DIRS "${WORKSPACE_DIR}/${SHADER_DIR}") +endforeach() +``` + +**Building specific workspace:** +```bash +# Default (main workspace) +cmake -S . -B build +cmake --build build -j4 + +# Specific workspace +cmake -S . -B build_test -DDEMO_WORKSPACE=test +cmake --build build_test -j4 + +# Experimental workspace +cmake -S . -B build_exp -DDEMO_WORKSPACE=experiments/demo2024_revision +cmake --build build_exp -j4 +``` + +### Asset Packer Changes + +**Support workspace-relative paths:** +```cpp +// asset_packer.cc +void PackerContext::resolve_asset_path(const char* rel_path) { + // Try workspace-local first + std::string workspace_path = workspace_dir + "/" + rel_path; + if (file_exists(workspace_path)) { + return workspace_path; + } + + // Try common assets + std::string common_path = "assets/common/" + rel_path; + if (file_exists(common_path)) { + return common_path; + } + + error("Asset not found: %s", rel_path); +} +``` + +### Shader Composer Changes + +**Support multiple include paths:** +```cpp +// shader_composer.cc +class ShaderComposer { + std::vector include_paths_; + + void add_include_path(const char* path) { + include_paths_.push_back(path); + } + + std::string resolve_include(const char* filename) { + for (const auto& base : include_paths_) { + std::string full = base + "/" + filename; + if (file_exists(full)) { + return full; + } + } + error("Shader include not found: %s", filename); + } +}; +``` + +**Shader includes with search paths:** +```wgsl +// workspace-specific shader +#include "custom_uniforms.wgsl" // From workspaces/main/shaders/ +#include "math/common_utils.wgsl" // From src/shaders/ +``` + +### CLI Tool + +**`scripts/workspace.sh` - Workspace management:** +```bash +#!/bin/bash +# workspace.sh - Manage demo workspaces + +case "$1" in + list) + # List all workspaces + ls -1 workspaces/ + ;; + + create) + # Create new workspace from template + NAME=$2 + mkdir -p workspaces/$NAME/{assets,shaders} + cp templates/workspace.cfg workspaces/$NAME/ + echo "Created workspace: $NAME" + ;; + + build) + # Build specific workspace + NAME=$2 + cmake -S . -B build_$NAME -DDEMO_WORKSPACE=$NAME + cmake --build build_$NAME -j4 + ;; + + info) + # Show workspace details + NAME=$2 + cat workspaces/$NAME/workspace.cfg + ;; +esac +``` + +**Usage:** +```bash +./scripts/workspace.sh list +./scripts/workspace.sh create my_demo +./scripts/workspace.sh build main +./scripts/workspace.sh info test +``` + +## Implementation Plan + +### Phase 1: Create Workspace Structure + +**1.1 Create directory tree:** +```bash +mkdir -p workspaces/{main,test}/assets +mkdir -p workspaces/{main,test}/shaders +mkdir -p assets/common/{shaders,audio} +``` + +**1.2 Move existing files:** +```bash +# Main demo +mv assets/demo.seq workspaces/main/timeline.seq +mv assets/music.track workspaces/main/music.track +mv assets/final/demo_assets.txt workspaces/main/assets.txt +mv assets/final/music workspaces/main/assets/ + +# Test demo +mv assets/test_demo.seq workspaces/test/timeline.seq +mv assets/test_demo.track workspaces/test/music.track +cp assets/final/demo_assets.txt workspaces/test/assets.txt # Or create minimal version + +# Common shaders +mv assets/shaders/math assets/common/shaders/ +mv assets/shaders/common_uniforms assets/common/shaders/ +``` + +**1.3 Create workspace configs:** +```bash +# Generate workspace.cfg for main and test +# See format above +``` + +### Phase 2: Update Build System + +**2.1 Add workspace parsing:** +- Create `cmake/ParseWorkspace.cmake` +- Parse INI-style config +- Set CMake variables + +**2.2 Update CMakeLists.txt:** +- Add `DEMO_WORKSPACE` option +- Use workspace paths for assets/timeline/music +- Pass shader include paths to ShaderComposer + +**2.3 Update compiler tools:** +- `asset_packer`: Accept workspace root dir +- `seq_compiler`: Accept workspace root dir +- `tracker_compiler`: Accept workspace root dir + +### Phase 3: Update Code + +**3.1 ShaderComposer:** +- Add multi-path include resolution +- Search workspace shaders first, then common + +**3.2 Asset loading:** +- Update paths to match new structure +- Ensure backward compatibility during transition + +**3.3 Generated code:** +- Verify generated assets.h/timeline.cc still work +- Update include paths if needed + +### Phase 4: Documentation & Tools + +**4.1 Update docs:** +- README.md: Explain workspace structure +- HOWTO.md: Document workspace build commands +- CONTRIBUTING.md: Workspace best practices + +**4.2 Create helper scripts:** +- `scripts/workspace.sh`: Workspace management CLI +- `templates/workspace.cfg`: Template for new workspaces + +**4.3 Update CI:** +- Build both main and test workspaces +- Validate workspace isolation + +### Phase 5: Migration & Validation + +**5.1 Test builds:** +```bash +# Build both workspaces +cmake -S . -B build_main -DDEMO_WORKSPACE=main +cmake --build build_main -j4 + +cmake -S . -B build_test -DDEMO_WORKSPACE=test +cmake --build build_test -j4 + +# Run tests +cd build_main && ctest +cd build_test && ctest +``` + +**5.2 Verify isolation:** +- Modify workspace-specific shader → only that workspace affected +- Modify common shader → both workspaces affected + +**5.3 Update .gitignore:** +``` +/build_*/ +/workspaces/*/generated/ +``` + +## Benefits + +### 1. Clear Ownership +Each workspace is self-contained: +```bash +workspaces/main/ +├── workspace.cfg # Configuration +├── timeline.seq # Timeline +├── music.track # Music +├── assets.txt # Assets +├── assets/ # Local assets +└── shaders/ # Local shaders +``` + +### 2. Parallel Development +Multiple developers can work on different demos without conflicts: +```bash +# Developer A: Main demo +cd workspaces/main +vim timeline.seq + +# Developer B: Experimental demo +cd workspaces/experiments/christmas_demo +vim timeline.seq +``` + +### 3. Easy Switching +```bash +# Build main demo +cmake -B build -DDEMO_WORKSPACE=main +./build/demo64k + +# Build test demo +cmake -B build -DDEMO_WORKSPACE=test +./build/test_demo +``` + +### 4. Clean Sharing +Common resources in one place: +``` +assets/common/ +├── shaders/ +│ ├── math/ # Shared by all +│ └── common_uniforms/ +└── audio/ + └── standard_drums.spec +``` + +### 5. Scalability +Easy to add new demos: +```bash +./scripts/workspace.sh create party2025 +cd workspaces/party2025 +# Start developing... +``` + +## Migration Path + +### Backward Compatibility + +During transition, support both structures: +```cmake +if(EXISTS "${CMAKE_SOURCE_DIR}/workspaces/${DEMO_WORKSPACE}") + # New workspace structure + set(USING_WORKSPACES TRUE) +else() + # Legacy flat structure + set(USING_WORKSPACES FALSE) +endif() +``` + +### Gradual Migration + +1. **Phase 1:** Create workspace structure alongside existing +2. **Phase 2:** Update build system to support both +3. **Phase 3:** Move files to workspaces +4. **Phase 4:** Remove legacy paths +5. **Phase 5:** Update all documentation + +**Timeline:** 1-2 weeks for full migration + +## Trade-offs + +### Pros +- Clear project organization +- Parallel demo development +- Clean resource sharing +- Better onboarding for new devs +- Scales to many demos + +### Cons +- Initial migration effort +- Build system complexity increases +- More directories to navigate +- Learning curve for contributors + +### Alternative Approaches + +**1. Monorepo with subprojects:** +- Separate CMake projects per demo +- More isolated but harder to share code + +**2. Configuration files only:** +- Keep flat structure, use config to select files +- Less clear, harder to understand ownership + +**3. Git branches per demo:** +- Each demo is a branch +- Conflicts when merging shared code + +**Chosen:** Workspace system (best balance) + +## Implementation Effort + +**Estimated time: 12-16 hours** + +- Phase 1: Create structure (2-3 hours) +- Phase 2: Build system (4-5 hours) +- Phase 3: Code updates (3-4 hours) +- Phase 4: Documentation (2-3 hours) +- Phase 5: Validation (1-2 hours) + +**Priority:** Medium (improves workflow, not blocking) + +## Success Criteria + +1. Both `main` and `test` workspaces build successfully +2. Switching workspaces requires only CMake flag change +3. Workspace-specific changes don't affect other workspaces +4. Common shader changes affect all workspaces +5. New workspace creation takes < 5 minutes +6. All tests pass for both workspaces +7. Documentation clear for new contributors + +## Related Files + +**New files:** +- `workspaces/main/workspace.cfg` - Main demo config +- `workspaces/test/workspace.cfg` - Test demo config +- `cmake/ParseWorkspace.cmake` - Config parser +- `scripts/workspace.sh` - Workspace CLI tool +- `templates/workspace.cfg` - New workspace template + +**Modified files:** +- `CMakeLists.txt` - Workspace support +- `tools/asset_packer.cc` - Multi-path resolution +- `src/gpu/shader_composer.cc` - Multi-path includes +- `README.md` - Workspace documentation +- `doc/HOWTO.md` - Build commands +- `doc/CONTRIBUTING.md` - Workspace guidelines + +**Moved files:** +- `assets/demo.seq` → `workspaces/main/timeline.seq` +- `assets/music.track` → `workspaces/main/music.track` +- `assets/final/demo_assets.txt` → `workspaces/main/assets.txt` +- `assets/test_demo.*` → `workspaces/test/` +- `assets/shaders/math/` → `assets/common/shaders/math/` + +## Notes + +- This is a **structural refactor**, not a feature +- No runtime behavior changes +- Enables future scalability +- Makes project more professional +- Good foundation for demoscene releases (multiple demos per repo) -- cgit v1.2.3