Greetings from the Unity Graphics team.
Unity 6 introduces a new and powerful Pipeline State Object (PSO) tracing and precooking workflow, to achieve a smoother and stutter-less player experience.
This set of APIs provide a significant upgrade from the “shader warmup" API, previously introduced in older Unity versions. While traditional shader warmup is sufficient for older graphics APIs (e.g. OpenGL, DirectX11), the new PSO workflow can better utilize modern graphics APIs, such as Vulkan, DirectX12 and Metal.
PSO Creation and Caching
When targeting modern graphics APIs, the GPU vendor’s graphics drivers will perform runtime shader compilation (and other rendering state translation) as part of the Pipeline State Object creation process. As a result, PSO creation is a lengthy process, which may lead to noticeable stutters in the runtime application. This overhead can be exacerbated for more-complex projects, which require the application to compile a large amount of PSOs on the fly.
You can identify PSO creation stutters in the Unity Profiler, using the GraphicsPipelineImpl markers:
In many cases, the GPU vendor’s graphics drivers will automatically cache any compiled PSO to disk, in order to accelerate PSO creation for subsequent application runs. However, the application may still need to compile PSOs for newly encountered shader variants and materials. Furthermore, OS and driver updates will often invalidate the driver-managed PSO cache.
To eliminate PSO stutters for initial (and subsequent) application runs, it is recommended to trace any PSOs required by a certain level and or scene, and ensure they are pre-compiled ahead of their first use when drawing the scene. Effective use of PSO precooking will improve the player’s experience, and leads to a better first impression:
The ideal way to warm PSOs may vary depending on your application and use case. For example, you may choose to synchronously precook PSOs during level transitions and scene loading. This can be done progressively (time-sliced) in order to increase application responsiveness, creating a fixed amount of PSOs per frame.
Alternatively, you can choose to asynchronously precook PSOs in the application’s background. This will not block the application, but may temporarily regress CPU performance for the duration of the warm up.
Tracing a new PSO Collection
PSO tracing should be performed using a development build. You can write C# scripts to collect the PSOs created by the application during rendering:
-
In a C# script, create a new GraphicsStateCollection. This collection corresponds to your application’s or scene’s PSOs.
-
To begin tracing PSOs into your collection, call the GraphicsStateCollection.BeginTrace method. Any new graphics pipelines created by the application will be added to the collection. In most cases, you should begin tracing during scene or application start up.
-
To finalize the tracing process, call the GraphicsStateCollection.EndTrace method. In most cases, you should end tracing during scene or application end.
Saving a PSO Collection
Once tracing is complete, you can save the PSO collection to disk using GraphicsStateCollection.SaveToFile. This stores the file at the provided path on the player’s device:
string path = Application.persistentDataPath + "/" + "Scene.graphicsstate";
m_GraphicsStateCollection.SaveToFile(path);
Debug.Log("Sending GSC to path: " + path);
When deploying the player on another device (e.g. Android, iOS), you will need to manually retrieve the file back to the Editor’s machine. For example, using adb pull on Android.
Alternatively, you can use GraphicsStateCollection.SendToEditor to retrieve the PSO collection using Player connection. Before building the project, make sure you enable both the “Development Build” and “Autoconnect Profiler” in the Build Profile settings:
Note: manually connecting the console from the editor to the local player also works to establish the connection.
Precooking a PSO Collection
Once tracing is completed, you can request Unity to precook PSO collection, ideally well ahead of drawing time. In most cases, the ideal time to perform warmup is during application or scene loading sequences.
You can perform PSO precooking using two warm up methods:
- GraphicsStateCollection.WarmUp will schedule the creation of all PSOs in the collection.
- GraphicsStateCollection.WarmUpProgressively will schedule the creation of a set number of PSOs in the collection.
Both methods will return a job handle, which can be used to determine whether PSO Warmup is performed synchronously or asynchronously.
Once the PSOs are created, the drivers will often cache those to disk. Next time the PSOs are precooked, they could be loaded directly from cache.
Inspecting and modifying a PSO Collection
You can query the platform used when tracing the PSO collection via GraphicsStateCollection.runtimePlatform.
For additional control, you can inspect the recorded PSOs and variant data, and modify the collection as needed. GraphicsStateCollection.GetVariants can be used to retrieve all shader variants recorded in a PSO collection. You can then read the graphics states used by each variant via GraphicsStateCollection.GetGraphicsStatesForVariant. Lastly, you can modify the graphics states associated with each variant using AddGraphicsStateForVariant / RemoveGraphicsStatesForVariant.
Note: GPU representation of the PSO may vary across platforms. It is highly recommended to perform tracing in a development build, targeting the relevant graphics API. And maintain separate collections per target.
Sample project (Update: 13.01.2025)
You can experiment with PSO tracing via the URP 3D Sample project, obtainable from the Unity Hub. As of version 17.0.6, the project includes the following scripts:
Assets/SharedAssets/Scripts/Runtime/GraphicsStateCollectionManager.cs
This is the main script, which performs PSO tracing and warmup at runtime, and holds a list of the recorded collections.
Assets/SharedAssets/Scripts/Editor/GraphicsStateCollectionStripper.cs
This script ensures the build only includes the relevant collections for the target platform. We recommend tracing a separate collection per graphics API, since graphics states vary per API.
The sample project targets multiple platforms and quality levels, so it includes multiple collections under the “Assets/SharedAssets/GraphicsStateCollections” folder. Every collection can take a few MBs of disk space, so this filtering script is needed to minimize build size.
Assets/SharedAssets/Scripts/Editor/GraphicsStateCollectionCombiner.cs
This utility script combines multiple selected collection files into one. The first collection you selected will be used as the result collection. Note that it will only combine collections that match platform + gfx api + quality level.
To enable PSO warmup for the sample project, follow these steps:
- Open the main scene located at “Assets/Scenes/Terminal/TerminalScene.unity”
- Add an empty game object to the scene, and assign the
Graphics State Collection Managercomponent - In the inspector, set the Mode property to “Warmup”
- Right click on the component and select “Update collection list”:
- Save the scene and project. Build and run standalone player targeting Windows (DX12/Vulkan) or OSX (Metal).
PSO warmup should now be performed automatically, when loading the Terminal scene in the Player.
Note: If you previously ran the URP 3D Sample project, your graphics drivers may have already cached some shaders and PSOs locally. To better test the new API, we recommend you first clear your shader cache. The steps for this vary based on platform. For example, on Windows (DX12) using an Nvidia GPU, you may find the shader cache in the following folders: \Users\<Username>\AppData\LocalLow\NVIDIA\PerDriverVersion\DXCache, \Users\<Username>\AppData\Local\D3DSCache.
Platform support
The new PSO workflow is available as of Unity 6, for Players targeting Metal, Vulkan and Direct3D12.
Starting with Unity 6.1, we also provide compatibility for older graphics APIs such as OpenGLES and Direct3D11, via a fallback to legacy shader-warmup.
Please give the new PSO precooking workflow a try and let us know what you think!
As always, you can follow our progress and discover new features via the public roadmap. If you cannot find the feature you are looking for, feel free to submit a feature request, or contact the team directly in the graphics forum.



