"Editor vsync hides physics cost — time the step directly"
▸ SYMPTOM
You're profiling a physics-heavy scene in the editor and average fps pins at exactly 60.0 regardless of how many Rigidbodies, colliders, or raycast-wheel vehicles you add. Your performance metrics suggest everything is fine, even when the physics workload should clearly be saturating the CPU.
▸ CAUSE
Editor-embedded play mode is hard-capped at the desktop compositor's vsync (60 Hz). Neither fps_max 0 nor r_frame_sync_enable 0 lifts the cap — it's the OS windowed-present sync on the editor window, not the engine frame-sync. Frame-based fps measurement only tells you "the engine met every 60 Hz deadline," never how much headroom remains or where the real knee is.
▸ FIX
Implement IScenePhysicsEvents on a component and time the physics step directly with a Stopwatch:
public sealed class PhysicsStepTimer : Component, Component.IScenePhysicsEvents
{
private readonly Stopwatch _sw = new();
private readonly List<double> _samples = new();
private const int WindowSize = 250; // ~5 s at 50 Hz
void IScenePhysicsEvents.PrePhysicsStep()
{
_sw.Restart();
}
void IScenePhysicsEvents.PostPhysicsStep()
{
_sw.Stop();
_samples.Add(_sw.Elapsed.TotalMilliseconds);
if (_samples.Count >= WindowSize)
{
_samples.Sort();
var avg = _samples.Average();
var p99 = _samples[(int)(_samples.Count * 0.99)];
Log.Info($"PhysicsStep: avg={avg:F2} ms, p99={p99:F2} ms ({_samples.Count} samples)");
_samples.Clear();
}
}
}The PrePhysicsStep/PostPhysicsStep callbacks bracket the real 50 Hz solver step. Log avg + worst-1% step milliseconds per window alongside your fps p1 — this is your true, vsync-free physics CPU cost.
Budget rule of thumb: each physics step has a 20 ms tick budget. A per-car cost of ~13 us/step (compound-folded Rigidbody) means 16 cars with 128 colliders each can sit at 0.39 ms avg / 0.78 ms p99 — under 4% of the tick. Physics part count and collider count are effectively free at roster scale; look for your binding cost elsewhere (world regen, triangle rebuild).
▸ WHY IT WORKS
Without this technique, you'll either (a) believe your physics is fine because avg fps = 60, or (b) try to optimize systems that aren't actually bound. Frame fps under vsync is a binary "met deadline / didn't" signal — the step timer gives you the continuous cost curve you need to find the real knee before it arrives.
- Published