53 comments

[ 2.9 ms ] story [ 123 ms ] thread
(comment deleted)
Since the article mentions that "all shading languages are very close to C" I would like to mention Futhark as a language that compiles to Spir-V. It is still super experimental and I would never suggest it as a true replacement for a shading language but it atleast puts up for discussion the case that all shading languages forever will be C-clones.
Lots of problems with this (https://xkcd.com/386/).

"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...

> "All shader languages are very similar to C". Even counting only mainstream shader languages, MSL is explicitly based on C++ and not C.

C++ is a C variant. I think they were more getting at where’s Shader Pascal, Ruby, Lisp?

While it has roots in C, I think it's better to consider it a separate language, especially in more modern forms (Metal is C++14). Metal is locked into Apple's proprietary platforms, but there is other work in compiling C++ to portable shaders, particularly Sean Baxter's Circle, as well as some academic work[1].

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.

[1]: https://arxiv.org/pdf/2109.14682.pdf

Python has roots in C, but you can still write valid compilable C alongside C++, so I'm not sure what you're trying to imply.

Sure it's another language, but the C at it's core isn't going anywhere.

The deference between C and C++ is vast. You can do templates and meta programming in C++ that you can't in C. Similarly you can do templates and meta programming in Metal

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

You and I are ape variants. But it is the sum of our differences from apes that matters, almost always.

C++ code that looks like C is, typically, bad code. The C legacy is not going away, but is less and less relevant.

Now that is something I agree on. :)
Bunch of internal lisps generating all kinds of shader code (or OpenCL in at least one case), plus things like CEPL (which also generates shaders from CL data/code) and Shinmera uses in his game engine automatic composition of shaders using CLOS metaobjects, iirc
OpenGL is obsolete?
yes ... or maybe for all intents and purposes, deprecated or mostly abandoned are better words. Hardware vendors really don't want to spend any more resources on it.
It is not going anywhere, but don't expect anything after 4.6 to ever come up.
Very. It is a terrible API to talk through a modern GPU to, and I doubt any vendor is supporting it as anything but legacy at this point.
What are people in that camp using then? Vulkan? That seems too low level for most people
In my experience shaders are one of those things that doesn't really get enough use in data viz because of how inaccessible the langauage is. Sure CUDA is great, but it requires a ton of libraries and specific GPUs. What makes shaders so useful as a tool is that they work nearly everywhere and when they work they're super fast.
Especially compute shaders. A very nice and concise way of writing prototype GPU bound code without muddling in lower level details.
Hi guys, author here! Thanks for sharing, I'll be lurking in the comments!
Nice article. But why no scrollbars (I mean, why scrollbar's slider has the same color as track?)
I've adjusted the scrollbars to help make things easier to spot, thanks for the feedback! :)
Are current shader languages adequate? I've seen various posts mentioning that better alternatives can be developed for higher level languages for generating SPIR-V.
Adequate for what purpose? My personal feeling is that if you are sufficiently motivated, you can get your work done in them, but they are painful and tedious enough that in most cases your GPU is sitting there idle while the CPU with approximately 1/10 the raw computational throughput does its best to chug through compute workloads that could be done on GPU instead. And virtually nobody[1] is using them for machine learning; CUDA is so overwhelmingly better that people will buy into vendor lock-in to use it.

[1] kompute.cc is the exception that proves the rule

That is a good example of why they aren't adequate (if that's indeed the case).
Aside from being easier to use, what other advantages does Cuda have? Does Nvidia hinder Vulkan compute in any way?
I think the problem is not with compute itself but supposedly with higher level languages like GLSL and usability / expressiveness of their abstractions. You don't write your logic in pure SPIR-V.
The SPIR-V OpenCL profile is suitable, the Vulkan one is much less suitable. (you can’t even transpile OpenCL C 1.2 to it fully, with quite heavily limited pointers…)

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

> (you can’t even transpile OpenCL C 1.2 to it fully, with quite heavily limited pointers…)

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.

> Is that still true as of Vulkan 1.3?

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…)

That still sounds like not a good situation, if the only option if C-like expressiveness level I suppose.

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.

It's not just easier to use, but more powerful in many ways. A huge advantage of CUDA is the ecosystem of high quality libraries, including cuBLAS, cuDNN (machine learning), CUB, and Thrust. In the compute shader landscape, there's nothing remotely comparable, largely because GLSL doesn't support abstractions to express concepts like "do a prefix sum (scan) with this custom monoid", so you end up having to custom code everything yourself in C.

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.

> CUDA also supports independent thread scheduling

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)

Cuda is c++ with all the expressive power it has
Shaders would be so much more accessible, if there simply was an easy way to debug them. How many developer hours would have been saved with a simple printf statement? Or god forbid a step-through-debugger where you can inspect values...

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?

This is definitely a problem. I always resort to doing some kind of 'debug rendering,' modulating colors and trying to read back vals from that, in lieu of a printf.

Works fine for some things, but I've definitely dead-ended on others, no way I could think of to inspect.

Microsoft Pix has been able to debug shaders for nearly twenty years.
You just use modern APIs like Metal and DirectX that have GPGPU debuggers, which include shader debugging, just like a normal CPU language.

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 have used RenderDoc (https://renderdoc.org/) a couple of times, where i could click on a pixel to debug, and step through the shader step by step like regular debugging.

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.

if only it had macos support...
Xcode has a decent graphics debugger as well, or so I’ve heard.
Do you mean "if only MacOS had Vulkan support"?
Xcode has a good Metal frame debugger, which also allows to step through shader code.

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.

RenderDoc's debugger is great, and before it was shot in the head Direct3D9 PIX had a world-class shader debugger.

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).

Wow, WGSL looks different from the last time I checked it out (not too long ago iirc). I've been playing around with wgpu and vulkan, but I disliked the wgsl syntax so much that I didn't bother pursuing it any further at the time. I'm actually going to give it another look now since I don't really like glsl syntax much at all either.
Just be prepared for the shaders to break with each Chrome release, e.g. the new attribute style broke all existing samples.
Yeah, wgpu isn't updated for the new attributes yet apparently. I looked it up and it was just changed in the 2022-01-19 revision. Definitely a "working draft" as stated.
So, it sounds like learning HLSL (which is also what Unreal and Unity use natively, I believe) and cross-compile it where needed is the most portable approach nowadays? How well does the Kronos crosscompiler perform in practice?
I hope the future is that shaders along with the entire 3D APIs "dissolve" into regular programming languages and compiler toolchains. E.g. shader functions and GPU resources are mixed with regular CPU side code in the same source files, just marked with attributes (like [[gpubuffer(...)]], and shader functions with something like [[gpustage(vertex)]]), the "3D API" should become part of the standard library, and the CPU- and GPU-side are closely integrated for things like binding information (this would also make large areas of current 3D APIs simply "disappear").

Sean Baxter's Circle is going into that direction, and a handful other language projects too.

If I recall correctly, Scopes is also one of those languages (I know that it at least has a SPIR-V component).
What this is missing is "what is SPIR-V?" All the other languages and acronyms were introduced, but not that one.
The acronym itself doesn't really matter (Standard Portable Intermediate Representation), but the article points out what it is: an intermediate representation.

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).