diff --git a/example/main.zig b/example/main.zig index 92056f2..169a6cc 100644 --- a/example/main.zig +++ b/example/main.zig @@ -19,7 +19,7 @@ pub fn main() !void { 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[{d}, {d}, {d}, {d}]", .{ output[0], output[1], output[2], output[3] }); + std.log.info("Output: Vec4{any}", .{output}); } std.log.info("Successfully executed", .{}); } diff --git a/example/shader.nzsl b/example/shader.nzsl index 0498f61..7c4331d 100644 --- a/example/shader.nzsl +++ b/example/shader.nzsl @@ -1,24 +1,21 @@ - [nzsl_version("1.1")] - [feature(float64)] - module; - - struct FragOut - { - [location(0)] color: vec4[f32] - } +[nzsl_version("1.1")] +[feature(float64)] +module; - [entry(frag)] - fn main() -> FragOut - { - let op1: f64 = 0.0; - let op2: f64 = 9.0; - let color: f32; - if (op1 == op2) - color = f32(op1); - else - color = f32(op2); +struct FragOut +{ + [location(0)] color: vec4[f32] +} - let output: FragOut; - output.color = vec4[f32](color, color, color, color); - return output; - } +fn computeColor() -> f32 +{ + return 1.0; +} + +[entry(frag)] +fn main() -> FragOut +{ + let output: FragOut; + output.color = vec4[f32](computeColor(), computeColor(), computeColor(), computeColor()); + return output; +} diff --git a/example/shader.spv b/example/shader.spv index b9de341..e4a0322 100644 Binary files a/example/shader.spv and b/example/shader.spv differ diff --git a/example/shader.spv.txt b/example/shader.spv.txt index 870c27b..d11da02 100644 --- a/example/shader.spv.txt +++ b/example/shader.spv.txt @@ -1,70 +1,50 @@ Version 1.0 Generator: 2560130 -Bound: 42 +Bound: 27 Schema: 0 OpCapability Capability(Shader) OpCapability Capability(Float64) OpMemoryModel AddressingModel(Logical) MemoryModel(GLSL450) - OpEntryPoint ExecutionModel(Fragment) %17 "main" %6 - OpExecutionMode %17 ExecutionMode(OriginUpperLeft) + OpEntryPoint ExecutionModel(Fragment) %14 "main" %8 + OpExecutionMode %14 ExecutionMode(OriginUpperLeft) OpSource SourceLanguage(NZSL) 4198400 OpSourceExtension "Version: 1.1" - OpName %7 "FragOut" - OpMemberName %7 0 "color" - OpName %6 "color" - OpName %17 "main" - OpDecorate %6 Decoration(Location) 0 - OpMemberDecorate %7 0 Decoration(Offset) 0 - %1 = OpTypeVoid + OpName %9 "FragOut" + OpMemberName %9 0 "color" + OpName %8 "color" + OpName %13 "computeColor" + OpName %14 "main" + OpDecorate %8 Decoration(Location) 0 + OpMemberDecorate %9 0 Decoration(Offset) 0 + %1 = OpTypeFloat 32 %2 = OpTypeFunction %1 - %3 = OpTypeFloat 32 - %4 = OpTypeVector %3 4 - %5 = OpTypePointer StorageClass(Output) %4 - %7 = OpTypeStruct %4 - %8 = OpTypeFloat 64 - %9 = OpConstant %8 f64(0) -%10 = OpTypePointer StorageClass(Function) %8 -%11 = OpConstant %8 f64(9) -%12 = OpTypePointer StorageClass(Function) %3 -%13 = OpTypeBool -%14 = OpTypePointer StorageClass(Function) %7 -%15 = OpTypeInt 32 1 -%16 = OpConstant %15 i32(0) -%39 = OpTypePointer StorageClass(Function) %4 - %6 = OpVariable %5 StorageClass(Output) -%17 = OpFunction %1 FunctionControl(0) %2 -%18 = OpLabel -%19 = OpVariable %10 StorageClass(Function) -%20 = OpVariable %10 StorageClass(Function) -%21 = OpVariable %12 StorageClass(Function) -%22 = OpVariable %14 StorageClass(Function) - OpStore %19 %9 - OpStore %20 %11 -%26 = OpLoad %8 %19 -%27 = OpLoad %8 %20 -%28 = OpFOrdEqual %13 %26 %27 - OpSelectionMerge %23 SelectionControl(0) - OpBranchConditional %28 %24 %25 -%24 = OpLabel -%29 = OpLoad %8 %19 -%30 = OpFConvert %3 %29 - OpStore %21 %30 - OpBranch %23 -%25 = OpLabel -%31 = OpLoad %8 %20 -%32 = OpFConvert %3 %31 - OpStore %21 %32 - OpBranch %23 -%23 = OpLabel -%33 = OpLoad %3 %21 -%34 = OpLoad %3 %21 -%35 = OpLoad %3 %21 -%36 = OpLoad %3 %21 -%37 = OpCompositeConstruct %4 %33 %34 %35 %36 -%38 = OpAccessChain %39 %22 %16 - OpStore %38 %37 -%40 = OpLoad %7 %22 -%41 = OpCompositeExtract %4 %40 0 - OpStore %6 %41 + %3 = OpConstant %1 f32(1) + %4 = OpTypeVoid + %5 = OpTypeFunction %4 + %6 = OpTypeVector %1 4 + %7 = OpTypePointer StorageClass(Output) %6 + %9 = OpTypeStruct %6 +%10 = OpTypePointer StorageClass(Function) %9 +%11 = OpTypeInt 32 1 +%12 = OpConstant %11 i32(0) +%24 = OpTypePointer StorageClass(Function) %6 + %8 = OpVariable %7 StorageClass(Output) +%13 = OpFunction %1 FunctionControl(0) %2 +%15 = OpLabel + OpReturnValue %3 + OpFunctionEnd +%14 = OpFunction %4 FunctionControl(0) %5 +%16 = OpLabel +%17 = OpVariable %10 StorageClass(Function) +%18 = OpFunctionCall %1 %13 +%19 = OpFunctionCall %1 %13 +%20 = OpFunctionCall %1 %13 +%21 = OpFunctionCall %1 %13 +%22 = OpCompositeConstruct %6 %18 %19 %20 %21 +%23 = OpAccessChain %24 %17 %12 + OpStore %23 %22 +%25 = OpLoad %9 %17 +%26 = OpCompositeExtract %6 %25 0 + OpStore %8 %26 OpReturn OpFunctionEnd diff --git a/src/opcodes.zig b/src/opcodes.zig index 970504f..fa02366 100644 --- a/src/opcodes.zig +++ b/src/opcodes.zig @@ -36,6 +36,152 @@ const CondOp = enum { LessEqual, }; +pub const OpCodeFunc = *const fn (std.mem.Allocator, SpvWord, *Runtime) RuntimeError!void; + +pub const SetupDispatcher = block: { + @setEvalBranchQuota(65535); + break :block std.EnumMap(spv.SpvOp, OpCodeFunc).init(.{ + .Bitcast = autoSetupConstant, + .Capability = opCapability, + .CompositeConstruct = autoSetupConstant, + .Constant = opConstant, + .ConvertFToS = autoSetupConstant, + .ConvertFToU = autoSetupConstant, + .ConvertPtrToU = autoSetupConstant, + .ConvertSToF = autoSetupConstant, + .ConvertUToF = autoSetupConstant, + .ConvertUToPtr = autoSetupConstant, + .Decorate = opDecorate, + .EntryPoint = opEntryPoint, + .ExecutionMode = opExecutionMode, + .FAdd = autoSetupConstant, + .FConvert = autoSetupConstant, + .FDiv = autoSetupConstant, + .FMod = autoSetupConstant, + .FMul = autoSetupConstant, + .FOrdEqual = autoSetupConstant, + .FOrdGreaterThan = autoSetupConstant, + .FOrdGreaterThanEqual = autoSetupConstant, + .FOrdLessThan = autoSetupConstant, + .FOrdLessThanEqual = autoSetupConstant, + .FOrdNotEqual = autoSetupConstant, + .FSub = autoSetupConstant, + .FUnordEqual = autoSetupConstant, + .FUnordGreaterThan = autoSetupConstant, + .FUnordGreaterThanEqual = autoSetupConstant, + .FUnordLessThan = autoSetupConstant, + .FUnordLessThanEqual = autoSetupConstant, + .FUnordNotEqual = autoSetupConstant, + .Function = opFunction, + .FunctionCall = autoSetupConstant, + .FunctionEnd = opFunctionEnd, + .IAdd = autoSetupConstant, + .IEqual = autoSetupConstant, + .IMul = autoSetupConstant, + .INotEqual = autoSetupConstant, + .ISub = autoSetupConstant, + .Label = opLabel, + .Load = autoSetupConstant, + .MemberDecorate = opDecorateMember, + .MemberName = opMemberName, + .MemoryModel = opMemoryModel, + .Name = opName, + .QuantizeToF16 = autoSetupConstant, + .SConvert = autoSetupConstant, + .SDiv = autoSetupConstant, + .SGreaterThan = autoSetupConstant, + .SGreaterThanEqual = autoSetupConstant, + .SLessThan = autoSetupConstant, + .SLessThanEqual = autoSetupConstant, + .SMod = autoSetupConstant, + .SatConvertSToU = autoSetupConstant, + .SatConvertUToS = autoSetupConstant, + .Source = opSource, + .SourceExtension = opSourceExtension, + .TypeBool = opTypeBool, + .TypeFloat = opTypeFloat, + .TypeFunction = opTypeFunction, + .TypeInt = opTypeInt, + .TypeMatrix = opTypeMatrix, + .TypePointer = opTypePointer, + .TypeStruct = opTypeStruct, + .TypeVector = opTypeVector, + .TypeVoid = opTypeVoid, + .UConvert = autoSetupConstant, + .UDiv = autoSetupConstant, + .UGreaterThan = autoSetupConstant, + .UGreaterThanEqual = autoSetupConstant, + .ULessThan = autoSetupConstant, + .ULessThanEqual = autoSetupConstant, + .UMod = autoSetupConstant, + .Variable = opVariable, + }); +}; + +pub const RuntimeDispatcher = block: { + @setEvalBranchQuota(65535); + break :block std.EnumMap(spv.SpvOp, OpCodeFunc).init(.{ + .AccessChain = opAccessChain, + .Bitcast = opBitcast, + .Branch = opBranch, + .BranchConditional = opBranchConditional, + .CompositeConstruct = opCompositeConstruct, + .CompositeExtract = opCompositeExtract, + .ConvertFToS = ConversionEngine(.Float, .SInt).op, + .ConvertFToU = ConversionEngine(.Float, .UInt).op, + .ConvertSToF = ConversionEngine(.SInt, .Float).op, + .ConvertUToF = ConversionEngine(.UInt, .Float).op, + .FAdd = MathEngine(.Float, .Add).op, + .FConvert = ConversionEngine(.Float, .Float).op, + .FDiv = MathEngine(.Float, .Div).op, + .FMod = MathEngine(.Float, .Mod).op, + .FMul = MathEngine(.Float, .Mul).op, + .FOrdEqual = CondEngine(.Float, .Equal).op, + .FOrdGreaterThan = CondEngine(.Float, .Greater).op, + .FOrdGreaterThanEqual = CondEngine(.Float, .GreaterEqual).op, + .FOrdLessThan = CondEngine(.Float, .Less).op, + .FOrdLessThanEqual = CondEngine(.Float, .LessEqual).op, + .FOrdNotEqual = CondEngine(.Float, .NotEqual).op, + .FSub = MathEngine(.Float, .Sub).op, + .FUnordEqual = CondEngine(.Float, .Equal).op, + .FUnordGreaterThan = CondEngine(.Float, .Greater).op, + .FUnordGreaterThanEqual = CondEngine(.Float, .GreaterEqual).op, + .FUnordLessThan = CondEngine(.Float, .Less).op, + .FUnordLessThanEqual = CondEngine(.Float, .LessEqual).op, + .FUnordNotEqual = CondEngine(.Float, .NotEqual).op, + .FunctionCall = opFunctionCall, + .IAdd = MathEngine(.SInt, .Add).op, + .IEqual = CondEngine(.SInt, .Equal).op, + .IMul = MathEngine(.SInt, .Mul).op, + .INotEqual = CondEngine(.SInt, .NotEqual).op, + .ISub = MathEngine(.SInt, .Sub).op, + .Load = opLoad, + .Return = opReturn, + .ReturnValue = opReturnValue, + .SConvert = ConversionEngine(.SInt, .SInt).op, + .SDiv = MathEngine(.SInt, .Div).op, + .SGreaterThan = CondEngine(.SInt, .Greater).op, + .SGreaterThanEqual = CondEngine(.SInt, .GreaterEqual).op, + .SLessThan = CondEngine(.SInt, .Less).op, + .SLessThanEqual = CondEngine(.SInt, .LessEqual).op, + .SMod = MathEngine(.SInt, .Mod).op, + .Store = opStore, + .UConvert = ConversionEngine(.UInt, .UInt).op, + .UDiv = MathEngine(.UInt, .Div).op, + .UGreaterThan = CondEngine(.UInt, .Greater).op, + .UGreaterThanEqual = CondEngine(.UInt, .GreaterEqual).op, + .ULessThan = CondEngine(.UInt, .Less).op, + .ULessThanEqual = CondEngine(.UInt, .LessEqual).op, + .UMod = MathEngine(.UInt, .Mod).op, + + //.QuantizeToF16 = , + //.ConvertPtrToU = , + //.SatConvertSToU = , + //.SatConvertUToS = , + //.ConvertUToPtr = , + }); +}; + fn CondEngine(comptime T: ValueType, comptime Op: CondOp) type { return struct { fn op(_: std.mem.Allocator, _: SpvWord, rt: *Runtime) RuntimeError!void { @@ -156,148 +302,6 @@ fn ConversionEngine(comptime From: ValueType, comptime To: ValueType) type { }; } -pub const OpCodeFunc = *const fn (std.mem.Allocator, SpvWord, *Runtime) RuntimeError!void; - -pub const SetupDispatcher = block: { - @setEvalBranchQuota(65535); - break :block std.EnumMap(spv.SpvOp, OpCodeFunc).init(.{ - .Capability = opCapability, - .CompositeConstruct = autoSetupConstant, - .Constant = opConstant, - .Decorate = opDecorate, - .EntryPoint = opEntryPoint, - .ExecutionMode = opExecutionMode, - .FAdd = autoSetupConstant, - .FDiv = autoSetupConstant, - .FMod = autoSetupConstant, - .FMul = autoSetupConstant, - .FOrdEqual = autoSetupConstant, - .FOrdGreaterThan = autoSetupConstant, - .FOrdGreaterThanEqual = autoSetupConstant, - .FOrdLessThan = autoSetupConstant, - .FOrdLessThanEqual = autoSetupConstant, - .FOrdNotEqual = autoSetupConstant, - .FSub = autoSetupConstant, - .FUnordEqual = autoSetupConstant, - .FUnordGreaterThan = autoSetupConstant, - .FUnordGreaterThanEqual = autoSetupConstant, - .FUnordLessThan = autoSetupConstant, - .FUnordLessThanEqual = autoSetupConstant, - .FUnordNotEqual = autoSetupConstant, - .Function = opFunction, - .FunctionEnd = opFunctionEnd, - .IAdd = autoSetupConstant, - .IEqual = autoSetupConstant, - .IMul = autoSetupConstant, - .INotEqual = autoSetupConstant, - .ISub = autoSetupConstant, - .Label = opLabel, - .Load = autoSetupConstant, - .MemberDecorate = opDecorateMember, - .MemberName = opMemberName, - .MemoryModel = opMemoryModel, - .Name = opName, - .SDiv = autoSetupConstant, - .SGreaterThan = autoSetupConstant, - .SGreaterThanEqual = autoSetupConstant, - .SLessThan = autoSetupConstant, - .SLessThanEqual = autoSetupConstant, - .SMod = autoSetupConstant, - .Source = opSource, - .SourceExtension = opSourceExtension, - .TypeBool = opTypeBool, - .TypeFloat = opTypeFloat, - .TypeFunction = opTypeFunction, - .TypeInt = opTypeInt, - .TypeMatrix = opTypeMatrix, - .TypePointer = opTypePointer, - .TypeStruct = opTypeStruct, - .TypeVector = opTypeVector, - .TypeVoid = opTypeVoid, - .UDiv = autoSetupConstant, - .UGreaterThan = autoSetupConstant, - .UGreaterThanEqual = autoSetupConstant, - .ULessThan = autoSetupConstant, - .ULessThanEqual = autoSetupConstant, - .UMod = autoSetupConstant, - .Variable = opVariable, - - .ConvertFToU = autoSetupConstant, - .ConvertFToS = autoSetupConstant, - .ConvertSToF = autoSetupConstant, - .ConvertUToF = autoSetupConstant, - .UConvert = autoSetupConstant, - .SConvert = autoSetupConstant, - .FConvert = autoSetupConstant, - .QuantizeToF16 = autoSetupConstant, - .ConvertPtrToU = autoSetupConstant, - .SatConvertSToU = autoSetupConstant, - .SatConvertUToS = autoSetupConstant, - .ConvertUToPtr = autoSetupConstant, - }); -}; - -pub const RuntimeDispatcher = block: { - @setEvalBranchQuota(65535); - break :block std.EnumMap(spv.SpvOp, OpCodeFunc).init(.{ - .AccessChain = opAccessChain, - .Branch = opBranch, - .BranchConditional = opBranchConditional, - .CompositeConstruct = opCompositeConstruct, - .CompositeExtract = opCompositeExtract, - .FAdd = MathEngine(.Float, .Add).op, - .FDiv = MathEngine(.Float, .Div).op, - .FMod = MathEngine(.Float, .Mod).op, - .FMul = MathEngine(.Float, .Mul).op, - .FOrdEqual = CondEngine(.Float, .Equal).op, - .FOrdGreaterThan = CondEngine(.Float, .Greater).op, - .FOrdGreaterThanEqual = CondEngine(.Float, .GreaterEqual).op, - .FOrdLessThan = CondEngine(.Float, .Less).op, - .FOrdLessThanEqual = CondEngine(.Float, .LessEqual).op, - .FOrdNotEqual = CondEngine(.Float, .NotEqual).op, - .FSub = MathEngine(.Float, .Sub).op, - .FUnordEqual = CondEngine(.Float, .Equal).op, - .FUnordGreaterThan = CondEngine(.Float, .Greater).op, - .FUnordGreaterThanEqual = CondEngine(.Float, .GreaterEqual).op, - .FUnordLessThan = CondEngine(.Float, .Less).op, - .FUnordLessThanEqual = CondEngine(.Float, .LessEqual).op, - .FUnordNotEqual = CondEngine(.Float, .NotEqual).op, - .IAdd = MathEngine(.SInt, .Add).op, - .IEqual = CondEngine(.SInt, .Equal).op, - .IMul = MathEngine(.SInt, .Mul).op, - .INotEqual = CondEngine(.SInt, .NotEqual).op, - .ISub = MathEngine(.SInt, .Sub).op, - .Load = opLoad, - .Return = opReturn, - .SDiv = MathEngine(.SInt, .Div).op, - .SGreaterThan = CondEngine(.SInt, .Greater).op, - .SGreaterThanEqual = CondEngine(.SInt, .GreaterEqual).op, - .SLessThan = CondEngine(.SInt, .Less).op, - .SLessThanEqual = CondEngine(.SInt, .LessEqual).op, - .SMod = MathEngine(.SInt, .Mod).op, - .Store = opStore, - .UDiv = MathEngine(.UInt, .Div).op, - .UGreaterThan = CondEngine(.UInt, .Greater).op, - .UGreaterThanEqual = CondEngine(.UInt, .GreaterEqual).op, - .ULessThan = CondEngine(.UInt, .Less).op, - .ULessThanEqual = CondEngine(.UInt, .LessEqual).op, - .UMod = MathEngine(.UInt, .Mod).op, - - .ConvertFToU = ConversionEngine(.Float, .UInt).op, - .ConvertFToS = ConversionEngine(.Float, .SInt).op, - .ConvertSToF = ConversionEngine(.SInt, .Float).op, - .ConvertUToF = ConversionEngine(.UInt, .Float).op, - .UConvert = ConversionEngine(.UInt, .UInt).op, - .SConvert = ConversionEngine(.SInt, .SInt).op, - .FConvert = ConversionEngine(.Float, .Float).op, - //.QuantizeToF16 = autoSetupConstant, - //.ConvertPtrToU = autoSetupConstant, - //.SatConvertSToU = autoSetupConstant, - //.SatConvertUToS = autoSetupConstant, - //.ConvertUToPtr = autoSetupConstant, - }); -}; - fn MathEngine(comptime T: ValueType, comptime Op: MathOp) type { return struct { fn op(_: std.mem.Allocator, _: SpvWord, rt: *Runtime) RuntimeError!void { @@ -405,6 +409,35 @@ fn autoSetupConstant(allocator: std.mem.Allocator, _: SpvWord, rt: *Runtime) Run _ = try setupConstant(allocator, rt); } +fn opBitcast(_: std.mem.Allocator, _: SpvWord, rt: *Runtime) RuntimeError!void { + _ = rt.it.skip(); + const to_value = try rt.results[try rt.it.next()].getValue(); + const from_value = try rt.results[try rt.it.next()].getValue(); + + const caster = struct { + /// Asumes that values passed are primitives ints or floats + fn cast(to: *Result.Value, from: *const Result.Value) RuntimeError!void { + const from_bytes: u64 = switch (from.*) { + .Float => |f| @bitCast(f.float64), + .Int => |i| i.uint64, + else => return RuntimeError.InvalidSpirV, + }; + + switch (to.*) { + .Float => |*f| f.float64 = @bitCast(from_bytes), + .Int => |*i| i.uint64 = from_bytes, + else => return RuntimeError.InvalidSpirV, + } + } + }; + + switch (to_value.*) { + .Int, .Float => try caster.cast(to_value, from_value), + .Vector => |vec| for (vec, from_value.Vector) |*t, *f| try caster.cast(t, f), + else => return RuntimeError.InvalidSpirV, + } +} + fn copyValue(dst: *Result.Value, src: *const Result.Value) void { if (src.getCompositeDataOrNull()) |src_slice| { if (dst.getCompositeDataOrNull()) |dst_slice| { @@ -742,6 +775,18 @@ fn opName(allocator: std.mem.Allocator, word_count: SpvWord, rt: *Runtime) Runti } fn opReturn(_: std.mem.Allocator, _: SpvWord, rt: *Runtime) RuntimeError!void { + rt.last_return_id = null; + _ = rt.function_stack.pop(); + if (rt.function_stack.getLastOrNull()) |function| { + _ = rt.it.jumpToSourceLocation(function.source_location); + rt.current_function = function.result; + } else { + rt.current_function = null; + rt.it.skipToEnd(); + } +} + +fn opReturnValue(_: std.mem.Allocator, _: SpvWord, rt: *Runtime) RuntimeError!void { _ = rt.function_stack.pop(); if (rt.function_stack.getLastOrNull()) |function| { _ = rt.it.jumpToSourceLocation(function.source_location); diff --git a/test/casts.zig b/test/casts.zig new file mode 100644 index 0000000..e99d712 --- /dev/null +++ b/test/casts.zig @@ -0,0 +1,62 @@ +const std = @import("std"); +const root = @import("root.zig"); +const compileNzsl = root.compileNzsl; +const case = root.case; + +test "Primitives casts" { + const allocator = std.testing.allocator; + const types = [_][2]type{ + [2]type{ f32, u32 }, + [2]type{ f32, i32 }, + [2]type{ u32, f32 }, + [2]type{ u32, i32 }, + [2]type{ i32, f32 }, + [2]type{ i32, u32 }, + [2]type{ f32, f64 }, + [2]type{ f64, f32 }, + [2]type{ f64, u32 }, + [2]type{ f64, i32 }, + [2]type{ u32, f64 }, + [2]type{ i32, f64 }, + }; + + inline for (types) |T| { + const base = case.random(T[0]); + const expected = std.math.lossyCast(T[1], base); + + const shader = try std.fmt.allocPrint( + allocator, + \\ [nzsl_version("1.1")] + \\ [feature(float64)] + \\ module; + \\ + \\ struct FragOut + \\ {{ + \\ [location(0)] color: vec4[{s}] + \\ }} + \\ + \\ [entry(frag)] + \\ fn main() -> FragOut + \\ {{ + \\ let base = {s}({d}); + \\ let color = {s}(base); + \\ + \\ let output: FragOut; + \\ output.color = vec4[{s}](color, color, color, color); + \\ return output; + \\ }} + , + .{ + @typeName(T[1]), + @typeName(T[0]), + base, + @typeName(T[1]), + @typeName(T[1]), + }, + ); + defer allocator.free(shader); + const code = try compileNzsl(allocator, shader); + defer allocator.free(code); + try case.expectOutput(T[1], 4, code, "color", &.{ expected, expected, expected, expected }); + } +} diff --git a/test/root.zig b/test/root.zig index d464ee1..6c7ec9c 100644 --- a/test/root.zig +++ b/test/root.zig @@ -58,5 +58,6 @@ pub const case = struct { test { std.testing.refAllDecls(@import("basics.zig")); std.testing.refAllDecls(@import("branching.zig")); + std.testing.refAllDecls(@import("casts.zig")); std.testing.refAllDecls(@import("maths.zig")); }