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 ms | the per-frame budget at 60 fps; the number every layer is working inside |
|---|---|
| ~33 ms | roughly the frame gap above which most human viewers notice a hitch |
| 1% low | the 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.

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.

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