Basically what I want to know is are we doomed with either bad stuttering or compiling shaders every update/driver update? What is the reason so many modern games have these issues?
EDIT: Sorry for the wall of text, I was't expecting this to be this long lol.
This is a million dollars question and the solution is very complex.
The first step is to understand *why* the problem exists.
Shaders are simply programs (written in textual form), that are running on the GPU rather than the CPU.
And just like CPU code, they need to be compiled into a binary form that the GPU can understand and execute, which is a very complex and expensive process.
On the CPU side, the target set of instructions supported by all Intel and AMD CPUs (the ISA), is well defined and you can compile ahead of time, produce a binary, and if everything is done correctly, it'll work on all machines (spending a few hours to compile the game on the customer machine before launching it wouldn't be very fun after all).
On the GPU side, there's no standardised ISA. While all GPUs implement the same abstract model defined by graphics APIs, they do so in *very* different ways, and things can change radically even from one generation to the next of the same IHV.
This means shaders needs to be compiled for every possible architecture, and the easiest solution is to let the specific driver on the customer machine handle it.
To make it worse, shader compilation is a process that depends on *what* you're trying to draw, and the same shader is usually compiled tens or hundreds of times in different variations to support different features.
The problem is not new and has always been there, but there are some factors that exacerbated it in recent years:
- The complexity of games is exploding, and with them the sheer number of shaders and their complexity is also skyrocketing. This is a double whammy for perf. Not only it'll cause a stutter because the game needs to wait for the compilation process to complete, but in general GPU perf will decrease with the increase in the number of different shaders used by a frame (UE and any engine with support for a visual node-based editor for materials are especially bad at this, as it's easy to make thousands of small different permutations by changing a node without realising it)
- New features added to DirectX 12 made the compilation process even more complex and expensive
- GPU performance is dramatically affected by the quality of compilation (way more than on the CPU side). It's a huge trade-off of compilation speed vs. performance of the code produced. As you can imagine no IHV wants to leave perf on the table, so compilation speed is not prioritised
- Old style APIs (especially DirectX 11) had constraints on the compilation that gave developers some baseline guarantees. Microsoft required that games could compile a reasonable number of shaders synchronously per frame without any visible effect (ie. stuttering) to the final player. It also allowed background and multi-tiered shader compilation. Of course exactly define what "not having visible side effects in reasonable scenarios" means is hard, but in practice, together with the much lower complexity of games, drivers were doing a pretty good job at hiding the issue. In DirectX 12 (and equivalent APIs) things changed, these requirements are gone and some additional restrictions have been added on *what* drivers can do in the background effectively limiting how far IHV can go to solve the issue (even tho Microsoft later backtracked to a certain degree), and the complexity has been moved into the hands of developers. Turns out, developers thought drivers were doing a shitty job and they wanted lower level access, but they actually don't (but that's a whole different issue).
- Shaders need to be recompiled every time the OS or driver are updated, and updates have become more and more common
Consoles don't have the same issue (with some extreme exceptions) because, since the hardware is fixed, you can precompile all shaders down to the real machine code for the GPU and ship it, pretty much avoiding the shader compilation cost.
This also means you can spend as much time as you want compiling shaders, generating the most efficient code without worrying about runtime issues.
Even system/driver updates don't affect existing games because usually each game keeps being executed in the same environment it was build and deployed against, guaranteeing future proofing against updates.
One of the many reasons consoles have an advantage over open platforms like PCs.
Vendors, both IHV and ISV are actively working hard to mitigate the issue obviously, but finding a solution that doesn't affect GPU performance negatively is extremely hard. Whatever solution you apply, it also needs to be future proof, ie. future GPUs with radically different architectures still need to be able to run that code correctly. No one would want a new GPU that can't run existing games after all.
Most of the burden today is in the hands of developers. It requires a mixture of reducing shader complexity and combinatorial explosion by imposing tighter control on the development process, precompiling the most commonly used shader on first launch or during the loading screen, compile the remaining less common shaders asynchronously in the background (which requires some knowledge of what content is gonna be displayed next).
The real solution however is to allow precompilation of shaders ahead of time, similarly to consoles.
This however requires complex new infrastructure to support the use case, and a lot of coordination between OS makers (Microsoft but also Apple or Linux), IHVs (Nvidia, AMD, Intel, Apple, possibly all mobile vendors), and possibly even storefronts like Steam or Epic Games to ship the correct shader binaries to customers. I'm sure it goes without saying this is a nearly impossible ordeal.
Ideally, games would ship a much lower level IR, one per vendor rather than a common high level IR defined by the API, one that is defined by each IHV (with tools provided to developers by them) and allow the driver to convert this into the final machine code for the real GPU much much faster.
At the same time a tighter integration of OS, drivers and APIs could allow shaders to be recompiled in the background on a system or driver update before game are even ran in the first place (but this obviously comes with its own set of issues and complexities).
TL; DR: shader compilation is a fucking nightmare.
It wasn't an issue at all because you can ship intermediate compiled shaders (so there's no real runtime cost). DX12/Vulkan don't work like that, and shaders must be compiled at runtime (either doing it in bulk when you start the game or on demand).
Shipping the intermediate representation (IR) of compiled shaders is still the norm in DirectX 12 (DXIL), Vulkan (SPIR-V) or Metal (AIR).
Nothing changed on that front.
There definitely is a runtime cost, and a massive one a that.
In fact, the first step of the compilation process that developers do offline during development (shader source -> IR) is the cheap part and only account for probably 5-10% of the total compilation cost.
Which is why IRs (at least in their current form) are not actually a good solution to the problem.