Sheet 10/10 — Driver · Shader JIT
mzJIT
A SPIR-V JIT that makes software shading respectably fast.
| Layer | Driver · Shader JIT |
|---|---|
| Language | C |
| Targets | x86_64 · x86 · arm32 · aarch64 (NEON) |
| Depends on | — (foundation) |
| Used by | mzVolk |
| Organization | ZeunO8 |
| Source | Private alpha — opens with a change order on this site’s log |
General notes
mzJIT executes SPIR-V shaders on the CPU: a SPIR-V parser, a custom SSA intermediate representation, a reference interpreter, and per-architecture JIT backends that compile shaders to native machine code at pipeline-creation time.
Inside mzVolk it is the difference between "software Vulkan works" and "software Vulkan performs" — JIT-compiled fragment shaders feed the SSE rasterizer lanes on x86_64, arm32 has op parity, and aarch64 has a real NEON backend with safe executable-memory handling. A pass-through detector skips shader invocation entirely for trivially-interpolating fragment shaders.
A rapid release run (v0.3 through v0.21) built out real shader capability: multi-block control flow lowered on every backend (Rosetta-verified on x86_64), integer ops that ride the float lanes so loops JIT-compile, access-chain component loads and stores, descriptor-backed uniform buffer loads, uniform-resident mat4 transforms, and the full GLSL.std.450 extended-instruction map — behind an IR builder that fails closed on any SPIR-V it does not support. On aarch64, direct-binding loads and stores resolve I/O at compile time, worth roughly +70% FPS in the uniform-buffer benchmark. The latest wave added texture sampling (OpImageSampleImplicitLod across interpreter and JIT), OpPhi support so optimizer-compiled shaders (glslc -O) JIT too, and compute shaders: storage-buffer writes, gl_GlobalInvocationID, dynamic-indexed SSBO arrays, and workgroup-size parsing from entry points. OpFunctionCall now lowers by inlining at IR-build time, so shaders from glslang — which emits real function calls rather than inlining them itself — JIT-compile like any other.
Bill of materials — what’s in the box
- SPIR-V parser and custom SSA IR — fails closed on unsupported input
- Interpreter for correctness, JIT backends for speed
- Native code generation at pipeline-creation time
- Backends: x86_64 (SSE lanes), x86, arm32, aarch64 (NEON)
- Backend parity: control flow, matrix ops, and extended instructions on every target
- Full int/bit ops — div, mod, shifts, bitwise, int↔uint bitcasts — plus loops, access chains, builtins, full GLSL.std.450
- Specialization constants: VkSpecializationInfo resolved through OpSpecConstant* at pipeline creation
- OpFunctionCall lowered by IR-build-time inlining — glslang-emitted shaders JIT-compile
- Texture sampling (implicit + explicit LOD, texelFetch/textureSize, sRGB), fragment discard (OpKill), and OpPhi — optimized (glslc -O) shaders JIT
- Compute: storage-buffer writes, gl_GlobalInvocationID, shared memory + OpControlBarrier, dynamic-indexed SSBO arrays, workgroup sizes
- Atomics and OpSwitch, lowered in the JIT backends — not just interpreted
- std140/std430 layout decorations and push-constant member offsets — mixed buffer layouts stay JIT-compiled
- Descriptor-backed uniforms, uniform-resident mat4, aarch64 direct-binding I/O (~+70% FPS)
- Batched fragment entry point on aarch64 — one call shades a whole run of fragments (−69% per-fragment overhead)