Brian Duffy

Writing

Technical

Getting good-looking work onto a Quest

The standalone frame budget, where it actually goes, and why the worst frame is the only number that matters.

Start with the arithmetic, because everything else follows from it.

At 72Hz you have 13.9 milliseconds per frame. At 90Hz, 11.1. On a Quest 3 running at 120Hz, 8.3. In that window you render the scene twice, once per eye, and the compositor takes a share of it before you get any. Call it ten or eleven milliseconds of application time at 72Hz if you want a number to plan against.

That sounds like a small budget, and it is, but the size is not the interesting part. The interesting part is that you are optimising a different statistic than you are used to.

On a desktop game you optimise the average. A frame that takes twice as long as its neighbours is a stutter, the player notices, and it is annoying. In VR that same frame is not annoying, it is physical. The image lags where the head has already moved, and the vestibular system – which has no idea it is playing a game – registers a mismatch between what the eyes report and what the inner ear reports. The body’s interpretation of that mismatch is that it has been poisoned. That is what motion sickness is for.

So the number that matters is not your average frame time. It is your worst frame time, in the worst room, with the most enemies, on the oldest headset you support, forty minutes into a session. Everything below is about that frame.

Where the time actually goes

Not usually polygons. On mobile hardware the two things that will hurt you are draw calls and fill rate, and the third is the one nobody budgets for.

Draw calls are a CPU cost, and the CPU in a standalone headset is a phone chip with a thermal ceiling. Fill rate is about how many times you shade each pixel, which is why transparency and overlapping particles will destroy you at resolutions that a flat game would consider modest – you are drawing a lot of pixels, twice.

The third is memory. It does not degrade, it kills. Exceed what the headset will give you and the process is terminated. There is no graceful path, which is why content streaming stops being an optimisation and becomes an architectural requirement somewhere around the point your game gets large.

Four structural moves

Not micro-optimisations – things that change the shape of the problem.

Compile your shaders before the player sees anything. The first time a material appears, its shader variant is compiled, and that compile happens on the frame it is needed. The result is a hitch precisely when something interesting happens, which is the worst possible moment. Warm them during the loading screen. This one is nearly free and buys back more than most of what follows.

Switch things off by distance, from one place. Every AI agent that thinks every frame is a tax you pay whether or not the player can see it. Move awareness, target selection and hearing into one coordinator that ticks the whole population on a slow schedule rather than each agent ticking itself. The behaviour barely changes and the CPU cost falls off a cliff. This is the single most effective AI performance decision on standalone hardware.

Let the architecture do your occlusion. A door that closes behind the player is a design feature and an occlusion boundary. A headset that never renders the room it cannot see is not doing clever culling, it just has nothing to draw. Doors, corridors and airlocks are performance tools that happen to also be doors.

Tune per device, not for the worst one. A Quest 2 and a Quest 3 are different machines. Shipping one lowest-common-denominator configuration means the Quest 3 owner gets a Quest 2 game. Separate render configurations per device tier are not much work and the difference is visible immediately.

The thing that will catch you anyway

Thermals. A standalone headset throttles, and it throttles on the timescale of a real play session, not a test. The frame rate you can hold at minute forty is your actual budget; the one you measured at minute two is a fiction.

Profile warm. Issue sustained-performance hints so the system knows what you are asking for. Subscribe to thermal notifications and be prepared to give something up rather than let the whole frame collapse. And do your final validation on hardware that has been running for half an hour, because that is the state your player will be in and it is not the state your desk is in.