A Review of Shader Languages
alain.xyz
alain.xyz
The most common cause for errors in my shader code are values outside of the expected range or matrix transformation errors where I have to go through them step by step on paper...
So, how do you debug shaders? Is there a reliable tool which actually helps? Or am I missing something?
Works fine for some things, but I've definitely dead-ended on others, no way I could think of to inspect.
The problem with Khronos standards is that they always let the community fix their lack of tooling, so the best you can hope for is vendor specific debugging tools, or something like RenderDoc, which isn't as feature rich.
I haven't used it alot, but it was quite excellent out of the box for me. I needed to pass some debug flags to shader compilation - and it was only automatically able to capture output to the screen buffer as far as i remember. But there is an api to integrate it if you want offscreen target debugging i think.
The Graphics Debugger that's integrated into Visual Studio also has a shader debugger (for D3D/HLSL).
RenderDoc allows shader debugging for some backends, although I currently don't remember which one (either D3D, GL or Vulkan).
Nvidia has the NSIGHT debugger, which AFAIK exists both as standalone tool and integrated into Visual Studio and also allows to step through shader code.
Game consoles usually come with their own graphics debugging tools.
For game console development you have access to world-class shader debuggers that let you step through the raw shader assembly on the GPU at the hardware level (so you can see the individual waves/threads and register values).
In practice shader debugging only sucks in OpenGL (vulkan is not great, but is closer to ideal). In Direct3D it's totally fine. This is partly due to Direct3D having well-specified semantics and always having a reference rasterizer, so a debugger can use refrast to do accurate shader step-through (unless you hit driver bugs).
I've only done very basic shader stuff, but it helped me when I got stuck.
Discussion: https://news.ycombinator.com/item?id=29532110
Sean Baxter's Circle is going into that direction, and a handful other language projects too.
[1] kompute.cc is the exception that proves the rule
Metal has the best GPGPU story of the graphics APIs, with D3D12 coming second after that. Both at least can have OpenCL C transpiled without catches to them…
Microsoft is writing CLon12 on that front: https://github.com/microsoft/OpenCLOn12
Is that still true as of Vulkan 1.3? We have buffer device address now, which I think counts as a real pointer.
> Metal has the best GPGPU story of the graphics APIs
I wish I could agree wholeheartedly with this. Yes, it's wonderful in many ways, but the lack of device-scope barriers means an entire class of advanced coordination patterns is unavailable.
Of course, you're talking about transpiling OpenCL C 1.2, which I consider incredibly primitive and limited compared to modern compute shaders: no subgroups, no device-scope barriers, no 16 bit float.
In practice every GPU vendor that mattered already implemented it to 1.2, not much of a change on that front.
The core problem, that pointers are an abstract type with no known compile-time size remains.
> but the lack of device-scope barriers means an entire class of advanced coordination patterns is unavailable.
(And OpenCL on Apple Silicon seems to have the opposite problem, stuff being promoted to device wide barriers when it should not be…)
Can something like Rust level of expressiveness be compiled into OpenCL profile SPIR-V?
Basically I'm talking about the higher level language, not about just IR being able to express compute use cases. Metal, D3D and CUDA are all dead ends becasue neither of them is really free of some kind of lock-in.
In more recent iterations (ie since Volta/GTX 20XX), CUDA also supports independent thread scheduling (allowing mutexes with thread granularity) and cooperative groups. For really advanced workloads, that's well beyond what compute shaders can do, and I suspect it will be a decade or so to catch up, if they ever do. Of course, part of what I enjoy about compute shaders is the challenge of making algorithms run well in a more constrained computing model.
Nvidia does not hinder Vulkan compute in any way. I have a bunch of experience with Vulkan implementations and Nvidia's is certainly one of the best. In particular, their implementation of the Vulkan memory model is top-notch, with no correctness issues I've uncovered, and with a measurable performance improvement from using the fancy atomic semantics over older-style barriers. In fact, this is arguably one way in which compute shaders are more advanced than CUDA, which still does not have a formal memory model.
And that’s technically only present on NVIDIA and Arm Mali GPUs as of today. (Both do independent thread scheduling, not so for the other GPU vendors)
It even spells out how it works: Shader API -> IR API [this is where SPIR-V lives] -> IR Vendor
It really doesn't matter, since you never interact with it directly (as in it's not a shading language).
"All shader languages are very similar to C". Even counting only mainstream shader languages, MSL is explicitly based on C++ and not C. There is a big difference. The non-mainstream shader languages will also be increasingly important, because who wants to be restricted to C dialects?
"and have all the usual features associated with C save for pointers." MSL has strong built-in support for pointers. In addition, the future of Vulkan is increasingly pointer-friendly.
HLSL compilation. This oversimplifies the story a bit. DXC only produces DXIL which is D3D12+ (and SPIR-V, so it can be used on Vulkan with little hassle). Older dialects (shader model 5) work on D3D11 as well, and can also be compiled offline, but that requires FXC [1] (FXC output, DXBC, can be used in D3D12, you just don't get as much access to newer features).
"Some languages however encourage you not to compile shaders to an IR such as GLSL with OpenGL". This is no longer true as of OpenGL 4.6, which can use SPIR-V. On the other hand, it's probably best to think of OpenGL as a nearly obsolete API, only useful for compatibility.
"The metallib compiler is available in the command line in any recent MacOS installation." It might also be worth pointing out that it is also now available on Windows. Thus, on a single Windows box it is possible to go from your source language to all major shader IL's.
"GLSLang even supports compiling HLSL to SPIR-V, though this feature is still experimental." Yes, but use DXC instead, that's actively being developed.
"transpiling it to either GLSL, HLSL, or MSL is trivial." It's not trivial. There are important semantic differences between these shader languages, and transpilation is a leaky abstraction. One case in point, memory barriers of device scope are not available in current MSL, so transpilation of such barriers is likely to silently result in barriers of merely workgroup scope. These kinds of problems are fun to debug.
"Mozilla Naga is the Firefox WGSL to SPIR-V compiler written in Rust." It has many more transpilation options, and in fact a major goal was to avoid the need for spirv-cross to generate HLSL and MSL. Similar for Dawn.
This might be a useful resource for some, but don't take its advice too seriously.
[1]: https://asawicki.info/news_1719_two_shader_compilers_of_dire...
C++ is a C variant. I think they were more getting at where’s Shader Pascal, Ruby, Lisp?
There's also of course rust-gpu. I think it's fair to say that in 5 to 10 years we will no longer be writing shaders in C, but will have moved on to a higher level language.
Sure it's another language, but the C at it's core isn't going anywhere.
So to say all shader languages are based on C is as useful as saying they're all based on assembly. I'm not saying C++ style templates are good or bad but they're vastly more powerful than not having them and if you're using those other C based shader languages you'll end up having to do code generation in some other language to provide the same features as a C++ based shader language
C++ code that looks like C is, typically, bad code. The C legacy is not going away, but is less and less relevant.