#zig-gpu: a small helper library for zig shaders

1 messages · Page 1 of 1 (latest)

remote shoal
#

zig-gpu: a small helper library for zig shaders

#

example of a zig shader using this lib:

const gpu = @import("gpu");

pub const uniform = gpu.ptr(@This(), struct { time: f32 }, .uniform, "uniform");
pub const xy = gpu.ptr(@This(), @Vector(2, f32), .input, "xy");
pub const color = gpu.ptr(@This(), @Vector(4, f32), .output, "color");

fn main() void {
    const x: f32 = xy[0];
    const y: f32 = xy[1];
    const t_max = 5.0;
    const rr_max = 2.0;
    const t: f32 = @mod(uniform.time, t_max) / t_max;
    if (x * x + y * y < rr_max * t * t) {
        color.* = .{ 1, t, t, 1 };
    } else {
        color.* = .{ 1, 1, 1, 1 };
    }
    color.* *= .{ 0.9, 0.9, 0.9, 1 };
}

comptime {
    gpu.main(@This(), main, .spirv_fragment);
}

It can be compiled to spirv for a gpu target and imported when compiling to a cpu target to perform unit tests like pipeline checks:

test "compatibility check" {
    const Canvas = @import("shaders/canvas.vert.zig");
    const Color = @import("shaders/color.frag.zig");
    gpu.Check.render(Canvas, Color);
}
remote shoal
halcyon dove
#

have you considered having pointers generated from input/outputs? This would enforce usage of inputs and writing of outputs. Something like:

const gpu = @import("gpu");

const Outputs = struct {
    color: @Vector(4, f32),
};

fn main(
    xy: @Vector(2, f32),
) Outputs {
    const x: f32 = xy[0];
    const y: f32 = xy[1];
    const color = if (x * x + y * y < 0.1)
        .{ 0.9, 0, 0, 1 }
    else
        .{ 0.9, 0.9, 0.9, 1 };
    return .{
        .color = color,
    };
}

comptime {
    gpu.main(@This(), main, .spirv_fragment);
}

if gpu.main takes the function as anytype, you could probably do some trickery to generate the pointers. From the inputs to the function, you can generate a struct for it

remote shoal
#

Awesome idea :D
I will definetly give it a try

remote shoal
digital lion
#

I don't know

#

Maybe just do uniforms the current way as they kinda act like globals anyways?

remote shoal
#

added support for uniforms

echo mica
#

Isnt this std.gpu?

#

Or mb Im dumb

#

Idk seems usefull tho

remote shoal
#

added helpers functions for number of buffers and their locations. So one can do (using zig-sdl3 as an example):

const ShaderFrag = @import("shaders/color.frag.zig");

// pipeline creation:
shader_frag = try device.createShader(.{
        ///...
        .num_uniform_buffers = gpu.decls(ShaderFrag, .uniform).len,
        //...
    });

// updating buffers:
command_buffer.pushFragmentUniformData(gpu.location(ShaderFrag.uniform), bytes);

it should also be pretty easy to type check the bytes of the buffer push.

remote shoal
# echo mica Isnt this std.gpu?

not quite, this is a very thin layer on top of std.gpu. I hope its usefulness becomes more apparent with the new functions i just described above :)