r3f-frame-profiler 1.0.2 → 1.0.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (128) hide show
  1. package/dist/cjs/index.js +1 -61
  2. package/dist/cjs/index.js.map +1 -1
  3. package/dist/esm/index.js +7 -12890
  4. package/dist/esm/index.js.map +1 -1
  5. package/dist/index-D_LaF0YP.cjs +2 -0
  6. package/dist/index-D_LaF0YP.cjs.map +1 -0
  7. package/dist/index-o5zvhDta.js +4406 -0
  8. package/dist/index-o5zvhDta.js.map +1 -0
  9. package/dist/report-generator-BCq_enQo.js +283 -0
  10. package/dist/report-generator-BCq_enQo.js.map +1 -0
  11. package/dist/report-generator-BsoP53qp.cjs +61 -0
  12. package/dist/report-generator-BsoP53qp.cjs.map +1 -0
  13. package/dist/types/FrameProfiler.d.ts +2 -0
  14. package/dist/types/FrameProfiler.d.ts.map +1 -0
  15. package/dist/types/collectors/frame-time-collector.d.ts +41 -0
  16. package/dist/types/collectors/frame-time-collector.d.ts.map +1 -0
  17. package/dist/types/collectors/gpu-collector.d.ts +32 -0
  18. package/dist/types/collectors/gpu-collector.d.ts.map +1 -0
  19. package/dist/types/collectors/hot-path-detector.d.ts +42 -0
  20. package/dist/types/collectors/hot-path-detector.d.ts.map +1 -0
  21. package/dist/types/collectors/index.d.ts +7 -0
  22. package/dist/types/collectors/index.d.ts.map +1 -0
  23. package/dist/types/collectors/memory-collector.d.ts +49 -0
  24. package/dist/types/collectors/memory-collector.d.ts.map +1 -0
  25. package/dist/types/collectors/scene-collector.d.ts +47 -0
  26. package/dist/types/collectors/scene-collector.d.ts.map +1 -0
  27. package/dist/types/components/ReactProfiler.d.ts +12 -0
  28. package/dist/types/components/ReactProfiler.d.ts.map +1 -0
  29. package/dist/types/core/alert-system.d.ts +37 -0
  30. package/dist/types/core/alert-system.d.ts.map +1 -0
  31. package/dist/types/core/auto-instrumentation.d.ts +33 -0
  32. package/dist/types/core/auto-instrumentation.d.ts.map +1 -0
  33. package/dist/types/core/budget-manager.d.ts +78 -0
  34. package/dist/types/core/budget-manager.d.ts.map +1 -0
  35. package/dist/types/core/data-export.d.ts +24 -0
  36. package/dist/types/core/data-export.d.ts.map +1 -0
  37. package/dist/types/core/index.d.ts +4 -0
  38. package/dist/types/core/index.d.ts.map +1 -0
  39. package/dist/types/core/monitor-state.d.ts +21 -0
  40. package/dist/types/core/monitor-state.d.ts.map +1 -0
  41. package/dist/types/core/profiler.d.ts +83 -0
  42. package/dist/types/core/profiler.d.ts.map +1 -0
  43. package/dist/types/core/react-monitor.d.ts +51 -0
  44. package/dist/types/core/react-monitor.d.ts.map +1 -0
  45. package/dist/types/core/report-generator.d.ts +83 -0
  46. package/dist/types/core/report-generator.d.ts.map +1 -0
  47. package/dist/types/hooks/index.d.ts +17 -0
  48. package/dist/types/hooks/index.d.ts.map +1 -0
  49. package/dist/types/hooks/useAlerts.d.ts +11 -0
  50. package/dist/types/hooks/useAlerts.d.ts.map +1 -0
  51. package/dist/types/hooks/useBudgetChecker.d.ts +6 -0
  52. package/dist/types/hooks/useBudgetChecker.d.ts.map +1 -0
  53. package/dist/types/hooks/useFrameHistory.d.ts +2 -0
  54. package/dist/types/hooks/useFrameHistory.d.ts.map +1 -0
  55. package/dist/types/hooks/useFrameTime.d.ts +3 -0
  56. package/dist/types/hooks/useFrameTime.d.ts.map +1 -0
  57. package/dist/types/hooks/useGPUStats.d.ts +3 -0
  58. package/dist/types/hooks/useGPUStats.d.ts.map +1 -0
  59. package/dist/types/hooks/useHotPath.d.ts +3 -0
  60. package/dist/types/hooks/useHotPath.d.ts.map +1 -0
  61. package/dist/types/hooks/useMemoryHistory.d.ts +5 -0
  62. package/dist/types/hooks/useMemoryHistory.d.ts.map +1 -0
  63. package/dist/types/hooks/useMemoryStats.d.ts +3 -0
  64. package/dist/types/hooks/useMemoryStats.d.ts.map +1 -0
  65. package/dist/types/hooks/useMetricsCollector.d.ts +31 -0
  66. package/dist/types/hooks/useMetricsCollector.d.ts.map +1 -0
  67. package/dist/types/hooks/usePerformanceBudget.d.ts +20 -0
  68. package/dist/types/hooks/usePerformanceBudget.d.ts.map +1 -0
  69. package/dist/types/hooks/useProfiler.d.ts +5 -0
  70. package/dist/types/hooks/useProfiler.d.ts.map +1 -0
  71. package/dist/types/hooks/useR3FProfiler.d.ts +2 -0
  72. package/dist/types/hooks/useR3FProfiler.d.ts.map +1 -0
  73. package/dist/types/hooks/useR3FProfilerCollector.d.ts +2 -0
  74. package/dist/types/hooks/useR3FProfilerCollector.d.ts.map +1 -0
  75. package/dist/types/hooks/useR3FStatsCollector.d.ts +2 -0
  76. package/dist/types/hooks/useR3FStatsCollector.d.ts.map +1 -0
  77. package/dist/types/hooks/useReactProfiler.d.ts +3 -0
  78. package/dist/types/hooks/useReactProfiler.d.ts.map +1 -0
  79. package/dist/types/hooks/useReportMetric.d.ts +19 -0
  80. package/dist/types/hooks/useReportMetric.d.ts.map +1 -0
  81. package/dist/types/hooks/useSceneStats.d.ts +3 -0
  82. package/dist/types/hooks/useSceneStats.d.ts.map +1 -0
  83. package/dist/types/index.d.ts +5 -0
  84. package/dist/types/index.d.ts.map +1 -0
  85. package/dist/types/ui/PerformanceMonitor.d.ts +7 -0
  86. package/dist/types/ui/PerformanceMonitor.d.ts.map +1 -0
  87. package/dist/types/ui/components/AlertBadge.d.ts +9 -0
  88. package/dist/types/ui/components/AlertBadge.d.ts.map +1 -0
  89. package/dist/types/ui/components/MetricCard.d.ts +14 -0
  90. package/dist/types/ui/components/MetricCard.d.ts.map +1 -0
  91. package/dist/types/ui/components/SimpleChart.d.ts +13 -0
  92. package/dist/types/ui/components/SimpleChart.d.ts.map +1 -0
  93. package/dist/types/ui/components/Tabs.d.ts +15 -0
  94. package/dist/types/ui/components/Tabs.d.ts.map +1 -0
  95. package/dist/types/ui/components/index.d.ts +5 -0
  96. package/dist/types/ui/components/index.d.ts.map +1 -0
  97. package/dist/types/ui/panels/BudgetPanel.d.ts +2 -0
  98. package/dist/types/ui/panels/BudgetPanel.d.ts.map +1 -0
  99. package/dist/types/ui/panels/FramePanel.d.ts +2 -0
  100. package/dist/types/ui/panels/FramePanel.d.ts.map +1 -0
  101. package/dist/types/ui/panels/FrameTimePanel.d.ts +2 -0
  102. package/dist/types/ui/panels/FrameTimePanel.d.ts.map +1 -0
  103. package/dist/types/ui/panels/GPUPanel.d.ts +2 -0
  104. package/dist/types/ui/panels/GPUPanel.d.ts.map +1 -0
  105. package/dist/types/ui/panels/HotPathPanel.d.ts +2 -0
  106. package/dist/types/ui/panels/HotPathPanel.d.ts.map +1 -0
  107. package/dist/types/ui/panels/MemoryPanel.d.ts +2 -0
  108. package/dist/types/ui/panels/MemoryPanel.d.ts.map +1 -0
  109. package/dist/types/ui/panels/ReactPanel.d.ts +2 -0
  110. package/dist/types/ui/panels/ReactPanel.d.ts.map +1 -0
  111. package/dist/types/ui/panels/ScenePanel.d.ts +2 -0
  112. package/dist/types/ui/panels/ScenePanel.d.ts.map +1 -0
  113. package/dist/types/ui/panels/index.d.ts +9 -0
  114. package/dist/types/ui/panels/index.d.ts.map +1 -0
  115. package/dist/types/ui/styles/theme.d.ts +76 -0
  116. package/dist/types/ui/styles/theme.d.ts.map +1 -0
  117. package/dist/types/ui/visualizations/FrameGraph.d.ts +10 -0
  118. package/dist/types/ui/visualizations/FrameGraph.d.ts.map +1 -0
  119. package/dist/types/ui/visualizations/MemoryChart.d.ts +10 -0
  120. package/dist/types/ui/visualizations/MemoryChart.d.ts.map +1 -0
  121. package/dist/types/ui/visualizations/PerformanceChart.d.ts +16 -0
  122. package/dist/types/ui/visualizations/PerformanceChart.d.ts.map +1 -0
  123. package/dist/types/ui/visualizations/TimelineView.d.ts +7 -0
  124. package/dist/types/ui/visualizations/TimelineView.d.ts.map +1 -0
  125. package/dist/types/ui/visualizations/index.d.ts +5 -0
  126. package/dist/types/ui/visualizations/index.d.ts.map +1 -0
  127. package/package.json +12 -7
  128. package/README.md +0 -60
