diff options
| -rw-r--r-- | CHECK_RETURN_ASSESSMENT.md | 326 | ||||
| -rw-r--r-- | CHECK_RETURN_IMPLEMENTATION.md | 181 | ||||
| -rw-r--r-- | EFFECT_DEPTH_ANALYSIS.md | 115 | ||||
| -rw-r--r-- | PROJECT_CONTEXT.md | 7 | ||||
| -rw-r--r-- | README.md | 2 | ||||
| -rw-r--r-- | SHADER_REFACTOR_EXAMPLE.md | 82 | ||||
| -rw-r--r-- | TODO.md | 1 | ||||
| -rw-r--r-- | WGSL_REFACTOR_COMPLETE.md | 191 | ||||
| -rw-r--r-- | convert_track.py | 77 | ||||
| -rw-r--r-- | doc/HOWTO.md | 8 | ||||
| -rw-r--r-- | doc/SIZE_MEASUREMENT.md | 341 | ||||
| -rw-r--r-- | timeline_analysis.html | 425 |
12 files changed, 357 insertions, 1399 deletions
diff --git a/CHECK_RETURN_ASSESSMENT.md b/CHECK_RETURN_ASSESSMENT.md deleted file mode 100644 index 0af818f..0000000 --- a/CHECK_RETURN_ASSESSMENT.md +++ /dev/null @@ -1,326 +0,0 @@ -# CHECK_AND_RETURN Macro Assessment & Plan - -## Current Situation - -**Problem:** Repetitive error handling pattern throughout codebase: -```cpp -if (condition) { - fprintf(stderr, "Error: ...\n", ...); - // Optional cleanup - return error_value; -} -``` - -This pattern appears in: -- Input validation (command-line args, file I/O) -- Resource allocation failures -- Runtime configuration errors -- API parameter validation - -**Unlike FATAL_XXX:** These are recoverable errors - caller can handle them. - -## Assessment - -### Files with Error Handling Patterns - -Found in: -- `src/util/asset_manager.cc` - 3+ instances (nullptr returns) -- `src/test_demo.cc` - 2+ instances (return 1) -- `src/gpu/texture_manager.cc` - 1 instance -- Test files (webgpu_test_fixture.cc, etc.) - -### Common Patterns - -#### Pattern 1: Return nullptr on error -```cpp -if (!valid) { - fprintf(stderr, "Error: Invalid input: %s\n", name); - if (out_size) *out_size = 0; - return nullptr; -} -``` - -#### Pattern 2: Return error code -```cpp -if (arg_invalid) { - fprintf(stderr, "Error: Unknown option '%s'\n", option); - print_usage(argv[0]); - return 1; -} -``` - -#### Pattern 3: Return false on failure -```cpp -if (!initialized) { - fprintf(stderr, "Error: Not initialized\n"); - return false; -} -``` - -#### Pattern 4: Warning (continue execution) -```cpp -if (non_critical) { - fprintf(stderr, "Warning: %s\n", msg); - // Continue execution -} -``` - -## Design Requirements - -### 1. Multiple Return Types -- `nullptr` (most common for pointer functions) -- `false` (for bool functions) -- `-1` or error codes (for int functions) -- `{}` or default values (for struct functions) - -### 2. Optional Cleanup -- Some cases need cleanup before return (e.g., `delete[]`) -- Some need to set output parameters (e.g., `*out_size = 0`) - -### 3. Stripping Behavior -- **STRIP_ALL:** Keep error checking, strip messages (save size) -- **FINAL_STRIP:** Strip everything (optional - may keep checks) - -Unlike FATAL_XXX which always abort, CHECK_RETURN should preserve control flow even when stripped. - -### 4. Debug Builds -- Print full error messages with context -- Optional file:line info (like FATAL_XXX) - -## Proposed Macro Design - -### Option A: Single Macro with Return Value - -```cpp -// Usage: -CHECK_RETURN(ptr == nullptr, nullptr, "Asset not found: %s", name); -CHECK_RETURN(argc < 2, 1, "Too few arguments"); -CHECK_RETURN(!initialized, false, "Not initialized"); - -// Expands to: -#if !defined(STRIP_ALL) - if (ptr == nullptr) { - fprintf(stderr, "Error: Asset not found: %s [file:line]\n", name); - return nullptr; - } -#else - if (ptr == nullptr) return nullptr; // Silent check -#endif -``` - -**Pros:** Simple, covers most cases -**Cons:** No cleanup before return, rigid pattern - -### Option B: Separate Macros per Return Type - -```cpp -CHECK_RETURN_NULL(cond, msg, ...) // returns nullptr -CHECK_RETURN_FALSE(cond, msg, ...) // returns false -CHECK_RETURN_ERROR(cond, msg, ...) // returns -1 -CHECK_RETURN_CODE(cond, code, msg, ...) // returns custom code - -// Usage: -CHECK_RETURN_NULL(ptr == nullptr, "Asset not found: %s", name); -CHECK_RETURN_ERROR(fd < 0, "Failed to open file: %s", path); -CHECK_RETURN_CODE(invalid_arg, 1, "Unknown option: %s", arg); -``` - -**Pros:** Type-safe, explicit intent -**Cons:** More macros, more verbose - -### Option C: Block-Based with Cleanup - -```cpp -CHECK_AND_RETURN_IF(condition, return_value) { - // Cleanup code here (optional) - delete[] buffer; - *out_size = 0; - ERROR_MSG("Failed: %s", reason); -} - -// Expands to: -#if !defined(STRIP_ALL) - if (condition) { - delete[] buffer; - *out_size = 0; - fprintf(stderr, "Error: Failed: %s [file:line]\n", reason); - return return_value; - } -#endif -``` - -**Pros:** Flexible, allows cleanup -**Cons:** More complex, harder to read - -### Option D: Hybrid (Recommended) - -```cpp -// Simple cases (90%) -CHECK_RETURN_IF(cond, retval, msg, ...) - -// Complex cases with cleanup (10%) -CHECK_RETURN_BEGIN(cond, retval) - delete[] buffer; - *out_size = 0; - ERROR_MSG("Failed: %s", reason); -CHECK_RETURN_END - -// Warning messages (non-fatal) -WARN_IF(cond, msg, ...) -``` - -**Pros:** Covers all cases, clean separation -**Cons:** Two patterns to learn - -## Recommendation: Option D (Hybrid) - -### Macro Definitions - -```cpp -// ============================================================================ -// Simple error check with immediate return -// ============================================================================ -#if !defined(STRIP_ALL) - #define CHECK_RETURN_IF(cond, retval, ...) \ - do { \ - if (cond) { \ - fprintf(stderr, "Error: " __VA_ARGS__); \ - fprintf(stderr, " [%s:%d]\n", __FILE__, __LINE__); \ - return retval; \ - } \ - } while (0) -#else - #define CHECK_RETURN_IF(cond, retval, ...) \ - do { if (cond) return retval; } while (0) -#endif - -// ============================================================================ -// Block-based error check with cleanup -// ============================================================================ -#if !defined(STRIP_ALL) - #define CHECK_RETURN_BEGIN(cond, retval) if (cond) { - #define ERROR_MSG(...) fprintf(stderr, "Error: " __VA_ARGS__); \ - fprintf(stderr, " [%s:%d]\n", __FILE__, __LINE__) - #define CHECK_RETURN_END return retval; } -#else - #define CHECK_RETURN_BEGIN(cond, retval) if (cond) { - #define ERROR_MSG(...) ((void)0) - #define CHECK_RETURN_END return retval; } -#endif - -// ============================================================================ -// Warning message (non-fatal, continue execution) -// ============================================================================ -#if !defined(STRIP_ALL) - #define WARN_IF(cond, ...) \ - do { \ - if (cond) { \ - fprintf(stderr, "Warning: " __VA_ARGS__); \ - fprintf(stderr, "\n"); \ - } \ - } while (0) -#else - #define WARN_IF(cond, ...) ((void)0) -#endif -``` - -## Usage Examples - -### Before (asset_manager.cc) -```cpp -if (proc_gen_func_ptr == nullptr) { - fprintf(stderr, "Error: Unknown procedural function at runtime: %s\n", - source_record.proc_func_name_str); - if (out_size) *out_size = 0; - return nullptr; -} -``` - -### After (simple version) -```cpp -CHECK_RETURN_BEGIN(proc_gen_func_ptr == nullptr, nullptr) - if (out_size) *out_size = 0; - ERROR_MSG("Unknown procedural function: %s", source_record.proc_func_name_str); -CHECK_RETURN_END -``` - -### Before (test_demo.cc) -```cpp -if (strcmp(argv[i], "--log-peaks") == 0) { - if (i + 1 < argc) { - log_peaks_file = argv[++i]; - } else { - fprintf(stderr, "Error: --log-peaks requires a filename argument\n\n"); - print_usage(argv[0]); - return 1; - } -} -``` - -### After -```cpp -if (strcmp(argv[i], "--log-peaks") == 0) { - CHECK_RETURN_BEGIN(i + 1 >= argc, 1) - print_usage(argv[0]); - ERROR_MSG("--log-peaks requires a filename argument"); - CHECK_RETURN_END - log_peaks_file = argv[++i]; -} -``` - -## Size Impact - -### STRIP_ALL Build -- Error messages stripped (~50-100 bytes per call site) -- Control flow preserved (if-return kept) -- Estimated savings: ~1-2KB for typical project - -### FINAL_STRIP Build (Optional) -- Could strip checks entirely for performance -- NOT recommended for input validation -- Recommended only for internal invariants - -## Implementation Plan - -### Phase 1: Create Header -- [ ] Create `src/util/check_return.h` -- [ ] Define macros (CHECK_RETURN_IF, CHECK_RETURN_BEGIN/END, WARN_IF) -- [ ] Add comprehensive documentation -- [ ] Add usage examples - -### Phase 2: Apply to Key Files -- [ ] `src/util/asset_manager.cc` (3 call sites) -- [ ] `src/test_demo.cc` (2 call sites) -- [ ] `src/gpu/texture_manager.cc` (1 call site) -- [ ] Test files as needed - -### Phase 3: Testing -- [ ] Verify normal build (messages appear) -- [ ] Verify STRIP_ALL build (messages stripped, checks remain) -- [ ] Verify all tests pass -- [ ] Check binary size impact - -### Phase 4: Documentation -- [ ] Update CONTRIBUTING.md with new patterns -- [ ] Add to HOWTO.md "Error Handling" section -- [ ] Update AI_RULES.md if needed - -## Alternative Considered: Don't Do It - -**Argument:** Only ~10-15 call sites, not worth adding complexity. - -**Counter-argument:** -- Improves consistency across codebase -- Reduces boilerplate (3-5 lines → 1-2 lines) -- Matches existing FATAL_XXX pattern (developers already familiar) -- Easy to strip for size optimization -- Low maintenance burden (simple macros) - -## Decision Point - -**Proceed?** User feedback needed: -1. Is the benefit worth the added complexity? -2. Should FINAL_STRIP strip these checks entirely? -3. Prefer Option A (simple), D (hybrid), or alternative? - -**If approved:** Implement Phase 1 first, review, then proceed with Phase 2-4. diff --git a/CHECK_RETURN_IMPLEMENTATION.md b/CHECK_RETURN_IMPLEMENTATION.md deleted file mode 100644 index 31c25ec..0000000 --- a/CHECK_RETURN_IMPLEMENTATION.md +++ /dev/null @@ -1,181 +0,0 @@ -# CHECK_RETURN Implementation Complete - -## Summary - -Implemented Option D (Hybrid) for non-fatal error handling with early return. - -## What Was Created - -### 1. Header File: `src/util/check_return.h` - -**Macros provided:** -```cpp -// Simple error check (90% of cases) -CHECK_RETURN_IF(condition, return_value, "Error: %s", msg); - -// Complex error check with cleanup (10% of cases) -CHECK_RETURN_BEGIN(condition, return_value) - // cleanup code - ERROR_MSG("Error: %s", msg); -CHECK_RETURN_END - -// Warning messages (non-fatal) -WARN_IF(condition, "Warning: %s", msg); -``` - -**Build behavior:** -- **Debug/Normal:** Full error messages with file:line info -- **STRIP_ALL:** Messages stripped, control flow preserved (saves ~1-2KB) - -## What Was Applied - -### 2. src/util/asset_manager.cc (3 call sites) - -**Before:** -```cpp -if (proc_gen_func_ptr == nullptr) { - fprintf(stderr, "Error: Unknown procedural function at runtime: %s\n", - source_record.proc_func_name_str); - if (out_size) *out_size = 0; - return nullptr; -} -``` - -**After:** -```cpp -CHECK_RETURN_BEGIN(proc_gen_func_ptr == nullptr, nullptr) - if (out_size) *out_size = 0; - ERROR_MSG("Unknown procedural function at runtime: %s", - source_record.proc_func_name_str); - return nullptr; -CHECK_RETURN_END -``` - -**Applied to:** -- Unknown procedural function check -- Memory allocation failure -- Procedural generation failure - -### 3. src/test_demo.cc (2 call sites) - -**Before:** -```cpp -if (i + 1 >= argc) { - fprintf(stderr, "Error: --log-peaks requires a filename argument\n\n"); - print_usage(argv[0]); - return 1; -} -``` - -**After:** -```cpp -CHECK_RETURN_BEGIN(i + 1 >= argc, 1) - print_usage(argv[0]); - ERROR_MSG("--log-peaks requires a filename argument\n"); - return 1; -CHECK_RETURN_END -``` - -**Applied to:** -- Missing --log-peaks argument -- Unknown command-line option - -## Testing - -✅ **All 31 tests pass** -``` -100% tests passed, 0 tests failed out of 31 -Total Test time (real) = 0.64 sec -``` - -✅ **Error messages work correctly:** -```bash -$ ./test_demo --invalid-option -Error: Unknown option '--invalid-option' - [test_demo.cc:199] -Usage: ./test_demo [OPTIONS] -... -``` - -## Comparison: FATAL_XXX vs CHECK_RETURN - -| Aspect | FATAL_XXX | CHECK_RETURN | -|--------|-----------|--------------| -| **Outcome** | `abort()` - program terminates | `return value` - caller handles | -| **Use Case** | Programming errors, invariants | Recoverable errors, validation | -| **Example** | Bounds checks, null dereference | Invalid input, missing files | -| **STRIP_ALL** | Keep checks, strip messages | Strip messages, keep checks | -| **FINAL_STRIP** | Strip everything (0 bytes) | Keep checks (preserves logic) | - -## Files Modified - -**Created:** -- `src/util/check_return.h` (180 lines) - -**Modified:** -- `src/util/asset_manager.cc` (+1 include, 3 call sites refactored) -- `src/test_demo.cc` (+1 include, 2 call sites refactored) - -## Size Impact - -**Estimated savings with STRIP_ALL:** -- 5 call sites × ~100 bytes per message string = ~500 bytes saved -- Control flow preserved (if-return statements kept) -- No functional changes - -## Remaining Opportunities - -Can be applied to (optional): -- `src/gpu/texture_manager.cc` - 1 call site -- `src/audio/backend/wav_dump_backend.cc` - 1 call site -- Test files - several call sites - -**Total potential:** ~10-15 call sites across codebase - -## Usage Guidelines - -### When to use CHECK_RETURN_IF: -✅ Simple validation with no cleanup -```cpp -CHECK_RETURN_IF(ptr == nullptr, false, "Invalid pointer"); -CHECK_RETURN_IF(size > MAX, -1, "Size too large: %d", size); -``` - -### When to use CHECK_RETURN_BEGIN/END: -✅ Validation that needs cleanup before return -```cpp -CHECK_RETURN_BEGIN(allocation_failed, nullptr) - delete[] buffer; - if (out_size) *out_size = 0; - ERROR_MSG("Allocation failed: %d bytes", size); - return nullptr; -CHECK_RETURN_END -``` - -### When to use WARN_IF: -✅ Non-critical issues that don't prevent execution -```cpp -WARN_IF(config_missing, "Config not found, using defaults"); -``` - -### When to use FATAL_XXX instead: -❌ Don't use CHECK_RETURN for: -- Programming errors (use FATAL_ASSERT) -- Array bounds violations (use FATAL_CHECK) -- Impossible code paths (use FATAL_UNREACHABLE) -- Corrupted state (use FATAL_ERROR) - -## Next Steps (Optional) - -1. Apply to remaining files (texture_manager.cc, wav_dump_backend.cc) -2. Update CONTRIBUTING.md with CHECK_RETURN guidelines -3. Update AI_RULES.md if needed -4. Consider FINAL_STRIP policy (strip checks vs keep checks) - -## Documentation - -Full documentation in header file: -- Usage examples -- Build mode behavior -- Comparison with FATAL_XXX -- Size impact analysis diff --git a/EFFECT_DEPTH_ANALYSIS.md b/EFFECT_DEPTH_ANALYSIS.md deleted file mode 100644 index 7a33baf..0000000 --- a/EFFECT_DEPTH_ANALYSIS.md +++ /dev/null @@ -1,115 +0,0 @@ -# Effect Depth Analysis Results - -## Overview -The `seq_compiler` tool now includes a `--analyze` flag to identify performance bottlenecks by analyzing how many effects are stacked (running simultaneously) at each moment in the demo. - -## Usage - -```bash -# Analyze effect stacking depth -./build/seq_compiler assets/demo.seq --analyze - -# Generate analysis with visual Gantt chart -./build/seq_compiler assets/demo.seq --analyze --gantt-html=timeline_analysis.html -``` - -## Current Demo Analysis (demo.seq) - -### Summary -- **Timeline duration**: 36s (65 beats @ 120 BPM) -- **Total effects**: 57 -- **Max concurrent effects**: 11 at t=8.8s ⚠️ -- **Bottleneck periods**: 28 time periods with >5 effects - -### Effect Depth Distribution - -| Depth | Time % | Severity | -|-------|--------|----------| -| 1-2 | 28.3% | Light | -| 3-4 | 26.1% | Moderate | -| 5-6 | 15.9% | Heavy | -| 7-11 | 29.6% | Critical ⚠️ | - -**Key Finding**: Nearly 30% of the demo has 7+ effects running simultaneously, which may cause frame rate drops on lower-end hardware. - -### Identified Bottlenecks - -**Most Critical Sections** (10+ concurrent effects): - -1. **t=4.3s** (10 effects): FlashCubeEffect, FadeEffect, ParticleSprayEffect, ParticlesEffect, GaussianBlurEffect, HeptagonEffect, ThemeModulationEffect, ChromaAberrationEffect, SolarizeEffect, FlashEffect -2. **t=8.6s** (10 effects): FlashCubeEffect, ThemeModulationEffect, ParticleSprayEffect, ParticlesEffect, Hybrid3DEffect, GaussianBlurEffect, ChromaAberrationEffect, HeptagonEffect, FlashEffect (×2) -3. **t=8.8s** (11 effects) - **Peak bottleneck** - -**Heavy Sections** (7-9 concurrent effects): -- t=4.9s: 9 effects -- t=6.7s: 9 effects -- t=7.3s: 7 effects -- t=7.9s: 8 effects -- t=9.1s: 9 effects -- ... and 18 more peaks - -### Recommendations - -**Immediate Actions**: -1. **Reduce overlap at t=8.8s**: The 11-effect peak is excessive. Consider: - - Staggering effect start/end times by 0.1-0.2s - - Removing redundant effects (e.g., duplicate FlashEffect instances) - - Combining similar effects (e.g., multiple GaussianBlur passes) - -2. **Optimize sequence at 8b-12b** (4-6 seconds): - - This section has sustained high effect counts (6-10 effects) - - Consider moving some post-processing effects to later sequences - - Test frame rate on target hardware during this section - -3. **Profile specific effects**: - - GaussianBlurEffect appears frequently (28 instances) - - Hybrid3DEffect runs for long durations (20s total) - - ChromaAberrationEffect appears in most bottleneck periods - -**Optimization Strategies**: -- Use priority layering to ensure critical effects render at higher priority -- Consider effect LOD (Level of Detail) system for lower-end hardware -- Combine multiple post-process passes into single shaders where possible -- Profile GPU time per effect to identify true bottlenecks (not just count) - -### Effect Frequency Analysis - -**Most Used Effects** (estimated from analysis output): -1. GaussianBlurEffect: ~10 instances -2. HeptagonEffect: ~9 instances -3. ThemeModulationEffect: ~7 instances -4. ChromaAberrationEffect: ~6 instances -5. FlashCubeEffect: ~6 instances -6. ParticleSprayEffect: ~5 instances -7. ParticlesEffect: ~5 instances -8. SolarizeEffect: ~4 instances -9. Hybrid3DEffect: ~3 instances -10. FadeEffect: ~2 instances - -**Insight**: GaussianBlur and Heptagon effects are the most heavily used - optimizing these will have the biggest impact on overall performance. - -## Technical Details - -### Analysis Method -- Samples timeline at 10 Hz (every 0.1s) -- Counts overlapping effect time ranges at each sample -- Generates histogram of effect depth distribution -- Identifies peaks (>5 effects) with at least 0.5s separation - -### Limitations -- Does not measure actual GPU/CPU time per effect (only counts) -- Assumes all effects have equal performance cost (not true in practice) -- Does not account for effect complexity (e.g., particle count, shader passes) - -### Future Improvements -- Add GPU profiling integration (Tracy, RenderDoc) -- Weight effects by estimated performance cost -- Suggest specific optimizations per bottleneck -- Generate timeline heatmap visualization -- Compare multiple .seq files (before/after optimization) - ---- - -**Generated**: February 8, 2026 -**Analysis Tool**: seq_compiler v1.1 (--analyze flag) -**Target**: assets/demo.seq diff --git a/PROJECT_CONTEXT.md b/PROJECT_CONTEXT.md index 181bffc..42cbda1 100644 --- a/PROJECT_CONTEXT.md +++ b/PROJECT_CONTEXT.md @@ -37,7 +37,7 @@ - 3D rendering: Hybrid SDF/rasterization with BVH acceleration and binary scene loader. Object data loading pipeline enhanced. - Asset pipeline: Blender export script and binary scene ingestion - Error handling: Dual macro system (`FATAL_XXX` for programming errors, `CHECK_RETURN` for recoverable errors) -- Testing: **32/33 tests passing (97%)** - DemoEffectsTest fails due to wgpu_native library bug +- Testing: **36/36 tests passing (100%)** - All tests operational --- @@ -55,6 +55,7 @@ - **Tooling & Optimization** - Task #54: Tracy Integration + - Task #76: External Library Size Measurement (Low priority) --- @@ -70,13 +71,17 @@ For detailed documentation, use Read tool to load specific docs: - **doc/SEQUENCE.md**: .seq timeline format with BPM notation - **doc/MASKING_SYSTEM.md**: Auxiliary texture registry - **doc/SCENE_FORMAT.md**: Binary scene format (SCN1) +- **doc/SIZE_MEASUREMENT.md**: External library size measurement strategy - **doc/test_demo_README.md**: 16s audio/visual sync test tool +- **doc/HOT_RELOAD.md**: Debug-only file change detection - **doc/CONTEXT_MAINTENANCE.md**: Context hygiene protocol --- ## Recently Completed (February 2026) +- **Hot-Reload File Watcher** (Feb 9) - Debug-only file change detection with `--hot-reload` flag. Watches config files (assets, sequences, music) and notifies on changes. 0 bytes overhead in release builds. + - **WGSL Uniform Buffer Validation (Task #75)** (Feb 9) - Standardized uniform buffer layout. Validation tool integrated into build. All effects use `CommonPostProcessUniforms` (binding 2) + effect-specific params (binding 3). Added `UNIFORM_BUFFER_GUIDELINES.md`. - **Uniform Buffer Alignment (Task #74)** (Feb 9) - Fixed WGSL `vec3<f32>` alignment issues. Demo runs with 0 validation errors. @@ -17,11 +17,13 @@ cmake --build build - `BUILD.md`: Instructions for building the project. - `CONTRIBUTING.md`: Guidelines for contributing, including coding style and commit policies. - `FETCH_DEPS.md`: Information on third-party dependencies. +- `HOT_RELOAD.md`: Debug-only file change detection system. - `HOWTO.md`: General instructions for running and interacting with the demo. - `PHASE2_COMPRESSION.md`: Details on future asset compression strategies. - `PROCEDURAL.md`: Details on the procedural texture generation system. - `PROJECT_CONTEXT.md`: A comprehensive overview of the project's architecture, features, and current state. - `SEQUENCE.md`: Complete reference for `.seq` file format and timeline system. +- `SIZE_MEASUREMENT.md`: Strategy for measuring core demo size vs external library overhead. - `SPEC_EDITOR.md`: Plans for a future web-based spectrogram editor tool. - `3D.md`: Overview of the 3D rendering pipeline and future enhancements. - `TODO.md`: A list of immediate, small-scale tasks. diff --git a/SHADER_REFACTOR_EXAMPLE.md b/SHADER_REFACTOR_EXAMPLE.md deleted file mode 100644 index c875c7e..0000000 --- a/SHADER_REFACTOR_EXAMPLE.md +++ /dev/null @@ -1,82 +0,0 @@ -# Shader Composability Improvement - -## Created: `math/common_utils.wgsl` - -Common utility functions to reduce duplication: -- `transform_normal()` - Normal matrix transform (was in 2 shaders) -- `spherical_uv()` - Spherical UV mapping (was in 7+ places) -- `spherical_uv_from_dir()` - For skybox/direction vectors -- `calc_sdf_normal_bumped()` - Bump-mapped normal (36 lines → 1 call) -- Constants: `PI`, `TAU` - -## Usage in Shaders - -### Bump Mapping Note: -`calc_sdf_normal_bumped()` was removed from common_utils - too specialized, depends on `get_dist()` from scene_query snippets. Keep bump mapping inline in shaders that use it. - -### Transform Normal (appears in renderer_3d.wgsl:174, mesh_render.wgsl:38): -```wgsl -// Before -let normal_matrix = mat3x3<f32>(obj.inv_model[0].xyz, obj.inv_model[1].xyz, obj.inv_model[2].xyz); -normal = normalize(normal_matrix * n_local); - -// After (if common_utils included) -normal = transform_normal(obj.inv_model, n_local); -``` - -### Spherical UV (appears 6x in renderer_3d.wgsl): -```wgsl -// Before -let uv = vec2<f32>(atan2(q.x, q.z) / 6.28 + 0.5, - acos(clamp(q.y / length(q), -1.0, 1.0)) / 3.14); - -// After -let uv = spherical_uv(q); -``` - -### Skybox (skybox.wgsl:41-42): -```wgsl -// Before -let u = atan2(ray_dir.z, ray_dir.x) / 6.28318 + 0.5; -let v = asin(clamp(ray_dir.y, -1.0, 1.0)) / 3.14159 + 0.5; - -// After -let uv = spherical_uv_from_dir(ray_dir); -``` - -## Integration with ShaderComposer - -ShaderComposer already supports `#include` directives. To use: - -```cpp -// In gpu/effects initialization -ShaderComposer::Get().RegisterSnippet( - "math/common_utils", - GetAssetString(AssetId::SHADER_MATH_COMMON_UTILS) -); - -// In shader composition -composer.Compose( - {"common_uniforms", "math/common_utils"}, - main_shader_code -); -``` - -## Size Impact - -**Estimated binary size change:** -- Add common_utils.wgsl: +800 bytes -- Remove duplication: -1500 bytes (6 spherical_uv + bump mapping) -- **Net savings: ~700 bytes** - -Plus improved maintainability and consistency. - -## Next Steps - -1. Refactor `renderer_3d.wgsl` to use new utilities -2. Refactor `skybox.wgsl` to use `spherical_uv_from_dir()` -3. Refactor `mesh_render.wgsl` to use `transform_normal()` -4. Consider extracting more patterns: - - Grid pattern (appears 2x) - - Light direction constant - - Ray-box intersection variants @@ -117,6 +117,7 @@ class ComposedShader { - [ ] **Task #22: Windows Native Platform** - Replace GLFW with Win32 API - [ ] **Task #28: Spectrogram Quantization** - Research optimal frequency distribution - [ ] **Task #35: CRT Replacement** - Investigation and implementation of CRT-free entry +- [ ] **Task #76: External Library Size Measurement** - STRIP_EXTERNAL_LIBS mode with stub implementations to measure core demo size vs external library overhead. See `doc/SIZE_MEASUREMENT.md`. --- diff --git a/WGSL_REFACTOR_COMPLETE.md b/WGSL_REFACTOR_COMPLETE.md deleted file mode 100644 index 9bdc73c..0000000 --- a/WGSL_REFACTOR_COMPLETE.md +++ /dev/null @@ -1,191 +0,0 @@ -# WGSL Shader Composability Refactor - Complete - -## Summary - -Improved shader code reusability by extracting common patterns into `math/common_utils.wgsl`. - -## Changes Made - -### 1. Created `math/common_utils.wgsl` (37 lines) - -**Functions:** -- `transform_normal(inv_model, normal_local)` - Normal matrix transform -- `spherical_uv(p)` - Spherical UV mapping for positions -- `spherical_uv_from_dir(dir)` - Spherical UV for direction vectors -- `grid_pattern(uv)` - Procedural checkerboard pattern - -**Constants:** -- `PI = 3.14159265359` -- `TAU = 6.28318530718` - -### 2. Refactored Shaders - -#### renderer_3d.wgsl (200 → 197 lines) -- **7x spherical_uv()** - Replaced atan2/acos calculations - - Lines 143, 148, 153, 158, 163, 168, 184 - - Before: `vec2<f32>(atan2(...) / 6.28 + 0.5, acos(...) / 3.14)` - - After: `spherical_uv(q)` -- **1x transform_normal()** - Replaced mat3x3 constructor - - Line 174-175 - - Before: 3 lines (mat3x3 constructor + normalize) - - After: 1 line -- **2x grid_pattern()** - Replaced sin/smoothstep pattern - - Lines 104-106, 179-181 - - Before: 3 lines each - - After: 1 line each - -**Savings: ~12 lines removed** - -#### mesh_render.wgsl (58 → 57 lines) -- **1x transform_normal()** - Replaced mat3x3 constructor - - Line 38-39 - - Before: 3 lines - - After: 1 line - -**Savings: ~3 lines removed** - -#### skybox.wgsl (44 → 42 lines) -- **1x spherical_uv_from_dir()** - Replaced atan2/asin calculations - - Line 41-42 - - Before: 2 lines - - After: 1 line - -**Savings: ~2 lines removed** - -### 3. Registered Snippet - -Added to `src/gpu/effects/shaders.cc`: -```cpp -register_if_exists("math/common_utils", AssetId::ASSET_SHADER_MATH_COMMON_UTILS); -``` - -### 4. Updated Asset Manifest - -Added to `assets/final/demo_assets.txt`: -``` -SHADER_MATH_COMMON_UTILS, NONE, shaders/math/common_utils.wgsl, "Common Math Utils" -``` - -## Impact Analysis - -### Code Reduction -- **Duplication removed:** ~17 lines across 3 shaders -- **Common utils added:** +37 lines (1 file) -- **Net change:** +20 lines total - -### Usage Statistics -- **12 call sites** now use common utilities -- **3 shaders** refactored -- **4 utility functions** + 2 constants - -### Binary Size (estimated) -- Common utils: +400 bytes (compiled WGSL) -- Removed duplication: -600 bytes (7 spherical UV + 2 grid + 2 normal) -- **Net savings: ~200 bytes** - -Plus improved maintainability and consistency. - -### Maintainability Benefits -1. **Single source of truth** - UV/normal calculations in one place -2. **Consistent precision** - All shaders use same PI/TAU constants -3. **Easier debugging** - Fix bugs once, all shaders benefit -4. **Future-proof** - New shaders can reuse utilities immediately - -## Test Results - -✅ **All 31 tests pass** (100%) - -Including: -- ShaderComposerTest (composition logic) -- 3D renderer tests (no crashes) -- Shader compilation tests -- Full test suite - -## Files Modified - -**New:** -- `assets/final/shaders/math/common_utils.wgsl` -- `SHADER_REFACTOR_EXAMPLE.md` (design doc) -- `WGSL_REFACTOR_COMPLETE.md` (this file) - -**Modified:** -- `assets/final/shaders/renderer_3d.wgsl` -- `assets/final/shaders/mesh_render.wgsl` -- `assets/final/shaders/skybox.wgsl` -- `assets/final/demo_assets.txt` -- `src/gpu/effects/shaders.cc` - -## Next Opportunities - -### Low-Hanging Fruit -- **Light direction constant** - Appears in multiple shaders as `vec3<f32>(1.0, 1.0, 1.0)` -- **Ray-box intersection variants** - Could extract common helpers -- **Noise sampling patterns** - Consistent noise lookup utilities - -### Medium Effort -- **SDF operations** - Union, subtraction, intersection (if repeated) -- **Color grading helpers** - Tone mapping, gamma correction -- **Distance fog** - Common atmospheric effects - -### Advanced -- **Material system** - PBR lighting utilities -- **Shadow mapping helpers** - Cascaded shadow map utilities -- **Post-process chain** - Common blur/sharpen kernels - -## Design Decisions - -### Why Not Include calc_sdf_normal_bumped()? -- Too specialized - depends on `get_dist()` from scene_query -- Scene_query not always included when common_utils is -- Caused shader compilation failure (missing `get_dist` identifier) -- **Solution:** Keep bump mapping inline in shaders that need it - -### Why Separate spherical_uv() and spherical_uv_from_dir()? -- Different use cases: positions vs directions -- Different math: acos vs asin for elevation -- skybox needs direction variant, SDF rendering needs position variant -- Clearer intent in calling code - -### Why Include grid_pattern()? -- Appeared 2x in renderer_3d.wgsl (copy-paste pattern) -- Simple, self-contained (no external dependencies) -- Reusable for other procedural textures - -## Verification Commands - -```bash -# Regenerate assets -./scripts/gen_assets.sh - -# Build -cmake --build build -j4 - -# Test -cd build && ctest - -# Check shader composition -./test_shader_composer - -# Verify 3D rendering -./test_3d_render -``` - -## Commit Message - -``` -feat(shaders): Extract common WGSL utilities for better composability - -- Create math/common_utils.wgsl with 4 utility functions -- Refactor renderer_3d.wgsl (7 spherical_uv, 1 normal, 2 grid) -- Refactor mesh_render.wgsl (1 normal transform) -- Refactor skybox.wgsl (1 spherical UV) -- Register common_utils snippet in ShaderComposer - -Impact: ~200 bytes saved, 12 call sites deduplicated -Tests: 31/31 passing -``` - ---- - -**Status:** ✅ Complete - Ready for commit -**Date:** 2026-02-08 diff --git a/convert_track.py b/convert_track.py deleted file mode 100644 index ec9d62c..0000000 --- a/convert_track.py +++ /dev/null @@ -1,77 +0,0 @@ -#!/usr/bin/env python3 -"""Convert .track files from beat-based to unit-less timing.""" - -import re -import sys - -def convert_beat_to_unit(beat_str): - """Convert beat value to unit-less (beat / 4).""" - beat = float(beat_str) - unit = beat / 4.0 - return f"{unit:.2f}" - -def process_line(line): - """Process a single line, converting beat values.""" - line = line.rstrip('\n') - - # Skip comments and empty lines - if not line.strip() or line.strip().startswith('#'): - return line - - # PATTERN line - add LENGTH 1.0 - if line.strip().startswith('PATTERN '): - # Check if LENGTH already exists - if ' LENGTH ' in line: - return line - parts = line.split() - if len(parts) >= 2: - return f"PATTERN {parts[1]} LENGTH 1.0" - return line - - # Event line (starts with a number) - match = re.match(r'^(\s*)([0-9.]+),\s*(.+)$', line) - if match: - indent, beat_str, rest = match.groups() - unit_str = convert_beat_to_unit(beat_str) - return f"{indent}{unit_str}, {rest}" - - return line - -def main(): - if len(sys.argv) != 3: - print(f"Usage: {sys.argv[0]} <input.track> <output.track>") - sys.exit(1) - - input_file = sys.argv[1] - output_file = sys.argv[2] - - with open(input_file, 'r') as f: - lines = f.readlines() - - # Add header comment about timing - output_lines = [] - output_lines.append("# Enhanced Demo Track - Progressive buildup with varied percussion\n") - output_lines.append("# Features acceleration/deceleration with diverse samples and melodic progression\n") - output_lines.append("#\n") - output_lines.append("# TIMING: Unit-less (1 unit = 4 beats at 120 BPM = 2 seconds)\n") - output_lines.append("# Pattern events use unit-less time (0.0-1.0 for 4-beat pattern)\n") - output_lines.append("# Score triggers use unit-less time\n") - - # Skip old header lines - start_idx = 0 - for i, line in enumerate(lines): - if line.strip() and not line.strip().startswith('#'): - start_idx = i - break - - # Process rest of file - for line in lines[start_idx:]: - output_lines.append(process_line(line) + '\n') - - with open(output_file, 'w') as f: - f.writelines(output_lines) - - print(f"Converted {input_file} -> {output_file}") - -if __name__ == '__main__': - main() diff --git a/doc/HOWTO.md b/doc/HOWTO.md index 876d7dc..c6e4e79 100644 --- a/doc/HOWTO.md +++ b/doc/HOWTO.md @@ -13,10 +13,16 @@ cmake --build build -j4 ./build/demo64k ``` -Options: `--fullscreen`, `--resolution WxH`, `--seek TIME` (debug only) +Options: `--fullscreen`, `--resolution WxH`, `--seek TIME`, `--hot-reload` (debug only) Keyboard: `Esc` (exit), `F` (toggle fullscreen) +**Hot-Reload Mode:** +```bash +./build/demo64k --hot-reload +``` +Watches config files and notifies when changes detected. See `doc/HOT_RELOAD.md`. + ### Size-Optimized Build ```bash cmake -S . -B build -DDEMO_SIZE_OPT=ON diff --git a/doc/SIZE_MEASUREMENT.md b/doc/SIZE_MEASUREMENT.md new file mode 100644 index 0000000..b997ea5 --- /dev/null +++ b/doc/SIZE_MEASUREMENT.md @@ -0,0 +1,341 @@ +# External Library Size Measurement (Task #76) + +## Goal + +Measure the true binary size of demo code vs external library overhead by stubbing out all system dependencies with minimal implementations. + +**Motivation:** Current STRIP_ALL builds include wgpu_native (~3-4MB), GLFW, and system libraries. We need to isolate core demo size to: +- Track size budget accurately +- Identify optimization targets +- Measure progress toward 64KB goal + +## Problem Statement + +Current size breakdown is opaque: +``` +STRIP_ALL build: ~5.1MB total +├── wgpu_native: ~3-4MB (estimate) +├── GLFW: ~500KB (estimate) +├── System libs: ~500KB (estimate) +└── Demo code: ??? KB (unknown) +``` + +**Goal:** Measure demo code size precisely by replacing external dependencies with stubs. + +## Approach + +Create `STRIP_EXTERNAL_LIBS` build mode: +- Replaces real libraries with minimal stub implementations +- Functions compile but do nothing (return nullptr, no-ops) +- Binary compiles successfully but **does not run** +- Purpose: Size measurement only + +## Architecture + +### Stub Implementations + +#### 1. wgpu_native Stubs (`third_party/stubs/wgpu_native.c`) + +Minimal WebGPU API implementation: +```c +// All functions return nullptr or void +WGPUInstance wgpuCreateInstance(const WGPUInstanceDescriptor* desc) { + (void)desc; + return (WGPUInstance)0; +} + +WGPUAdapter wgpuInstanceRequestAdapter(...) { + return (WGPUAdapter)0; +} + +// ~200 functions to stub +``` + +**Strategy:** +- Parse `webgpu.h` to generate stubs automatically +- All pointers return nullptr +- All numeric returns = 0 +- All void functions are empty + +#### 2. GLFW Stubs (`third_party/stubs/glfw3.c`) + +Window/input API stubs: +```c +int glfwInit(void) { return 1; } +void glfwTerminate(void) {} +GLFWwindow* glfwCreateWindow(...) { return (GLFWwindow*)0; } +void glfwPollEvents(void) {} +int glfwWindowShouldClose(GLFWwindow* w) { (void)w; return 0; } +// ~100 functions +``` + +#### 3. miniaudio Stubs (`third_party/stubs/miniaudio.c`) + +Audio backend stubs: +```c +ma_result ma_context_init(...) { return MA_SUCCESS; } +ma_result ma_device_init(...) { return MA_SUCCESS; } +ma_result ma_device_start(...) { return MA_SUCCESS; } +void ma_device_stop(...) {} +void ma_device_uninit(...) {} +// ~50 functions used by demo +``` + +#### 4. System Library Stubs + +**pthread:** +```c +int pthread_create(...) { return 0; } +int pthread_join(...) { return 0; } +int pthread_mutex_init(...) { return 0; } +// ~20 functions +``` + +**Math (libm):** +```c +double sin(double x) { (void)x; return 0.0; } +double cos(double x) { (void)x; return 0.0; } +float sinf(float x) { (void)x; return 0.0f; } +// ~30 functions +``` + +**Platform-specific (macOS):** +```c +// Stub Metal/Foundation/Cocoa frameworks +// Minimal Objective-C stubs if needed +``` + +### Build System Integration + +**CMakeLists.txt:** +```cmake +if(DEMO_STRIP_EXTERNAL_LIBS) + # Build stub libraries + add_library(wgpu_stub STATIC third_party/stubs/wgpu_native.c) + add_library(glfw_stub STATIC third_party/stubs/glfw3.c) + add_library(miniaudio_stub STATIC third_party/stubs/miniaudio.c) + add_library(system_stub STATIC third_party/stubs/system.c) + + # Override library targets + set(DEMO_LIBS wgpu_stub glfw_stub miniaudio_stub system_stub) + + # Minimal flags (no LTO to see unoptimized size) + set(CMAKE_C_FLAGS "-Os") + set(CMAKE_CXX_FLAGS "-Os") +endif() +``` + +**Build command:** +```bash +cmake -S . -B build_size -DDEMO_STRIP_EXTERNAL_LIBS=ON +cmake --build build_size -j4 +ls -lh build_size/demo64k # True demo size +``` + +## Implementation Plan + +### Phase 1: Stub Generation Tool + +**Script: `scripts/generate_stubs.py`** + +Parses header files and generates stub implementations: +```python +# Parse webgpu.h, glfw3.h, miniaudio.h +# Extract function signatures +# Generate minimal implementations +# Output to third_party/stubs/ +``` + +**Input:** Header files with annotations +**Output:** Complete stub .c files + +**Heuristics:** +- Pointer returns → nullptr +- Numeric returns → 0 +- Boolean returns → true/1 +- Void functions → empty body +- Enum returns → first enum value + +### Phase 2: Build System Integration + +1. Add `DEMO_STRIP_EXTERNAL_LIBS` option +2. Create stub library targets +3. Override `DEMO_LIBS` when flag enabled +4. Disable features requiring runtime (audio playback, window creation) + +### Phase 3: Compatibility Layer + +Some demo code expects valid objects. Add thin shims: + +**`src/platform/stub_platform.cc`:** +```cpp +#if defined(STRIP_EXTERNAL_LIBS) +// Override platform_init to skip GLFW +PlatformState platform_init(bool, int, int) { + PlatformState state = {}; + state.width = 1280; + state.height = 720; + return state; +} +#endif +``` + +**`src/audio/stub_audio.cc`:** +```cpp +#if defined(STRIP_EXTERNAL_LIBS) +// Override audio_init to skip miniaudio +void audio_init() {} +void audio_start() {} +#endif +``` + +### Phase 4: Validation + +Ensure binary compiles and links: +```bash +# Should compile successfully +cmake -S . -B build_size -DDEMO_STRIP_EXTERNAL_LIBS=ON +cmake --build build_size -j4 + +# Size measurement +ls -lh build_size/demo64k + +# Strip debug symbols +strip build_size/demo64k +ls -lh build_size/demo64k + +# Compare with real build +ls -lh build_strip/demo64k +``` + +## Expected Results + +**Size Breakdown (Projected):** +``` +Real STRIP_ALL build: 5.1 MB +├── wgpu_native: 3.5 MB (68%) +├── GLFW: 0.5 MB (10%) +├── System libs: 0.6 MB (12%) +└── Demo code: 0.5 MB (10%) + +STRIP_EXTERNAL_LIBS build: 0.5 MB +└── Demo code only: 0.5 MB (100%) +``` + +**Validation:** +- Binary compiles without errors +- All symbols resolve +- Size is significantly smaller (< 1MB) +- Binary does NOT run (expected) + +## Use Cases + +### 1. Size Budget Tracking +```bash +# Weekly measurement +./scripts/measure_size.sh +# Outputs: Demo=512KB, External=4.5MB +``` + +### 2. Optimization Targeting +Identify which subsystems contribute most to demo size: +- GPU effects: 150KB +- 3D rendering: 120KB +- Audio synthesis: 100KB +- Asset system: 80KB + +### 3. CI Integration +Track size growth over time: +```yaml +# .github/workflows/size_check.yml +- name: Measure demo size + run: | + cmake -B build_size -DDEMO_STRIP_EXTERNAL_LIBS=ON + size=$(stat -f%z build_size/demo64k) + echo "Demo size: $size bytes" +``` + +## Trade-offs + +### Pros +- Accurate size measurement +- Identifies optimization targets +- Simple implementation (stubs) +- No runtime required + +### Cons +- Maintenance burden (stubs must match real APIs) +- Binary doesn't run (testing impossible) +- Platform-specific stubbing (macOS frameworks complex) +- Stub generation tool needs maintenance + +### Alternative Approaches + +**1. Link-time size analysis:** +```bash +# Use linker map to attribute size +-Wl,-map,output.map +# Parse map file to see per-symbol sizes +``` +**Pro:** No stubs needed +**Con:** Complex parsing, less accurate + +**2. Binary diff analysis:** +```bash +# Build with/without each library +# Diff binary sizes +``` +**Pro:** Simpler +**Con:** Doesn't isolate demo code cleanly + +**3. Compiler size reports:** +```bash +-ffunction-sections -fdata-sections +-Wl,--print-gc-sections +``` +**Pro:** Built-in tooling +**Con:** Still includes external library overhead + +**Chosen:** Stub approach (most accurate, clear results) + +## Implementation Effort + +**Estimated time: 8-12 hours** + +- Phase 1: Stub generation (3-4 hours) +- Phase 2: Build integration (2-3 hours) +- Phase 3: Compatibility layer (2-3 hours) +- Phase 4: Validation & documentation (1-2 hours) + +**Priority:** Low (measurement tool, not critical for demo) + +## Success Criteria + +1. Binary compiles successfully with `STRIP_EXTERNAL_LIBS=ON` +2. Size < 1MB (proves external lib overhead is measured) +3. Repeatable builds produce consistent sizes +4. Can track size changes over time +5. Documentation clear for future use + +## Related Files + +**New files:** +- `third_party/stubs/wgpu_native.c` - WebGPU stubs (~200 functions) +- `third_party/stubs/glfw3.c` - GLFW stubs (~100 functions) +- `third_party/stubs/miniaudio.c` - Audio stubs (~50 functions) +- `third_party/stubs/system.c` - System library stubs (pthread, math) +- `scripts/generate_stubs.py` - Stub generation tool +- `scripts/measure_size.sh` - Size measurement script + +**Modified files:** +- `CMakeLists.txt` - Add STRIP_EXTERNAL_LIBS mode +- `src/platform/platform.cc` - Conditional stub overrides +- `src/audio/audio.cc` - Conditional stub overrides + +## Notes + +- This is a **measurement tool**, not a runtime mode +- Binary will not execute (all APIs stubbed) +- Useful for tracking optimization progress +- Can reveal surprising size contributors +- Platform-specific (stubs may differ on Windows/Linux/macOS) diff --git a/timeline_analysis.html b/timeline_analysis.html deleted file mode 100644 index 92e4a3f..0000000 --- a/timeline_analysis.html +++ /dev/null @@ -1,425 +0,0 @@ -<!DOCTYPE html> -<html> -<head> -<meta charset="UTF-8"> -<title>Demo Timeline - BPM 120</title> -<style> -body { font-family: 'Courier New', monospace; margin: 20px; background: #1e1e1e; color: #d4d4d4; } -h1 { color: #569cd6; } -.info { background: #252526; padding: 10px; border-radius: 4px; margin: 10px 0; } -svg { background: #252526; border-radius: 4px; } -.sequence-bar { fill: #3a3a3a; stroke: #569cd6; stroke-width: 2; } -.effect-bar { fill: #4ec9b0; opacity: 0.8; stroke: #2a7a6a; stroke-width: 1; } -.effect-bar.invalid { fill: #f48771; stroke: #d16969; } -.label { fill: #d4d4d4; font-size: 12px; } -.label.effect { fill: #cccccc; font-size: 11px; } -.axis-line { stroke: #6a6a6a; stroke-width: 1; } -.axis-label { fill: #858585; font-size: 10px; } -.time-marker { stroke: #444444; stroke-width: 1; stroke-dasharray: 2,2; } -rect:hover { opacity: 1; } -title { font-size: 11px; } -</style> -</head> -<body> -<h1>Demo Timeline Gantt Chart</h1> -<div class="info"> -<strong>BPM:</strong> 120 | <strong>Duration:</strong> 36s (explicit end) | <strong>Sequences:</strong> 14 -</div> - -<svg width="1400" height="2230" xmlns="http://www.w3.org/2000/svg"> - <!-- Time axis --> - <line x1="250" y1="50" x2="1350" y2="50" class="axis-line"/> - <line x1="250" y1="45" x2="250" y2="55" class="axis-line"/> - <text x="250" y="40" class="axis-label" text-anchor="middle">0s</text> - <line x1="250" y1="60" x2="250" y2="2210" class="time-marker"/> - <line x1="311" y1="45" x2="311" y2="55" class="axis-line"/> - <text x="311" y="40" class="axis-label" text-anchor="middle">2s</text> - <line x1="311" y1="60" x2="311" y2="2210" class="time-marker"/> - <line x1="372" y1="45" x2="372" y2="55" class="axis-line"/> - <text x="372" y="40" class="axis-label" text-anchor="middle">4s</text> - <line x1="372" y1="60" x2="372" y2="2210" class="time-marker"/> - <line x1="433" y1="45" x2="433" y2="55" class="axis-line"/> - <text x="433" y="40" class="axis-label" text-anchor="middle">6s</text> - <line x1="433" y1="60" x2="433" y2="2210" class="time-marker"/> - <line x1="494" y1="45" x2="494" y2="55" class="axis-line"/> - <text x="494" y="40" class="axis-label" text-anchor="middle">8s</text> - <line x1="494" y1="60" x2="494" y2="2210" class="time-marker"/> - <line x1="555" y1="45" x2="555" y2="55" class="axis-line"/> - <text x="555" y="40" class="axis-label" text-anchor="middle">10s</text> - <line x1="555" y1="60" x2="555" y2="2210" class="time-marker"/> - <line x1="616" y1="45" x2="616" y2="55" class="axis-line"/> - <text x="616" y="40" class="axis-label" text-anchor="middle">12s</text> - <line x1="616" y1="60" x2="616" y2="2210" class="time-marker"/> - <line x1="677" y1="45" x2="677" y2="55" class="axis-line"/> - <text x="677" y="40" class="axis-label" text-anchor="middle">14s</text> - <line x1="677" y1="60" x2="677" y2="2210" class="time-marker"/> - <line x1="738" y1="45" x2="738" y2="55" class="axis-line"/> - <text x="738" y="40" class="axis-label" text-anchor="middle">16s</text> - <line x1="738" y1="60" x2="738" y2="2210" class="time-marker"/> - <line x1="800" y1="45" x2="800" y2="55" class="axis-line"/> - <text x="800" y="40" class="axis-label" text-anchor="middle">18s</text> - <line x1="800" y1="60" x2="800" y2="2210" class="time-marker"/> - <line x1="861" y1="45" x2="861" y2="55" class="axis-line"/> - <text x="861" y="40" class="axis-label" text-anchor="middle">20s</text> - <line x1="861" y1="60" x2="861" y2="2210" class="time-marker"/> - <line x1="922" y1="45" x2="922" y2="55" class="axis-line"/> - <text x="922" y="40" class="axis-label" text-anchor="middle">22s</text> - <line x1="922" y1="60" x2="922" y2="2210" class="time-marker"/> - <line x1="983" y1="45" x2="983" y2="55" class="axis-line"/> - <text x="983" y="40" class="axis-label" text-anchor="middle">24s</text> - <line x1="983" y1="60" x2="983" y2="2210" class="time-marker"/> - <line x1="1044" y1="45" x2="1044" y2="55" class="axis-line"/> - <text x="1044" y="40" class="axis-label" text-anchor="middle">26s</text> - <line x1="1044" y1="60" x2="1044" y2="2210" class="time-marker"/> - <line x1="1105" y1="45" x2="1105" y2="55" class="axis-line"/> - <text x="1105" y="40" class="axis-label" text-anchor="middle">28s</text> - <line x1="1105" y1="60" x2="1105" y2="2210" class="time-marker"/> - <line x1="1166" y1="45" x2="1166" y2="55" class="axis-line"/> - <text x="1166" y="40" class="axis-label" text-anchor="middle">30s</text> - <line x1="1166" y1="60" x2="1166" y2="2210" class="time-marker"/> - <line x1="1227" y1="45" x2="1227" y2="55" class="axis-line"/> - <text x="1227" y="40" class="axis-label" text-anchor="middle">32s</text> - <line x1="1227" y1="60" x2="1227" y2="2210" class="time-marker"/> - <line x1="1288" y1="45" x2="1288" y2="55" class="axis-line"/> - <text x="1288" y="40" class="axis-label" text-anchor="middle">34s</text> - <line x1="1288" y1="60" x2="1288" y2="2210" class="time-marker"/> - <line x1="1350" y1="45" x2="1350" y2="55" class="axis-line"/> - <text x="1350" y="40" class="axis-label" text-anchor="middle">36s</text> - <line x1="1350" y1="60" x2="1350" y2="2210" class="time-marker"/> - <!-- Sequence --> - <rect x="250" y="60" width="61" height="30" class="sequence-bar"> - <title>SEQ@0s [pri=0] (0-2s)</title> - </rect> - <text x="10" y="79" class="label">SEQ@0s [pri=0]</text> - <rect x="256" y="95" width="39" height="20" class="effect-bar"> - <title>FlashCubeEffect [pri=-1] (0.2-1.5s)</title> - </rect> - <text x="20" y="110" class="label effect">FlashCubeEffect [pri=-1]</text> - <rect x="250" y="125" width="30" height="20" class="effect-bar"> - <title>FlashEffect [pri=0] (0-1s)</title> - </rect> - <text x="20" y="140" class="label effect">FlashEffect [pri=0]</text> - <rect x="253" y="155" width="27" height="20" class="effect-bar"> - <title>FadeEffect [pri=1] (0.1-1s)</title> - </rect> - <text x="20" y="170" class="label effect">FadeEffect [pri=1]</text> - <rect x="250" y="185" width="61" height="20" class="effect-bar"> - <title>SolarizeEffect [pri=2] (0-2s)</title> - </rect> - <text x="20" y="200" class="label effect">SolarizeEffect [pri=2]</text> - <!-- Separator --> - <line x1="250" y1="215" x2="1350" y2="215" style="stroke:#444444; stroke-width:1; stroke-dasharray:4,2;"/> - <!-- Sequence --> - <rect x="311" y="220" width="91" height="30" class="sequence-bar"> - <title>SEQ@2s [pri=0] (2-5s)</title> - </rect> - <text x="10" y="239" class="label">SEQ@2s [pri=0]</text> - <rect x="314" y="255" width="88" height="20" class="effect-bar"> - <title>FlashCubeEffect [pri=-1] (2.1-5s)</title> - </rect> - <text x="20" y="270" class="label effect">FlashCubeEffect [pri=-1]</text> - <rect x="311" y="285" width="6" height="20" class="effect-bar"> - <title>FlashEffect [pri=0] (2-2.2s)</title> - </rect> - <text x="20" y="300" class="label effect">FlashEffect [pri=0]</text> - <!-- Separator --> - <line x1="250" y1="315" x2="1350" y2="315" style="stroke:#444444; stroke-width:1; stroke-dasharray:4,2;"/> - <!-- Sequence --> - <rect x="341" y="320" width="122" height="30" class="sequence-bar"> - <title>SEQ@3s [pri=1] (3-7s)</title> - </rect> - <text x="10" y="339" class="label">SEQ@3s [pri=1]</text> - <rect x="341" y="355" width="61" height="20" class="effect-bar"> - <title>ParticleSprayEffect [pri=0] (3-5s)</title> - </rect> - <text x="20" y="370" class="label effect">ParticleSprayEffect [pri=0]</text> - <rect x="341" y="385" width="61" height="20" class="effect-bar"> - <title>ParticlesEffect [pri=1] (3-5s)</title> - </rect> - <text x="20" y="400" class="label effect">ParticlesEffect [pri=1]</text> - <rect x="341" y="415" width="122" height="20" class="effect-bar"> - <title>GaussianBlurEffect [pri=1] (3-7s)</title> - </rect> - <text x="20" y="430" class="label effect">GaussianBlurEffect [pri=1]</text> - <!-- Separator --> - <line x1="250" y1="445" x2="1350" y2="445" style="stroke:#444444; stroke-width:1; stroke-dasharray:4,2;"/> - <!-- Sequence --> - <rect x="356" y="450" width="31" height="30" class="sequence-bar"> - <title>SEQ@3.5s [pri=0] (3.5-4.5s)</title> - </rect> - <text x="10" y="469" class="label">SEQ@3.5s [pri=0]</text> - <rect x="356" y="485" width="7" height="20" class="effect-bar"> - <title>HeptagonEffect [pri=0] (3.5-3.7s)</title> - </rect> - <text x="20" y="500" class="label effect">HeptagonEffect [pri=0]</text> - <rect x="360" y="515" width="27" height="20" class="effect-bar"> - <title>FadeEffect [pri=1] (3.6-4.5s)</title> - </rect> - <text x="20" y="530" class="label effect">FadeEffect [pri=1]</text> - <!-- Separator --> - <line x1="250" y1="545" x2="1350" y2="545" style="stroke:#444444; stroke-width:1; stroke-dasharray:4,2;"/> - <!-- Sequence --> - <rect x="372" y="550" width="153" height="30" class="sequence-bar"> - <title>SEQ@4s [pri=3] (4-9s)</title> - </rect> - <text x="10" y="569" class="label">SEQ@4s [pri=3]</text> - <rect x="372" y="585" width="61" height="20" class="effect-bar"> - <title>ThemeModulationEffect [pri=0] (4-6s)</title> - </rect> - <text x="20" y="600" class="label effect">ThemeModulationEffect [pri=0]</text> - <rect x="372" y="615" width="122" height="20" class="effect-bar"> - <title>HeptagonEffect [pri=0] (4-8s)</title> - </rect> - <text x="20" y="630" class="label effect">HeptagonEffect [pri=0]</text> - <rect x="372" y="645" width="122" height="20" class="effect-bar"> - <title>GaussianBlurEffect [pri=1] (4-8s)</title> - </rect> - <text x="20" y="660" class="label effect">GaussianBlurEffect [pri=1]</text> - <rect x="372" y="675" width="91" height="20" class="effect-bar"> - <title>ChromaAberrationEffect [pri=2] (4-7s)</title> - </rect> - <text x="20" y="690" class="label effect">ChromaAberrationEffect [pri=2]</text> - <rect x="372" y="705" width="153" height="20" class="effect-bar"> - <title>SolarizeEffect [pri=3] (4-9s)</title> - </rect> - <text x="20" y="720" class="label effect">SolarizeEffect [pri=3]</text> - <!-- Separator --> - <line x1="250" y1="735" x2="1350" y2="735" style="stroke:#444444; stroke-width:1; stroke-dasharray:4,2;"/> - <!-- Sequence --> - <rect x="433" y="740" width="61" height="30" class="sequence-bar"> - <title>SEQ@6s [pri=2] (6-8s)</title> - </rect> - <text x="10" y="759" class="label">SEQ@6s [pri=2]</text> - <rect x="439" y="775" width="40" height="20" class="effect-bar"> - <title>FlashCubeEffect [pri=-1] (6.2-7.5s)</title> - </rect> - <text x="20" y="790" class="label effect">FlashCubeEffect [pri=-1]</text> - <rect x="433" y="805" width="61" height="20" class="effect-bar"> - <title>HeptagonEffect [pri=0] (6-8s)</title> - </rect> - <text x="20" y="820" class="label effect">HeptagonEffect [pri=0]</text> - <rect x="433" y="835" width="61" height="20" class="effect-bar"> - <title>ParticleSprayEffect [pri=1] (6-8s)</title> - </rect> - <text x="20" y="850" class="label effect">ParticleSprayEffect [pri=1]</text> - <rect x="433" y="865" width="61" height="20" class="effect-bar"> - <title>ParticlesEffect [pri=2] (6-8s)</title> - </rect> - <text x="20" y="880" class="label effect">ParticlesEffect [pri=2]</text> - <!-- Separator --> - <line x1="250" y1="895" x2="1350" y2="895" style="stroke:#444444; stroke-width:1; stroke-dasharray:4,2;"/> - <!-- Sequence --> - <rect x="479" y="900" width="46" height="30" class="sequence-bar"> - <title>SEQ@7.5s [pri=2] (7.5-9s)</title> - </rect> - <text x="10" y="919" class="label">SEQ@7.5s [pri=2]</text> - <rect x="485" y="935" width="40" height="20" class="effect-bar"> - <title>FlashCubeEffect [pri=-1] (7.7-9s)</title> - </rect> - <text x="20" y="950" class="label effect">FlashCubeEffect [pri=-1]</text> - <rect x="479" y="965" width="15" height="20" class="effect-bar"> - <title>FlashEffect [pri=0] (7.5-8s)</title> - </rect> - <text x="20" y="980" class="label effect">FlashEffect [pri=0]</text> - <!-- Separator --> - <line x1="250" y1="995" x2="1350" y2="995" style="stroke:#444444; stroke-width:1; stroke-dasharray:4,2;"/> - <!-- Sequence --> - <rect x="494" y="1000" width="122" height="30" class="sequence-bar"> - <title>SEQ@8s [pri=10] (8-12s)</title> - </rect> - <text x="10" y="1019" class="label">SEQ@8s [pri=10]</text> - <rect x="500" y="1035" width="40" height="20" class="effect-bar"> - <title>FlashCubeEffect [pri=-1] (8.2-9.5s)</title> - </rect> - <text x="20" y="1050" class="label effect">FlashCubeEffect [pri=-1]</text> - <rect x="494" y="1065" width="122" height="20" class="effect-bar"> - <title>GaussianBlurEffect [pri=0] (8-12s)</title> - </rect> - <text x="20" y="1080" class="label effect">GaussianBlurEffect [pri=0]</text> - <rect x="494" y="1095" width="6" height="20" class="effect-bar"> - <title>FlashEffect [pri=1] (8-8.2s)</title> - </rect> - <text x="20" y="1110" class="label effect">FlashEffect [pri=1]</text> - <rect x="509" y="1125" width="2" height="20" class="effect-bar invalid"> - <title>FlashEffect [pri=1] (8.5-8.2s) *** INVALID TIME RANGE ***</title> - </rect> - <text x="20" y="1140" class="label effect">FlashEffect [pri=1] ⚠</text> - <!-- Separator --> - <line x1="250" y1="1155" x2="1350" y2="1155" style="stroke:#444444; stroke-width:1; stroke-dasharray:4,2;"/> - <!-- Sequence --> - <rect x="509" y="1160" width="122" height="30" class="sequence-bar"> - <title>SEQ@8.5s [pri=2] (8.5-12.5s)</title> - </rect> - <text x="10" y="1179" class="label">SEQ@8.5s [pri=2]</text> - <rect x="509" y="1195" width="61" height="20" class="effect-bar"> - <title>ThemeModulationEffect [pri=0] (8.5-10.5s)</title> - </rect> - <text x="20" y="1210" class="label effect">ThemeModulationEffect [pri=0]</text> - <rect x="515" y="1225" width="55" height="20" class="effect-bar"> - <title>HeptagonEffect [pri=1] (8.7-10.5s)</title> - </rect> - <text x="20" y="1240" class="label effect">HeptagonEffect [pri=1]</text> - <rect x="509" y="1255" width="61" height="20" class="effect-bar"> - <title>ParticleSprayEffect [pri=2] (8.5-10.5s)</title> - </rect> - <text x="20" y="1270" class="label effect">ParticleSprayEffect [pri=2]</text> - <rect x="509" y="1285" width="61" height="20" class="effect-bar"> - <title>ParticlesEffect [pri=2] (8.5-10.5s)</title> - </rect> - <text x="20" y="1300" class="label effect">ParticlesEffect [pri=2]</text> - <rect x="509" y="1315" width="61" height="20" class="effect-bar"> - <title>Hybrid3DEffect [pri=3] (8.5-10.5s)</title> - </rect> - <text x="20" y="1330" class="label effect">Hybrid3DEffect [pri=3]</text> - <rect x="509" y="1345" width="122" height="20" class="effect-bar"> - <title>GaussianBlurEffect [pri=4] (8.5-12.5s)</title> - </rect> - <text x="20" y="1360" class="label effect">GaussianBlurEffect [pri=4]</text> - <rect x="509" y="1375" width="92" height="20" class="effect-bar"> - <title>ChromaAberrationEffect [pri=5] (8.5-11.5s)</title> - </rect> - <text x="20" y="1390" class="label effect">ChromaAberrationEffect [pri=5]</text> - <!-- Separator --> - <line x1="250" y1="1405" x2="1350" y2="1405" style="stroke:#444444; stroke-width:1; stroke-dasharray:4,2;"/> - <!-- Sequence --> - <rect x="616" y="1410" width="306" height="30" class="sequence-bar"> - <title>SEQ@12s [pri=1] (12-22s)</title> - </rect> - <text x="10" y="1429" class="label">SEQ@12s [pri=1]</text> - <rect x="616" y="1445" width="122" height="20" class="effect-bar"> - <title>ThemeModulationEffect [pri=0] (12-16s)</title> - </rect> - <text x="20" y="1460" class="label effect">ThemeModulationEffect [pri=0]</text> - <rect x="622" y="1475" width="55" height="20" class="effect-bar"> - <title>HeptagonEffect [pri=1] (12.2-14s)</title> - </rect> - <text x="20" y="1490" class="label effect">HeptagonEffect [pri=1]</text> - <rect x="616" y="1505" width="122" height="20" class="effect-bar"> - <title>ParticleSprayEffect [pri=2] (12-16s)</title> - </rect> - <text x="20" y="1520" class="label effect">ParticleSprayEffect [pri=2]</text> - <rect x="616" y="1535" width="306" height="20" class="effect-bar"> - <title>Hybrid3DEffect [pri=3] (12-22s)</title> - </rect> - <text x="20" y="1550" class="label effect">Hybrid3DEffect [pri=3]</text> - <rect x="616" y="1565" width="122" height="20" class="effect-bar"> - <title>GaussianBlurEffect [pri=4] (12-16s)</title> - </rect> - <text x="20" y="1580" class="label effect">GaussianBlurEffect [pri=4]</text> - <rect x="616" y="1595" width="153" height="20" class="effect-bar"> - <title>ChromaAberrationEffect [pri=5] (12-17s)</title> - </rect> - <text x="20" y="1610" class="label effect">ChromaAberrationEffect [pri=5]</text> - <rect x="616" y="1625" width="153" height="20" class="effect-bar"> - <title>SolarizeEffect [pri=6] (12-17s)</title> - </rect> - <text x="20" y="1640" class="label effect">SolarizeEffect [pri=6]</text> - <!-- Separator --> - <line x1="250" y1="1655" x2="1350" y2="1655" style="stroke:#444444; stroke-width:1; stroke-dasharray:4,2;"/> - <!-- Sequence --> - <rect x="738" y="1660" width="245" height="30" class="sequence-bar"> - <title>SEQ@16s [pri=0] (16-24s)</title> - </rect> - <text x="10" y="1679" class="label">SEQ@16s [pri=0]</text> - <rect x="738" y="1695" width="62" height="20" class="effect-bar"> - <title>ThemeModulationEffect [pri=0] (16-18s)</title> - </rect> - <text x="20" y="1710" class="label effect">ThemeModulationEffect [pri=0]</text> - <rect x="738" y="1725" width="245" height="20" class="effect-bar"> - <title>HeptagonEffect [pri=1] (16-24s)</title> - </rect> - <text x="20" y="1740" class="label effect">HeptagonEffect [pri=1]</text> - <rect x="738" y="1755" width="245" height="20" class="effect-bar"> - <title>ChromaAberrationEffect [pri=2] (16-24s)</title> - </rect> - <text x="20" y="1770" class="label effect">ChromaAberrationEffect [pri=2]</text> - <rect x="738" y="1785" width="123" height="20" class="effect-bar"> - <title>GaussianBlurEffect [pri=3] (16-20s)</title> - </rect> - <text x="20" y="1800" class="label effect">GaussianBlurEffect [pri=3]</text> - <!-- Separator --> - <line x1="250" y1="1815" x2="1350" y2="1815" style="stroke:#444444; stroke-width:1; stroke-dasharray:4,2;"/> - <!-- Sequence --> - <rect x="983" y="1820" width="122" height="30" class="sequence-bar"> - <title>SEQ@24s [pri=0] (24-28s)</title> - </rect> - <text x="10" y="1839" class="label">SEQ@24s [pri=0]</text> - <rect x="983" y="1855" width="61" height="20" class="effect-bar"> - <title>ThemeModulationEffect [pri=0] (24-26s)</title> - </rect> - <text x="20" y="1870" class="label effect">ThemeModulationEffect [pri=0]</text> - <rect x="989" y="1885" width="55" height="20" class="effect-bar"> - <title>HeptagonEffect [pri=1] (24.2-26s)</title> - </rect> - <text x="20" y="1900" class="label effect">HeptagonEffect [pri=1]</text> - <rect x="983" y="1915" width="122" height="20" class="effect-bar"> - <title>GaussianBlurEffect [pri=2] (24-28s)</title> - </rect> - <text x="20" y="1930" class="label effect">GaussianBlurEffect [pri=2]</text> - <rect x="983" y="1945" width="30" height="20" class="effect-bar"> - <title>SolarizeEffect [pri=3] (24-25s)</title> - </rect> - <text x="20" y="1960" class="label effect">SolarizeEffect [pri=3]</text> - <!-- Separator --> - <line x1="250" y1="1975" x2="1350" y2="1975" style="stroke:#444444; stroke-width:1; stroke-dasharray:4,2;"/> - <!-- Sequence --> - <rect x="1105" y="1980" width="245" height="30" class="sequence-bar"> - <title>SEQ@28s [pri=0] (28-36s)</title> - </rect> - <text x="10" y="1999" class="label">SEQ@28s [pri=0]</text> - <rect x="1105" y="2015" width="122" height="20" class="effect-bar"> - <title>ThemeModulationEffect [pri=0] (28-32s)</title> - </rect> - <text x="20" y="2030" class="label effect">ThemeModulationEffect [pri=0]</text> - <rect x="1111" y="2045" width="55" height="20" class="effect-bar"> - <title>HeptagonEffect [pri=0] (28.2-30s)</title> - </rect> - <text x="20" y="2060" class="label effect">HeptagonEffect [pri=0]</text> - <rect x="1105" y="2075" width="61" height="20" class="effect-bar"> - <title>Hybrid3DEffect [pri=1] (28-30s)</title> - </rect> - <text x="20" y="2090" class="label effect">Hybrid3DEffect [pri=1]</text> - <rect x="1105" y="2105" width="122" height="20" class="effect-bar"> - <title>ParticleSprayEffect [pri=2] (28-32s)</title> - </rect> - <text x="20" y="2120" class="label effect">ParticleSprayEffect [pri=2]</text> - <rect x="1105" y="2135" width="245" height="20" class="effect-bar"> - <title>HeptagonEffect [pri=3] (28-36s)</title> - </rect> - <text x="20" y="2150" class="label effect">HeptagonEffect [pri=3]</text> - <rect x="1105" y="2165" width="245" height="20" class="effect-bar"> - <title>ChromaAberrationEffect [pri=4] (28-36s)</title> - </rect> - <text x="20" y="2180" class="label effect">ChromaAberrationEffect [pri=4]</text> - <rect x="1105" y="2195" width="122" height="20" class="effect-bar"> - <title>GaussianBlurEffect [pri=5] (28-32s)</title> - </rect> - <text x="20" y="2210" class="label effect">GaussianBlurEffect [pri=5]</text> - <!-- Separator --> - <line x1="250" y1="2225" x2="1350" y2="2225" style="stroke:#444444; stroke-width:1; stroke-dasharray:4,2;"/> - <!-- Sequence --> - <rect x="1197" y="2230" width="46" height="30" class="sequence-bar"> - <title>SEQ@31s [pri=0] (31-32.5s)</title> - </rect> - <text x="10" y="2249" class="label">SEQ@31s [pri=0]</text> - <rect x="1197" y="2265" width="46" height="20" class="effect-bar"> - <title>ThemeModulationEffect [pri=0] (31-32.5s)</title> - </rect> - <text x="20" y="2280" class="label effect">ThemeModulationEffect [pri=0]</text> - <rect x="1197" y="2295" width="46" height="20" class="effect-bar"> - <title>SolarizeEffect [pri=1] (31-32.5s)</title> - </rect> - <text x="20" y="2310" class="label effect">SolarizeEffect [pri=1]</text> - <!-- Legend --> - <rect x="10" y="2215" width="20" height="10" class="sequence-bar"/> - <text x="35" y="2223" class="axis-label">Sequence</text> - <rect x="120" y="2215" width="20" height="10" class="effect-bar"/> - <text x="145" y="2223" class="axis-label">Effect</text> - <rect x="220" y="2215" width="20" height="10" class="effect-bar invalid"/> - <text x="245" y="2223" class="axis-label">Invalid Time Range</text> -</svg> -<div class="info"> -<strong>Tip:</strong> Hover over bars to see details. Higher priority numbers render later (on top). -</div> -</body> -</html> |
