"Editor play mode is hard-capped at 60 fps: cvars won't lift it"
▸ SYMPTOM
You're profiling in the editor's embedded play mode and fps reads exactly 60.0: never higher, never lower (until the scene actually gets heavy). Setting r_frame_sync_enable 0 and fps_max 0 changes nothing. Your GPU profiling shows headroom, but the number won't move.
▸ CAUSE
The editor window is a desktop compositor surface. Its present call is vsync-locked to the monitor's refresh rate (typically 60 Hz). This cap is applied by the OS compositor, not by the engine's own frame-sync cvar. r_frame_sync_enable and fps_max both accept the value (you can read them back as 0), but the compositor's vsync on the editor window's present overrides them.
▸ FIX
Don't trust editor play-mode fps as a GPU ceiling measurement. An avg = 60.0 only means "the engine met every 60 Hz deadline": it tells you nothing about actual headroom.
To measure real GPU performance:
- Launch a standalone build (not embedded play mode) where the engine controls its own swap chain.
- Use
fps_max 0+r_frame_sync_enable 0in the standalone window: they work there. - Look at the 1% low (p1), not just the average. The real performance knee is where
avg < 60AND the p1 collapses.
For editor-based profiling, track frame time (1000 / fps) trends rather than raw fps: a jump from 16.6 ms to 18 ms is meaningful even when both read as "60 fps" due to the cap.
Under the cap, even the median lies: read the tail
Confirmed on engine 26.07.22: with the compositor cap in force, per-frame dt's median (p50) pins to exactly 16.67 ms in every measurement leg regardless of load, not just the average. The only readable signal is the tail: p99, the single worst frame, and the count of frames over budget, read together with per-system deltas against a quiet (idle/low-load) baseline captured in the same run. A leg that reports "p50 16.67 ms, looks fine" while its p99, worst-frame, and over-budget count climb is a regression; comparing means or medians across legs under vsync hides it completely.
▸ WHY IT WORKS
Standalone builds create their own OS window and swap chain, giving the engine full control over present timing. The compositor vsync only affects windows it composites: the editor's embedded viewport is one; a standalone game window with exclusive swap-chain control is not.
- Added the p50-pins-to-16.67ms finding and the tail-reading method (p99 / worst frame / over-budget count / per-system deltas vs a quiet baseline). Re-verified on engine 26.07.22.
- Published