Your PSO cache is only as good as your playthrough

Your PSO cache is only as good as your playthrough

June 1, 2026 ยท 4 min read

Multi-second performance spikes in games happen for many reasons.

CPU stalls. Asset streaming. Garbage collection. Synchronous loads. GPU bubbles. Bad content workflows.

And then there is one that keeps coming back in modern Unity projects:

Shader and PSO creation on Vulkan, DX12, and Metal.

The symptom is simple.

The player enters an area, triggers a VFX, changes a graphics setting, equips a cosmetic, or hits some content combination QA never saw.

The game freezes for a moment.

Sometimes half a second. Sometimes several seconds. Sometimes enough for the player to close the game and ask for a refund.

Hello spike & refund.

Why shader warmup is not enough anymore

In the older rendering world, the explanation was easier.

A shader had not been seen during gameplay yet, so the driver compiled it when the player hit it for the first time. The solution was painful but understandable: collect the shaders, warm them up earlier, move the cost out of gameplay.

That model is still useful, but it is incomplete for DX12, Vulkan, and Metal.

Today you are not only dealing with "a shader".

You are dealing with PSOs: Pipeline State Objects.

A PSO is the shader plus the render state around it.

That can include:

  • MSAA level
  • render target format
  • blend state
  • depth state
  • mesh layout
  • pass configuration
  • graphics API
  • material variant
  • quality setting

Change one of those and you may get a different PSO.

Same shader. Different pipeline. New runtime cost.

That is why the classic "we warmed up the shaders" answer can fail so badly.

You trace the game at MSAA x1.

A player switches to MSAA x4.

The game now needs PSOs your trace never collected.

And the spike happens in front of the player.

Unity 6.5 helps, but it does not remove the work

Unity 6.5 improves this with GraphicsStateCollection cache miss collection.

The idea is good.

Unity can detect graphics states that were missing from the original warmup, collect them, and let you append them back into your cache.

This is exactly the kind of tooling teams need, because the old workflow had a nasty blind spot: you often did not know what you failed to collect until the game stuttered.

But here is the part most teams will miss:

โ†’ Cache miss collection tells you what you failed to cover after you already failed to cover it.

It does not design your trace path.

It does not know which late-game VFX matter.

It does not know which quality settings your players use.

It does not know which Addressables content never appeared in your warmup scene.

It does not make PSO creation cheap.

And it does not magically fix a huge warmup collection that stalls the main thread.

So yes, use cache miss collection.

But do not treat it as a checkbox.

The real problem is coverage

Most PSO warmup failures I see are not caused by one mysterious shader.

They come from coverage gaps.

The trace route was too clean.

The QA pass did not include real player settings.

The warmup scene did not load the content the player actually sees later.

Addressables, rare VFX, late-game enemies, cosmetic skins, optional weapons, platform-specific settings, quality-level differences. These are exactly the places where the missing PSOs hide.

And because these combinations are often content-driven, the problem is not only a rendering problem.

It becomes a release engineering problem.

You need a workflow that keeps asking:

  • Which PSOs are missing?
  • Why were they missed?
  • Which content path created them?
  • Which settings or platform combinations were absent from the trace?
  • How do we stop the same class of miss from coming back next month?

That last question matters.

If your content pipeline keeps creating new PSO combinations, your cache will always be chasing the project instead of protecting it.

What I would check first

I would start with the boring suspects.

Not because they are glamorous.

Because they are usually where the bodies are buried.

Trace real gameplay paths, not just a clean demo route.

Include the graphics settings your players actually use.

Treat rare VFX and late-loaded prefabs as suspects.

Check Addressables content that never appears in the warmup scene.

Look for material and shader variant inconsistency across similar assets.

Use cache miss collection as a feedback loop, not as proof that the problem is solved.

The goal is not to collect a massive PSO cache and hope.

The goal is to understand which combinations your game can generate, which ones players hit, and which ones your warmup process never touches.

That is the work.

The dangerous version of this bug

The dangerous version is not the obvious one.

If the game freezes every time you load the first level, the team notices.

The worse version is quieter.

The happy path looks fine.

Your internal route looks fine.

The build used for review looks fine.

Then players start changing settings, using different GPUs, equipping different content, reaching late-game areas, or triggering VFX combinations QA never covered.

Now the PSO cache you shipped is not wrong exactly.

It is just incomplete.

And incomplete is enough to create the spike.

Use Unity 6.5 as a loop, not a ritual

GraphicsStateCollection cache miss collection is useful because it closes part of the feedback loop.

But the loop still needs people, tooling, QA coverage, and content discipline.

Collect the misses.

Append them back.

Then ask why the warmup missed them in the first place.

If you skip that question, the cache becomes a ritual.

Run the trace. Build the cache. Ship the cache. Hope the player does not walk somewhere new.

That is not a performance strategy.

That is gambling with a loading hitch.

Need help finding the missing PSOs?

If you are wasting weeks chasing DX12, Vulkan, or Metal spikes after "doing shader warmup", the problem may not be the one shader you are staring at.

It may be the trace path.

Or the quality settings.

Or Addressables.

Or the VFX content.

Or the content workflow that keeps creating new pipeline combinations after every sprint.

Book a consulting call and I will help you find the missing PSOs and the workflow creating them:

https://calendly.com/thegamedevguru-meet/a-chat-with-ruben

Ruben (TheGameDev.Guru)