Including Cached Shaders in Android Builds

Hey all,
Ive been fighting the issue of shader compilation hitches during gameplay when an object is rendered for the first time. Ive attempted all of the shader prewarming stuff but its both very cumbersome and unreliable.
I know that on android (Oculus Quest in my case), once a shader variant has been compiled, it gets cached on device so on subsequent playthroughs, that hitch wont occur.

Is there anyway to take that shader cache and include it in the apk so that all of the shaders are already cached when installing a build? It would be incredibly helpful as I could just deploy a build to my headset, playthrough all of the levels and generate the full shader cache, then use that shader cache for shipping builds, thus eliminating end users from experiencing those hitches.

I believe Unreal Engine does something similar with PSOs, would be a game changer to have this feature in Unity.

Thanks!

i believe adding it to your always included shaders in the graphics tab of setting compiles them at download time, but i’m not 100% sure

Thanks for the reply! I dont believe thats the case, unfortunately.

Im talking about the cache of compiled shaders that gets created while running the application, which, for oculus quest at least, ends up in sdcard/Android/data/com.CompanyName.ProductName/cache/UnityShaderCache.
9804072--1407585--upload_2024-4-29_14-47-36.png
In my experience, a fresh install of the game will have an empty shader cache, then as the game is run and objects using a specific shader variant are rendered for the first time, theres a hitch, then that compilation result gets cached in the above location. On subsequent plays of the game, those hitches no longer occur, as the cache is used, I assume.

Ive tried tackling this from the angle of prewarming shader variant collections but its a very arduous process that also has some issues:
https://discussions.unity.com/t/945259

I haven’t found a decent way to do this either. Would also be interested in a good solution, but I feel like one doesn’t exist currently.

1 Like

Going down a rabbit hole on this just trying to test something…
I wanted to try the following:

  1. Save all of the cached shaders from the device into a folder in the Unity project
  2. Build the game, then at runtime, copy the cached shader files from the folder in Unity into the device’s cached shader folder

My first attempt was to create a StreamingAssets folder in my project, copy the cached shader files into this folder, then use System.IO to iterate over all the files in the StreamingAssets folder and copy them to the device’s folder like so

public const string SourceCachePath = "ShaderCache";
    public const string DestinationCachePath0 = "cache";
    public const string DestinationCachePath1 = "UnityShaderCache";

    public static void LoadCache()
    {
        DirectoryInfo rootDir = Directory.GetParent(Application.persistentDataPath);

        string destPath = Path.Combine(rootDir.FullName, DestinationCachePath0, DestinationCachePath1);
        // Attempt to create directory if it doesnt already exist
        Directory.CreateDirectory(destPath);

        string[] cache = Directory.GetFiles(Path.Combine(Application.streamingAssetsPath, SourceCachePath));
        for (int i = 0; i < cache.Length; i++)
        {
            // Remove source path from file name
            string f = cache[i].Substring(SourceCachePath.Length + 1);
            // Copy file to destination
            File.Copy(cache[i], Path.Combine(destPath, f));
        }
    }

I found out that you cant access StreamingAssets directly on Android and need to use a UnityWebRequest. However, the UnityWebRequest requires that you specify each file name, which is difficult as there are thousands of files. I could generate a text file with all of the names of these cached shader files, put that in the StreamingAssets folder, read that file then use that to read the rest of the files, but this is a huge PITA for such a simple task.

Going over the other options; theres AssetBundles and Addressables. In either case, Im not sure if loading the files into memory would allow me to write them into the device’s destination folder, or how to do that. Additionally, it looks like a lot of these functions require you to specify the Type of the asset that youre loading, and these cached shader files dont have any extension, so Im not sure what type to specify, or if just using Object as a type will work.

Im avoiding doing all this work as I dont even know if copying the cached shader files into the device folder will actually accomplish the goal of eliminating the shader compile hitches, or if Ill just be copying junk data over. Are the names of these generated files unique to the shader variant? Are they random? Will it work across different Quest headsets with the same GPU??

Any info from Unity or someone whos perhaps tried this would be extremely helpful and save me a lot of potentially wasted time.

Thanks.

on a different note,

i’ve heard of people putting an object in front of the camera with every shader/ material on it at the games startup to cache them all before any gameplay, but that was in godot and i haven’t tried it in unity though i assume it would work the same.

I just tried doing this and while it appears some of the shader variants are getting compiled, theres no visible difference when actually playing and the hitches are still present, with more variants being compiled after.

Unity… Please help

1 Like

I have the same problem. First time run have terrible experience for users on Meta Quest 2-3. This is a shame that Unity have no solution for this basic problem.

I’ve done this on a few shipped titles now. Each time I’ve just hidden it behind a loading screen. Works well!

1 Like

Just to clarify that it is not Unity being too lazy to solve this problem, but that it is nearly impossible to have a cached shader prebuilt into one’s application. If I’m correct, every device could have a different GPU, hence every device may have different GPU instructions to run the shader; so to prebuild a shader means you have to prebuild hundrends/thousands kinds of GPU instructions for just a single shader variant, AND you will not be able to support any new GPUs.

1 Like