@@ -0,0 +1,283 @@
1
+ import { p as y } from "./index-o5zvhDta.js";
2
+ class F {
3
+ startTime = Date.now();
4
+ frameCount = 0;
5
+ fpsSamples = [];
6
+ frameTimeSamples = [];
7
+ memorySamples = [];
8
+ customMetricHistory = /* @__PURE__ */ new Map();
9
+ /**
10
+ * Enregistre une frame pour les statistiques
11
+ */
12
+ recordFrame(t, e, r) {
13
+ this.frameCount++, this.fpsSamples.push(t), this.frameTimeSamples.push(e), r !== void 0 && this.memorySamples.push(r), this.fpsSamples.length > 1e3 && (this.fpsSamples.shift(), this.frameTimeSamples.shift(), this.memorySamples.length > 0 && this.memorySamples.shift());
14
+ }
15
+ /**
16
+ * Enregistre une métrique custom
17
+ */
18
+ recordMetric(t, e) {
19
+ this.customMetricHistory.has(t) || this.customMetricHistory.set(t, []);
20
+ const r = this.customMetricHistory.get(t);
21
+ r.push(e), r.length > 100 && r.shift();
22
+ }
23
+ /**
24
+ * Génère le rapport complet
25
+ */
26
+ generateReport() {
27
+ const t = Date.now() - this.startTime, e = this.average(this.fpsSamples), r = this.fpsSamples.filter((g) => isFinite(g)), i = r.length > 0 ? Math.min(...r) : 0, n = r.length > 0 ? Math.max(...r) : 0, s = this.average(this.frameTimeSamples), o = this.memorySamples[this.memorySamples.length - 1] || 0, c = Math.max(...this.memorySamples, 0), a = this.calculateTrend(this.memorySamples), m = this.detectIssues(e, i, s, o, c), p = this.extractHotspots(), u = this.analyzeCustomMetrics(), l = isFinite(e) ? e : 0, d = isFinite(i) && i !== 1 / 0 ? i : 0, f = isFinite(n) && n !== -1 / 0 ? n : 0, h = isFinite(s) ? s : 0;
28
+ return {
29
+ timestamp: (/* @__PURE__ */ new Date()).toISOString(),
30
+ duration: t,
31
+ summary: {
32
+ avgFps: l,
33
+ minFps: d,
34
+ maxFps: f,
35
+ avgFrameTime: h,
36
+ totalFrames: this.frameCount
37
+ },
38
+ memory: {
39
+ current: o,
40
+ peak: c,
41
+ trend: a
42
+ },
43
+ issues: m,
44
+ hotspots: p,
45
+ customMetrics: u
46
+ };
47
+ }
48
+ /**
49
+ * Génère un rapport markdown lisible
50
+ */
51
+ generateMarkdownReport() {
52
+ const t = this.generateReport();
53
+ let e = `# 📊 Performance Report
54
+
55
+ `;
56
+ if (e += `**Generated:** ${new Date(t.timestamp).toLocaleString()}
57
+ `, e += `**Duration:** ${(t.duration / 1e3).toFixed(1)}s
58
+
59
+ `, e += `## Summary
60
+
61
+ `, e += `- **FPS:** ${t.summary.avgFps.toFixed(1)} avg (${t.summary.minFps.toFixed(0)} min, ${t.summary.maxFps.toFixed(0)} max)
62
+ `, e += `- **Frame Time:** ${t.summary.avgFrameTime.toFixed(2)}ms avg
63
+ `, e += `- **Total Frames:** ${t.summary.totalFrames}
64
+ `, e += `- **Memory:** ${t.memory.current.toFixed(1)}MB current, ${t.memory.peak.toFixed(1)}MB peak (${t.memory.trend})
65
+
66
+ `, t.issues.length > 0) {
67
+ e += `## 🚨 Issues Detected
68
+
69
+ `;
70
+ const i = t.issues.filter((o) => o.severity === "critical"), n = t.issues.filter((o) => o.severity === "warning"), s = t.issues.filter((o) => o.severity === "info");
71
+ i.length > 0 && (e += `### Critical
72
+
73
+ `, i.forEach((o) => {
74
+ e += `- **${o.metric}:** ${o.value.toFixed(2)} (threshold: ${o.threshold})
75
+ `, e += ` - 💡 ${o.suggestion}
76
+ `;
77
+ }), e += `
78
+ `), n.length > 0 && (e += `### Warnings
79
+
80
+ `, n.forEach((o) => {
81
+ e += `- **${o.metric}:** ${o.value.toFixed(2)} (threshold: ${o.threshold})
82
+ `, e += ` - 💡 ${o.suggestion}
83
+ `;
84
+ }), e += `
85
+ `), s.length > 0 && (e += `### Info
86
+
87
+ `, s.forEach((o) => {
88
+ e += `- ${o.metric}: ${o.value.toFixed(2)}
89
+ `;
90
+ }), e += `
91
+ `);
92
+ } else
93
+ e += `## ✅ No Issues Detected
94
+
95
+ `;
96
+ if (t.hotspots.length > 0) {
97
+ e += `## 🔥 Performance Hotspots
98
+
99
+ `, e += `Functions consuming the most time:
100
+
101
+ `, e += `| # | Function | Avg | p95 | p99 | Max | % Frame | Jitter |
102
+ `, e += `|---|----------|-----|-----|-----|-----|---------|--------|
103
+ `, t.hotspots.slice(0, 10).forEach((s, o) => {
104
+ const c = s.variance > 1 ? "⚠️ High" : s.variance > 0.1 ? "~" : "✓", a = s.percentOfFrame?.toFixed(1) || "0.0";
105
+ e += `| ${o + 1} | **${s.name}** | ${s.avgTime.toFixed(2)}ms | ${(s.p95 || 0).toFixed(2)}ms | ${(s.p99 || 0).toFixed(2)}ms | ${s.maxTime.toFixed(2)}ms | ${a}% | ${c} |
106
+ `;
107
+ }), e += `
108
+ `;
109
+ const i = t.hotspots[0];
110
+ i && i.name !== "Frame" && i.percentOfFrame > 30 && (e += `> [!WARNING]
111
+ `, e += `> **${i.name}** consumes ${i.percentOfFrame.toFixed(1)}% of frame time. Consider optimizing this function.
112
+
113
+ `);
114
+ const n = t.hotspots.filter((s) => s.variance > 1 && s.name !== "Frame");
115
+ n.length > 0 && (e += `> [!CAUTION]
116
+ `, e += `> High jitter detected in: ${n.map((s) => s.name).join(", ")}. These cause frame rate stuttering.
117
+
118
+ `);
119
+ }
120
+ Object.keys(t.customMetrics).length > 0 && (e += `## 📈 Custom Metrics
121
+
122
+ `, Object.entries(t.customMetrics).forEach(([i, n]) => {
123
+ e += `- **${i}**: ${n.current.toFixed(2)} (min: ${n.min.toFixed(2)}, max: ${n.max.toFixed(2)}, avg: ${n.avg.toFixed(2)})
124
+ `;
125
+ }), e += `
126
+ `), e += `## 💡 Recommendations
127
+
128
+ `;
129
+ const r = [...t.issues].sort((i, n) => {
130
+ const s = { critical: 3, warning: 2, info: 1 };
131
+ return s[n.severity] - s[i.severity];
132
+ });
133
+ return r.length > 0 ? (e += `Priority actions:
134
+
135
+ `, r.slice(0, 5).forEach((i, n) => {
136
+ const s = i.severity === "critical" ? "🔴" : i.severity === "warning" ? "⚠️" : "ℹ️";
137
+ e += `${n + 1}. ${s} ${i.suggestion}
138
+ `;
139
+ })) : (e += `✅ Performance looks good! Continue monitoring.
140
+
141
+ `, e += `> [!TIP]
142
+ `, e += `> Keep an eye on the "Hotspots" section to prevent future bottlenecks as your scene grows.
143
+ `), e;
144
+ }
145
+ /**
146
+ * Copie le rapport dans le clipboard
147
+ */
148
+ async copyReportToClipboard() {
149
+ const t = this.generateMarkdownReport();
150
+ try {
151
+ await navigator.clipboard.writeText(t), console.log("✅ Report copied to clipboard!");
152
+ } catch (e) {
153
+ console.error("Failed to copy report:", e), console.log(`📋 Report:
154
+
155
+ ` + t);
156
+ }
157
+ }
158
+ // === Méthodes utilitaires ===
159
+ average(t) {
160
+ return t.length === 0 ? 0 : t.reduce((e, r) => e + r, 0) / t.length;
161
+ }
162
+ calculateTrend(t) {
163
+ if (t.length < 10) return "stable";
164
+ const e = t.slice(-10), r = t.slice(-20, -10);
165
+ if (r.length === 0) return "stable";
166
+ const i = this.average(e), n = this.average(r), s = i - n, o = n * 0.1;
167
+ return s > o ? "increasing" : s < -o ? "decreasing" : "stable";
168
+ }
169
+ detectIssues(t, e, r, i, n) {
170
+ const s = [];
171
+ return t < 30 ? s.push({
172
+ severity: "critical",
173
+ metric: "Average FPS",
174
+ value: t,
175
+ threshold: 30,
176
+ suggestion: "FPS critically low. Check frame time hotspots, reduce draw calls, optimize physics/collision detection."
177
+ }) : t < 50 && s.push({
178
+ severity: "warning",
179
+ metric: "Average FPS",
180
+ value: t,
181
+ threshold: 50,
182
+ suggestion: "FPS below optimal. Consider reducing shadow quality, LOD distance, or particle count."
183
+ }), e < t * 0.5 && s.push({
184
+ severity: "warning",
185
+ metric: "FPS Drops",
186
+ value: e,
187
+ threshold: t * 0.5,
188
+ suggestion: "Severe FPS drops detected. Check for GC pauses, asset loading spikes, or physics explosions."
189
+ }), r > 33 ? s.push({
190
+ severity: "critical",
191
+ metric: "Frame Time",
192
+ value: r,
193
+ threshold: 33,
194
+ suggestion: "Frame time exceeds 33ms budget. Profile CPU-heavy operations."
195
+ }) : r > 16 && s.push({
196
+ severity: "warning",
197
+ metric: "Frame Time",
198
+ value: r,
199
+ threshold: 16,
200
+ suggestion: "Frame time exceeds 60 FPS target (16.67ms). Optimize main thread work."
201
+ }), i > 500 ? s.push({
202
+ severity: "critical",
203
+ metric: "Memory Usage",
204
+ value: i,
205
+ threshold: 500,
206
+ suggestion: "High memory usage. Check for memory leaks, dispose unused textures/geometries."
207
+ }) : i > 300 && s.push({
208
+ severity: "warning",
209
+ metric: "Memory Usage",
210
+ value: i,
211
+ threshold: 300,
212
+ suggestion: "Elevated memory usage. Consider texture compression, geometry pooling."
213
+ }), s;
214
+ }
215
+ extractHotspots() {
216
+ const t = y.getData(), e = y.getRawData(), r = [], i = [
217
+ "Camera",
218
+ "Ball",
219
+ "Distance",
220
+ "Physics",
221
+ "Opacity",
222
+ "Following",
223
+ "(deg)",
224
+ "(m/s)",
225
+ "(rad/s)",
226
+ "(m)",
227
+ "Speed",
228
+ "Height",
229
+ "Moving",
230
+ "Occluded",
231
+ "Azimuth",
232
+ "Polar",
233
+ "Bodies",
234
+ "Colliders",
235
+ "Joints"
236
+ ];
237
+ let n = 0;
238
+ if (e) {
239
+ const s = e.get("Frame");
240
+ s && s.calls > 0 && (n = s.total / s.calls);
241
+ }
242
+ return t && t.entries && e && t.entries.forEach((s) => {
243
+ if (!i.some(
244
+ (c) => s.name.includes(c)
245
+ )) {
246
+ const a = e.get(s.name)?.times || [], m = [...a].sort((h, g) => h - g), p = m.length > 0 && m[Math.floor(m.length * 0.95)] || 0, u = m.length > 0 && m[Math.floor(m.length * 0.99)] || 0, l = s.avg || 0, d = a.length > 1 ? a.reduce((h, g) => h + Math.pow(g - l, 2), 0) / a.length : 0, f = n > 0 ? l / n * 100 : 0;
247
+ r.push({
248
+ name: s.name,
249
+ avgTime: l,
250
+ maxTime: s.max || 0,
251
+ callCount: s.calls || 0,
252
+ p95: p,
253
+ p99: u,
254
+ variance: d,
255
+ percentOfFrame: f
256
+ });
257
+ }
258
+ }), r.sort((s, o) => o.avgTime - s.avgTime);
259
+ }
260
+ analyzeCustomMetrics() {
261
+ const t = {};
262
+ return this.customMetricHistory.forEach((e, r) => {
263
+ e.length > 0 && (t[r] = {
264
+ min: Math.min(...e),
265
+ max: Math.max(...e),
266
+ avg: this.average(e),
267
+ current: e[e.length - 1]
268
+ });
269
+ }), t;
270
+ }
271
+ /**
272
+ * Reset toutes les statistiques
273
+ */
274
+ reset() {
275
+ this.startTime = Date.now(), this.frameCount = 0, this.fpsSamples = [], this.frameTimeSamples = [], this.memorySamples = [], this.customMetricHistory.clear();
276
+ }
277
+ }
278
+ const $ = new F();
279
+ export {
280
+ F as ReportGenerator,
281
+ $ as reportGenerator
282
+ };
283
+ //# sourceMappingURL=report-generator-BCq_enQo.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"report-generator-BCq_enQo.js","sources":["../src/core/report-generator.ts"],"sourcesContent":["import { profiler } from './profiler';\r\n\r\nexport interface PerformanceIssue {\r\n severity: 'critical' | 'warning' | 'info';\r\n metric: string;\r\n value: number;\r\n threshold: number;\r\n suggestion: string;\r\n}\r\n\r\nexport interface PerformanceReport {\r\n timestamp: string;\r\n duration: number; // Durée de la session en ms\r\n summary: {\r\n avgFps: number;\r\n minFps: number;\r\n maxFps: number;\r\n avgFrameTime: number;\r\n totalFrames: number;\r\n };\r\n memory: {\r\n current: number; // MB\r\n peak: number; // MB\r\n trend: 'increasing' | 'stable' | 'decreasing';\r\n };\r\n issues: PerformanceIssue[];\r\n hotspots: Array<{\r\n name: string;\r\n avgTime: number;\r\n maxTime: number;\r\n callCount: number;\r\n p95: number;\r\n p99: number;\r\n variance: number;\r\n percentOfFrame: number;\r\n }>;\r\n customMetrics: Record<string, {\r\n min: number;\r\n max: number;\r\n avg: number;\r\n current: number;\r\n }>;\r\n}\r\n\r\n/**\r\n * Génère un rapport de performance formaté en markdown\r\n * Optimisé pour être donné à une IA pour analyse\r\n */\r\nexport class ReportGenerator {\r\n private startTime: number = Date.now();\r\n private frameCount: number = 0;\r\n private fpsSamples: number[] = [];\r\n private frameTimeSamples: number[] = [];\r\n private memorySamples: number[] = [];\r\n private customMetricHistory: Map<string, number[]> = new Map();\r\n\r\n /**\r\n * Enregistre une frame pour les statistiques\r\n */\r\n recordFrame(fps: number, frameTime: number, memoryMB?: number) {\r\n this.frameCount++;\r\n this.fpsSamples.push(fps);\r\n this.frameTimeSamples.push(frameTime);\r\n\r\n if (memoryMB !== undefined) {\r\n this.memorySamples.push(memoryMB);\r\n }\r\n\r\n // Garder seulement les 1000 dernières frames\r\n if (this.fpsSamples.length > 1000) {\r\n this.fpsSamples.shift();\r\n this.frameTimeSamples.shift();\r\n if (this.memorySamples.length > 0) {\r\n this.memorySamples.shift();\r\n }\r\n }\r\n }\r\n\r\n /**\r\n * Enregistre une métrique custom\r\n */\r\n recordMetric(name: string, value: number) {\r\n if (!this.customMetricHistory.has(name)) {\r\n this.customMetricHistory.set(name, []);\r\n }\r\n const history = this.customMetricHistory.get(name)!;\r\n history.push(value);\r\n\r\n // Garder seulement les 100 dernières valeurs\r\n if (history.length > 100) {\r\n history.shift();\r\n }\r\n }\r\n\r\n /**\r\n * Génère le rapport complet\r\n */\r\n generateReport(): PerformanceReport {\r\n const duration = Date.now() - this.startTime;\r\n\r\n // Calculer les statistiques FPS\r\n const avgFps = this.average(this.fpsSamples);\r\n // Filter out potential Infinity values if happened\r\n const validFpsSamples = this.fpsSamples.filter(f => isFinite(f));\r\n const minFps = validFpsSamples.length > 0 ? Math.min(...validFpsSamples) : 0;\r\n const maxFps = validFpsSamples.length > 0 ? Math.max(...validFpsSamples) : 0;\r\n const avgFrameTime = this.average(this.frameTimeSamples);\r\n\r\n // Statistiques mémoire\r\n const currentMemory = this.memorySamples[this.memorySamples.length - 1] || 0;\r\n const peakMemory = Math.max(...this.memorySamples, 0);\r\n const memoryTrend = this.calculateTrend(this.memorySamples);\r\n\r\n // Détecter les problèmes\r\n const issues = this.detectIssues(avgFps, minFps, avgFrameTime, currentMemory, peakMemory);\r\n\r\n // Analyser les hotspots (depuis le profiler)\r\n const hotspots = this.extractHotspots();\r\n\r\n // Analyser les métriques custom\r\n const customMetrics = this.analyzeCustomMetrics();\r\n\r\n // Safe values for the report\r\n const safeAvgFps = isFinite(avgFps) ? avgFps : 0;\r\n const safeMinFps = isFinite(minFps) && minFps !== Infinity ? minFps : 0;\r\n const safeMaxFps = isFinite(maxFps) && maxFps !== -Infinity ? maxFps : 0;\r\n const safeAvgFrameTime = isFinite(avgFrameTime) ? avgFrameTime : 0;\r\n\r\n return {\r\n timestamp: new Date().toISOString(),\r\n duration,\r\n summary: {\r\n avgFps: safeAvgFps,\r\n minFps: safeMinFps,\r\n maxFps: safeMaxFps,\r\n avgFrameTime: safeAvgFrameTime,\r\n totalFrames: this.frameCount,\r\n },\r\n memory: {\r\n current: currentMemory,\r\n peak: peakMemory,\r\n trend: memoryTrend,\r\n },\r\n issues,\r\n hotspots,\r\n customMetrics,\r\n };\r\n }\r\n\r\n /**\r\n * Génère un rapport markdown lisible\r\n */\r\n generateMarkdownReport(): string {\r\n const report = this.generateReport();\r\n\r\n let md = '# 📊 Performance Report\\n\\n';\r\n md += `**Generated:** ${new Date(report.timestamp).toLocaleString()}\\n`;\r\n md += `**Duration:** ${(report.duration / 1000).toFixed(1)}s\\n\\n`;\r\n\r\n // Résumé\r\n md += '## Summary\\n\\n';\r\n md += `- **FPS:** ${report.summary.avgFps.toFixed(1)} avg (${report.summary.minFps.toFixed(0)} min, ${report.summary.maxFps.toFixed(0)} max)\\n`;\r\n md += `- **Frame Time:** ${report.summary.avgFrameTime.toFixed(2)}ms avg\\n`;\r\n md += `- **Total Frames:** ${report.summary.totalFrames}\\n`;\r\n md += `- **Memory:** ${report.memory.current.toFixed(1)}MB current, ${report.memory.peak.toFixed(1)}MB peak (${report.memory.trend})\\n\\n`;\r\n\r\n // Issues critiques\r\n if (report.issues.length > 0) {\r\n md += '## 🚨 Issues Detected\\n\\n';\r\n\r\n const critical = report.issues.filter(i => i.severity === 'critical');\r\n const warnings = report.issues.filter(i => i.severity === 'warning');\r\n const info = report.issues.filter(i => i.severity === 'info');\r\n\r\n if (critical.length > 0) {\r\n md += '### Critical\\n\\n';\r\n critical.forEach(issue => {\r\n md += `- **${issue.metric}:** ${issue.value.toFixed(2)} (threshold: ${issue.threshold})\\n`;\r\n md += ` - 💡 ${issue.suggestion}\\n`;\r\n });\r\n md += '\\n';\r\n }\r\n\r\n if (warnings.length > 0) {\r\n md += '### Warnings\\n\\n';\r\n warnings.forEach(issue => {\r\n md += `- **${issue.metric}:** ${issue.value.toFixed(2)} (threshold: ${issue.threshold})\\n`;\r\n md += ` - 💡 ${issue.suggestion}\\n`;\r\n });\r\n md += '\\n';\r\n }\r\n\r\n if (info.length > 0) {\r\n md += '### Info\\n\\n';\r\n info.forEach(issue => {\r\n md += `- ${issue.metric}: ${issue.value.toFixed(2)}\\n`;\r\n });\r\n md += '\\n';\r\n }\r\n } else {\r\n md += '## ✅ No Issues Detected\\n\\n';\r\n }\r\n\r\n // Hotspots\r\n if (report.hotspots.length > 0) {\r\n md += '## 🔥 Performance Hotspots\\n\\n';\r\n md += 'Functions consuming the most time:\\n\\n';\r\n\r\n // Table header\r\n md += '| # | Function | Avg | p95 | p99 | Max | % Frame | Jitter |\\n';\r\n md += '|---|----------|-----|-----|-----|-----|---------|--------|\\n';\r\n\r\n report.hotspots.slice(0, 10).forEach((hotspot: any, i: number) => {\r\n const jitterIndicator = hotspot.variance > 1 ? '⚠️ High' : hotspot.variance > 0.1 ? '~' : '✓';\r\n const percentFrame = hotspot.percentOfFrame?.toFixed(1) || '0.0';\r\n md += `| ${i + 1} | **${hotspot.name}** | ${hotspot.avgTime.toFixed(2)}ms | ${(hotspot.p95 || 0).toFixed(2)}ms | ${(hotspot.p99 || 0).toFixed(2)}ms | ${hotspot.maxTime.toFixed(2)}ms | ${percentFrame}% | ${jitterIndicator} |\\n`;\r\n });\r\n\r\n md += '\\n';\r\n\r\n // Add specific insights\r\n const topHotspot = report.hotspots[0];\r\n if (topHotspot && topHotspot.name !== 'Frame' && topHotspot.percentOfFrame > 30) {\r\n md += `> [!WARNING]\\n`;\r\n md += `> **${topHotspot.name}** consumes ${topHotspot.percentOfFrame.toFixed(1)}% of frame time. Consider optimizing this function.\\n\\n`;\r\n }\r\n\r\n // Find high variance functions\r\n const highVariance = report.hotspots.filter((h: any) => h.variance > 1 && h.name !== 'Frame');\r\n if (highVariance.length > 0) {\r\n md += `> [!CAUTION]\\n`;\r\n md += `> High jitter detected in: ${highVariance.map((h: any) => h.name).join(', ')}. These cause frame rate stuttering.\\n\\n`;\r\n }\r\n }\r\n\r\n // Métriques custom\r\n if (Object.keys(report.customMetrics).length > 0) {\r\n md += '## 📈 Custom Metrics\\n\\n';\r\n Object.entries(report.customMetrics).forEach(([name, stats]) => {\r\n md += `- **${name}**: ${stats.current.toFixed(2)} (min: ${stats.min.toFixed(2)}, max: ${stats.max.toFixed(2)}, avg: ${stats.avg.toFixed(2)})\\n`;\r\n });\r\n md += '\\n';\r\n }\r\n\r\n // Recommandations\r\n md += '## 💡 Recommendations\\n\\n';\r\n\r\n // Trier les problèmes par sévérité (critical d'abord)\r\n const sortedIssues = [...report.issues].sort((a, b) => {\r\n const severityScore = { critical: 3, warning: 2, info: 1 };\r\n return severityScore[b.severity] - severityScore[a.severity];\r\n });\r\n\r\n if (sortedIssues.length > 0) {\r\n md += 'Priority actions:\\n\\n';\r\n sortedIssues\r\n .slice(0, 5) // Show top 5 issues instead of 3\r\n .forEach((issue, i) => {\r\n const icon = issue.severity === 'critical' ? '🔴' : issue.severity === 'warning' ? '⚠️' : 'ℹ️';\r\n md += `${i + 1}. ${icon} ${issue.suggestion}\\n`;\r\n });\r\n } else {\r\n md += '✅ Performance looks good! Continue monitoring.\\n\\n';\r\n // Add general advice when everything is good\r\n md += '> [!TIP]\\n';\r\n md += '> Keep an eye on the \"Hotspots\" section to prevent future bottlenecks as your scene grows.\\n';\r\n }\r\n\r\n return md;\r\n }\r\n\r\n /**\r\n * Copie le rapport dans le clipboard\r\n */\r\n async copyReportToClipboard(): Promise<void> {\r\n const report = this.generateMarkdownReport();\r\n try {\r\n await navigator.clipboard.writeText(report);\r\n console.log('✅ Report copied to clipboard!');\r\n } catch (error) {\r\n console.error('Failed to copy report:', error);\r\n // Fallback: afficher dans la console\r\n console.log('📋 Report:\\n\\n' + report);\r\n }\r\n }\r\n\r\n // === Méthodes utilitaires ===\r\n\r\n private average(arr: number[]): number {\r\n if (arr.length === 0) return 0;\r\n return arr.reduce((a, b) => a + b, 0) / arr.length;\r\n }\r\n\r\n private calculateTrend(samples: number[]): 'increasing' | 'stable' | 'decreasing' {\r\n if (samples.length < 10) return 'stable';\r\n\r\n const recent = samples.slice(-10);\r\n const older = samples.slice(-20, -10);\r\n\r\n if (older.length === 0) return 'stable';\r\n\r\n const recentAvg = this.average(recent);\r\n const olderAvg = this.average(older);\r\n const diff = recentAvg - olderAvg;\r\n const threshold = olderAvg * 0.1; // 10% threshold\r\n\r\n if (diff > threshold) return 'increasing';\r\n if (diff < -threshold) return 'decreasing';\r\n return 'stable';\r\n }\r\n\r\n private detectIssues(avgFps: number, minFps: number, avgFrameTime: number, currentMemory: number, _peakMemory: number): PerformanceIssue[] {\r\n const issues: PerformanceIssue[] = [];\r\n\r\n // FPS bas\r\n if (avgFps < 30) {\r\n issues.push({\r\n severity: 'critical',\r\n metric: 'Average FPS',\r\n value: avgFps,\r\n threshold: 30,\r\n suggestion: 'FPS critically low. Check frame time hotspots, reduce draw calls, optimize physics/collision detection.',\r\n });\r\n } else if (avgFps < 50) {\r\n issues.push({\r\n severity: 'warning',\r\n metric: 'Average FPS',\r\n value: avgFps,\r\n threshold: 50,\r\n suggestion: 'FPS below optimal. Consider reducing shadow quality, LOD distance, or particle count.',\r\n });\r\n }\r\n\r\n // Drops FPS\r\n if (minFps < avgFps * 0.5) {\r\n issues.push({\r\n severity: 'warning',\r\n metric: 'FPS Drops',\r\n value: minFps,\r\n threshold: avgFps * 0.5,\r\n suggestion: 'Severe FPS drops detected. Check for GC pauses, asset loading spikes, or physics explosions.',\r\n });\r\n }\r\n\r\n // Frame time élevé\r\n if (avgFrameTime > 33) {\r\n issues.push({\r\n severity: 'critical',\r\n metric: 'Frame Time',\r\n value: avgFrameTime,\r\n threshold: 33,\r\n suggestion: 'Frame time exceeds 33ms budget. Profile CPU-heavy operations.',\r\n });\r\n } else if (avgFrameTime > 16) {\r\n issues.push({\r\n severity: 'warning',\r\n metric: 'Frame Time',\r\n value: avgFrameTime,\r\n threshold: 16,\r\n suggestion: 'Frame time exceeds 60 FPS target (16.67ms). Optimize main thread work.',\r\n });\r\n }\r\n\r\n // Mémoire élevée\r\n if (currentMemory > 500) {\r\n issues.push({\r\n severity: 'critical',\r\n metric: 'Memory Usage',\r\n value: currentMemory,\r\n threshold: 500,\r\n suggestion: 'High memory usage. Check for memory leaks, dispose unused textures/geometries.',\r\n });\r\n } else if (currentMemory > 300) {\r\n issues.push({\r\n severity: 'warning',\r\n metric: 'Memory Usage',\r\n value: currentMemory,\r\n threshold: 300,\r\n suggestion: 'Elevated memory usage. Consider texture compression, geometry pooling.',\r\n });\r\n }\r\n\r\n return issues;\r\n }\r\n\r\n private extractHotspots() {\r\n const data = profiler.getData();\r\n const rawData = profiler.getRawData();\r\n const hotspots: Array<{\r\n name: string;\r\n avgTime: number;\r\n maxTime: number;\r\n callCount: number;\r\n p95: number;\r\n p99: number;\r\n variance: number;\r\n percentOfFrame: number;\r\n }> = [];\r\n\r\n // Liste des patterns de métriques custom à exclure des hotspots\r\n const customMetricPatterns = [\r\n 'Camera', 'Ball', 'Distance', 'Physics', 'Opacity', 'Following',\r\n '(deg)', '(m/s)', '(rad/s)', '(m)', 'Speed', 'Height', 'Moving',\r\n 'Occluded', 'Azimuth', 'Polar', 'Bodies', 'Colliders', 'Joints'\r\n ];\r\n\r\n // Get Frame time for percentage calculation\r\n let frameAvgTime = 0;\r\n if (rawData) {\r\n const frameData = rawData.get('Frame');\r\n if (frameData && frameData.calls > 0) {\r\n frameAvgTime = frameData.total / frameData.calls;\r\n }\r\n }\r\n\r\n if (data && data.entries && rawData) {\r\n data.entries.forEach((entry: any) => {\r\n // Exclure les métriques custom qui ne sont pas des mesures de temps réelles\r\n const isCustomMetric = customMetricPatterns.some(pattern =>\r\n entry.name.includes(pattern)\r\n );\r\n\r\n if (!isCustomMetric) {\r\n const raw = rawData.get(entry.name);\r\n const times = raw?.times || [];\r\n\r\n // Calculate percentiles\r\n const sorted = [...times].sort((a, b) => a - b);\r\n const p95 = sorted.length > 0 ? sorted[Math.floor(sorted.length * 0.95)] || 0 : 0;\r\n const p99 = sorted.length > 0 ? sorted[Math.floor(sorted.length * 0.99)] || 0 : 0;\r\n\r\n // Calculate variance\r\n const avg = entry.avg || 0;\r\n const variance = times.length > 1\r\n ? times.reduce((sum, t) => sum + Math.pow(t - avg, 2), 0) / times.length\r\n : 0;\r\n\r\n // Calculate percentage of frame time\r\n const percentOfFrame = frameAvgTime > 0 ? (avg / frameAvgTime) * 100 : 0;\r\n\r\n hotspots.push({\r\n name: entry.name,\r\n avgTime: avg,\r\n maxTime: entry.max || 0,\r\n callCount: entry.calls || 0,\r\n p95,\r\n p99,\r\n variance,\r\n percentOfFrame,\r\n });\r\n }\r\n });\r\n }\r\n\r\n // Trier par temps moyen décroissant\r\n return hotspots.sort((a, b) => b.avgTime - a.avgTime);\r\n }\r\n\r\n private analyzeCustomMetrics() {\r\n const metrics: Record<string, { min: number; max: number; avg: number; current: number }> = {};\r\n\r\n this.customMetricHistory.forEach((samples, name) => {\r\n if (samples.length > 0) {\r\n metrics[name] = {\r\n min: Math.min(...samples),\r\n max: Math.max(...samples),\r\n avg: this.average(samples),\r\n current: samples[samples.length - 1],\r\n };\r\n }\r\n });\r\n\r\n return metrics;\r\n }\r\n\r\n /**\r\n * Reset toutes les statistiques\r\n */\r\n reset() {\r\n this.startTime = Date.now();\r\n this.frameCount = 0;\r\n this.fpsSamples = [];\r\n this.frameTimeSamples = [];\r\n this.memorySamples = [];\r\n this.customMetricHistory.clear();\r\n }\r\n}\r\n\r\n// Instance globale\r\nexport const reportGenerator = new ReportGenerator();\r\n"],"names":["ReportGenerator","fps","frameTime","memoryMB","name","value","history","duration","avgFps","validFpsSamples","f","minFps","maxFps","avgFrameTime","currentMemory","peakMemory","memoryTrend","issues","hotspots","customMetrics","safeAvgFps","safeMinFps","safeMaxFps","safeAvgFrameTime","report","md","critical","i","warnings","info","issue","hotspot","jitterIndicator","percentFrame","topHotspot","highVariance","h","stats","sortedIssues","a","b","severityScore","icon","error","arr","samples","recent","older","recentAvg","olderAvg","diff","threshold","_peakMemory","data","profiler","rawData","customMetricPatterns","frameAvgTime","frameData","entry","pattern","times","sorted","p95","p99","avg","variance","sum","t","percentOfFrame","metrics","reportGenerator"],"mappings":"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"}
@@ -0,0 +1,61 @@
1
+ "use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const y=require("./index-D_LaF0YP.cjs");class F{startTime=Date.now();frameCount=0;fpsSamples=[];frameTimeSamples=[];memorySamples=[];customMetricHistory=new Map;recordFrame(t,e,r){this.frameCount++,this.fpsSamples.push(t),this.frameTimeSamples.push(e),r!==void 0&&this.memorySamples.push(r),this.fpsSamples.length>1e3&&(this.fpsSamples.shift(),this.frameTimeSamples.shift(),this.memorySamples.length>0&&this.memorySamples.shift())}recordMetric(t,e){this.customMetricHistory.has(t)||this.customMetricHistory.set(t,[]);const r=this.customMetricHistory.get(t);r.push(e),r.length>100&&r.shift()}generateReport(){const t=Date.now()-this.startTime,e=this.average(this.fpsSamples),r=this.fpsSamples.filter(g=>isFinite(g)),i=r.length>0?Math.min(...r):0,n=r.length>0?Math.max(...r):0,s=this.average(this.frameTimeSamples),o=this.memorySamples[this.memorySamples.length-1]||0,m=Math.max(...this.memorySamples,0),a=this.calculateTrend(this.memorySamples),c=this.detectIssues(e,i,s,o,m),u=this.extractHotspots(),p=this.analyzeCustomMetrics(),l=isFinite(e)?e:0,d=isFinite(i)&&i!==1/0?i:0,f=isFinite(n)&&n!==-1/0?n:0,h=isFinite(s)?s:0;return{timestamp:new Date().toISOString(),duration:t,summary:{avgFps:l,minFps:d,maxFps:f,avgFrameTime:h,totalFrames:this.frameCount},memory:{current:o,peak:m,trend:a},issues:c,hotspots:u,customMetrics:p}}generateMarkdownReport(){const t=this.generateReport();let e=`# 📊 Performance Report
2
+
3
+ `;if(e+=`**Generated:** ${new Date(t.timestamp).toLocaleString()}
4
+ `,e+=`**Duration:** ${(t.duration/1e3).toFixed(1)}s
5
+
6
+ `,e+=`## Summary
7
+
8
+ `,e+=`- **FPS:** ${t.summary.avgFps.toFixed(1)} avg (${t.summary.minFps.toFixed(0)} min, ${t.summary.maxFps.toFixed(0)} max)
9
+ `,e+=`- **Frame Time:** ${t.summary.avgFrameTime.toFixed(2)}ms avg
10
+ `,e+=`- **Total Frames:** ${t.summary.totalFrames}
11
+ `,e+=`- **Memory:** ${t.memory.current.toFixed(1)}MB current, ${t.memory.peak.toFixed(1)}MB peak (${t.memory.trend})
12
+
13
+ `,t.issues.length>0){e+=`## 🚨 Issues Detected
14
+
15
+ `;const i=t.issues.filter(o=>o.severity==="critical"),n=t.issues.filter(o=>o.severity==="warning"),s=t.issues.filter(o=>o.severity==="info");i.length>0&&(e+=`### Critical
16
+
17
+ `,i.forEach(o=>{e+=`- **${o.metric}:** ${o.value.toFixed(2)} (threshold: ${o.threshold})
18
+ `,e+=` - 💡 ${o.suggestion}
19
+ `}),e+=`
20
+ `),n.length>0&&(e+=`### Warnings
21
+
22
+ `,n.forEach(o=>{e+=`- **${o.metric}:** ${o.value.toFixed(2)} (threshold: ${o.threshold})
23
+ `,e+=` - 💡 ${o.suggestion}
24
+ `}),e+=`
25
+ `),s.length>0&&(e+=`### Info
26
+
27
+ `,s.forEach(o=>{e+=`- ${o.metric}: ${o.value.toFixed(2)}
28
+ `}),e+=`
29
+ `)}else e+=`## ✅ No Issues Detected
30
+
31
+ `;if(t.hotspots.length>0){e+=`## 🔥 Performance Hotspots
32
+
33
+ `,e+=`Functions consuming the most time:
34
+
35
+ `,e+=`| # | Function | Avg | p95 | p99 | Max | % Frame | Jitter |
36
+ `,e+=`|---|----------|-----|-----|-----|-----|---------|--------|
37
+ `,t.hotspots.slice(0,10).forEach((s,o)=>{const m=s.variance>1?"⚠️ High":s.variance>.1?"~":"✓",a=s.percentOfFrame?.toFixed(1)||"0.0";e+=`| ${o+1} | **${s.name}** | ${s.avgTime.toFixed(2)}ms | ${(s.p95||0).toFixed(2)}ms | ${(s.p99||0).toFixed(2)}ms | ${s.maxTime.toFixed(2)}ms | ${a}% | ${m} |
38
+ `}),e+=`
39
+ `;const i=t.hotspots[0];i&&i.name!=="Frame"&&i.percentOfFrame>30&&(e+=`> [!WARNING]
40
+ `,e+=`> **${i.name}** consumes ${i.percentOfFrame.toFixed(1)}% of frame time. Consider optimizing this function.
41
+
42
+ `);const n=t.hotspots.filter(s=>s.variance>1&&s.name!=="Frame");n.length>0&&(e+=`> [!CAUTION]
43
+ `,e+=`> High jitter detected in: ${n.map(s=>s.name).join(", ")}. These cause frame rate stuttering.
44
+
45
+ `)}Object.keys(t.customMetrics).length>0&&(e+=`## 📈 Custom Metrics
46
+
47
+ `,Object.entries(t.customMetrics).forEach(([i,n])=>{e+=`- **${i}**: ${n.current.toFixed(2)} (min: ${n.min.toFixed(2)}, max: ${n.max.toFixed(2)}, avg: ${n.avg.toFixed(2)})
48
+ `}),e+=`
49
+ `),e+=`## 💡 Recommendations
50
+
51
+ `;const r=[...t.issues].sort((i,n)=>{const s={critical:3,warning:2,info:1};return s[n.severity]-s[i.severity]});return r.length>0?(e+=`Priority actions:
52
+
53
+ `,r.slice(0,5).forEach((i,n)=>{const s=i.severity==="critical"?"🔴":i.severity==="warning"?"⚠️":"ℹ️";e+=`${n+1}. ${s} ${i.suggestion}
54
+ `})):(e+=`✅ Performance looks good! Continue monitoring.
55
+
56
+ `,e+=`> [!TIP]
57
+ `,e+=`> Keep an eye on the "Hotspots" section to prevent future bottlenecks as your scene grows.
58
+ `),e}async copyReportToClipboard(){const t=this.generateMarkdownReport();try{await navigator.clipboard.writeText(t),console.log("✅ Report copied to clipboard!")}catch(e){console.error("Failed to copy report:",e),console.log(`📋 Report:
59
+
60
+ `+t)}}average(t){return t.length===0?0:t.reduce((e,r)=>e+r,0)/t.length}calculateTrend(t){if(t.length<10)return"stable";const e=t.slice(-10),r=t.slice(-20,-10);if(r.length===0)return"stable";const i=this.average(e),n=this.average(r),s=i-n,o=n*.1;return s>o?"increasing":s<-o?"decreasing":"stable"}detectIssues(t,e,r,i,n){const s=[];return t<30?s.push({severity:"critical",metric:"Average FPS",value:t,threshold:30,suggestion:"FPS critically low. Check frame time hotspots, reduce draw calls, optimize physics/collision detection."}):t<50&&s.push({severity:"warning",metric:"Average FPS",value:t,threshold:50,suggestion:"FPS below optimal. Consider reducing shadow quality, LOD distance, or particle count."}),e<t*.5&&s.push({severity:"warning",metric:"FPS Drops",value:e,threshold:t*.5,suggestion:"Severe FPS drops detected. Check for GC pauses, asset loading spikes, or physics explosions."}),r>33?s.push({severity:"critical",metric:"Frame Time",value:r,threshold:33,suggestion:"Frame time exceeds 33ms budget. Profile CPU-heavy operations."}):r>16&&s.push({severity:"warning",metric:"Frame Time",value:r,threshold:16,suggestion:"Frame time exceeds 60 FPS target (16.67ms). Optimize main thread work."}),i>500?s.push({severity:"critical",metric:"Memory Usage",value:i,threshold:500,suggestion:"High memory usage. Check for memory leaks, dispose unused textures/geometries."}):i>300&&s.push({severity:"warning",metric:"Memory Usage",value:i,threshold:300,suggestion:"Elevated memory usage. Consider texture compression, geometry pooling."}),s}extractHotspots(){const t=y.profiler.getData(),e=y.profiler.getRawData(),r=[],i=["Camera","Ball","Distance","Physics","Opacity","Following","(deg)","(m/s)","(rad/s)","(m)","Speed","Height","Moving","Occluded","Azimuth","Polar","Bodies","Colliders","Joints"];let n=0;if(e){const s=e.get("Frame");s&&s.calls>0&&(n=s.total/s.calls)}return t&&t.entries&&e&&t.entries.forEach(s=>{if(!i.some(m=>s.name.includes(m))){const a=e.get(s.name)?.times||[],c=[...a].sort((h,g)=>h-g),u=c.length>0&&c[Math.floor(c.length*.95)]||0,p=c.length>0&&c[Math.floor(c.length*.99)]||0,l=s.avg||0,d=a.length>1?a.reduce((h,g)=>h+Math.pow(g-l,2),0)/a.length:0,f=n>0?l/n*100:0;r.push({name:s.name,avgTime:l,maxTime:s.max||0,callCount:s.calls||0,p95:u,p99:p,variance:d,percentOfFrame:f})}}),r.sort((s,o)=>o.avgTime-s.avgTime)}analyzeCustomMetrics(){const t={};return this.customMetricHistory.forEach((e,r)=>{e.length>0&&(t[r]={min:Math.min(...e),max:Math.max(...e),avg:this.average(e),current:e[e.length-1]})}),t}reset(){this.startTime=Date.now(),this.frameCount=0,this.fpsSamples=[],this.frameTimeSamples=[],this.memorySamples=[],this.customMetricHistory.clear()}}const v=new F;exports.ReportGenerator=F;exports.reportGenerator=v;
61
+ //# sourceMappingURL=report-generator-BsoP53qp.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"report-generator-BsoP53qp.cjs","sources":["../src/core/report-generator.ts"],"sourcesContent":["import { profiler } from './profiler';\r\n\r\nexport interface PerformanceIssue {\r\n severity: 'critical' | 'warning' | 'info';\r\n metric: string;\r\n value: number;\r\n threshold: number;\r\n suggestion: string;\r\n}\r\n\r\nexport interface PerformanceReport {\r\n timestamp: string;\r\n duration: number; // Durée de la session en ms\r\n summary: {\r\n avgFps: number;\r\n minFps: number;\r\n maxFps: number;\r\n avgFrameTime: number;\r\n totalFrames: number;\r\n };\r\n memory: {\r\n current: number; // MB\r\n peak: number; // MB\r\n trend: 'increasing' | 'stable' | 'decreasing';\r\n };\r\n issues: PerformanceIssue[];\r\n hotspots: Array<{\r\n name: string;\r\n avgTime: number;\r\n maxTime: number;\r\n callCount: number;\r\n p95: number;\r\n p99: number;\r\n variance: number;\r\n percentOfFrame: number;\r\n }>;\r\n customMetrics: Record<string, {\r\n min: number;\r\n max: number;\r\n avg: number;\r\n current: number;\r\n }>;\r\n}\r\n\r\n/**\r\n * Génère un rapport de performance formaté en markdown\r\n * Optimisé pour être donné à une IA pour analyse\r\n */\r\nexport class ReportGenerator {\r\n private startTime: number = Date.now();\r\n private frameCount: number = 0;\r\n private fpsSamples: number[] = [];\r\n private frameTimeSamples: number[] = [];\r\n private memorySamples: number[] = [];\r\n private customMetricHistory: Map<string, number[]> = new Map();\r\n\r\n /**\r\n * Enregistre une frame pour les statistiques\r\n */\r\n recordFrame(fps: number, frameTime: number, memoryMB?: number) {\r\n this.frameCount++;\r\n this.fpsSamples.push(fps);\r\n this.frameTimeSamples.push(frameTime);\r\n\r\n if (memoryMB !== undefined) {\r\n this.memorySamples.push(memoryMB);\r\n }\r\n\r\n // Garder seulement les 1000 dernières frames\r\n if (this.fpsSamples.length > 1000) {\r\n this.fpsSamples.shift();\r\n this.frameTimeSamples.shift();\r\n if (this.memorySamples.length > 0) {\r\n this.memorySamples.shift();\r\n }\r\n }\r\n }\r\n\r\n /**\r\n * Enregistre une métrique custom\r\n */\r\n recordMetric(name: string, value: number) {\r\n if (!this.customMetricHistory.has(name)) {\r\n this.customMetricHistory.set(name, []);\r\n }\r\n const history = this.customMetricHistory.get(name)!;\r\n history.push(value);\r\n\r\n // Garder seulement les 100 dernières valeurs\r\n if (history.length > 100) {\r\n history.shift();\r\n }\r\n }\r\n\r\n /**\r\n * Génère le rapport complet\r\n */\r\n generateReport(): PerformanceReport {\r\n const duration = Date.now() - this.startTime;\r\n\r\n // Calculer les statistiques FPS\r\n const avgFps = this.average(this.fpsSamples);\r\n // Filter out potential Infinity values if happened\r\n const validFpsSamples = this.fpsSamples.filter(f => isFinite(f));\r\n const minFps = validFpsSamples.length > 0 ? Math.min(...validFpsSamples) : 0;\r\n const maxFps = validFpsSamples.length > 0 ? Math.max(...validFpsSamples) : 0;\r\n const avgFrameTime = this.average(this.frameTimeSamples);\r\n\r\n // Statistiques mémoire\r\n const currentMemory = this.memorySamples[this.memorySamples.length - 1] || 0;\r\n const peakMemory = Math.max(...this.memorySamples, 0);\r\n const memoryTrend = this.calculateTrend(this.memorySamples);\r\n\r\n // Détecter les problèmes\r\n const issues = this.detectIssues(avgFps, minFps, avgFrameTime, currentMemory, peakMemory);\r\n\r\n // Analyser les hotspots (depuis le profiler)\r\n const hotspots = this.extractHotspots();\r\n\r\n // Analyser les métriques custom\r\n const customMetrics = this.analyzeCustomMetrics();\r\n\r\n // Safe values for the report\r\n const safeAvgFps = isFinite(avgFps) ? avgFps : 0;\r\n const safeMinFps = isFinite(minFps) && minFps !== Infinity ? minFps : 0;\r\n const safeMaxFps = isFinite(maxFps) && maxFps !== -Infinity ? maxFps : 0;\r\n const safeAvgFrameTime = isFinite(avgFrameTime) ? avgFrameTime : 0;\r\n\r\n return {\r\n timestamp: new Date().toISOString(),\r\n duration,\r\n summary: {\r\n avgFps: safeAvgFps,\r\n minFps: safeMinFps,\r\n maxFps: safeMaxFps,\r\n avgFrameTime: safeAvgFrameTime,\r\n totalFrames: this.frameCount,\r\n },\r\n memory: {\r\n current: currentMemory,\r\n peak: peakMemory,\r\n trend: memoryTrend,\r\n },\r\n issues,\r\n hotspots,\r\n customMetrics,\r\n };\r\n }\r\n\r\n /**\r\n * Génère un rapport markdown lisible\r\n */\r\n generateMarkdownReport(): string {\r\n const report = this.generateReport();\r\n\r\n let md = '# 📊 Performance Report\\n\\n';\r\n md += `**Generated:** ${new Date(report.timestamp).toLocaleString()}\\n`;\r\n md += `**Duration:** ${(report.duration / 1000).toFixed(1)}s\\n\\n`;\r\n\r\n // Résumé\r\n md += '## Summary\\n\\n';\r\n md += `- **FPS:** ${report.summary.avgFps.toFixed(1)} avg (${report.summary.minFps.toFixed(0)} min, ${report.summary.maxFps.toFixed(0)} max)\\n`;\r\n md += `- **Frame Time:** ${report.summary.avgFrameTime.toFixed(2)}ms avg\\n`;\r\n md += `- **Total Frames:** ${report.summary.totalFrames}\\n`;\r\n md += `- **Memory:** ${report.memory.current.toFixed(1)}MB current, ${report.memory.peak.toFixed(1)}MB peak (${report.memory.trend})\\n\\n`;\r\n\r\n // Issues critiques\r\n if (report.issues.length > 0) {\r\n md += '## 🚨 Issues Detected\\n\\n';\r\n\r\n const critical = report.issues.filter(i => i.severity === 'critical');\r\n const warnings = report.issues.filter(i => i.severity === 'warning');\r\n const info = report.issues.filter(i => i.severity === 'info');\r\n\r\n if (critical.length > 0) {\r\n md += '### Critical\\n\\n';\r\n critical.forEach(issue => {\r\n md += `- **${issue.metric}:** ${issue.value.toFixed(2)} (threshold: ${issue.threshold})\\n`;\r\n md += ` - 💡 ${issue.suggestion}\\n`;\r\n });\r\n md += '\\n';\r\n }\r\n\r\n if (warnings.length > 0) {\r\n md += '### Warnings\\n\\n';\r\n warnings.forEach(issue => {\r\n md += `- **${issue.metric}:** ${issue.value.toFixed(2)} (threshold: ${issue.threshold})\\n`;\r\n md += ` - 💡 ${issue.suggestion}\\n`;\r\n });\r\n md += '\\n';\r\n }\r\n\r\n if (info.length > 0) {\r\n md += '### Info\\n\\n';\r\n info.forEach(issue => {\r\n md += `- ${issue.metric}: ${issue.value.toFixed(2)}\\n`;\r\n });\r\n md += '\\n';\r\n }\r\n } else {\r\n md += '## ✅ No Issues Detected\\n\\n';\r\n }\r\n\r\n // Hotspots\r\n if (report.hotspots.length > 0) {\r\n md += '## 🔥 Performance Hotspots\\n\\n';\r\n md += 'Functions consuming the most time:\\n\\n';\r\n\r\n // Table header\r\n md += '| # | Function | Avg | p95 | p99 | Max | % Frame | Jitter |\\n';\r\n md += '|---|----------|-----|-----|-----|-----|---------|--------|\\n';\r\n\r\n report.hotspots.slice(0, 10).forEach((hotspot: any, i: number) => {\r\n const jitterIndicator = hotspot.variance > 1 ? '⚠️ High' : hotspot.variance > 0.1 ? '~' : '✓';\r\n const percentFrame = hotspot.percentOfFrame?.toFixed(1) || '0.0';\r\n md += `| ${i + 1} | **${hotspot.name}** | ${hotspot.avgTime.toFixed(2)}ms | ${(hotspot.p95 || 0).toFixed(2)}ms | ${(hotspot.p99 || 0).toFixed(2)}ms | ${hotspot.maxTime.toFixed(2)}ms | ${percentFrame}% | ${jitterIndicator} |\\n`;\r\n });\r\n\r\n md += '\\n';\r\n\r\n // Add specific insights\r\n const topHotspot = report.hotspots[0];\r\n if (topHotspot && topHotspot.name !== 'Frame' && topHotspot.percentOfFrame > 30) {\r\n md += `> [!WARNING]\\n`;\r\n md += `> **${topHotspot.name}** consumes ${topHotspot.percentOfFrame.toFixed(1)}% of frame time. Consider optimizing this function.\\n\\n`;\r\n }\r\n\r\n // Find high variance functions\r\n const highVariance = report.hotspots.filter((h: any) => h.variance > 1 && h.name !== 'Frame');\r\n if (highVariance.length > 0) {\r\n md += `> [!CAUTION]\\n`;\r\n md += `> High jitter detected in: ${highVariance.map((h: any) => h.name).join(', ')}. These cause frame rate stuttering.\\n\\n`;\r\n }\r\n }\r\n\r\n // Métriques custom\r\n if (Object.keys(report.customMetrics).length > 0) {\r\n md += '## 📈 Custom Metrics\\n\\n';\r\n Object.entries(report.customMetrics).forEach(([name, stats]) => {\r\n md += `- **${name}**: ${stats.current.toFixed(2)} (min: ${stats.min.toFixed(2)}, max: ${stats.max.toFixed(2)}, avg: ${stats.avg.toFixed(2)})\\n`;\r\n });\r\n md += '\\n';\r\n }\r\n\r\n // Recommandations\r\n md += '## 💡 Recommendations\\n\\n';\r\n\r\n // Trier les problèmes par sévérité (critical d'abord)\r\n const sortedIssues = [...report.issues].sort((a, b) => {\r\n const severityScore = { critical: 3, warning: 2, info: 1 };\r\n return severityScore[b.severity] - severityScore[a.severity];\r\n });\r\n\r\n if (sortedIssues.length > 0) {\r\n md += 'Priority actions:\\n\\n';\r\n sortedIssues\r\n .slice(0, 5) // Show top 5 issues instead of 3\r\n .forEach((issue, i) => {\r\n const icon = issue.severity === 'critical' ? '🔴' : issue.severity === 'warning' ? '⚠️' : 'ℹ️';\r\n md += `${i + 1}. ${icon} ${issue.suggestion}\\n`;\r\n });\r\n } else {\r\n md += '✅ Performance looks good! Continue monitoring.\\n\\n';\r\n // Add general advice when everything is good\r\n md += '> [!TIP]\\n';\r\n md += '> Keep an eye on the \"Hotspots\" section to prevent future bottlenecks as your scene grows.\\n';\r\n }\r\n\r\n return md;\r\n }\r\n\r\n /**\r\n * Copie le rapport dans le clipboard\r\n */\r\n async copyReportToClipboard(): Promise<void> {\r\n const report = this.generateMarkdownReport();\r\n try {\r\n await navigator.clipboard.writeText(report);\r\n console.log('✅ Report copied to clipboard!');\r\n } catch (error) {\r\n console.error('Failed to copy report:', error);\r\n // Fallback: afficher dans la console\r\n console.log('📋 Report:\\n\\n' + report);\r\n }\r\n }\r\n\r\n // === Méthodes utilitaires ===\r\n\r\n private average(arr: number[]): number {\r\n if (arr.length === 0) return 0;\r\n return arr.reduce((a, b) => a + b, 0) / arr.length;\r\n }\r\n\r\n private calculateTrend(samples: number[]): 'increasing' | 'stable' | 'decreasing' {\r\n if (samples.length < 10) return 'stable';\r\n\r\n const recent = samples.slice(-10);\r\n const older = samples.slice(-20, -10);\r\n\r\n if (older.length === 0) return 'stable';\r\n\r\n const recentAvg = this.average(recent);\r\n const olderAvg = this.average(older);\r\n const diff = recentAvg - olderAvg;\r\n const threshold = olderAvg * 0.1; // 10% threshold\r\n\r\n if (diff > threshold) return 'increasing';\r\n if (diff < -threshold) return 'decreasing';\r\n return 'stable';\r\n }\r\n\r\n private detectIssues(avgFps: number, minFps: number, avgFrameTime: number, currentMemory: number, _peakMemory: number): PerformanceIssue[] {\r\n const issues: PerformanceIssue[] = [];\r\n\r\n // FPS bas\r\n if (avgFps < 30) {\r\n issues.push({\r\n severity: 'critical',\r\n metric: 'Average FPS',\r\n value: avgFps,\r\n threshold: 30,\r\n suggestion: 'FPS critically low. Check frame time hotspots, reduce draw calls, optimize physics/collision detection.',\r\n });\r\n } else if (avgFps < 50) {\r\n issues.push({\r\n severity: 'warning',\r\n metric: 'Average FPS',\r\n value: avgFps,\r\n threshold: 50,\r\n suggestion: 'FPS below optimal. Consider reducing shadow quality, LOD distance, or particle count.',\r\n });\r\n }\r\n\r\n // Drops FPS\r\n if (minFps < avgFps * 0.5) {\r\n issues.push({\r\n severity: 'warning',\r\n metric: 'FPS Drops',\r\n value: minFps,\r\n threshold: avgFps * 0.5,\r\n suggestion: 'Severe FPS drops detected. Check for GC pauses, asset loading spikes, or physics explosions.',\r\n });\r\n }\r\n\r\n // Frame time élevé\r\n if (avgFrameTime > 33) {\r\n issues.push({\r\n severity: 'critical',\r\n metric: 'Frame Time',\r\n value: avgFrameTime,\r\n threshold: 33,\r\n suggestion: 'Frame time exceeds 33ms budget. Profile CPU-heavy operations.',\r\n });\r\n } else if (avgFrameTime > 16) {\r\n issues.push({\r\n severity: 'warning',\r\n metric: 'Frame Time',\r\n value: avgFrameTime,\r\n threshold: 16,\r\n suggestion: 'Frame time exceeds 60 FPS target (16.67ms). Optimize main thread work.',\r\n });\r\n }\r\n\r\n // Mémoire élevée\r\n if (currentMemory > 500) {\r\n issues.push({\r\n severity: 'critical',\r\n metric: 'Memory Usage',\r\n value: currentMemory,\r\n threshold: 500,\r\n suggestion: 'High memory usage. Check for memory leaks, dispose unused textures/geometries.',\r\n });\r\n } else if (currentMemory > 300) {\r\n issues.push({\r\n severity: 'warning',\r\n metric: 'Memory Usage',\r\n value: currentMemory,\r\n threshold: 300,\r\n suggestion: 'Elevated memory usage. Consider texture compression, geometry pooling.',\r\n });\r\n }\r\n\r\n return issues;\r\n }\r\n\r\n private extractHotspots() {\r\n const data = profiler.getData();\r\n const rawData = profiler.getRawData();\r\n const hotspots: Array<{\r\n name: string;\r\n avgTime: number;\r\n maxTime: number;\r\n callCount: number;\r\n p95: number;\r\n p99: number;\r\n variance: number;\r\n percentOfFrame: number;\r\n }> = [];\r\n\r\n // Liste des patterns de métriques custom à exclure des hotspots\r\n const customMetricPatterns = [\r\n 'Camera', 'Ball', 'Distance', 'Physics', 'Opacity', 'Following',\r\n '(deg)', '(m/s)', '(rad/s)', '(m)', 'Speed', 'Height', 'Moving',\r\n 'Occluded', 'Azimuth', 'Polar', 'Bodies', 'Colliders', 'Joints'\r\n ];\r\n\r\n // Get Frame time for percentage calculation\r\n let frameAvgTime = 0;\r\n if (rawData) {\r\n const frameData = rawData.get('Frame');\r\n if (frameData && frameData.calls > 0) {\r\n frameAvgTime = frameData.total / frameData.calls;\r\n }\r\n }\r\n\r\n if (data && data.entries && rawData) {\r\n data.entries.forEach((entry: any) => {\r\n // Exclure les métriques custom qui ne sont pas des mesures de temps réelles\r\n const isCustomMetric = customMetricPatterns.some(pattern =>\r\n entry.name.includes(pattern)\r\n );\r\n\r\n if (!isCustomMetric) {\r\n const raw = rawData.get(entry.name);\r\n const times = raw?.times || [];\r\n\r\n // Calculate percentiles\r\n const sorted = [...times].sort((a, b) => a - b);\r\n const p95 = sorted.length > 0 ? sorted[Math.floor(sorted.length * 0.95)] || 0 : 0;\r\n const p99 = sorted.length > 0 ? sorted[Math.floor(sorted.length * 0.99)] || 0 : 0;\r\n\r\n // Calculate variance\r\n const avg = entry.avg || 0;\r\n const variance = times.length > 1\r\n ? times.reduce((sum, t) => sum + Math.pow(t - avg, 2), 0) / times.length\r\n : 0;\r\n\r\n // Calculate percentage of frame time\r\n const percentOfFrame = frameAvgTime > 0 ? (avg / frameAvgTime) * 100 : 0;\r\n\r\n hotspots.push({\r\n name: entry.name,\r\n avgTime: avg,\r\n maxTime: entry.max || 0,\r\n callCount: entry.calls || 0,\r\n p95,\r\n p99,\r\n variance,\r\n percentOfFrame,\r\n });\r\n }\r\n });\r\n }\r\n\r\n // Trier par temps moyen décroissant\r\n return hotspots.sort((a, b) => b.avgTime - a.avgTime);\r\n }\r\n\r\n private analyzeCustomMetrics() {\r\n const metrics: Record<string, { min: number; max: number; avg: number; current: number }> = {};\r\n\r\n this.customMetricHistory.forEach((samples, name) => {\r\n if (samples.length > 0) {\r\n metrics[name] = {\r\n min: Math.min(...samples),\r\n max: Math.max(...samples),\r\n avg: this.average(samples),\r\n current: samples[samples.length - 1],\r\n };\r\n }\r\n });\r\n\r\n return metrics;\r\n }\r\n\r\n /**\r\n * Reset toutes les statistiques\r\n */\r\n reset() {\r\n this.startTime = Date.now();\r\n this.frameCount = 0;\r\n this.fpsSamples = [];\r\n this.frameTimeSamples = [];\r\n this.memorySamples = [];\r\n this.customMetricHistory.clear();\r\n }\r\n}\r\n\r\n// Instance globale\r\nexport const reportGenerator = new ReportGenerator();\r\n"],"names":["ReportGenerator","fps","frameTime","memoryMB","name","value","history","duration","avgFps","validFpsSamples","f","minFps","maxFps","avgFrameTime","currentMemory","peakMemory","memoryTrend","issues","hotspots","customMetrics","safeAvgFps","safeMinFps","safeMaxFps","safeAvgFrameTime","report","md","critical","i","warnings","info","issue","hotspot","jitterIndicator","percentFrame","topHotspot","highVariance","h","stats","sortedIssues","a","b","severityScore","icon","error","arr","samples","recent","older","recentAvg","olderAvg","diff","threshold","_peakMemory","data","profiler","rawData","customMetricPatterns","frameAvgTime","frameData","entry","pattern","times","sorted","p95","p99","avg","variance","sum","t","percentOfFrame","metrics","reportGenerator"],"mappings":"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"}
@@ -0,0 +1,2 @@
1
+ export declare const FrameProfiler: () => import("react/jsx-runtime").JSX.Element | null;
2
+ //# sourceMappingURL=FrameProfiler.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"FrameProfiler.d.ts","sourceRoot":"","sources":["../../src/FrameProfiler.tsx"],"names":[],"mappings":"AAIA,eAAO,MAAM,aAAa,sDAwIzB,CAAC"}
@@ -0,0 +1,41 @@
1
+ export interface FramePhase {
2
+ name: string;
3
+ start: number;
4
+ end: number;
5
+ duration: number;
6
+ }
7
+ export interface FrameBreakdown {
8
+ total: number;
9
+ interval: number;
10
+ phases: {
11
+ react: number;
12
+ update: number;
13
+ physics: number;
14
+ render: number;
15
+ postProcess: number;
16
+ other: number;
17
+ };
18
+ timeline: FramePhase[];
19
+ timestamp: number;
20
+ }
21
+ declare class FrameTimeCollector {
22
+ private phases;
23
+ private timeline;
24
+ private frameStartTime;
25
+ private lastFrameTime;
26
+ private currentInterval;
27
+ private lastBreakdown;
28
+ private readonly maxTimelineEntries;
29
+ startFrame(): void;
30
+ startPhase(name: string): void;
31
+ endPhase(name: string): void;
32
+ endFrame(): FrameBreakdown | null;
33
+ private getPhaseDuration;
34
+ measure<T>(name: string, fn: () => T): T;
35
+ measureAsync<T>(name: string, fn: () => Promise<T>): Promise<T>;
36
+ getLastBreakdown(): FrameBreakdown | null;
37
+ reset(): void;
38
+ }
39
+ export declare const frameTimeCollector: FrameTimeCollector;
40
+ export {};
41
+ //# sourceMappingURL=frame-time-collector.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"frame-time-collector.d.ts","sourceRoot":"","sources":["../../../src/collectors/frame-time-collector.ts"],"names":[],"mappings":"AAAA,MAAM,WAAW,UAAU;IACzB,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,EAAE,MAAM,CAAC;IACd,GAAG,EAAE,MAAM,CAAC;IACZ,QAAQ,EAAE,MAAM,CAAC;CAClB;AAED,MAAM,WAAW,cAAc;IAC7B,KAAK,EAAE,MAAM,CAAC;IACd,QAAQ,EAAE,MAAM,CAAC;IACjB,MAAM,EAAE;QACN,KAAK,EAAE,MAAM,CAAC;QACd,MAAM,EAAE,MAAM,CAAC;QACf,OAAO,EAAE,MAAM,CAAC;QAChB,MAAM,EAAE,MAAM,CAAC;QACf,WAAW,EAAE,MAAM,CAAC;QACpB,KAAK,EAAE,MAAM,CAAC;KACf,CAAC;IACF,QAAQ,EAAE,UAAU,EAAE,CAAC;IACvB,SAAS,EAAE,MAAM,CAAC;CACnB;AAED,cAAM,kBAAkB;IACtB,OAAO,CAAC,MAAM,CAA2D;IACzE,OAAO,CAAC,QAAQ,CAAoB;IACpC,OAAO,CAAC,cAAc,CAAa;IACnC,OAAO,CAAC,aAAa,CAAa;IAClC,OAAO,CAAC,eAAe,CAAa;IACpC,OAAO,CAAC,aAAa,CAA+B;IACpD,OAAO,CAAC,QAAQ,CAAC,kBAAkB,CAAO;IAE1C,UAAU,IAAI,IAAI;IAclB,UAAU,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI;IAK9B,QAAQ,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI;IAkB5B,QAAQ,IAAI,cAAc,GAAG,IAAI;IAkCjC,OAAO,CAAC,gBAAgB;IASxB,OAAO,CAAC,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,CAAC,GAAG,CAAC;IAUlC,YAAY,CAAC,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,OAAO,CAAC,CAAC,CAAC,GAAG,OAAO,CAAC,CAAC,CAAC;IASrE,gBAAgB,IAAI,cAAc,GAAG,IAAI;IAIzC,KAAK,IAAI,IAAI;CAKd;AAED,eAAO,MAAM,kBAAkB,oBAA2B,CAAC"}
@@ -0,0 +1,32 @@
1
+ import type { WebGLRenderer } from 'three';
2
+ export interface GPUStats {
3
+ drawCalls: number;
4
+ triangles: number;
5
+ points: number;
6
+ lines: number;
7
+ textures: number;
8
+ geometries: number;
9
+ programs: number;
10
+ memory: {
11
+ textures: number;
12
+ geometries: number;
13
+ programs: number;
14
+ total: number;
15
+ };
16
+ frameTime: number;
17
+ timestamp: number;
18
+ }
19
+ declare class GPUCollector {
20
+ private renderer;
21
+ private lastStats;
22
+ private frameStartTime;
23
+ setRenderer(renderer: WebGLRenderer): void;
24
+ getStats(): GPUStats | null;
25
+ startFrame(): void;
26
+ endFrame(): void;
27
+ getLastStats(): GPUStats | null;
28
+ reset(): void;
29
+ }
30
+ export declare const gpuCollector: GPUCollector;
31
+ export {};
32
+ //# sourceMappingURL=gpu-collector.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"gpu-collector.d.ts","sourceRoot":"","sources":["../../../src/collectors/gpu-collector.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,OAAO,CAAC;AAE3C,MAAM,WAAW,QAAQ;IACvB,SAAS,EAAE,MAAM,CAAC;IAClB,SAAS,EAAE,MAAM,CAAC;IAClB,MAAM,EAAE,MAAM,CAAC;IACf,KAAK,EAAE,MAAM,CAAC;IACd,QAAQ,EAAE,MAAM,CAAC;IACjB,UAAU,EAAE,MAAM,CAAC;IACnB,QAAQ,EAAE,MAAM,CAAC;IACjB,MAAM,EAAE;QACN,QAAQ,EAAE,MAAM,CAAC;QACjB,UAAU,EAAE,MAAM,CAAC;QACnB,QAAQ,EAAE,MAAM,CAAC;QACjB,KAAK,EAAE,MAAM,CAAC;KACf,CAAC;IACF,SAAS,EAAE,MAAM,CAAC;IAClB,SAAS,EAAE,MAAM,CAAC;CACnB;AAED,cAAM,YAAY;IAChB,OAAO,CAAC,QAAQ,CAA8B;IAC9C,OAAO,CAAC,SAAS,CAAyB;IAC1C,OAAO,CAAC,cAAc,CAAa;IAEnC,WAAW,CAAC,QAAQ,EAAE,aAAa,GAAG,IAAI;IAI1C,QAAQ,IAAI,QAAQ,GAAG,IAAI;IA+E3B,UAAU,IAAI,IAAI;IAIlB,QAAQ,IAAI,IAAI;IAIhB,YAAY,IAAI,QAAQ,GAAG,IAAI;IAI/B,KAAK,IAAI,IAAI;CAGd;AAED,eAAO,MAAM,YAAY,cAAqB,CAAC"}
@@ -0,0 +1,42 @@
1
+ import type { ProfilerEntry } from '../core/profiler';
2
+ export interface FunctionCall {
3
+ name: string;
4
+ duration: number;
5
+ timestamp: number;
6
+ callCount: number;
7
+ averageDuration: number;
8
+ maxDuration: number;
9
+ stack?: string[];
10
+ }
11
+ export interface HotPath {
12
+ id: string;
13
+ path: string[];
14
+ totalDuration: number;
15
+ averageDuration: number;
16
+ callCount: number;
17
+ frequency: number;
18
+ impact: 'high' | 'medium' | 'low';
19
+ recommendations: string[];
20
+ }
21
+ export interface HotPathAnalysis {
22
+ hotPaths: HotPath[];
23
+ topFunctions: FunctionCall[];
24
+ bottlenecks: HotPath[];
25
+ recommendations: string[];
26
+ timestamp: number;
27
+ }
28
+ declare class HotPathDetector {
29
+ private functionCalls;
30
+ private callHistory;
31
+ private readonly maxHistorySize;
32
+ private readonly hotPathThreshold;
33
+ private readonly frequencyThreshold;
34
+ recordCall(name: string, duration: number, stack?: string[]): void;
35
+ analyze(profilerEntries?: ProfilerEntry[]): HotPathAnalysis;
36
+ detectCallChain(_stack: string[]): HotPath[];
37
+ reset(): void;
38
+ getTopFunctions(limit?: number): FunctionCall[];
39
+ }
40
+ export declare const hotPathDetector: HotPathDetector;
41
+ export {};
42
+ //# sourceMappingURL=hot-path-detector.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"hot-path-detector.d.ts","sourceRoot":"","sources":["../../../src/collectors/hot-path-detector.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,kBAAkB,CAAC;AAEtD,MAAM,WAAW,YAAY;IAC3B,IAAI,EAAE,MAAM,CAAC;IACb,QAAQ,EAAE,MAAM,CAAC;IACjB,SAAS,EAAE,MAAM,CAAC;IAClB,SAAS,EAAE,MAAM,CAAC;IAClB,eAAe,EAAE,MAAM,CAAC;IACxB,WAAW,EAAE,MAAM,CAAC;IACpB,KAAK,CAAC,EAAE,MAAM,EAAE,CAAC;CAClB;AAED,MAAM,WAAW,OAAO;IACtB,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,EAAE,MAAM,EAAE,CAAC;IACf,aAAa,EAAE,MAAM,CAAC;IACtB,eAAe,EAAE,MAAM,CAAC;IACxB,SAAS,EAAE,MAAM,CAAC;IAClB,SAAS,EAAE,MAAM,CAAC;IAClB,MAAM,EAAE,MAAM,GAAG,QAAQ,GAAG,KAAK,CAAC;IAClC,eAAe,EAAE,MAAM,EAAE,CAAC;CAC3B;AAED,MAAM,WAAW,eAAe;IAC9B,QAAQ,EAAE,OAAO,EAAE,CAAC;IACpB,YAAY,EAAE,YAAY,EAAE,CAAC;IAC7B,WAAW,EAAE,OAAO,EAAE,CAAC;IACvB,eAAe,EAAE,MAAM,EAAE,CAAC;IAC1B,SAAS,EAAE,MAAM,CAAC;CACnB;AAED,cAAM,eAAe;IACnB,OAAO,CAAC,aAAa,CAAwC;IAC7D,OAAO,CAAC,WAAW,CAAoE;IACvF,OAAO,CAAC,QAAQ,CAAC,cAAc,CAAQ;IACvC,OAAO,CAAC,QAAQ,CAAC,gBAAgB,CAAK;IACtC,OAAO,CAAC,QAAQ,CAAC,kBAAkB,CAAM;IAEzC,UAAU,CAAC,IAAI,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,EAAE,KAAK,CAAC,EAAE,MAAM,EAAE,GAAG,IAAI;IA+BlE,OAAO,CAAC,eAAe,CAAC,EAAE,aAAa,EAAE,GAAG,eAAe;IAoG3D,eAAe,CAAC,MAAM,EAAE,MAAM,EAAE,GAAG,OAAO,EAAE;IAqC5C,KAAK,IAAI,IAAI;IAKb,eAAe,CAAC,KAAK,GAAE,MAAW,GAAG,YAAY,EAAE;CAKpD;AAED,eAAO,MAAM,eAAe,iBAAwB,CAAC"}
@@ -0,0 +1,7 @@
1
+ export { gpuCollector } from './gpu-collector';
2
+ export type { GPUStats } from './gpu-collector';
3
+ export { sceneCollector } from './scene-collector';
4
+ export type { SceneStats } from './scene-collector';
5
+ export { hotPathDetector } from './hot-path-detector';
6
+ export type { HotPath, HotPathAnalysis, FunctionCall } from './hot-path-detector';
7
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/collectors/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,MAAM,iBAAiB,CAAC;AAC/C,YAAY,EAAE,QAAQ,EAAE,MAAM,iBAAiB,CAAC;AAChD,OAAO,EAAE,cAAc,EAAE,MAAM,mBAAmB,CAAC;AACnD,YAAY,EAAE,UAAU,EAAE,MAAM,mBAAmB,CAAC;AACpD,OAAO,EAAE,eAAe,EAAE,MAAM,qBAAqB,CAAC;AACtD,YAAY,EAAE,OAAO,EAAE,eAAe,EAAE,YAAY,EAAE,MAAM,qBAAqB,CAAC"}