NNabeel Hassan

Blog · October 12, 2026 · 8 min read

Unity Performance on Standalone VR Headsets: How I Hold 90 FPS on a Mobile Chip

By Nabeel Hassan — AI Engineer · ICPC World Finalist

TL;DR: A standalone VR headset is a phone chip strapped to someone's face, asked to render every frame twice at 72 to 90 frames per second while it sits on a warm forehead. At 90 Hz you get about 11 milliseconds per frame, for both eyes, every frame, with no "it's fine on average." After building a clinical eye-tracking app on the Vive Focus 3 at Nystag and XR work like the ERIS platform at ARCortex, my approach is always the same: profile on the device from week one, set a hard frame budget, make the CPU side cheap (fewer draw calls, no garbage collection spikes, stereo rendered in a single pass), make the GPU side cheap (no full-screen post-processing, baked lighting, compressed textures, foveated rendering where the SDK supports it), and then soak-test for thermal throttling. Most of the wins come from what you decide not to render, not from clever shaders.

A headset changes the stakes of performance. On a phone game, a dropped frame is a stutter. In a headset, a dropped frame is the world lurching against your inner ear. And in a clinical app, a dropped frame can be something worse: a measurement that is quietly wrong.

Why standalone headsets are a different performance problem

A tethered headset borrows a desktop GPU. A standalone headset like the Vive Focus 3 runs everything on a mobile chip (in its case, a Snapdragon XR2) inside the headset itself. That is what makes standalone devices practical for real deployments, which I covered in choosing an XR headset for enterprise deployment. It is also what makes them hard to render for.

Three things stack up:

So the question is never "is it fast enough on my machine." It is "does it hold frame rate, on the device, at minute thirty."

Step one: profile on the device, from the first week

The Unity editor tells you almost nothing about headset performance. Your desktop GPU is far faster and there is no thermal limit, so I treat editor frame times as decoration.

What I do instead:

  1. Build to the headset early and often, so the baseline is known before there is much to render.
  2. Attach the Unity Profiler to a development build on the device. It shows whether a slow frame is CPU-bound or GPU-bound, which decides everything that follows.
  3. Use the GPU tools for the chip. For Snapdragon-based headsets, Qualcomm's profiling tools show GPU load, clock speeds and temperature, which Unity's profiler does not.
  4. Put a tiny on-device overlay in the app. Frame time, dropped frame count, and a temperature or throttle indicator if the platform exposes one. I wrote about the value of this overlay in how I test AR apps, and it matters just as much for VR.

If a headset build takes an afternoon, nobody profiles. If it is a button on CI, as in my Unity CI/CD pipeline, profiling becomes routine.

Step two: set a frame budget and split it

At 90 Hz, the whole frame has roughly 11.1 ms. At 72 Hz, about 13.9 ms. You do not get all of that, because the runtime needs its share for compositing and reprojection, so I plan to use noticeably less than the theoretical number and treat anything near the edge as a failure.

The useful move is to split the budget between CPU and GPU, because they run in parallel and the slower one sets your frame rate. Then every feature request gets a simple question: which side of the budget does this cost, and what are we removing to pay for it? This is the same discipline I later used for voice agents, where the budget is milliseconds of silence instead of milliseconds per frame. I wrote about that in the voice agent latency budget, and the thinking is almost identical.

Making the CPU side cheap

On standalone headsets, I find the CPU is the bottleneck more often than people expect, mostly because of draw calls and scripts.

Render both eyes in one pass

Unity's Single Pass Instanced stereo rendering draws both eyes in one pass instead of two. If a project is still on multi-pass, switching is usually the single biggest CPU saving available. Custom shaders need small changes to support it, which is a good reason to make the switch early instead of after you have written twenty shaders.

Cut draw calls

Every separate object with its own material is work for the CPU before the GPU even starts. The usual tools all help:

The SRP Batcher in the Universal Render Pipeline helps too, if your shaders support it.

Kill garbage collection spikes

A garbage collection pause that would be invisible in a desktop app shows up as a dropped frame in a headset. The fix is boring discipline: no allocations in Update loops, no string building every frame, no LINQ in hot paths, pooled objects instead of Instantiate and Destroy. The profiler's allocation column should be zero in steady state. When it is not, find out why.

Making the GPU side cheap

Once the CPU is under control, the GPU usually becomes the wall. Headset displays have a lot of pixels, and you are filling them twice.

Drop full-screen post-processing

