You just shelled out your hard-earned dollars for the latest AAA game to play on your treasured gaming PC. One lengthy download later, you jump right into the game, only to be faced with a long wait at the main menu while it sorts itself out…
Or maybe that doesn’t happen. But you find yourself suffering lots of frame rate problems during gameplay. Desperate for a solution, you trawl the web in search for an answer and stumble across an oft-repeated phrase: “shader compilation.”
Do you want to know what is shader compilation, why is it needed, and why it receives such negative press? Well, this is the perfect article for you.
Okay, so what is a shader?
Essentially, shaders are just blocks of code. In the world of graphics and 3D rendering, each one is a little program designed to produce a very specific output. It might be to transform the shape or position of a triangle, map a texture to a shape, or calculate a set of values that are required for other shaders.
When APIs such as Direct3D and OpenGL first supported shaders, they were extremely limited in size and scope. For example, when pixel shaders first appeared on PCs, over 20 years ago, there was room for just 4 instructions involving textures and 8 for doing math.
Today, in the latest version of all shaders, there is effectively no limit to how many instructions can be packed into the code…
Although the various APIs used by developers have different terminology, there are seven categories of shaders. Vertex, geometry, hull, and domain shaders are all about handling the vertices of triangles — creating and shaping the 3D world before it gets colored in.
That task is handled by pixel (and more recently, ray) shaders, which process the math involved in lighting and texturing. Finally, compute shaders are all about doing general-purpose arithmetic and logic processing, rather than anything specific to graphics.
Modern 3D games use thousands of shaders to calculate the images displayed on the screen. Some are very short and simple, others can be extremely long and highly complex, sucking up all the resources a GPU can offer it.
And every one of them needs processing before the GPU can do anything with them.
The how and when of shader compilation
Software compilation is the process of transforming source code into machine-executable code. The source code can be written in any programming language, which is then turned into binary to be executed by the computer’s hardware.
Shaders are no different, they are written in a certain language so it’s easier to create them and understand what’s going on. An example of this can be seen using ShaderToy. To use the online version of this tool, code is written using GLSL (OpenGL Shading Language) and purposes graphics API WebGL for the rendering.
In the example below, the code shown is part of the process required to generate the surface of the terrain seen in the final image — you can follow how it was all created by watching this video.
Even if you don’t know what any of that code means, it doesn’t matter — your graphics chip doesn’t either. It can perform all of the calculations, but it needs to be given the instructions in binary. The code needs to be compiled from WebGL into GPU instructions — in the case of anything that uses a GPU to get the end result, the compiling is done by the GPU drivers, with the CPU handling that workload.
This ShaderToy example only uses a handful of shaders and only one of them is reasonably long. Even so, it only takes an average desktop CPU a fraction of a second to compile them all, so this is done once, the moment you start the demo.
Pick any modern 3D game, though, and it’s a different story altogether. Traveling about the environment, going to a different level, or combating a new enemy requires the game to load new shaders from the storage drive and then compile them.
Games on consoles, such as the PS5 or Xbox Series, usually have all of the shaders pre-compiled, though not always. This is because the hardware configuration is very static — developers creating a new title for a machine from Sony or Microsoft only need to manage one type of GPU and its operating system and drivers change infrequently.
Desktop PCs, though, present a major headache in this respect, as there are thousands of possible combinations of operating systems, drivers, and graphics chips. It would be impossible to pre-compile shaders for all of them, so developers use a variety of approaches to solving this puzzle.
The problems surrounding shader compiling
One common approach is to convert all of the shaders that are going to be used by the game as it loads up — sometimes during the main startup sequence, other times while at the main menu, or at initial level loading.
If a game has tens of thousands of shaders, compiling them all at the start can potentially make these load times excessively long. Poor management of the shader library, during the making of the game, will only compound this issue, as time and storage space will be wasted compiling something that’s never going to get used.
However, it does at least mean that the shader code is ready to be used by the drivers, the moment the game’s rendering engine issues commands to the GPU. The opposite method to all of this is to eschew converting them at the start and do it all when they are required, during gameplay.
This will obviously speed up loading but real-time compiling can generate stalls in the graphics processing chain, while the CPU runs the conversion process.
These are usually experienced in the form of a sudden, but very brief, drop in the average frame rate of the game. Better known as stuttering, this issue can range from being nothing more than a singular, momentary hiccup all the way to a routinely jerky, awful mess.
Stuttering in games isn’t just caused by shader compiling, as there are a great many aspects to rendering that can result in this, but this specific issue is one that’s becoming increasingly more prevalent.
This is because the trend in AAA titles is to have ever more realistic or visually impressive graphics, especially in the popular open-world genre. Thus shaders are getting longer and more complex, as well as increasing in number, and these all take longer to compile.
There are many games that take an intermediary approach to shader compiling, processing the main ones during loading or at the main menu, and doing the rest as they’re required. But getting this right on the PC platform is a challenge all of its own.
Gamers who are familiar with titles made using the Unreal Engine, particularly version 4, will know this all too well, though it’s not a problem that’s exclusive to UE4. The launches of the PC ports of Horizon Zero Dawn and Death Stranding, both made using the proprietary Decima engine, were marred by frequent reports of incessant stuttering, much of which was attributed to shader compiling issues and addressed in post-launch patches.
So what can developers and gamers do to minimize the issue?
Unfortunately for gamers, there’s not much you can do about it. Since it’s the CPU handling the compiling duties, minimizing the amount of background tasks may help, as will reducing the quality of the game’s graphics, but that’s pretty much all users can do.
Few games offer an option to alter how the shader compiling is managed. The aforementioned Horizon Zero Dawn, with the latest patch, will do shader compiling in the main menu, but does let you skip it if it’s taking too long — which it will on low-end hardware.
Something like the recent Hogwarts Legacy doesn’t give you such an option and the bulk of the compiling is carried out as the game fires up, prior to reaching the main menu. Neither approach seems to be especially optimal as both titles will still noticeably process more shaders while you’re playing.
GPU makers also play a role here, as it’s their software that’s doing the compilation. AMD and Nvidia’s drivers will store compiled shaders from commonly played games in a folder on the main storage drive, but the moment the game or drivers are updated, the process needs to be repeated.
You might think that these vendors could do more to improve the performance of the shader compiling, but these days it’s as fast as it can be.
At this point, you might also be thinking that some kind of online service, which stores compiled shaders for every game, hardware, and driver configuration would be worth having. Valve does offer such a thing, as a cache service in Steam, but only for games that use OpenGL or Vulkan. More often than not, though, the cache always appears empty.
Ultimately, it’s the game developers that are responsible for minimizing the impact of shader compiling. Some are very good at this and seemingly go out of their way to minimize stuttering, whereas others are far less diligent about it (here’s looking at you, FromSoftware).
The savviest of developers (e.g. Infinity Ward, makers of CoD) will know that they need to use every trick in the book to prevent the code conversion from interrupting the flow of the rendering, by hiding it in menus, cut-scenes, mid-level loading stages, and so on.
Having lots of experience and understanding of the finer details of the engine being used is key, too. For example, when making a game in UE4, the surface appearance of every object and section of the environment is generated by items called materials. By default, creating these in turn generates a raft of shaders, many of which may be unnecessary.
If one doesn’t manage this correctly, then the problem will only present itself during quality/performance testing. Development managers allocate finances and time to such things, of course, but this only goes so far, and if the game is experiencing lots of other problems, addressing stuttering due to shader compilation may not receive sufficient attention in time for launch.
Post-release patches often try to alleviate such problems, which strongly suggests that development houses just aren’t giving PC versions of their games enough QA testing. And given that this costs money, as long as millions of people are still buying the latest games avec stuttering, lots of managers just aren’t going to be willing to spend more funds on optimizing things.
So maybe, just maybe, the problem can be fixed by us — all through the power of our wallets.