Currently Vulkan and DX12 seem to be in a state of turmoil. The main assumption with the two low-level APIs are that you need to have a fully specified static list of configuration options to send to the GPU when you send render commands (I'm essentially talking about the Pipeline State Object) - which will aid in optimization since there's not any dynamic state for the GPU driver to wrangle with. However, many devs in the game industry has tried making performant renderers with this model and not many have succeeded, because if you see actual GPU usage patterns in game engines they do need some of the flexibility previously had in DX11 / OpenGL. Without this dynamism, as a rendering engineer you essentially need to do one of the two:
- Pre-build all combinations of PSOs to implement your shader effects (if you have three different options for your rendering with A, B, C possible states each then you need to prebuild all A * B * C combinations of PSOs)... which will become ridiculous later on.
- Create an automatic caching system that caches PSOs, so that the initial pipeline compilation will introduce some lag but later cached usage of the same PSOs will be fast. Now you have a much worse version of what DX11 / OpenGL was already doing behind the scenes... (unless you git gud and surpass the abilities of NVIDIA driver developers)
With this in mind and also the various difficulties with managing the complexity of the new APIs (such as synchronization)... the reality is that a DX11 backend will probably be more performant than a naively written DX12 / Vulkan engine, since it's really just hard to beat NVIDIA's ridiculously optimized DX11 drivers that they have spent ridiculous amounts of time and money on. Unless you have a world-class team like Unreal or the ID tech folks... it seems like a gargantuan task to do. (Even Unreal Engine 5 had problems with heavy stuttering that are now fixed... and I suspect the issues were with how DX12 / Vulkan works)
Nowadays the problem seems to be widely known in the industry, up to the point that both DX12 and Vulkan have introduced experimental extensions which provide alternatives to the PSO model:
- The initial attempt is Vulkan's VK_EXT_shader_object extension (basically "Vulkan without Pipelines": https://www.khronos.org/blog/you-can-use-vulkan-without-pipe...) - which overhauls the API by introducing shader objects that you can dynamically link at runtime. Basically somewhere in the middle of DX11/OpenGL and DX12: give back some dynamism to the API, but not up to the point where the driver struggles to maintain performance because of it.
- Other attempts seem to resemble more closely towards Render Graphs, which was a strategy that various game engines have already implemented on top of DX12 / Vulkan / console APIs. (See https://logins.github.io/graphics/2021/05/31/RenderGraphs.ht... for a introduction). For example, DX12's Work Graph API as preview (https://devblogs.microsoft.com/directx/d3d12-work-graphs-pre...), or Vulkan's VK_AMDX_shader_enqueue extension (https://gpuopen.com/gpu-work-graphs-in-vulkan/)
But nonetheless, I think I should just wait touching these APIs until things have sorted out between GPU manufacturers and engine developers. (If I would start making a game engine from scratch for my indie game I would probably just use DX11.)