Congrats to @ali for his first devlog entry!
Back to back devlogs! We are getting spoiled.
Following up with some handy links for those who want to play with shaders in Zig.
Iām not sure how up to date this one is, but here are some examples:
also, @ali said
gota7 has been helping me to make sure real world examples work. these are pretty up to date i believe https://codeberg.org/7Games/zig-sdl3/src/branch/master-gpu/gpu_examples/shaders/zig
Iām really happy to hear this. Last I checked, the SPIR-V support hasnāt included Metal/macOS. Has this changed?
Metal doesnāt consume SPIR-V format so thereās nothing we can do in terms of supporting it.
Perhaps i misunderstood what you meant?
I hope itās fine that Iām asking this here and not in a dedicated question thread but what does Zig gain from SPIR-V? I read that it is an intermediate representation for shading languages. Does this mean Zig is getting improved type safety with shading applications or something else?
(Iām not massively well-versed in GPU stuffāIām pretty confident everything below is correct, but apologies for any errors!)
Although itās an IR, as far as the Zig compiler is concerned, SPIR-V is an instruction format, just like x86_64 machine code or WASM bytecode (which is actually also an IR). Like you said, the purpose of SPIR-V is for shadersāthe way todayās graphics APIs generally work is that you give them a compiled shader in a format like SPIR-V (SPIR-V is used by Vulkan, and is also supported by modern OpenGL extensions) which the graphics driver then compiles down to whatever hardware-specific ISA it needs. So, the Zig compiler supporting SPIR-V allows you to write GPU shaders in Zig. This is useful for a few reasons:
-
Most shading languages are, frankly, not goodāfor instance, I believe GLSL is still pretty common, despite it essentially being a subset of C with some hacked-on extensions and incredibly limited tooling. Using Zig for your shaders has the immediate benefit that, to Zig users, itās probably nicer than any existing shader language!
-
A key part of GPU programming is transferring data between the CPU and the GPU. When you do this, you write that data to a buffer on the CPU, and transfer it to the GPU, where it is read from a buffer. If the source language is the same between your CPU and GPU code, this becomes very convenient, because you can keep all of your shared data structures in a ācommonā source file which is imported from both your CPU and GPU codeāso you donāt need to duplicate your type definitions and remember to keep them in sync!
-
Kind of a sub-point of the above: modern GPU programming often has a focus on minimizing the amount of data transferred between CPU and GPU by using dense memory layouts. This could involve bit-packing data, and thatās something which Zig is quite good at thanks to
packed structandpacked uniontypes! So this is one specific case where Zig can significantly improve the ergonomics of dealing with data coming into the shader, whereas existing shader languages would (AFAIK) all require manual bit manipulation to pull out the bits you need.
I was silently hoping that I would one day be able to write shaders in Zig, this is really really cool!
Is this already available to users? I think itās better if I just try to use this myself instead of bombarding everyone with questions ![]()
spirv-cross may interest you. It supports SPIR-V to GLSL, MSL, and HLSL. I canāt vouch for how good it is, but itās perhaps worth exploring.
Ohh, what great news! In a half-abandoned project by now I made a Vulkan rendergraph thingy. Back then the shader support wasnāt there yet, but canāt wait to get back to it, to try out throwing out spirv-reflect and have some inbuilt reflection. Thank you, @ali!
This is a very interesting read! Iād love to dabble with this SPIR-V work in my free time, but I lack the prerequisite knowledge to get started. Any reading materials or exercises youād recommend for a newbie to get started hacking away at shaders in Zig?
If you havenāt messed with shaders at all before, Iād suggest doing an intro tutorial to them, just in GLSL. there are lots and lots, but Iād be happy to find you some links if youād like.
After that, maybe the next step would be to take one of the shaders you wrote as part of that tutorial and using the links @andrewrk posted as a guide, make that same shader but in Zig instead.
what does Zig gain from SPIR-V?
Potential advantages are that you can remain in the same language and directly share type definitions between the CPU and GPU side, and maybe even share code (e.g. common helper functions).
Making the compiler understand both the GPU- and CPU-side has amazing potential beyond ājustā sharing code, when this works properly we could basically get rid of at least 75% of the API surface of current 3D APIs (most of that API surface only exists to describe the āshapeā of data thatās transferred from the CPU to the GPU, but that shape can be easily inferred by the compiler when it could see āboth sidesā).
For a new language which does the whole CPU/GPU integration right, check https://minc.dev/, specifically this integrated rendering demo where both CPU- and GPU-side code lives in the same file:
I am personally betting on this being a very big deal for game development. The glue code between CPU/GPU is such a pain in the butt that is just accepted as the cost of doing business today. Everyone rewrites or copy/pastes the same data structures and algorithms around because thereās not a good way to reuse shader code.
Writing your shaders and games in one language with an advanced build pipeline and package manager will be one of those obvious in hindsight, table stakes things once people who are complacent with the current standard realize what theyāve been missing
I use SPIRVCross in sokol-shdc and can vouch for its robustness ![]()
AFAIK SPIRVCross is also used in Valveās Proton btw (the SPIRVCross author has or is working on VKD3D-Proton).
The only downside is that SPIRVCross doesnāt support WGSL output (and most likely never will), so for that a separate solution is needed (in sokol-shdc I link with an older version of Googleās Tint).
PS: Apparently MoltenVK also uses SPIRVCross.
Additionally, some languages could conceptually compile to SPIR-V, but at the end of the day you have to hand write and maintain, or codegen, the binding code for Vulkan and so on. I believe Zig comptime brings an edge here, since the meta programming is basically best in class, thereās no costly tooling or compiler level support required, it can be done in user space
Apart from SPIRVCross (which is also used by the older Vulkan-on-Metal solution MoltenVK) maybe the new SPIRV-to-MSL code from KosmicKrisp (the new Vulkan-on-Metal ādriverā) can also be salvaged, e.g.
SPIRV-to-NIR: Making sure you're not a bot!
NIR-to-MSL: Making sure you're not a bot!
ā¦the problem with both SPIRVCross and this KosmicKrisp thingie is that they create MSL (e.g. textual source code) instead of the Metal bytecode format (which I think isnāt documented by Apple).
(this SPIRV-to-MSL translation would probably need to be the job of a build-system tool though)
Also D3D would have the same problem, it needs a translation layer from SPIRV to HLSL or DXIL (D3D will switch to a SPIRV-flavour though, so in the future it might be easier to directly support D3D).