Zig gpu kernel for macOS (via spirv)

I would like to play around with writing some gpu kernels in zig code similar to the examples for Nvidia and AMD gpus in GitHub - Snektron/shallenge: https://shallenge.quirino.net/ in zig, but I would like it to work on macOS, and I am looking into the best solution for this.

I guess the simplest solution would be to use OpenCL (as described in the spirv example in Zig and GPUs), but I would like to try to go through Metal if possible. From my current understanding this can be done by compiling the zig kernel to spirv with something similar to the following from the above link:

// SPIR-V:
//     zig build-obj -target spirv64-vulkan-none -mcpu vulkan_v1_2+int64 \
//     -ofmt=spirv -fno-llvm kernel.zig
//     zig build-obj -target spirv64-opencl-none -mcpu opencl_v2+int64 \
//     -ofmt=spirv -fno-llvm kernel.zig

And then use SPIRV-Cross to cross compile from spirv to Metal Shading Language (MSL), which can then be compiled going .metal → .ir → .metallib.

Does anyone have experience with a setup like this (or zig kernels on macOS some other way)? Is there a simpler way than what I am imaging?

If you want to use SPIRV for a kernel, I would look into using the KosmicKrisp Vulkan implementaion for macOS, since it’s an official project from mesa. That’s all I can say on the matter since I don’t use a mac myself.

I use SPIRVCross in the sokol-shdc shader compiler, it does what you want, but the details can get tricky. One of those details is that SPIRVCross only gives you Metal shader source code, to compile that into a .metallib binary file you need to invoke the Metal shader compiler commandline tool.

I also never tested SPIRVCross on Zig output, only on SPIRV created from ‘Vulkan-flavoured GLSL’ compiled via glslang.

Going through a Vulkan wrapper via this new KosmicKrisp thingie might indeed be the better solution - and if I’m not mistaken that’s open source, so maybe another option is to rip out the SPIRV-to-Metal translation pipeline from KosmicKrisp and use that as replacement for SPIRVCross and the metal command line tool.

Didn’t know about KosmicKrisp, it looks interesting. But it seems to be pretty new and mainly a runtime translation (I think), and I mainly want to try to compile some smaller compute kernels at first, so I think I will still start out by trying to use SPIRV-Cross.

Thank you for verifying that the idea should work at least. You say the details can get tricky; how so? Other than compiling the Metal source code into a .metallib binary via the command line tool, have you run into any harder problems? Or is it mostly a lot of minor annoyances about going through all these different formats?

I assume that SPIRV-Cross should work the same on Zig generated spirv, but I haven’t tested that yet (will do so soon though).

Trying to pull out the translation from KosmicKrisp seems hard, since they go through an internal mesa format (.nir) from my understanding.

Mostly this, and the need for calling out into the metal shader compiler to generate the final binary files (you can of course also skip this step and compile the generate Metal source files on load in your application via the Metal API).

The other thing that’s untested is whether SPIRVCross can handle the Zig SPIRV output (e.g. SPIRV files come in different incompatible ‘flavours’ (GLSL, Vulkan, in the future D3D, and maybe the same flavours exist for compute-only APIs?). AFAIK for graphics APIs this is mainly about differences about how the shader inputs/outputs are defined but there may be more subtle differences.

1 Like