diff --git a/example/main.zig b/example/main.zig index 169a6cc..c4aede2 100644 --- a/example/main.zig +++ b/example/main.zig @@ -17,9 +17,11 @@ pub fn main() !void { 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}); + var value: f32 = undefined; + var value2: f32 = undefined; + try rt.readOutput(f32, @as([*]f32, @ptrCast(&value))[0..1], try rt.getResultByName("value")); + try rt.readOutput(f32, @as([*]f32, @ptrCast(&value2))[0..1], try rt.getResultByName("value2")); + std.log.info("Output: {d} {d}", .{ value, value2 }); } std.log.info("Successfully executed", .{}); } diff --git a/example/shader.nzsl b/example/shader.nzsl index 2c15105..4c68214 100644 --- a/example/shader.nzsl +++ b/example/shader.nzsl @@ -1,22 +1,27 @@ [nzsl_version("1.1")] -[feature(float64)] module; struct FragOut { - [location(0)] color: vec4[f32] + [location(0)] value: f32, + [location(1)] value2: f32 +} + +fn Half(inout color: vec3[f32], out value: f32, in inValue: f32, inValue2: f32) +{ + color *= 2.0; + value = 10.0; } [entry(frag)] fn main() -> FragOut { - let value: f32 = 1.0; - for i in 1 -> 5 { - if (i == 3) - continue; - value *= f32(i); - } - let output: FragOut; - output.color = vec4[f32](value, value, value, value); - return output; + let output: FragOut; + let mainColor = vec3[f32](1.0, 1.0, 1.0); + let inValue = 2.0; + let inValue2 = 1.0; + Half(inout mainColor, out output.value2, in inValue, inValue2); + output.value = mainColor.x; + + return output; } diff --git a/example/shader.spv b/example/shader.spv index 0709c8c..ac2a675 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 a4965a5..c5210b3 100644 --- a/example/shader.spv.txt +++ b/example/shader.spv.txt @@ -3,83 +3,86 @@ Generator: 2560130 Bound: 51 Schema: 0 OpCapability Capability(Shader) - OpCapability Capability(Float64) OpMemoryModel AddressingModel(Logical) MemoryModel(GLSL450) - OpEntryPoint ExecutionModel(Fragment) %18 "main" %6 - OpExecutionMode %18 ExecutionMode(OriginUpperLeft) + OpEntryPoint ExecutionModel(Fragment) %20 "main" %11 %12 + OpExecutionMode %20 ExecutionMode(OriginUpperLeft) OpSource SourceLanguage(NZSL) 4198400 OpSourceExtension "Version: 1.1" - OpName %7 "FragOut" - OpMemberName %7 0 "color" - OpName %6 "color" - OpName %18 "main" - OpDecorate %6 Decoration(Location) 0 - OpMemberDecorate %7 0 Decoration(Offset) 0 + OpName %13 "FragOut" + OpMemberName %13 0 "value" + OpMemberName %13 1 "value2" + OpName %11 "value" + OpName %12 "value2" + OpName %19 "Half" + OpName %20 "main" + OpDecorate %11 Decoration(Location) 0 + OpDecorate %12 Decoration(Location) 1 + OpMemberDecorate %13 0 Decoration(Offset) 0 + OpMemberDecorate %13 1 Decoration(Offset) 4 %1 = OpTypeVoid - %2 = OpTypeFunction %1 - %3 = OpTypeFloat 32 - %4 = OpTypeVector %3 4 - %5 = OpTypePointer StorageClass(Output) %4 - %7 = OpTypeStruct %4 - %8 = OpConstant %3 f32(1) - %9 = OpTypePointer StorageClass(Function) %3 -%10 = OpTypeInt 32 1 -%11 = OpConstant %10 i32(1) -%12 = OpTypePointer StorageClass(Function) %10 -%13 = OpConstant %10 i32(5) -%14 = OpTypeBool -%15 = OpConstant %10 i32(3) -%16 = OpTypePointer StorageClass(Function) %7 -%17 = OpConstant %10 i32(0) -%48 = OpTypePointer StorageClass(Function) %4 - %6 = OpVariable %5 StorageClass(Output) -%18 = OpFunction %1 FunctionControl(0) %2 -%19 = OpLabel -%20 = OpVariable %9 StorageClass(Function) -%21 = OpVariable %12 StorageClass(Function) -%22 = OpVariable %12 StorageClass(Function) -%23 = OpVariable %16 StorageClass(Function) - OpStore %20 %8 - OpStore %21 %11 - OpStore %22 %13 - OpBranch %24 -%24 = OpLabel -%28 = OpLoad %10 %21 -%29 = OpLoad %10 %22 -%30 = OpSLessThan %14 %28 %29 - OpLoopMerge %26 %27 LoopControl(0) - OpBranchConditional %30 %25 %26 + %2 = OpTypeFloat 32 + %3 = OpTypeVector %2 3 + %4 = OpTypePointer StorageClass(Function) %3 + %5 = OpTypePointer StorageClass(Function) %2 + %6 = OpTypeFunction %1 %4 %5 %5 %5 + %7 = OpConstant %2 f32(2) + %8 = OpConstant %2 f32(10) + %9 = OpTypeFunction %1 +%10 = OpTypePointer StorageClass(Output) %2 +%13 = OpTypeStruct %2 %2 +%14 = OpTypePointer StorageClass(Function) %13 +%15 = OpConstant %2 f32(1) +%16 = OpTypeInt 32 1 +%17 = OpConstant %16 i32(1) +%18 = OpConstant %16 i32(0) +%11 = OpVariable %10 StorageClass(Output) +%12 = OpVariable %10 StorageClass(Output) +%19 = OpFunction %1 FunctionControl(0) %6 +%21 = OpFunctionParameter %4 +%22 = OpFunctionParameter %5 +%23 = OpFunctionParameter %5 +%24 = OpFunctionParameter %5 %25 = OpLabel -%34 = OpLoad %10 %21 -%35 = OpIEqual %14 %34 %15 - OpSelectionMerge %31 SelectionControl(0) - OpBranchConditional %35 %32 %33 -%32 = OpLabel - OpBranch %27 -%33 = OpLabel - OpBranch %31 -%31 = OpLabel -%36 = OpLoad %3 %20 -%37 = OpLoad %10 %21 -%38 = OpConvertSToF %3 %37 -%39 = OpFMul %3 %36 %38 - OpStore %20 %39 -%40 = OpLoad %10 %21 -%41 = OpIAdd %10 %40 %11 - OpStore %21 %41 - OpBranch %27 -%27 = OpLabel - OpBranch %24 -%26 = OpLabel -%42 = OpLoad %3 %20 -%43 = OpLoad %3 %20 -%44 = OpLoad %3 %20 -%45 = OpLoad %3 %20 -%46 = OpCompositeConstruct %4 %42 %43 %44 %45 -%47 = OpAccessChain %48 %23 %17 - OpStore %47 %46 -%49 = OpLoad %7 %23 -%50 = OpCompositeExtract %4 %49 0 - OpStore %6 %50 +%26 = OpLoad %3 %21 +%27 = OpVectorTimesScalar %3 %26 %7 + OpStore %21 %27 + OpStore %22 %8 + OpReturn + OpFunctionEnd +%20 = OpFunction %1 FunctionControl(0) %9 +%28 = OpLabel +%29 = OpVariable %14 StorageClass(Function) +%30 = OpVariable %4 StorageClass(Function) +%31 = OpVariable %5 StorageClass(Function) +%32 = OpVariable %5 StorageClass(Function) +%33 = OpVariable %4 StorageClass(Function) +%34 = OpVariable %5 StorageClass(Function) +%35 = OpVariable %5 StorageClass(Function) +%36 = OpVariable %5 StorageClass(Function) +%37 = OpCompositeConstruct %3 %15 %15 %15 + OpStore %30 %37 + OpStore %31 %7 + OpStore %32 %15 +%38 = OpLoad %3 %30 + OpStore %33 %38 +%39 = OpLoad %2 %31 + OpStore %35 %39 +%40 = OpLoad %2 %32 + OpStore %36 %40 +%41 = OpFunctionCall %1 %19 %33 %34 %35 %36 +%42 = OpLoad %3 %33 + OpStore %30 %42 +%43 = OpLoad %2 %34 +%44 = OpAccessChain %5 %29 %17 + OpStore %44 %43 +%45 = OpLoad %3 %30 +%46 = OpCompositeExtract %2 %45 0 +%47 = OpAccessChain %5 %29 %18 + OpStore %47 %46 +%48 = OpLoad %13 %29 +%49 = OpCompositeExtract %2 %48 0 + OpStore %11 %49 +%50 = OpCompositeExtract %2 %48 1 + OpStore %12 %50 OpReturn OpFunctionEnd diff --git a/src/Result.zig b/src/Result.zig index 81b9fc3..ee6ebf2 100644 --- a/src/Result.zig +++ b/src/Result.zig @@ -55,7 +55,7 @@ const Decoration = struct { }; pub const Value = union(Type) { - Void: noreturn, + Void: struct {}, Bool: bool, Int: extern union { sint8: i8, @@ -74,7 +74,7 @@ pub const Value = union(Type) { }, Vector: []Value, Matrix: []Value, - Array: struct {}, + Array: []Value, RuntimeArray: struct {}, Structure: []Value, Function: noreturn, @@ -87,7 +87,7 @@ pub const Value = union(Type) { return switch (self.*) { .Vector => |v| v, .Matrix => |m| m, - .Array => |_| unreachable, + .Array => |a| a, .Structure => |s| s, else => null, }; @@ -120,8 +120,14 @@ pub const Value = union(Type) { } break :blk self; }, - .Array => |_| { - unreachable; + .Array => |a| blk: { + var self: Value = .{ .Array = allocator.alloc(Value, member_count) catch return RuntimeError.OutOfMemory }; + errdefer self.deinit(allocator); + + for (self.Array) |*value| { + value.* = try Value.init(allocator, results, a.components_type_word); + } + break :blk self; }, .Structure => |s| blk: { var self: Value = .{ .Structure = allocator.alloc(Value, member_count) catch return RuntimeError.OutOfMemory }; @@ -155,6 +161,13 @@ pub const Value = union(Type) { break :blk values; }, }, + .Array => |a| .{ + .Array = blk: { + const values = allocator.dupe(Value, a) catch return RuntimeError.OutOfMemory; + for (values, a) |*new_value, value| new_value.* = try value.dupe(allocator); + break :blk values; + }, + }, .Structure => |s| .{ .Structure = blk: { const values = allocator.dupe(Value, s) catch return RuntimeError.OutOfMemory; @@ -176,6 +189,10 @@ pub const Value = union(Type) { for (values) |*value| value.deinit(allocator); allocator.free(values); }, + .Array => |values| { + for (values) |*value| value.deinit(allocator); + allocator.free(values); + }, .Structure => |values| { for (values) |*value| value.deinit(allocator); allocator.free(values); @@ -208,7 +225,11 @@ pub const VariantData = union(Variant) { column_type: Type, member_count: SpvWord, }, - Array: struct {}, + Array: struct { + components_type_word: SpvWord, + components_type: Type, + member_count: SpvWord, + }, RuntimeArray: struct {}, Structure: struct { members_type_word: []const SpvWord, @@ -433,6 +454,7 @@ pub fn getMemberCounts(self: *const Self) usize { .Bool, .Int, .Float, .Image, .Sampler => return 1, .Vector => |v| return v.member_count, .Matrix => |m| return m.member_count, + .Array => |a| return a.member_count, .SampledImage => return 2, .Structure => |s| return s.members.len, .Function => |f| return f.params.len, @@ -447,6 +469,7 @@ pub fn getMemberCounts(self: *const Self) usize { pub fn initValue(allocator: std.mem.Allocator, member_count: usize, results: []const Self, resolved: *const Self) RuntimeError!Value { return switch (resolved.variant.?) { .Type => |t| switch (t) { + .Void => .{ .Void = .{} }, .Bool => .{ .Bool = false }, .Int => .{ .Int = .{ .uint64 = 0 } }, .Float => .{ .Float = .{ .float64 = 0.0 } }, @@ -466,7 +489,14 @@ pub fn initValue(allocator: std.mem.Allocator, member_count: usize, results: []c } break :blk value; }, - .Array => |_| RuntimeError.ToDo, + .Array => |a| blk: { + const value: Value = .{ .Array = allocator.alloc(Value, member_count) catch return RuntimeError.OutOfMemory }; + errdefer allocator.free(value.Vector); + for (value.Array) |*val| { + val.* = try Value.init(allocator, results, a.components_type_word); + } + break :blk value; + }, .Structure => |s| blk: { const value: Value = .{ .Structure = allocator.alloc(Value, member_count) catch return RuntimeError.OutOfMemory }; errdefer allocator.free(value.Structure); diff --git a/src/opcodes.zig b/src/opcodes.zig index bffabcc..713f181 100644 --- a/src/opcodes.zig +++ b/src/opcodes.zig @@ -99,6 +99,7 @@ pub const SetupDispatcher = block: { .SatConvertUToS = autoSetupConstant, .Source = opSource, .SourceExtension = opSourceExtension, + .TypeArray = opTypeArray, .TypeBool = opTypeBool, .TypeFloat = opTypeFloat, .TypeFunction = opTypeFunction, @@ -505,7 +506,10 @@ fn opAccessChain(_: std.mem.Allocator, word_count: SpvWord, rt: *Runtime) Runtim if (i.uint32 > m.len) return RuntimeError.InvalidSpirV; value_ptr = &m[i.uint32]; }, - .Array => |_| return RuntimeError.ToDo, + .Array => |a| { + if (i.uint32 > a.len) return RuntimeError.InvalidSpirV; + value_ptr = &a[i.uint32]; + }, .Structure => |s| { if (i.uint32 > s.len) return RuntimeError.InvalidSpirV; value_ptr = &s[i.uint32]; @@ -866,6 +870,23 @@ fn opStore(_: std.mem.Allocator, _: SpvWord, rt: *Runtime) RuntimeError!void { copyValue(try rt.results[ptr_id].getValue(), try rt.results[val_id].getValue()); } +fn opTypeArray(_: std.mem.Allocator, _: SpvWord, rt: *Runtime) RuntimeError!void { + const id = try rt.it.next(); + const components_type_word = try rt.it.next(); + rt.mod.results[id].variant = .{ + .Type = .{ + .Array = .{ + .components_type_word = components_type_word, + .components_type = switch ((try rt.mod.results[components_type_word].getVariant()).*) { + .Type => |t| @as(Result.Type, t), + else => return RuntimeError.InvalidSpirV, + }, + .member_count = try rt.it.next(), + }, + }, + }; +} + fn opTypeBool(_: std.mem.Allocator, _: SpvWord, rt: *Runtime) RuntimeError!void { const id = try rt.it.next(); rt.mod.results[id].variant = .{ @@ -1076,9 +1097,6 @@ fn setupConstant(allocator: std.mem.Allocator, rt: *Runtime) RuntimeError!*Resul const resolved = rt.mod.results[res_type].resolveType(rt.mod.results); const member_count = resolved.getMemberCounts(); - if (member_count == 0) { - return RuntimeError.InvalidSpirV; - } target.variant = .{ .Constant = .{ .value = try Result.initValue(allocator, member_count, rt.mod.results, resolved), diff --git a/test/arrays.zig b/test/arrays.zig new file mode 100644 index 0000000..bf5d592 --- /dev/null +++ b/test/arrays.zig @@ -0,0 +1,30 @@ +const std = @import("std"); +const root = @import("root.zig"); +const compileNzsl = root.compileNzsl; +const case = root.case; + +test "Simple array" { + const allocator = std.testing.allocator; + const shader = + \\ [nzsl_version("1.1")] + \\ module; + \\ + \\ struct FragOut + \\ { + \\ [location(0)] color: vec4[f32] + \\ } + \\ + \\ [entry(frag)] + \\ fn main() -> FragOut + \\ { + \\ let value = array[f32](4.0, 3.0, 2.0, 1.0); + \\ let output: FragOut; + \\ output.color = vec4[f32](value[0], value[1], value[2], value[3]); + \\ return output; + \\ } + ; + const code = try compileNzsl(allocator, shader); + defer allocator.free(code); + + try case.expectOutput(f32, 4, code, "color", &.{ 4, 3, 2, 1 }); +} diff --git a/test/loops.zig b/test/loops.zig index 05e6714..30c335e 100644 --- a/test/loops.zig +++ b/test/loops.zig @@ -3,51 +3,6 @@ const root = @import("root.zig"); const compileNzsl = root.compileNzsl; const case = root.case; -test "Simple for loop" { - const allocator = std.testing.allocator; - const base = @mod(case.random(f32), 5.0); - const iterations = 5; - - var expected = base; - for (1..iterations) |i| { - expected *= @floatFromInt(i); - } - - const shader = try std.fmt.allocPrint( - allocator, - \\ [nzsl_version("1.1")] - \\ [feature(float64)] - \\ module; - \\ - \\ struct FragOut - \\ {{ - \\ [location(0)] color: vec4[f32] - \\ }} - \\ - \\ [entry(frag)] - \\ fn main() -> FragOut - \\ {{ - \\ let value = f32({d}); - \\ for i in 1 -> {d} - \\ {{ - \\ value *= f32(i); - \\ }} - \\ let output: FragOut; - \\ output.color = vec4[f32](value, value, value, value); - \\ return output; - \\ }} - , - .{ - base, - iterations, - }, - ); - defer allocator.free(shader); - const code = try compileNzsl(allocator, shader); - defer allocator.free(code); - try case.expectOutput(f32, 4, code, "color", &.{ expected, expected, expected, expected }); -} - test "Simple while loop" { const allocator = std.testing.allocator; const base = @mod(case.random(f32), 5.0); diff --git a/test/root.zig b/test/root.zig index 4741193..9b057e7 100644 --- a/test/root.zig +++ b/test/root.zig @@ -56,6 +56,7 @@ pub const case = struct { }; test { + std.testing.refAllDecls(@import("arrays.zig")); std.testing.refAllDecls(@import("basics.zig")); std.testing.refAllDecls(@import("branching.zig")); std.testing.refAllDecls(@import("casts.zig"));