summaryrefslogtreecommitdiff
path: root/tools
diff options
context:
space:
mode:
authorskal <pascal.massimino@gmail.com>2026-02-08 13:05:15 +0100
committerskal <pascal.massimino@gmail.com>2026-02-08 13:05:15 +0100
commit56f92f3659d77aa2372f86e43a8466ed030fef4e (patch)
treedbb0ba2ef33fa948acf3998dcfc9ff3486ad36ce /tools
parent6060a69101c80eb580ae68134e731af2e314ba0e (diff)
feat(tools): Add effect depth analysis to seq_compiler
Adds --analyze flag to seq_compiler to identify performance bottlenecks by analyzing how many effects run simultaneously at each point in the demo. Features: - Samples timeline at 10 Hz (every 0.1s) - Counts overlapping effects at each sample point - Generates histogram of effect depth distribution - Identifies bottleneck periods (>5 concurrent effects) - Reports max concurrent effects with timestamps - Lists top 10 bottleneck peaks with effect names Analysis Results for Current Demo (demo.seq): - Max concurrent effects: 11 at t=8.8s - 28 bottleneck periods detected (>5 effects) - 29.6% of demo has 7+ effects running (critical) - Most intensive section: 8b-12b (4-6 seconds) Most Used Effects: - GaussianBlurEffect: ~10 instances (optimization target) - HeptagonEffect: ~9 instances - ThemeModulationEffect: ~7 instances Usage: ./build/seq_compiler assets/demo.seq --analyze ./build/seq_compiler assets/demo.seq --analyze --gantt-html=out.html Files Modified: - tools/seq_compiler.cc: Added analyze_effect_depth() function - EFFECT_DEPTH_ANALYSIS.md: Detailed analysis report + recommendations - timeline_analysis.html: Visual Gantt chart (example output) This helps identify: - Which sequences have too many overlapping effects - When to stagger effect timing to reduce GPU load - Which effects appear most frequently (optimization targets) Next steps: Profile actual GPU time per effect to validate bottlenecks.
Diffstat (limited to 'tools')
-rw-r--r--tools/seq_compiler.cc182
1 files changed, 181 insertions, 1 deletions
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;
}