diff --git a/.gitea/workflows/test.yml b/.gitea/workflows/test.yml index d0f5d0e..20c9639 100644 --- a/.gitea/workflows/test.yml +++ b/.gitea/workflows/test.yml @@ -20,8 +20,4 @@ jobs: - uses: mlugg/setup-zig@v2 - name: Test - run: | - zig build test -Dno-example=true - zig build test -Dno-example=true --release=fast - zig build test -Dno-example=true --release=safe - zig build test -Dno-example=true --release=small + run: zig build test -Dno-example=true diff --git a/README.md b/README.md index 03aa4f8..7d03b29 100644 --- a/README.md +++ b/README.md @@ -15,7 +15,7 @@ pub fn main() !void { const allocator = gpa.allocator(); - var module = try spv.Module.init(allocator, @ptrCast(@alignCast(shader_source))); + var module = try spv.Module.init(allocator, @ptrCast(@alignCast(shader_source)), .{}); defer module.deinit(allocator); var rt = try spv.Runtime.init(allocator, &module); diff --git a/build.zig b/build.zig index cb7b0f3..e6057c5 100644 --- a/build.zig +++ b/build.zig @@ -4,6 +4,8 @@ pub fn build(b: *std.Build) void { const target = b.standardTargetOptions(.{}); const optimize = b.standardOptimizeOption(.{}); + const use_llvm = b.option(bool, "use-llvm", "use llvm") orelse false; + const mod = b.createModule(.{ .root_source_file = b.path("src/lib.zig"), .target = target, @@ -22,7 +24,7 @@ pub fn build(b: *std.Build) void { .name = "spirv_interpreter", .root_module = mod, .linkage = .dynamic, - //.use_llvm = true, + .use_llvm = use_llvm, }); const lib_install = b.addInstallArtifact(lib, .{}); @@ -44,6 +46,7 @@ pub fn build(b: *std.Build) void { //.{ .name = "pretty", .module = pretty.module("pretty") }, }, }), + .use_llvm = use_llvm, }); const example_install = b.addInstallArtifact(example_exe, .{}); @@ -60,6 +63,35 @@ pub fn build(b: *std.Build) void { compile_shader_step.dependOn(&compile_shader_cmd.step); } + // Zig sandbox setup + + const sandbox_exe = b.addExecutable(.{ + .name = "spirv_interpreter_sandbpx", + .root_module = b.createModule(.{ + .root_source_file = b.path("sandbox/main.zig"), + .target = target, + .optimize = optimize, + .imports = &.{ + .{ .name = "spv", .module = mod }, + //.{ .name = "pretty", .module = pretty.module("pretty") }, + }, + }), + .use_llvm = use_llvm, + }); + + const sandbox_install = b.addInstallArtifact(sandbox_exe, .{}); + sandbox_install.step.dependOn(&lib_install.step); + + const run_sandbox = b.addRunArtifact(sandbox_exe); + run_sandbox.step.dependOn(&sandbox_install.step); + + const run_sandbox_step = b.step("sandbox", "Run the sandbox"); + run_sandbox_step.dependOn(&run_sandbox.step); + + const compile_shader_cmd = b.addSystemCommand(&[_][]const u8{ "nzslc", "sandbox/shader.nzsl", "--compile=spv,spv-dis", "-o", "sandbox" }); + const compile_shader_step = b.step("sandbox-shader", "Compiles sandbox's shader (needs nzslc installed)"); + compile_shader_step.dependOn(&compile_shader_cmd.step); + // Zig unit tests setup const nzsl = b.lazyDependency("NZSL", .{ .target = target, .optimize = optimize }) orelse return; diff --git a/sandbox/main.zig b/sandbox/main.zig new file mode 100644 index 0000000..1f3061e --- /dev/null +++ b/sandbox/main.zig @@ -0,0 +1,25 @@ +const std = @import("std"); +const spv = @import("spv"); + +const shader_source = @embedFile("shader.spv"); + +pub fn main() !void { + { + var gpa: std.heap.DebugAllocator(.{}) = .init; + defer _ = gpa.deinit(); + + const allocator = gpa.allocator(); + + var module = try spv.Module.init(allocator, @ptrCast(@alignCast(shader_source)), .{}); + defer module.deinit(allocator); + + var rt = try spv.Runtime.init(allocator, &module); + defer rt.deinit(allocator); + + try rt.callEntryPoint(allocator, try rt.getEntryPointByName("main")); + var output: [4]f32 = undefined; + try rt.readOutput(f32, output[0..output.len], try rt.getResultByName("color")); + std.log.info("Output: Vec4{any}", .{output}); + } + std.log.info("Successfully executed", .{}); +} diff --git a/sandbox/shader.nzsl b/sandbox/shader.nzsl new file mode 100644 index 0000000..cc696f1 --- /dev/null +++ b/sandbox/shader.nzsl @@ -0,0 +1,15 @@ +[nzsl_version("1.1")] +module; + +struct FragOut +{ + [location(0)] color: vec4[f32] +} + +[entry(frag)] +fn main() -> FragOut +{ + let output: FragOut; + output.color = vec4[f32](1.0, 1.0, 1.0, 1.0); + return output; +} diff --git a/sandbox/shader.spv b/sandbox/shader.spv new file mode 100644 index 0000000..db7c59e Binary files /dev/null and b/sandbox/shader.spv differ diff --git a/sandbox/shader.spv.txt b/sandbox/shader.spv.txt new file mode 100644 index 0000000..0d65b26 --- /dev/null +++ b/sandbox/shader.spv.txt @@ -0,0 +1,39 @@ +Version 1.0 +Generator: 2560130 +Bound: 20 +Schema: 0 + OpCapability Capability(Shader) + OpMemoryModel AddressingModel(Logical) MemoryModel(GLSL450) + OpEntryPoint ExecutionModel(Fragment) %12 "main" %6 + OpExecutionMode %12 ExecutionMode(OriginUpperLeft) + OpSource SourceLanguage(NZSL) 4198400 + OpSourceExtension "Version: 1.1" + OpName %7 "FragOut" + OpMemberName %7 0 "color" + OpName %6 "color" + OpName %12 "main" + OpDecorate %6 Decoration(Location) 0 + OpMemberDecorate %7 0 Decoration(Offset) 0 + %1 = OpTypeVoid + %2 = OpTypeFunction %1 + %3 = OpTypeFloat 32 + %4 = OpTypeVector %3 4 + %5 = OpTypePointer StorageClass(Output) %4 + %7 = OpTypeStruct %4 + %8 = OpTypePointer StorageClass(Function) %7 + %9 = OpTypeInt 32 1 +%10 = OpConstant %9 i32(0) +%11 = OpConstant %3 f32(1) +%17 = OpTypePointer StorageClass(Function) %4 + %6 = OpVariable %5 StorageClass(Output) +%12 = OpFunction %1 FunctionControl(0) %2 +%13 = OpLabel +%14 = OpVariable %8 StorageClass(Function) +%15 = OpCompositeConstruct %4 %11 %11 %11 %11 +%16 = OpAccessChain %17 %14 %10 + OpStore %16 %15 +%18 = OpLoad %7 %14 +%19 = OpCompositeExtract %4 %18 0 + OpStore %6 %19 + OpReturn + OpFunctionEnd diff --git a/src/lib.zig b/src/lib.zig index e4a1bc1..edef74f 100644 --- a/src/lib.zig +++ b/src/lib.zig @@ -13,7 +13,7 @@ //! //! const allocator = gpa.allocator(); //! -//! var module = try spv.Module.init(allocator, @ptrCast(@alignCast(shader_source))); +//! var module = try spv.Module.init(allocator, @ptrCast(@alignCast(shader_source)), .{}); //! defer module.deinit(allocator); //! //! var rt = try spv.Runtime.init(allocator, &module); diff --git a/test/inputs.zig b/test/inputs.zig new file mode 100644 index 0000000..af9e100 --- /dev/null +++ b/test/inputs.zig @@ -0,0 +1,51 @@ +const std = @import("std"); +const root = @import("root.zig"); +const compileNzsl = root.compileNzsl; +const case = root.case; + +test "Inputs" { + const allocator = std.testing.allocator; + const types = [_]type{ f64, f32, i32, u32 }; + + inline for (2..5) |L| { + inline for (types) |T| { + const input: case.Vec(L, T) = .{ .val = case.random(@Vector(L, T)) }; + + const shader = try std.fmt.allocPrint( + allocator, + \\ [nzsl_version("1.1")] + \\ [feature(float64)] + \\ module; + \\ + \\ struct FragIn + \\ {{ + \\ [location(0)] pos: vec{d}[{s}] + \\ }} + \\ + \\ struct FragOut + \\ {{ + \\ [location(0)] color: vec{d}[{s}] + \\ }} + \\ + \\ [entry(frag)] + \\ fn main(input: FragIn) -> FragOut + \\ {{ + \\ let output: FragOut; + \\ output.color = input.pos; + \\ return output; + \\ }} + , + .{ + L, + @typeName(T), + L, + @typeName(T), + }, + ); + defer allocator.free(shader); + const code = try compileNzsl(allocator, shader); + defer allocator.free(code); + try case.expectOutputWithInput(T, L, code, "color", &@as([L]T, input.val), "pos", &@as([L]T, input.val)); + } + } +} diff --git a/test/root.zig b/test/root.zig index 6eed822..784074d 100644 --- a/test/root.zig +++ b/test/root.zig @@ -47,6 +47,35 @@ pub const case = struct { } } + pub fn expectOutputWithInput(comptime T: type, comptime len: usize, source: []const u32, output_name: []const u8, expected: []const T, input_name: []const u8, input: []const T) !void { + const allocator = std.testing.allocator; + + const module_options = [_]spv.Module.ModuleOptions{ + .{ + .use_simd_vectors_specializations = true, + }, + .{ + .use_simd_vectors_specializations = false, + }, + }; + + for (module_options) |opt| { + var module = try spv.Module.init(allocator, source, opt); + defer module.deinit(allocator); + + var rt = try spv.Runtime.init(allocator, &module); + defer rt.deinit(allocator); + + try rt.writeInput(T, input[0..len], try rt.getResultByName(input_name)); + + try rt.callEntryPoint(allocator, try rt.getEntryPointByName("main")); + var output: [len]T = undefined; + try rt.readOutput(T, output[0..len], try rt.getResultByName(output_name)); + + try std.testing.expectEqualSlices(T, expected, &output); + } + } + pub fn random(comptime T: type) T { var prng: std.Random.DefaultPrng = .init(@intCast(std.time.microTimestamp())); const rand = prng.random(); @@ -86,6 +115,7 @@ test { std.testing.refAllDecls(@import("branching.zig")); std.testing.refAllDecls(@import("casts.zig")); std.testing.refAllDecls(@import("functions.zig")); + std.testing.refAllDecls(@import("inputs.zig")); std.testing.refAllDecls(@import("loops.zig")); std.testing.refAllDecls(@import("maths.zig")); }