| name | performance |
| description | FlatRedBallService.Performance — opt-in rolling FPS/timing/collision stats. Triggers: PerformanceMonitor, GenerateReport, FPS, frame time, DeepCollisionCount, "why is my game slow". |
Performance Monitoring
FlatRedBallService.Default.Performance (a PerformanceMonitor, src/Diagnostics/PerformanceMonitor.cs) tracks rolling FPS, per-phase frame timing, and a per-collision-relationship severity breakdown over a 120-frame window.
Off by default — set IsEnabled = true before it records anything; until then every stat reads zero/empty.
FlatRedBallService.Default.Performance.IsEnabled = true;
var perf = FlatRedBallService.Default.Performance;
Console.WriteLine(perf.GenerateReport());
var fps = perf.Fps;
var worst = perf.GetCollisionReport();
GenerateReport() only builds a string — it does no I/O itself, so printing/logging/writing it is on the caller.
Timer resolution (web)
GenerateReport() opens with a Platform: line and the measured clock step (TimerResolutionMs, probed once via ProfileClock.MeasureResolutionMs).
Landmine: browsers coarsen their clock as a Spectre mitigation — ~1ms on Firefox/Safari, ~0.1ms on Chrome — and Stopwatch.Frequency does not reflect it. Whole-pass totals stay accurate (start/end errors cancel), but any per-phase number smaller than one step reads as 0 or one full step and nothing between. The report warns automatically above 0.5ms.
Set PlatformLabel from the host — the engine targets net10.0 and cannot read navigator.userAgent itself.
Every row in the collision report carries a PartitionStatus. Unpartitioned is the only value worth acting on, and the fix is to set the same Factory<T>.PartitionAxis on both sides of the relationship. NotApplicable means one side is not a factory, such as a TileShapes, a single entity, or a plain List<T>, so no axis setting would change it. See the collision-relationships skill.
See src/Diagnostics/FrameProfile.cs for the underlying per-frame timing struct.