Bloom, depth of field, screen-space ambient occlusion and colour grading passes look great on a desktop and are expensive on a mobile chip in stereo. My default for standalone VR is no full-screen post-processing at all, and each one has to earn its way back in. Antialiasing is the exception: 4x MSAA is relatively cheap on mobile GPUs because of how they render, and without it, edges shimmer badly in a headset.

Bake the lighting

Real-time lights and real-time shadows are some of the most expensive things you can ask a mobile GPU for. Baked lightmaps and light probes give you most of the look for a fraction of the cost. If a scene needs one real-time light, it gets one, not five.

Watch overdraw and transparency

Transparent objects stacked on top of each other force the GPU to shade the same pixel many times. Particle effects, layered UI panels and glass materials are common culprits. It is usually an art direction fix more than a code fix.

Compress textures and keep them small

Use ASTC compression on Android-based headsets, generate mipmaps, and question every 4K texture. Memory bandwidth on a mobile chip is precious.

Use foveated rendering if the SDK offers it

The lenses are sharpest in the centre, so rendering the edges of each eye at lower resolution is mostly invisible to the user and saves real GPU time. The Vive Wave SDK and other headset SDKs expose fixed foveated rendering, and on headsets with eye tracking it can follow the gaze. On a clinical app, though, I would be careful: if the content in the periphery is part of the test, reducing its quality is a product decision, not just a performance setting.

Thermal throttling: the bug that appears at minute twenty

The most misleading performance test is the short one. A fresh headset at room temperature will run your app better than the same headset twenty minutes into a session.

So every release gets a soak test: the heaviest scene, running for thirty minutes or more, with the overlay recording frame time and temperature. If frame time creeps up as the device warms, the app is living too close to its budget. The fix is to leave headroom from the start, and to use the platform's CPU and GPU performance level settings where available instead of always requesting maximum clocks, which just reaches the thermal wall faster.

Real users do not stop after two minutes, whether that is a firefighter using a system like ERIS or a clinic running sessions back to back.

When a dropped frame is a wrong measurement

This is the part I care about most from the Nystag work. In a clinical eye-tracking app, the stimulus on screen is effectively the specification of the test. If a target is supposed to move smoothly and the app drops frames, the target jumps. The patient's eyes respond to the jump, the tracker records it faithfully, and the data now describes a test that was not the one you designed.

That changes how you treat performance:

I wrote more about this mindset in the Nystag build story: clinical-grade means boring and repeatable before it means impressive. Performance is part of being honest about what the data means.

My quick checklist for standalone VR in Unity

  1. Device build in week one, profiler attached, overlay on.
  2. Frame budget written down, split between CPU and GPU, with headroom.
  3. Single Pass Instanced stereo rendering on.
  4. Draw calls batched, materials shared, zero per-frame allocations.
  5. No full-screen post-processing by default, 4x MSAA on, lighting baked.
  6. Textures compressed and right-sized, overdraw checked.
  7. Foveated rendering where it does not affect what the user must see.
  8. A thirty-minute soak test before every release.

None of this is glamorous. Most gains come from deciding what not to render and refusing to trust any number that did not come from the device, a habit that followed me into AI work, as I described in going from Unity game dev to AI engineer.


I have built XR systems for public safety and healthcare on standalone and mobile hardware, and now build production AI agents and products for startups across the US, UK and Europe. If your headset app runs fine in the editor and stutters on the device, more about my background is here, or book a call.

FAQ

What frame rate does a standalone VR app need?

It needs to match the headset's display refresh rate, typically 72 to 90 Hz. At 90 Hz that is about 11 milliseconds per frame for both eyes, and in practice you should plan to use noticeably less so the runtime has room for compositing and the app has headroom when the device warms up.

What is the biggest performance win for Unity VR on a standalone headset?

Usually switching to Single Pass Instanced stereo rendering and cutting draw calls on the CPU side, then removing full-screen post-processing and baking lighting on the GPU side. Most of the gains come from deciding what not to render rather than from clever shaders.

Why does my VR app slow down after twenty minutes?

Thermal throttling. A standalone headset is a sealed mobile chip on someone's head, and as it heats up the clocks drop. Soak-test the heaviest scene for thirty minutes or more, leave headroom in your frame budget, and avoid always requesting maximum CPU and GPU performance levels.

Can the Unity editor tell me how my VR app will perform?

No. The editor runs on a much faster desktop GPU with no thermal limit. Build to the headset early, attach the Unity Profiler to a development build on the device, and use the chip vendor's GPU tools to see real load and temperature.

Building something in this space?

I take on AI-agent, automation and product work directly — scoped fast, shipped fast.

Book a discovery call →

Keep reading