The number you report is not the number you feel

Sixty frames per second sounds clean. Divide one second by sixty and you get 16.67 milliseconds — the budget for everything the CPU and GPU must do before the next frame reaches the display. Report an average over a ten-second run and you might see 59.8 fps, which looks fine. What that average does not show is the single frame that took 68 milliseconds while every other frame took 14. You felt that frame. You did not feel the average.

This is why frame time — the duration of each individual frame, measured in milliseconds — is the number worth watching. An average fps collapses the distribution into a single scalar and discards exactly the information that corresponds to perceived smoothness. A frame-time trace keeps every data point. The spike that represents a shader compilation event, a translation-layer cache miss, or a sudden burst of API state reconstruction stands out immediately against the baseline. Buried in an fps average, it disappears.

Key thresholds worth calling out

FROM THIS ENTRY
16.67 msthe per-frame budget at 60 fps; the number every layer is working inside
~33 msroughly the frame gap above which most human viewers notice a hitch
1% lowthe percentile that captures spike behaviour rather than average behaviour

What the compatibility layer adds to the budget

Inside a compatibility layer, the 16.67-millisecond budget is shared between the workload the program actually intended and the work the layer is doing on its behalf. Neither category is fixed, and neither is evenly distributed across frames.

The translation overhead on any given frame depends on what that frame asks for. A frame that issues a thousand draw calls through a path the layer has already seen, with all relevant shaders compiled and cached, may cost almost nothing extra. A frame that encounters a novel shader variant for the first time pays the full compilation penalty — potentially tens of milliseconds in a single frame, right in the middle of gameplay. That is shader compilation stutter: not an average cost, a spike cost, and a spike that the fps counter will average away into invisibility.

State tracking adds overhead more evenly but not uniformly. When the target graphics API is stateless and the source API is stateful, the layer must reconstruct what state the program implicitly assumed on every draw call. If the program changes a single render state and issues a hundred draws, each of those hundred draws carries a small reconstruction cost. But some state changes are cheaper than others, and the distribution across frames follows the scene's own logic — heavy during transitions, light during steady gameplay. The frame-time trace shows this structure. An fps average does not.

CPU-side translation adds its own asymmetries. Rewriting a system call or an API call takes time proportional to the complexity of the call, not to whether this happens to be a quiet frame or a busy one. A frame that coincides with file I/O, registry emulation, or a threading decision the layer has to mediate will be longer than a frame that does not. These costs do not cluster predictably. They appear when the program's own logic demands them, and they land wherever they land in the frame budget.

A monitor showing a flat test pattern in a dim room
FIG. 2A flat test pattern holds every variable still except the one being measured.

Reading the trace instead of the headline

The practical consequence is that a frame-time trace, even a simple one, tells you things an fps counter cannot. The 1% low — the frame duration at the worst-performing percentile of a run — captures the spikes rather than the mean, and spikes are what smoothness actually depends on. A system running at a reported 60 fps average with a 1% low of 22 fps has a meaningful number of frames that took 45 milliseconds or more. A human visual system notices frame gaps above roughly 33 milliseconds. The average hid the problem; the percentile found it.

Within a compatibility layer specifically, the shape of the frame-time distribution is diagnostic. Sparse, high spikes that diminish over a session point toward a shader cache warming up. A raised floor across all frames suggests constant translation overhead — perhaps state reconstruction or call-rewriting that never amortises. Irregular clusters tied to specific scenes point toward geometry or effect complexity that the translation path handles unevenly. Each pattern has a different cause, and seeing the pattern is only possible if you kept the individual frame times rather than collapsing them into a mean.

The milliseconds do not go evenly. That is the whole point.

A mechanical keyboard pushed aside, one cable across it
FIG. 3Input starts here and ends at a photon; every queue in between is part of the latency budget.PHOTO: FOX ^.ᆽ.^= ∫ / PEXELS

Cedega is an independent publication about compatibility layers. It is not a software vendor, distributor, or support service.

FILED UNDER: FRAME TIMEMEDIUM ENTRY