summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--EFFECT_DEPTH_ANALYSIS.md115
-rw-r--r--timeline_analysis.html425
-rw-r--r--tools/seq_compiler.cc182
3 files changed, 721 insertions, 1 deletions
diff --git a/EFFECT_DEPTH_ANALYSIS.md b/EFFECT_DEPTH_ANALYSIS.md
new file mode 100644
index 0000000..7a33baf
--- /dev/null
+++ b/EFFECT_DEPTH_ANALYSIS.md
@@ -0,0 +1,115 @@
+# 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/timeline_analysis.html b/timeline_analysis.html
new file mode 100644
index 0000000..92e4a3f
--- /dev/null
+++ b/timeline_analysis.html
@@ -0,0 +1,425 @@
+<!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>
diff --git a/tools/seq_compiler.cc b/tools/seq_compiler.cc
index f9409de..87d6222 100644
--- a/tools/seq_compiler.cc
+++ b/tools/seq_compiler.cc
@@ -3,8 +3,11 @@
// Converts a text-based timeline description into C++ code.
#include <algorithm>
+#include <cmath>
#include <fstream>
+#include <iomanip>
#include <iostream>
+#include <numeric>
#include <sstream>
#include <string>
#include <vector>
@@ -46,6 +49,171 @@ int calculate_tick_interval(float max_time) {
return 20;
}
+// Analyze effect stacking depth across the timeline
+void analyze_effect_depth(const std::vector<SequenceEntry>& sequences,
+ const std::string& demo_end_time,
+ float sample_rate = 10.0f) {
+ // Find max time for analysis
+ float max_time = demo_end_time.empty() ? 0.0f : std::stof(demo_end_time);
+ for (const auto& seq : sequences) {
+ float seq_start = std::stof(seq.start_time);
+ for (const auto& eff : seq.effects) {
+ float eff_end = seq_start + std::stof(eff.end);
+ max_time = std::max(max_time, eff_end);
+ }
+ }
+
+ if (max_time <= 0.0f) {
+ std::cout << "\n=== Effect Depth Analysis ===\n";
+ std::cout << "No effects found in timeline.\n";
+ return;
+ }
+
+ // Build list of all effects with absolute times
+ struct ActiveEffect {
+ std::string name;
+ float start_time;
+ float end_time;
+ int priority;
+ };
+ std::vector<ActiveEffect> all_effects;
+
+ for (const auto& seq : sequences) {
+ float seq_start = std::stof(seq.start_time);
+ float seq_end = seq_start;
+ if (seq.end_time != "-1.0") {
+ seq_end = seq_start + std::stof(seq.end_time);
+ }
+
+ for (const auto& eff : seq.effects) {
+ float eff_start = seq_start + std::stof(eff.start);
+ float eff_end = seq_start + std::stof(eff.end);
+
+ // Clamp effect end to sequence end if specified
+ if (seq.end_time != "-1.0") {
+ eff_end = std::min(eff_end, seq_end);
+ }
+
+ all_effects.push_back(
+ {eff.class_name, eff_start, eff_end, std::stoi(eff.priority)});
+ }
+ }
+
+ // Sample timeline at regular intervals
+ const float dt = 1.0f / sample_rate;
+ int max_depth = 0;
+ float max_depth_time = 0.0f;
+ std::vector<int> depth_histogram(21, 0); // Track depths 0-20+
+
+ struct PeakInfo {
+ float time;
+ int depth;
+ std::vector<std::string> effects;
+ };
+ std::vector<PeakInfo> peaks;
+
+ for (float t = 0.0f; t <= max_time; t += dt) {
+ int depth = 0;
+ std::vector<std::string> active_effects;
+
+ for (const auto& eff : all_effects) {
+ if (t >= eff.start_time && t < eff.end_time) {
+ depth++;
+ active_effects.push_back(eff.name);
+ }
+ }
+
+ // Update max depth
+ if (depth > max_depth) {
+ max_depth = depth;
+ max_depth_time = t;
+ }
+
+ // Record histogram
+ int hist_idx = std::min(depth, 20);
+ depth_histogram[hist_idx]++;
+
+ // Record peaks (>5 effects)
+ if (depth > 5 && (peaks.empty() || t - peaks.back().time > 0.5f)) {
+ peaks.push_back({t, depth, active_effects});
+ }
+ }
+
+ // Print analysis report
+ std::cout << "\n=== Effect Depth Analysis ===\n";
+ std::cout << "Timeline duration: " << max_time << "s\n";
+ std::cout << "Total effects: " << all_effects.size() << "\n";
+ std::cout << "Sample rate: " << sample_rate << " Hz (every " << dt << "s)\n";
+ std::cout << "\n";
+
+ std::cout << "Max concurrent effects: " << max_depth << " at t=" << max_depth_time
+ << "s\n";
+ std::cout << "\n";
+
+ // Print histogram
+ std::cout << "Effect Depth Distribution:\n";
+ std::cout << "Depth | Count | Percentage | Bar\n";
+ std::cout << "------|---------|------------|"
+ "-------------------------------------\n";
+
+ int total_samples =
+ std::accumulate(depth_histogram.begin(), depth_histogram.end(), 0);
+
+ for (size_t i = 0; i < depth_histogram.size(); ++i) {
+ if (depth_histogram[i] == 0 && i > max_depth)
+ continue;
+
+ float percentage = 100.0f * depth_histogram[i] / total_samples;
+ int bar_length = (int)(percentage / 2.0f); // Scale to ~50 chars max
+
+ std::cout << std::setw(5) << (i < 20 ? std::to_string(i) : "20+") << " | "
+ << std::setw(7) << depth_histogram[i] << " | " << std::setw(9)
+ << std::fixed << std::setprecision(1) << percentage << "% | ";
+
+ for (int j = 0; j < bar_length; ++j) {
+ std::cout << "█";
+ }
+ std::cout << "\n";
+ }
+
+ // Print bottleneck warnings
+ if (max_depth > 5) {
+ std::cout << "\n⚠ WARNING: Performance bottlenecks detected!\n";
+ std::cout << "Found " << peaks.size() << " time periods with >5 effects:\n\n";
+
+ int peak_count = 0;
+ for (const auto& peak : peaks) {
+ if (peak_count >= 10)
+ break; // Limit output
+
+ std::cout << " t=" << std::fixed << std::setprecision(2) << peak.time
+ << "s: " << peak.depth << " effects [ ";
+
+ // Show first 5 effects
+ for (size_t i = 0; i < std::min(size_t(5), peak.effects.size()); ++i) {
+ std::cout << peak.effects[i];
+ if (i < peak.effects.size() - 1)
+ std::cout << ", ";
+ }
+ if (peak.effects.size() > 5) {
+ std::cout << " +" << (peak.effects.size() - 5) << " more";
+ }
+ std::cout << " ]\n";
+
+ peak_count++;
+ }
+
+ if (peaks.size() > 10) {
+ std::cout << " ... and " << (peaks.size() - 10) << " more peaks\n";
+ }
+ } else {
+ std::cout << "\n✓ No significant bottlenecks detected (max depth: "
+ << max_depth << " <= 5)\n";
+ }
+
+ std::cout << "\n";
+}
+
// Generate ASCII Gantt chart for timeline visualization
void generate_gantt_chart(const std::string& output_file,
const std::vector<SequenceEntry>& sequences,
@@ -452,16 +620,20 @@ int main(int argc, char* argv[]) {
if (argc < 2) {
std::cerr << "Usage: " << argv[0]
<< " <input.seq> [output.cc] [--gantt=<file.txt>] "
- "[--gantt-html=<file.html>]\n";
+ "[--gantt-html=<file.html>] [--analyze]\n";
std::cerr << "Examples:\n";
std::cerr << " " << argv[0]
<< " assets/demo.seq src/generated/timeline.cc\n";
std::cerr << " " << argv[0] << " assets/demo.seq --gantt=timeline.txt\n";
std::cerr << " " << argv[0]
<< " assets/demo.seq --gantt-html=timeline.html\n";
+ std::cerr << " " << argv[0] << " assets/demo.seq --analyze\n";
std::cerr << " " << argv[0]
<< " assets/demo.seq timeline.cc --gantt=timeline.txt "
"--gantt-html=timeline.html\n";
+ std::cerr << "\nOptions:\n";
+ std::cerr << " --analyze Analyze effect stacking depth and "
+ "identify bottlenecks\n";
std::cerr << "\nIf output.cc is omitted, only validation and Gantt "
"generation are performed.\n";
return 1;
@@ -470,6 +642,7 @@ int main(int argc, char* argv[]) {
std::string output_cc = "";
std::string gantt_output = "";
std::string gantt_html_output = "";
+ bool analyze_depth = false;
// Parse command line arguments
for (int i = 2; i < argc; ++i) {
@@ -478,6 +651,8 @@ int main(int argc, char* argv[]) {
gantt_output = arg.substr(8);
} else if (arg.rfind("--gantt-html=", 0) == 0) {
gantt_html_output = arg.substr(13);
+ } else if (arg == "--analyze") {
+ analyze_depth = true;
} else if (output_cc.empty() && arg[0] != '-') {
output_cc = arg;
}
@@ -738,5 +913,10 @@ int main(int argc, char* argv[]) {
generate_gantt_html(gantt_html_output, sequences, bpm, demo_end_time);
}
+ // Analyze effect stacking depth if requested
+ if (analyze_depth) {
+ analyze_effect_depth(sequences, demo_end_time);
+ }
+
return 0;
}