diff --git a/build.zig b/build.zig index ca86383..054add2 100644 --- a/build.zig +++ b/build.zig @@ -31,6 +31,7 @@ pub fn build(b: *std.Build) void { .optimize = optimize, .imports = &.{ .{ .name = "spv", .module = mod }, + .{ .name = "pretty", .module = pretty.module("pretty") }, }, }), }); diff --git a/example/main.zig b/example/main.zig index 7d91430..f0f400f 100644 --- a/example/main.zig +++ b/example/main.zig @@ -17,7 +17,7 @@ pub fn main() !void { try sdl3.init(init_flags); defer sdl3.quit(init_flags); - const window = try sdl3.video.Window.init("Hello SDL3", screen_width, screen_height, .{}); + const window = try sdl3.video.Window.init("Hello triangle", screen_width, screen_height, .{}); defer window.deinit(); const allocator = gpa.allocator(); @@ -32,19 +32,10 @@ pub fn main() !void { try surface.lock(); defer surface.unlock(); - const map = surface.getPixels() orelse return; + var pixel_map: [*]u32 = @as([*]u32, @ptrCast(@alignCast((surface.getPixels() orelse return).ptr))); - var pool: std.Thread.Pool = undefined; - try pool.init(.{ - .allocator = allocator, - .n_jobs = 16384, - }); - defer pool.deinit(); - - var wait_group: std.Thread.WaitGroup = .{}; - - const margin_x = @divTrunc(screen_width, 5); - const margin_y = @divTrunc(screen_height, 5); + const margin_x = @divTrunc(screen_width, 3); + const margin_y = @divTrunc(screen_height, 3); const top_y = margin_y; const bottom_y = (screen_height - 1) - margin_y; const center_x = @divTrunc(screen_width, 2); @@ -58,38 +49,37 @@ pub fn main() !void { const x1 = std.math.clamp(center_x + half_w, 0, screen_width - 1); for (x0..x1) |x| { - pool.spawnWg(&wait_group, run, .{ allocator, &module, map, surface, x, y }); + var rt = try spv.Runtime.init(allocator, &module); + defer rt.deinit(allocator); + + var output: [4]f32 = undefined; + + const entry = try rt.getEntryPointByName("main"); + const color = try rt.getResultByName("color"); + const dim = try rt.getResultByName("dim"); + const pos = try rt.getResultByName("pos"); + + try rt.writeInput(f32, &.{ @floatFromInt(x1 - x0), @floatFromInt(bottom_y - top_y) }, dim); + try rt.writeInput(f32, &.{ @floatFromInt(x), @floatFromInt(y) }, pos); + + try rt.callEntryPoint(allocator, entry); + try rt.readOutput(f32, output[0..], color); + + const rgba = surface.mapRgba( + @intFromFloat(output[0] * 255.0), + @intFromFloat(output[1] * 255.0), + @intFromFloat(output[2] * 255.0), + @intFromFloat(output[3] * 255.0), + ); + + pixel_map[(y * surface.getWidth()) + x] = rgba.value; } } - pool.waitAndWork(&wait_group); } try window.updateSurface(); - std.Thread.sleep(5_000_000_000); + std.Thread.sleep(10_000_000_000); } std.log.info("Successfully executed", .{}); } - -fn run(allocator: std.mem.Allocator, module: *spv.Module, map: []u8, surface: sdl3.surface.Surface, x: usize, y: usize) void { - var rt = spv.Runtime.init(allocator, module) catch |err| std.debug.panic("Failed with error {s}", .{@errorName(err)}); - defer rt.deinit(allocator); - - var output: [4]f32 = undefined; - - const entry = rt.getEntryPointByName("main") catch |err| std.debug.panic("Failed with error {s}", .{@errorName(err)}); - const color = rt.getResultByName("color") catch |err| std.debug.panic("Failed with error {s}", .{@errorName(err)}); - - rt.callEntryPoint(allocator, entry) catch |err| std.debug.panic("Failed with error {s}", .{@errorName(err)}); - rt.readOutput(f32, output[0..], color) catch |err| std.debug.panic("Failed with error {s}", .{@errorName(err)}); - - const rgba = surface.mapRgba( - @intFromFloat(output[0] * 255.0), - @intFromFloat(output[1] * 255.0), - @intFromFloat(output[2] * 255.0), - @intFromFloat(output[3] * 255.0), - ); - - var pixel_map: [*]u32 = @as([*]u32, @ptrCast(@alignCast(map.ptr))); - pixel_map[(y * surface.getWidth()) + x] = rgba.value; -} diff --git a/example/shader.nzsl b/example/shader.nzsl index ebb0c2f..7571d06 100644 --- a/example/shader.nzsl +++ b/example/shader.nzsl @@ -3,18 +3,24 @@ module; struct FragIn { - [location(0)] pos: vec2[f32] + [location(0)] dim: vec2[f32], + [location(1)] pos: vec2[f32], } struct FragOut { - [location(0)] color: vec4[f32] + [location(0)] color: vec4[f32] } [entry(frag)] fn main(input: FragIn) -> FragOut { - let output: FragOut; - output.color = vec4[f32](1.0, 0.0, 0.0, 1.0); - return output; + let output: FragOut; + output.color = vec4[f32]( + input.pos.x / input.dim.x, + input.pos.y / input.dim.y, + 1.0, + 1.0 + ); + return output; } diff --git a/example/shader.spv b/example/shader.spv index 77cdfa3..361d880 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 a2efb61..8473601 100644 --- a/example/shader.spv.txt +++ b/example/shader.spv.txt @@ -1,40 +1,75 @@ Version 1.0 Generator: 2560130 -Bound: 21 +Bound: 45 Schema: 0 OpCapability Capability(Shader) OpMemoryModel AddressingModel(Logical) MemoryModel(GLSL450) - OpEntryPoint ExecutionModel(Fragment) %13 "main" %6 - OpExecutionMode %13 ExecutionMode(OriginUpperLeft) + OpEntryPoint ExecutionModel(Fragment) %20 "main" %6 %10 %16 + OpExecutionMode %20 ExecutionMode(OriginUpperLeft) OpSource SourceLanguage(NZSL) 4198400 OpSourceExtension "Version: 1.1" - OpName %7 "FragOut" - OpMemberName %7 0 "color" - OpName %6 "color" - OpName %13 "main" + OpName %12 "FragIn" + OpMemberName %12 0 "dim" + OpMemberName %12 1 "pos" + OpName %17 "FragOut" + OpMemberName %17 0 "color" + OpName %6 "dim" + OpName %10 "pos" + OpName %16 "color" + OpName %20 "main" OpDecorate %6 Decoration(Location) 0 - OpMemberDecorate %7 0 Decoration(Offset) 0 + OpDecorate %10 Decoration(Location) 1 + OpDecorate %16 Decoration(Location) 0 + OpMemberDecorate %12 0 Decoration(Offset) 0 + OpMemberDecorate %12 1 Decoration(Offset) 8 + OpMemberDecorate %17 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) -%12 = OpConstant %3 f32(0) -%18 = OpTypePointer StorageClass(Function) %4 - %6 = OpVariable %5 StorageClass(Output) -%13 = OpFunction %1 FunctionControl(0) %2 -%14 = OpLabel -%15 = OpVariable %8 StorageClass(Function) -%16 = OpCompositeConstruct %4 %11 %12 %12 %11 -%17 = OpAccessChain %18 %15 %10 - OpStore %17 %16 -%19 = OpLoad %7 %15 -%20 = OpCompositeExtract %4 %19 0 - OpStore %6 %20 + %4 = OpTypeVector %3 2 + %5 = OpTypePointer StorageClass(Input) %4 + %7 = OpTypeInt 32 1 + %8 = OpConstant %7 i32(0) + %9 = OpTypePointer StorageClass(Function) %4 +%11 = OpConstant %7 i32(1) +%12 = OpTypeStruct %4 %4 +%13 = OpTypePointer StorageClass(Function) %12 +%14 = OpTypeVector %3 4 +%15 = OpTypePointer StorageClass(Output) %14 +%17 = OpTypeStruct %14 +%18 = OpTypePointer StorageClass(Function) %17 +%19 = OpConstant %3 f32(1) +%42 = OpTypePointer StorageClass(Function) %14 + %6 = OpVariable %5 StorageClass(Input) +%10 = OpVariable %5 StorageClass(Input) +%16 = OpVariable %15 StorageClass(Output) +%20 = OpFunction %1 FunctionControl(0) %2 +%21 = OpLabel +%22 = OpVariable %18 StorageClass(Function) +%23 = OpVariable %13 StorageClass(Function) +%24 = OpAccessChain %9 %23 %8 + OpCopyMemory %24 %6 +%25 = OpAccessChain %9 %23 %11 + OpCopyMemory %25 %10 +%26 = OpAccessChain %9 %23 %11 +%27 = OpLoad %4 %26 +%28 = OpCompositeExtract %3 %27 0 +%29 = OpAccessChain %9 %23 %8 +%30 = OpLoad %4 %29 +%31 = OpCompositeExtract %3 %30 0 +%32 = OpFDiv %3 %28 %31 +%33 = OpAccessChain %9 %23 %11 +%34 = OpLoad %4 %33 +%35 = OpCompositeExtract %3 %34 1 +%36 = OpAccessChain %9 %23 %8 +%37 = OpLoad %4 %36 +%38 = OpCompositeExtract %3 %37 1 +%39 = OpFDiv %3 %35 %38 +%40 = OpCompositeConstruct %14 %32 %39 %19 %19 +%41 = OpAccessChain %42 %22 %8 + OpStore %41 %40 +%43 = OpLoad %17 %22 +%44 = OpCompositeExtract %14 %43 0 + OpStore %16 %44 OpReturn OpFunctionEnd diff --git a/src/Result.zig b/src/Result.zig index ee6ebf2..48d103f 100644 --- a/src/Result.zig +++ b/src/Result.zig @@ -85,10 +85,7 @@ pub const Value = union(Type) { pub inline fn getCompositeDataOrNull(self: *const Value) ?[]Value { return switch (self.*) { - .Vector => |v| v, - .Matrix => |m| m, - .Array => |a| a, - .Structure => |s| s, + .Vector, .Matrix, .Array, .Structure => |v| v, else => null, }; } @@ -181,19 +178,7 @@ pub const Value = union(Type) { fn deinit(self: *Value, allocator: std.mem.Allocator) void { switch (self.*) { - .Vector => |values| { - for (values) |*value| value.deinit(allocator); - allocator.free(values); - }, - .Matrix => |values| { - for (values) |*value| value.deinit(allocator); - allocator.free(values); - }, - .Array => |values| { - for (values) |*value| value.deinit(allocator); - allocator.free(values); - }, - .Structure => |values| { + .Vector, .Matrix, .Array, .Structure => |values| { for (values) |*value| value.deinit(allocator); allocator.free(values); }, diff --git a/src/Runtime.zig b/src/Runtime.zig index 11f3519..9370e21 100644 --- a/src/Runtime.zig +++ b/src/Runtime.zig @@ -14,20 +14,16 @@ const WordIterator = @import("WordIterator.zig"); const Self = @This(); pub const RuntimeError = error{ - InvalidSpirV, - UnsupportedSpirV, - OutOfMemory, - Unreachable, - Killed, - InvalidEntryPoint, - ToDo, DivisionByZero, + InvalidEntryPoint, + InvalidSpirV, InvalidValueType, -}; - -pub const ReadOutputError = error{ + Killed, NotFound, - InvalidValueType, + OutOfMemory, + ToDo, + Unreachable, + UnsupportedSpirV, }; pub const Function = struct { @@ -141,7 +137,6 @@ pub fn callEntryPoint(self: *Self, allocator: std.mem.Allocator, entry_point_ind self.it = it_tmp; } else { self.it.did_jump = false; - //_ = self.it.skip(); } } @@ -153,11 +148,19 @@ pub fn callEntryPoint(self: *Self, allocator: std.mem.Allocator, entry_point_ind //}) catch return RuntimeError.OutOfMemory; } -pub fn readOutput(self: *const Self, comptime T: type, output: []T, result: SpvWord) ReadOutputError!void { +pub fn readOutput(self: *const Self, comptime T: type, output: []T, result: SpvWord) RuntimeError!void { if (std.mem.indexOf(SpvWord, self.mod.output_locations.items, &.{result})) |_| { try self.readValue(T, output, &self.results[result].variant.?.Variable.value); } else { - return ReadOutputError.NotFound; + return RuntimeError.NotFound; + } +} + +pub fn writeInput(self: *const Self, comptime T: type, input: []const T, result: SpvWord) RuntimeError!void { + if (std.mem.indexOf(SpvWord, self.mod.input_locations.items, &.{result})) |_| { + try self.writeValue(T, input, &self.results[result].variant.?.Variable.value); + } else { + return RuntimeError.NotFound; } } @@ -166,13 +169,13 @@ fn reset(self: *Self) void { self.current_function = null; } -fn readValue(self: *const Self, comptime T: type, output: []T, value: *const Result.Value) ReadOutputError!void { +fn readValue(self: *const Self, comptime T: type, output: []T, value: *const Result.Value) RuntimeError!void { switch (value.*) { .Bool => |b| { if (T == bool) { output[0] = b; } else { - return ReadOutputError.InvalidValueType; + return RuntimeError.InvalidValueType; } }, .Int => |i| { @@ -185,7 +188,7 @@ fn readValue(self: *const Self, comptime T: type, output: []T, value: *const Res u16 => output[0] = i.uint16, u32 => output[0] = i.uint32, u64 => output[0] = i.uint64, - inline else => return ReadOutputError.InvalidValueType, + inline else => return RuntimeError.InvalidValueType, } }, .Float => |f| { @@ -193,13 +196,45 @@ fn readValue(self: *const Self, comptime T: type, output: []T, value: *const Res f16 => output[0] = f.float16, f32 => output[0] = f.float32, f64 => output[0] = f.float64, - inline else => return ReadOutputError.InvalidValueType, + inline else => return RuntimeError.InvalidValueType, } }, - .Vector => |values| for (values, 0..) |v, i| try self.readValue(T, output[i..], &v), - .Matrix => |values| for (values, 0..) |v, i| try self.readValue(T, output[i..], &v), - .Array => |values| for (values, 0..) |v, i| try self.readValue(T, output[i..], &v), // Doubt if this is allowed - .Structure => |values| for (values, 0..) |v, i| try self.readValue(T, output[i..], &v), - else => return ReadOutputError.InvalidValueType, + .Vector, .Matrix, .Array, .Structure => |values| for (values, 0..) |v, i| try self.readValue(T, output[i..], &v), + else => return RuntimeError.InvalidValueType, + } +} + +fn writeValue(self: *const Self, comptime T: type, input: []const T, value: *Result.Value) RuntimeError!void { + switch (value.*) { + .Bool => |*b| { + if (T == bool) { + b.* = input[0]; + } else { + return RuntimeError.InvalidValueType; + } + }, + .Int => |*i| { + switch (T) { + i8 => i.sint8 = input[0], + i16 => i.sint16 = input[0], + i32 => i.sint32 = input[0], + i64 => i.sint64 = input[0], + u8 => i.uint8 = input[0], + u16 => i.uint16 = input[0], + u32 => i.uint32 = input[0], + u64 => i.uint64 = input[0], + inline else => return RuntimeError.InvalidValueType, + } + }, + .Float => |*f| { + switch (T) { + f16 => f.float16 = input[0], + f32 => f.float32 = input[0], + f64 => f.float64 = input[0], + inline else => return RuntimeError.InvalidValueType, + } + }, + .Vector, .Matrix, .Array, .Structure => |*values| for (values.*, 0..) |*v, i| try self.writeValue(T, input[i..], v), + else => return RuntimeError.InvalidValueType, } } diff --git a/src/opcodes.zig b/src/opcodes.zig index 121655c..9c24334 100644 --- a/src/opcodes.zig +++ b/src/opcodes.zig @@ -169,6 +169,7 @@ pub const RuntimeDispatcher = block: { .ConvertFToU = ConversionEngine(.Float, .UInt).op, .ConvertSToF = ConversionEngine(.SInt, .Float).op, .ConvertUToF = ConversionEngine(.UInt, .Float).op, + .CopyMemory = opCopyMemory, .FAdd = MathEngine(.Float, .Add).op, .FConvert = ConversionEngine(.Float, .Float).op, .FDiv = MathEngine(.Float, .Div).op, @@ -631,22 +632,10 @@ fn opAccessChain(_: std.mem.Allocator, word_count: SpvWord, rt: *Runtime) Runtim switch (member_value.*) { .Int => |i| { switch (value_ptr.*) { - .Vector => |v| { + .Vector, .Matrix, .Array, .Structure => |v| { if (i.uint32 > v.len) return RuntimeError.InvalidSpirV; value_ptr = &v[i.uint32]; }, - .Matrix => |m| { - if (i.uint32 > m.len) return RuntimeError.InvalidSpirV; - value_ptr = &m[i.uint32]; - }, - .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]; - }, else => return RuntimeError.InvalidSpirV, } }, @@ -752,6 +741,12 @@ fn opConstant(allocator: std.mem.Allocator, word_count: SpvWord, rt: *Runtime) R } } +fn opCopyMemory(_: std.mem.Allocator, _: SpvWord, rt: *Runtime) RuntimeError!void { + const target = try rt.it.next(); + const source = try rt.it.next(); + copyValue(try rt.results[target].getValue(), try rt.results[source].getValue()); +} + fn opDecorate(allocator: std.mem.Allocator, _: SpvWord, rt: *Runtime) RuntimeError!void { const target = try rt.it.next(); const decoration_type = try rt.it.nextAs(spv.SpvDecoration);