Here is a simple fragment shader with uniform buffers:
const std = @import("std");
const gpu = std.gpu;
const UBO = extern struct {
object_color: @Vector(4, f32),
light_color: @Vector(4, f32),
};
extern const ubo: UBO addrspace(.uniform);
extern var frag_color: Vec4 addrspace(.output);
export fn fragmentMain() callconv(.spirv_fragment) void {
// Annotation
gpu.binding(&ubo, 0, 0);
gpu.location(&frag_color, 0);
frag_color = ubo.object_color * ubo.light_color;
}
In CLI:
zig build-obj shader.zig -target spirv32-vulkan -ofmt=spirv -mcpu vulkan_v1_2 -fno-llvm
In build.zig
:
const vulkan12_target = b.resolveTargetQuery(.{
.cpu_arch = .spirv32,
.cpu_model = .{ .explicit = &std.Target.spirv.cpu.vulkan_v1_2 },
.os_tag = .vulkan,
.ofmt = .spirv,
});
const shader = b.addObject(.{
.name = "shader",
.root_source_file = b.path("shader.zig"),
.target = vulkan12_target,
.optimize = .ReleaseFast,
.use_llvm = false,
.use_lld = false,
});
// Use the emited SPIR-V with `shader.getEmitedBin()`
This is by no means complete, but it's a good starting point when you're looking to port some shaders between GLSL/HLSL to Zig.
GLSL | HLSL | Zig |
---|---|---|
gl_Position |
SV_Position |
gpu.position_in/gpu.position_out |
gl_VertexIndex |
SV_VertexID |
gpu.vertex_index |
gl_InstanceIndex |
SV_InstanceID |
gpu.instance_index |
gl_FragCoord |
SV_Position |
gpu.fragment_coord |
gl_FragDepth |
SV_Depth |
gpu.fragment_depth |
layout(location=N) |
SV_Target |
gpu.location() |
layout(binding=N) |
register() |
gpu.binding() |
gl_GlobalInvocationID |
SV_DispatchThreadID |
gpu.global_invocation_id |
gl_LocalInvocationID |
SV_GroupThreadID |
gpu.local_invocation_id |
You can directly write SPIR-V assembly using the inline assembly feature. As you probably have noitced, it requires a basic knowledge in both Zig's inline assembly syntax and SPIR-V so make sure to read Zig's inline assembly, SPIR-V Assembly Syntax and SPIR-V Specification docs.
Here's how std.gpu.binding()
is implemented:
pub fn binding(comptime ptr: anytype, comptime set: u32, comptime bind: u32) void {
asm volatile (
\\OpDecorate %ptr DescriptorSet $set
\\OpDecorate %ptr Binding $bind
:
: [ptr] "" (ptr),
[set] "c" (set),
[bind] "c" (bind),
);
}
OpDecorate
is an instruction
with no result-id which means it has no output. normal input constraints are declared by an empty string and a %
sign in the code.
There's also constant constraints ("c"
) which takes a comptime known value and are determined with a $
sign.
for more examples checkout std.gpu
.
Write code. SPIR-V backend is in early stages so we are eager to see how it works for real-world examples so
a reproducible bug in issue-tracker is appreciated.
If you have any questions, feel free to reach me (#alichraghi
) or
Snektron (#snektron
) in Discord or ZSF's zulip.
Sure, here's the full error:
This is being run through an Odin platform layer, but I can't imagine that being the problem since these are just passed into vulkan as shader code.
Either way, thank you dearly for all the resources both you and Robin have created regarding Zig on GPU. I will be very happy when this is fully fleshed out. Might try doing GCN for other things in the meantime.
EDIT: I figured out my issue, apparently, GPUs don't like concatenating arrays (++ operator)