diff --git a/sandbox/main.zig b/sandbox/main.zig index 03a23f0..69c9cd3 100644 --- a/sandbox/main.zig +++ b/sandbox/main.zig @@ -35,12 +35,12 @@ pub fn main() !void { }; try rt.writeBuiltIn(std.mem.asBytes(&global_invocation_indices), .GlobalInvocationId); - try rt.writeDescriptorSet(allocator, std.mem.asBytes(&ssbo), 0, 0); + try rt.writeDescriptorSet(std.mem.asBytes(&ssbo), 0, 0); rt.callEntryPoint(allocator, entry) catch |err| switch (err) { spv.Runtime.RuntimeError.OutOfBounds => continue, else => return err, }; - try rt.readDescriptorSet(std.mem.asBytes(&ssbo), 0, 0); + try rt.flushDescriptorSets(allocator); } } diff --git a/src/Result.zig b/src/Result.zig index 4d01646..b8358ec 100644 --- a/src/Result.zig +++ b/src/Result.zig @@ -113,28 +113,33 @@ pub const Value = union(Type) { Vector2u32: Vec2u32, Matrix: []Value, Array: []Value, - RuntimeArray: ?[]Value, + RuntimeArray: struct { + type_word: SpvWord, + data: []u8, + }, Structure: []Value, Function: noreturn, Image: struct {}, Sampler: struct {}, SampledImage: struct {}, - Pointer: union(enum) { - common: *Value, - f32_ptr: *f32, - i32_ptr: *i32, //< For vector specializations - u32_ptr: *u32, + Pointer: struct { + ptr: union(enum) { + common: *Value, + f32_ptr: *f32, + i32_ptr: *i32, //< For vector specializations + u32_ptr: *u32, + }, + runtime_array_window: ?[]u8 = null, }, pub inline fn getCompositeDataOrNull(self: *const Value) ?[]Value { return switch (self.*) { .Vector, .Matrix, .Array, .Structure => |v| v, - .RuntimeArray => |v| v, else => null, }; } - fn init(allocator: std.mem.Allocator, results: []const Self, target: SpvWord) RuntimeError!Value { + pub fn init(allocator: std.mem.Allocator, results: []const Self, target: SpvWord) RuntimeError!Value { const resolved = results[target].resolveType(results); const member_count = resolved.getMemberCounts(); @@ -194,7 +199,12 @@ pub const Value = union(Type) { } break :blk self; }, - .RuntimeArray => .{ .RuntimeArray = null }, + .RuntimeArray => |a| .{ + .RuntimeArray = .{ + .type_word = a.components_type_word, + .data = &.{}, + }, + }, else => unreachable, }, else => unreachable, @@ -225,17 +235,6 @@ pub const Value = union(Type) { break :blk values; }, }, - .RuntimeArray => |opt_a| .{ - .RuntimeArray = blk: { - if (opt_a) |a| { - 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; - } else { - break :blk null; - } - }, - }, .Structure => |s| .{ .Structure = blk: { const values = allocator.dupe(Value, s) catch return RuntimeError.OutOfMemory; @@ -247,16 +246,244 @@ pub const Value = union(Type) { }; } + pub fn read(self: *const Value, output: []u8) RuntimeError!usize { + switch (self.*) { + .Bool => |b| { + output[0] = if (b == true) 1 else 0; + return 1; + }, + .Int => |i| { + switch (i.bit_count) { + 8 => output[0] = @bitCast(i.value.uint8), + 16 => std.mem.copyForwards(u8, output[0..], std.mem.asBytes(&i.value.uint16)), + 32 => std.mem.copyForwards(u8, output[0..], std.mem.asBytes(&i.value.uint32)), + 64 => std.mem.copyForwards(u8, output[0..], std.mem.asBytes(&i.value.uint64)), + else => return RuntimeError.InvalidValueType, + } + return @divExact(i.bit_count, 8); + }, + .Float => |f| { + switch (f.bit_count) { + 16 => std.mem.copyForwards(u8, output[0..], std.mem.asBytes(&f.value.float16)), + 32 => std.mem.copyForwards(u8, output[0..], std.mem.asBytes(&f.value.float32)), + 64 => std.mem.copyForwards(u8, output[0..], std.mem.asBytes(&f.value.float64)), + else => return RuntimeError.InvalidValueType, + } + return @divExact(f.bit_count, 8); + }, + .Vector4f32 => |vec| { + inline for (0..4) |i| { + std.mem.copyForwards(u8, output[(i * 4)..], std.mem.asBytes(&vec[i])); + } + return 4 * 4; + }, + .Vector3f32 => |vec| { + inline for (0..3) |i| { + std.mem.copyForwards(u8, output[(i * 4)..], std.mem.asBytes(&vec[i])); + } + return 3 * 4; + }, + .Vector2f32 => |vec| { + inline for (0..2) |i| { + std.mem.copyForwards(u8, output[(i * 4)..], std.mem.asBytes(&vec[i])); + } + return 2 * 4; + }, + .Vector4i32 => |vec| { + inline for (0..4) |i| { + std.mem.copyForwards(u8, output[(i * 4)..], std.mem.asBytes(&vec[i])); + } + return 4 * 4; + }, + .Vector3i32 => |vec| { + inline for (0..3) |i| { + std.mem.copyForwards(u8, output[(i * 4)..], std.mem.asBytes(&vec[i])); + } + return 3 * 4; + }, + .Vector2i32 => |vec| { + inline for (0..2) |i| { + std.mem.copyForwards(u8, output[(i * 4)..], std.mem.asBytes(&vec[i])); + } + return 2 * 4; + }, + .Vector4u32 => |vec| { + inline for (0..4) |i| { + std.mem.copyForwards(u8, output[(i * 4)..], std.mem.asBytes(&vec[i])); + } + return 4 * 4; + }, + .Vector3u32 => |vec| { + inline for (0..3) |i| { + std.mem.copyForwards(u8, output[(i * 4)..], std.mem.asBytes(&vec[i])); + } + return 3 * 4; + }, + .Vector2u32 => |vec| { + inline for (0..2) |i| { + std.mem.copyForwards(u8, output[(i * 4)..], std.mem.asBytes(&vec[i])); + } + return 2 * 4; + }, + .Vector, + .Matrix, + .Array, + .Structure, + => |values| { + var offset: usize = 0; + for (values) |v| { + offset += try v.read(output[offset..]); + } + return offset; + }, + else => return RuntimeError.InvalidValueType, + } + return 0; + } + + pub fn writeConst(self: *Value, input: []const u8) RuntimeError!usize { + return self.write(@constCast(input)); + } + + pub fn write(self: *Value, input: []u8) RuntimeError!usize { + switch (self.*) { + .Bool => |*b| { + b.* = if (input[0] != 0) true else false; + return 1; + }, + .Int => |*i| { + switch (i.bit_count) { + 8 => i.value.uint8 = @bitCast(input[0]), + 16 => std.mem.copyForwards(u8, std.mem.asBytes(&i.value.uint16), input[0..2]), + 32 => std.mem.copyForwards(u8, std.mem.asBytes(&i.value.uint32), input[0..4]), + 64 => std.mem.copyForwards(u8, std.mem.asBytes(&i.value.uint64), input[0..8]), + else => return RuntimeError.InvalidValueType, + } + return @divExact(i.bit_count, 8); + }, + .Float => |*f| { + switch (f.bit_count) { + 16 => std.mem.copyForwards(u8, std.mem.asBytes(&f.value.float16), input[0..2]), + 32 => std.mem.copyForwards(u8, std.mem.asBytes(&f.value.float32), input[0..4]), + 64 => std.mem.copyForwards(u8, std.mem.asBytes(&f.value.float64), input[0..8]), + else => return RuntimeError.InvalidValueType, + } + return @divExact(f.bit_count, 8); + }, + .Vector4f32 => |*vec| { + inline for (0..4) |i| { + const start = i * 4; + const end = (i + 1) * 4; + std.mem.copyForwards(u8, std.mem.asBytes(&vec[i]), input[start..end]); + } + return 4 * 4; + }, + .Vector3f32 => |*vec| { + inline for (0..3) |i| { + const start = i * 4; + const end = (i + 1) * 4; + std.mem.copyForwards(u8, std.mem.asBytes(&vec[i]), input[start..end]); + } + return 3 * 4; + }, + .Vector2f32 => |*vec| { + inline for (0..2) |i| { + const start = i * 4; + const end = (i + 1) * 4; + std.mem.copyForwards(u8, std.mem.asBytes(&vec[i]), input[start..end]); + } + return 2 * 4; + }, + .Vector4i32 => |*vec| { + inline for (0..4) |i| { + const start = i * 4; + const end = (i + 1) * 4; + std.mem.copyForwards(u8, std.mem.asBytes(&vec[i]), input[start..end]); + } + return 4 * 4; + }, + .Vector3i32 => |*vec| { + inline for (0..3) |i| { + const start = i * 4; + const end = (i + 1) * 4; + std.mem.copyForwards(u8, std.mem.asBytes(&vec[i]), input[start..end]); + } + return 3 * 4; + }, + .Vector2i32 => |*vec| { + inline for (0..2) |i| { + const start = i * 4; + const end = (i + 1) * 4; + std.mem.copyForwards(u8, std.mem.asBytes(&vec[i]), input[start..end]); + } + return 2 * 4; + }, + .Vector4u32 => |*vec| { + inline for (0..4) |i| { + const start = i * 4; + const end = (i + 1) * 4; + std.mem.copyForwards(u8, std.mem.asBytes(&vec[i]), input[start..end]); + } + return 4 * 4; + }, + .Vector3u32 => |*vec| { + inline for (0..3) |i| { + const start = i * 4; + const end = (i + 1) * 4; + std.mem.copyForwards(u8, std.mem.asBytes(&vec[i]), input[start..end]); + } + return 3 * 4; + }, + .Vector2u32 => |*vec| { + inline for (0..2) |i| { + const start = i * 4; + const end = (i + 1) * 4; + std.mem.copyForwards(u8, std.mem.asBytes(&vec[i]), input[start..end]); + } + return 2 * 4; + }, + .Vector, + .Matrix, + .Array, + .Structure, + => |*values| { + var offset: usize = 0; + for (values.*) |*v| { + offset += try v.write(input[offset..]); + } + return offset; + }, + .RuntimeArray => |*arr| arr.data = input[0..], + else => return RuntimeError.InvalidValueType, + } + return 0; + } + + pub fn flushPtr(self: *Value, allocator: std.mem.Allocator) RuntimeError!void { + switch (self.*) { + .Pointer => |*p| { + if (p.runtime_array_window) |window| { + switch (p.ptr) { + .common => |ptr| { + _ = try ptr.read(window); + ptr.deinit(allocator); + allocator.destroy(ptr); + }, + else => {}, + } + } + p.runtime_array_window = null; + }, + else => {}, + } + } + fn deinit(self: *Value, allocator: std.mem.Allocator) void { switch (self.*) { .Vector, .Matrix, .Array, .Structure => |values| { for (values) |*value| value.deinit(allocator); allocator.free(values); }, - .RuntimeArray => |opt_values| if (opt_values) |values| { - for (values) |*value| value.deinit(allocator); - allocator.free(values); - }, else => {}, } } @@ -412,6 +639,7 @@ pub fn deinit(self: *Self, allocator: std.mem.Allocator) void { }, .Constant => |*c| c.value.deinit(allocator), .Variable => |*v| v.value.deinit(allocator), + .AccessChain => |*a| a.value.deinit(allocator), .Function => |f| allocator.free(f.params), else => {}, } @@ -649,16 +877,11 @@ pub fn initValue(allocator: std.mem.Allocator, member_count: usize, results: []c } break :blk value; }, - .RuntimeArray => |a| blk: { - if (member_count == 0) { - break :blk Value{ .RuntimeArray = null }; - } - const value: Value = .{ .RuntimeArray = allocator.alloc(Value, member_count) catch return RuntimeError.OutOfMemory }; - errdefer allocator.free(value.RuntimeArray.?); - for (value.RuntimeArray.?) |*val| { - val.* = try Value.init(allocator, results, a.components_type_word); - } - break :blk value; + .RuntimeArray => |a| .{ + .RuntimeArray = .{ + .type_word = a.components_type_word, + .data = &.{}, + }, }, .Structure => |s| blk: { const value: Value = .{ .Structure = allocator.alloc(Value, member_count) catch return RuntimeError.OutOfMemory }; @@ -676,3 +899,14 @@ pub fn initValue(allocator: std.mem.Allocator, member_count: usize, results: []c else => RuntimeError.InvalidSpirV, }; } + +pub fn flushPtr(self: *Self, allocator: std.mem.Allocator) RuntimeError!void { + if (self.variant) |*variant| { + switch (variant.*) { + .Constant => |*c| try c.value.flushPtr(allocator), + .Variable => |*v| try v.value.flushPtr(allocator), + .AccessChain => |*a| try a.value.flushPtr(allocator), + else => {}, + } + } +} diff --git a/src/Runtime.zig b/src/Runtime.zig index 7ff3100..65ce8ee 100644 --- a/src/Runtime.zig +++ b/src/Runtime.zig @@ -151,43 +151,9 @@ pub fn callEntryPoint(self: *Self, allocator: std.mem.Allocator, entry_point_ind //}) catch return RuntimeError.OutOfMemory; } -pub fn readDescriptorSet(self: *const Self, output: []u8, set: SpvWord, binding: SpvWord) RuntimeError!void { +pub fn writeDescriptorSet(self: *const Self, input: []u8, set: SpvWord, binding: SpvWord) RuntimeError!void { if (set < lib.SPIRV_MAX_SET and binding < lib.SPIRV_MAX_SET_BINDINGS) { - _ = try self.readValue(output, &self.results[self.mod.bindings[set][binding]].variant.?.Variable.value); - } else { - return RuntimeError.NotFound; - } -} - -pub fn writeDescriptorSet(self: *const Self, allocator: std.mem.Allocator, input: []const u8, set: SpvWord, binding: SpvWord) RuntimeError!void { - if (set < lib.SPIRV_MAX_SET and binding < lib.SPIRV_MAX_SET_BINDINGS) { - const variable = &self.results[self.mod.bindings[set][binding]].variant.?.Variable; - - const helper = struct { - fn init(allocator2: std.mem.Allocator, len: usize, value: *Result.Value, type_word: SpvWord, results: []Result) RuntimeError!void { - const resolved = results[type_word].resolveType(results); - - switch (value.*) { - .RuntimeArray => |a| if (a == null) { - const elem_size = resolved.variant.?.Type.getSize(results); - value.* = try Result.initValue(allocator2, std.math.divCeil(usize, len, elem_size) catch unreachable, results, resolved); - }, - .Structure => |*s| for (s.*, 0..) |*elem, i| { - try @This().init(allocator2, len, elem, resolved.variant.?.Type.Structure.members_type_word[i], results); - }, - else => {}, - } - } - }; - try helper.init(allocator, input.len, &variable.value, variable.type_word, self.results); - - //@import("pretty").print(allocator, variable, .{ - // .tab_size = 4, - // .max_depth = 0, - // .struct_max_len = 0, - // .array_max_len = 0, - //}) catch return RuntimeError.OutOfMemory; - _ = try self.writeValue(input, &variable.value); + _ = try self.results[self.mod.bindings[set][binding]].variant.?.Variable.value.write(input); } else { return RuntimeError.NotFound; } @@ -195,7 +161,7 @@ pub fn writeDescriptorSet(self: *const Self, allocator: std.mem.Allocator, input pub fn readOutput(self: *const Self, output: []u8, result: SpvWord) RuntimeError!void { if (std.mem.indexOfScalar(SpvWord, &self.mod.output_locations, result)) |_| { - _ = try self.readValue(output, &self.results[result].variant.?.Variable.value); + _ = try self.results[result].variant.?.Variable.value.read(output); } else { return RuntimeError.NotFound; } @@ -203,7 +169,7 @@ pub fn readOutput(self: *const Self, output: []u8, result: SpvWord) RuntimeError pub fn writeInput(self: *const Self, input: []const u8, result: SpvWord) RuntimeError!void { if (std.mem.indexOfScalar(SpvWord, &self.mod.input_locations, result)) |_| { - _ = try self.writeValue(input, &self.results[result].variant.?.Variable.value); + _ = try self.results[result].variant.?.Variable.value.writeConst(input); } else { return RuntimeError.NotFound; } @@ -211,235 +177,19 @@ pub fn writeInput(self: *const Self, input: []const u8, result: SpvWord) Runtime pub fn writeBuiltIn(self: *const Self, input: []const u8, builtin: spv.SpvBuiltIn) RuntimeError!void { if (self.mod.builtins.get(builtin)) |result| { - _ = try self.writeValue(input, &self.results[result].variant.?.Variable.value); + _ = try self.results[result].variant.?.Variable.value.writeConst(input); } else { return RuntimeError.NotFound; } } +pub fn flushDescriptorSets(self: *const Self, allocator: std.mem.Allocator) RuntimeError!void { + for (self.results) |*result| { + try result.flushPtr(allocator); + } +} + fn reset(self: *Self) void { self.function_stack.clearRetainingCapacity(); self.current_function = null; } - -fn readValue(self: *const Self, output: []u8, value: *const Result.Value) RuntimeError!usize { - switch (value.*) { - .Bool => |b| { - output[0] = if (b == true) 1 else 0; - return 1; - }, - .Int => |i| { - switch (i.bit_count) { - 8 => output[0] = @bitCast(i.value.uint8), - 16 => std.mem.copyForwards(u8, output[0..], std.mem.asBytes(&i.value.uint16)), - 32 => std.mem.copyForwards(u8, output[0..], std.mem.asBytes(&i.value.uint32)), - 64 => std.mem.copyForwards(u8, output[0..], std.mem.asBytes(&i.value.uint64)), - else => return RuntimeError.InvalidValueType, - } - return @divExact(i.bit_count, 8); - }, - .Float => |f| { - switch (f.bit_count) { - 16 => std.mem.copyForwards(u8, output[0..], std.mem.asBytes(&f.value.float16)), - 32 => std.mem.copyForwards(u8, output[0..], std.mem.asBytes(&f.value.float32)), - 64 => std.mem.copyForwards(u8, output[0..], std.mem.asBytes(&f.value.float64)), - else => return RuntimeError.InvalidValueType, - } - return @divExact(f.bit_count, 8); - }, - .Vector4f32 => |vec| { - inline for (0..4) |i| { - std.mem.copyForwards(u8, output[(i * 4)..], std.mem.asBytes(&vec[i])); - } - return 4 * 4; - }, - .Vector3f32 => |vec| { - inline for (0..3) |i| { - std.mem.copyForwards(u8, output[(i * 4)..], std.mem.asBytes(&vec[i])); - } - return 3 * 4; - }, - .Vector2f32 => |vec| { - inline for (0..2) |i| { - std.mem.copyForwards(u8, output[(i * 4)..], std.mem.asBytes(&vec[i])); - } - return 2 * 4; - }, - .Vector4i32 => |vec| { - inline for (0..4) |i| { - std.mem.copyForwards(u8, output[(i * 4)..], std.mem.asBytes(&vec[i])); - } - return 4 * 4; - }, - .Vector3i32 => |vec| { - inline for (0..3) |i| { - std.mem.copyForwards(u8, output[(i * 4)..], std.mem.asBytes(&vec[i])); - } - return 3 * 4; - }, - .Vector2i32 => |vec| { - inline for (0..2) |i| { - std.mem.copyForwards(u8, output[(i * 4)..], std.mem.asBytes(&vec[i])); - } - return 2 * 4; - }, - .Vector4u32 => |vec| { - inline for (0..4) |i| { - std.mem.copyForwards(u8, output[(i * 4)..], std.mem.asBytes(&vec[i])); - } - return 4 * 4; - }, - .Vector3u32 => |vec| { - inline for (0..3) |i| { - std.mem.copyForwards(u8, output[(i * 4)..], std.mem.asBytes(&vec[i])); - } - return 3 * 4; - }, - .Vector2u32 => |vec| { - inline for (0..2) |i| { - std.mem.copyForwards(u8, output[(i * 4)..], std.mem.asBytes(&vec[i])); - } - return 2 * 4; - }, - .Vector, - .Matrix, - .Array, - .Structure, - => |values| { - var offset: usize = 0; - for (values) |v| { - offset += try self.readValue(output[offset..], &v); - } - return offset; - }, - .RuntimeArray => |opt_values| if (opt_values) |values| { - var offset: usize = 0; - for (values) |v| { - offset += try self.readValue(output[offset..], &v); - } - return offset; - }, - else => return RuntimeError.InvalidValueType, - } - return 0; -} - -fn writeValue(self: *const Self, input: []const u8, value: *Result.Value) RuntimeError!usize { - switch (value.*) { - .Bool => |*b| { - b.* = if (input[0] != 0) true else false; - return 1; - }, - .Int => |*i| { - switch (i.bit_count) { - 8 => i.value.uint8 = @bitCast(input[0]), - 16 => std.mem.copyForwards(u8, std.mem.asBytes(&i.value.uint16), input[0..2]), - 32 => std.mem.copyForwards(u8, std.mem.asBytes(&i.value.uint32), input[0..4]), - 64 => std.mem.copyForwards(u8, std.mem.asBytes(&i.value.uint64), input[0..8]), - else => return RuntimeError.InvalidValueType, - } - return @divExact(i.bit_count, 8); - }, - .Float => |*f| { - switch (f.bit_count) { - 16 => std.mem.copyForwards(u8, std.mem.asBytes(&f.value.float16), input[0..2]), - 32 => std.mem.copyForwards(u8, std.mem.asBytes(&f.value.float32), input[0..4]), - 64 => std.mem.copyForwards(u8, std.mem.asBytes(&f.value.float64), input[0..8]), - else => return RuntimeError.InvalidValueType, - } - return @divExact(f.bit_count, 8); - }, - .Vector4f32 => |*vec| { - inline for (0..4) |i| { - const start = i * 4; - const end = (i + 1) * 4; - std.mem.copyForwards(u8, std.mem.asBytes(&vec[i]), input[start..end]); - } - return 4 * 4; - }, - .Vector3f32 => |*vec| { - inline for (0..3) |i| { - const start = i * 4; - const end = (i + 1) * 4; - std.mem.copyForwards(u8, std.mem.asBytes(&vec[i]), input[start..end]); - } - return 3 * 4; - }, - .Vector2f32 => |*vec| { - inline for (0..2) |i| { - const start = i * 4; - const end = (i + 1) * 4; - std.mem.copyForwards(u8, std.mem.asBytes(&vec[i]), input[start..end]); - } - return 2 * 4; - }, - .Vector4i32 => |*vec| { - inline for (0..4) |i| { - const start = i * 4; - const end = (i + 1) * 4; - std.mem.copyForwards(u8, std.mem.asBytes(&vec[i]), input[start..end]); - } - return 4 * 4; - }, - .Vector3i32 => |*vec| { - inline for (0..3) |i| { - const start = i * 4; - const end = (i + 1) * 4; - std.mem.copyForwards(u8, std.mem.asBytes(&vec[i]), input[start..end]); - } - return 3 * 4; - }, - .Vector2i32 => |*vec| { - inline for (0..2) |i| { - const start = i * 4; - const end = (i + 1) * 4; - std.mem.copyForwards(u8, std.mem.asBytes(&vec[i]), input[start..end]); - } - return 2 * 4; - }, - .Vector4u32 => |*vec| { - inline for (0..4) |i| { - const start = i * 4; - const end = (i + 1) * 4; - std.mem.copyForwards(u8, std.mem.asBytes(&vec[i]), input[start..end]); - } - return 4 * 4; - }, - .Vector3u32 => |*vec| { - inline for (0..3) |i| { - const start = i * 4; - const end = (i + 1) * 4; - std.mem.copyForwards(u8, std.mem.asBytes(&vec[i]), input[start..end]); - } - return 3 * 4; - }, - .Vector2u32 => |*vec| { - inline for (0..2) |i| { - const start = i * 4; - const end = (i + 1) * 4; - std.mem.copyForwards(u8, std.mem.asBytes(&vec[i]), input[start..end]); - } - return 2 * 4; - }, - .Vector, - .Matrix, - .Array, - .Structure, - => |*values| { - var offset: usize = 0; - for (values.*) |*v| { - offset += try self.writeValue(input[offset..], v); - } - return offset; - }, - .RuntimeArray => |opt_values| if (opt_values) |*values| { - var offset: usize = 0; - for (values.*) |*v| { - offset += try self.writeValue(input[offset..], v); - } - return offset; - }, - else => return RuntimeError.InvalidValueType, - } - return 0; -} diff --git a/src/opcodes.zig b/src/opcodes.zig index b16197f..25a2aab 100644 --- a/src/opcodes.zig +++ b/src/opcodes.zig @@ -945,14 +945,13 @@ fn copyValue(dst: *Result.Value, src: *const Result.Value) void { fn getDstSlice(v: *Result.Value) ?[]Result.Value { return switch (v.*) { .Vector, .Matrix, .Array, .Structure => |s| s, - .RuntimeArray => |s| s, else => null, }; } fn writeF32(dst_f32_ptr: *f32, src_v: *const Result.Value) void { switch (src_v.*) { - .Pointer => |src_ptr| switch (src_ptr) { + .Pointer => |src_ptr| switch (src_ptr.ptr) { .f32_ptr => |src_f32_ptr| dst_f32_ptr.* = src_f32_ptr.*, .common => |src_val_ptr| switch (src_val_ptr.*) { .Float => |f| dst_f32_ptr.* = f.value.float32, @@ -967,7 +966,7 @@ fn copyValue(dst: *Result.Value, src: *const Result.Value) void { fn writeI32(dst_i32_ptr: *i32, src_v: *const Result.Value) void { switch (src_v.*) { - .Pointer => |src_ptr| switch (src_ptr) { + .Pointer => |src_ptr| switch (src_ptr.ptr) { .i32_ptr => |src_i32_ptr| dst_i32_ptr.* = src_i32_ptr.*, .common => |src_val_ptr| switch (src_val_ptr.*) { .Int => |i| dst_i32_ptr.* = i.value.sint32, @@ -982,7 +981,7 @@ fn copyValue(dst: *Result.Value, src: *const Result.Value) void { fn writeU32(dst_u32_ptr: *u32, src_v: *const Result.Value) void { switch (src_v.*) { - .Pointer => |src_ptr| switch (src_ptr) { + .Pointer => |src_ptr| switch (src_ptr.ptr) { .u32_ptr => |src_u32_ptr| dst_u32_ptr.* = src_u32_ptr.*, .common => |src_val_ptr| switch (src_val_ptr.*) { .Int => |i| dst_u32_ptr.* = i.value.uint32, @@ -997,9 +996,9 @@ fn copyValue(dst: *Result.Value, src: *const Result.Value) void { }; if (std.meta.activeTag(dst.*) == .Pointer) { - switch (dst.Pointer) { + switch (dst.Pointer.ptr) { .common => |dst_val_ptr| return switch (src.*) { - .Pointer => |src_ptr| switch (src_ptr) { + .Pointer => |src_ptr| switch (src_ptr.ptr) { .common => |src_val_ptr| copyValue(dst_val_ptr, src_val_ptr), else => dst_val_ptr.* = src.*, }, @@ -1021,7 +1020,7 @@ fn copyValue(dst: *Result.Value, src: *const Result.Value) void { } if (std.meta.activeTag(src.*) == .Pointer) { - switch (src.Pointer) { + switch (src.Pointer.ptr) { .common => |src_val_ptr| { copyValue(dst, src_val_ptr); return; @@ -1036,16 +1035,13 @@ fn copyValue(dst: *Result.Value, src: *const Result.Value) void { .Vector, .Matrix, .Array, .Structure => |src_slice| { helpers.copySlice(dst_slice.?, src_slice); }, - .RuntimeArray => |opt_src_slice| if (opt_src_slice) |src_slice| { - helpers.copySlice(dst_slice.?, src_slice); - } else unreachable, else => dst.* = src.*, } } pub fn getValuePrimitiveField(comptime T: ValueType, comptime BitCount: SpvWord, v: *Result.Value) RuntimeError!*getValuePrimitiveFieldType(T, BitCount) { if (std.meta.activeTag(v.*) == .Pointer) { - return switch (v.Pointer) { + return switch (v.Pointer.ptr) { .common => |value| getValuePrimitiveField(T, BitCount, value), .f32_ptr => |ptr| @ptrCast(@alignCast(ptr)), .u32_ptr => |ptr| @ptrCast(@alignCast(ptr)), @@ -1078,7 +1074,7 @@ pub fn getValuePrimitiveFieldType(comptime T: ValueType, comptime BitCount: SpvW }; } -fn opAccessChain(_: std.mem.Allocator, word_count: SpvWord, rt: *Runtime) RuntimeError!void { +fn opAccessChain(allocator: std.mem.Allocator, word_count: SpvWord, rt: *Runtime) RuntimeError!void { const var_type = try rt.it.next(); const id = try rt.it.next(); const base_id = try rt.it.next(); @@ -1086,12 +1082,29 @@ fn opAccessChain(_: std.mem.Allocator, word_count: SpvWord, rt: *Runtime) Runtim const base = &rt.results[base_id]; var value_ptr = try base.getValue(); + var arena = std.heap.ArenaAllocator.init(allocator); + defer arena.deinit(); + + const arena_allocator = arena.allocator(); + const index_count = word_count - 3; + + if (rt.results[id].variant) |*variant| { + switch (variant.*) { + .AccessChain => |*a| try a.value.flushPtr(allocator), + else => {}, + } + } + rt.results[id].variant = .{ .AccessChain = .{ .target = var_type, .value = blk: { - for (0..index_count) |_| { + var runtime_array_window: ?[]u8 = null; + + for (0..index_count) |index| { + const is_last = (index == index_count - 1); + const member = &rt.results[try rt.it.next()]; const member_value = switch ((try member.getVariant()).*) { .Constant => |c| &c.value, @@ -1102,7 +1115,7 @@ fn opAccessChain(_: std.mem.Allocator, word_count: SpvWord, rt: *Runtime) Runtim switch (member_value.*) { .Int => |i| { if (std.meta.activeTag(value_ptr.*) == .Pointer) { - value_ptr = value_ptr.Pointer.common; // Don't know if I should check for specialized pointers + value_ptr = value_ptr.Pointer.ptr.common; // Don't know if I should check for specialized pointers } switch (value_ptr.*) { @@ -1110,45 +1123,54 @@ fn opAccessChain(_: std.mem.Allocator, word_count: SpvWord, rt: *Runtime) Runtim if (i.value.uint32 >= v.len) return RuntimeError.OutOfBounds; value_ptr = &v[i.value.uint32]; }, - .RuntimeArray => |opt_a| if (opt_a) |a| { - if (i.value.uint32 >= a.len) return RuntimeError.OutOfBounds; - value_ptr = &a[i.value.uint32]; - } else return RuntimeError.InvalidSpirV, + .RuntimeArray => |*arr| { + const concrete_allocator = if (is_last) allocator else arena_allocator; + const type_size = (try rt.results[arr.type_word].getVariant()).Type.getSize(rt.results); + + value_ptr = concrete_allocator.create(Result.Value) catch return RuntimeError.OutOfMemory; + errdefer concrete_allocator.destroy(value_ptr); + + value_ptr.* = try Result.Value.init(concrete_allocator, rt.results, arr.type_word); + _ = try value_ptr.writeConst(arr.data[(type_size * i.value.uint32)..]); + + if (is_last) + runtime_array_window = arr.data[(type_size * i.value.uint32)..]; + }, .Vector4f32 => |*v| { if (i.value.uint32 > 4) return RuntimeError.OutOfBounds; - break :blk .{ .Pointer = .{ .f32_ptr = &v[i.value.uint32] } }; + break :blk .{ .Pointer = .{ .ptr = .{ .f32_ptr = &v[i.value.uint32] } } }; }, .Vector3f32 => |*v| { if (i.value.uint32 > 3) return RuntimeError.OutOfBounds; - break :blk .{ .Pointer = .{ .f32_ptr = &v[i.value.uint32] } }; + break :blk .{ .Pointer = .{ .ptr = .{ .f32_ptr = &v[i.value.uint32] } } }; }, .Vector2f32 => |*v| { if (i.value.uint32 > 2) return RuntimeError.OutOfBounds; - break :blk .{ .Pointer = .{ .f32_ptr = &v[i.value.uint32] } }; + break :blk .{ .Pointer = .{ .ptr = .{ .f32_ptr = &v[i.value.uint32] } } }; }, .Vector4i32 => |*v| { if (i.value.uint32 > 4) return RuntimeError.OutOfBounds; - break :blk .{ .Pointer = .{ .i32_ptr = &v[i.value.uint32] } }; + break :blk .{ .Pointer = .{ .ptr = .{ .i32_ptr = &v[i.value.uint32] } } }; }, .Vector3i32 => |*v| { if (i.value.uint32 > 3) return RuntimeError.OutOfBounds; - break :blk .{ .Pointer = .{ .i32_ptr = &v[i.value.uint32] } }; + break :blk .{ .Pointer = .{ .ptr = .{ .i32_ptr = &v[i.value.uint32] } } }; }, .Vector2i32 => |*v| { if (i.value.uint32 > 2) return RuntimeError.OutOfBounds; - break :blk .{ .Pointer = .{ .i32_ptr = &v[i.value.uint32] } }; + break :blk .{ .Pointer = .{ .ptr = .{ .i32_ptr = &v[i.value.uint32] } } }; }, .Vector4u32 => |*v| { if (i.value.uint32 > 4) return RuntimeError.OutOfBounds; - break :blk .{ .Pointer = .{ .u32_ptr = &v[i.value.uint32] } }; + break :blk .{ .Pointer = .{ .ptr = .{ .u32_ptr = &v[i.value.uint32] } } }; }, .Vector3u32 => |*v| { if (i.value.uint32 > 3) return RuntimeError.OutOfBounds; - break :blk .{ .Pointer = .{ .u32_ptr = &v[i.value.uint32] } }; + break :blk .{ .Pointer = .{ .ptr = .{ .u32_ptr = &v[i.value.uint32] } } }; }, .Vector2u32 => |*v| { if (i.value.uint32 > 2) return RuntimeError.OutOfBounds; - break :blk .{ .Pointer = .{ .u32_ptr = &v[i.value.uint32] } }; + break :blk .{ .Pointer = .{ .ptr = .{ .u32_ptr = &v[i.value.uint32] } } }; }, else => return RuntimeError.InvalidSpirV, } @@ -1156,7 +1178,12 @@ fn opAccessChain(_: std.mem.Allocator, word_count: SpvWord, rt: *Runtime) Runtim else => return RuntimeError.InvalidSpirV, } } - break :blk .{ .Pointer = .{ .common = value_ptr } }; + break :blk .{ + .Pointer = .{ + .ptr = .{ .common = value_ptr }, + .runtime_array_window = runtime_array_window, + }, + }; }, }, }; @@ -1206,6 +1233,16 @@ fn opCompositeConstruct(_: std.mem.Allocator, word_count: SpvWord, rt: *Runtime) } switch (value.*) { + .RuntimeArray => |arr| { + const type_size = (try rt.results[arr.type_word].getVariant()).Type.getSize(rt.results); + var offset: usize = 0; + + for (0..index_count) |_| { + const elem_value = (try rt.results[try rt.it.next()].getVariant()).Constant.value; + std.mem.copyForwards(u8, arr.data[offset..(offset + type_size)], std.mem.asBytes(&elem_value)); + offset += type_size; + } + }, .Vector4f32 => |*vec| inline for (0..4) |i| { vec[i] = (try rt.results[try rt.it.next()].getVariant()).Constant.value.Float.value.float32; }, @@ -1233,7 +1270,7 @@ fn opCompositeConstruct(_: std.mem.Allocator, word_count: SpvWord, rt: *Runtime) .Vector2u32 => |*vec| inline for (0..2) |i| { vec[i] = (try rt.results[try rt.it.next()].getVariant()).Constant.value.Int.value.uint32; }, - else => return RuntimeError.InvalidSpirV, + else => return RuntimeError.InvalidValueType, } } @@ -1243,6 +1280,11 @@ fn opCompositeExtract(allocator: std.mem.Allocator, word_count: SpvWord, rt: *Ru const composite_id = try rt.it.next(); const index_count = word_count - 3; + var arena = std.heap.ArenaAllocator.init(allocator); + defer arena.deinit(); + + const arena_allocator = arena.allocator(); + rt.results[id].variant = .{ .Constant = .{ .type_word = res_type, @@ -1259,6 +1301,11 @@ fn opCompositeExtract(allocator: std.mem.Allocator, word_count: SpvWord, rt: *Ru continue; } switch (composite) { + .RuntimeArray => |arr| { + const type_size = (try rt.results[arr.type_word].getVariant()).Type.getSize(rt.results); + composite = try Result.Value.init(arena_allocator, rt.results, arr.type_word); + _ = try composite.writeConst(arr.data[(type_size * member_id)..]); + }, .Vector4f32 => |v| break :blk .{ .Float = .{ .bit_count = 32, .value = .{ .float32 = v[member_id] } } }, .Vector3f32 => |v| break :blk .{ .Float = .{ .bit_count = 32, .value = .{ .float32 = v[member_id] } } }, .Vector2f32 => |v| break :blk .{ .Float = .{ .bit_count = 32, .value = .{ .float32 = v[member_id] } } }, @@ -1268,7 +1315,7 @@ fn opCompositeExtract(allocator: std.mem.Allocator, word_count: SpvWord, rt: *Ru .Vector4u32 => |v| break :blk .{ .Int = .{ .bit_count = 32, .value = .{ .uint32 = v[member_id] } } }, .Vector3u32 => |v| break :blk .{ .Int = .{ .bit_count = 32, .value = .{ .uint32 = v[member_id] } } }, .Vector2u32 => |v| break :blk .{ .Int = .{ .bit_count = 32, .value = .{ .uint32 = v[member_id] } } }, - else => return RuntimeError.InvalidSpirV, + else => return RuntimeError.InvalidValueType, } } break :blk try composite.dupe(allocator); diff --git a/test/root.zig b/test/root.zig index 1c9f407..d437451 100644 --- a/test/root.zig +++ b/test/root.zig @@ -24,7 +24,7 @@ pub const case = struct { source: []const u32, inputs: []const []const u8 = &.{}, expected_outputs: []const []const u8 = &.{}, - descriptor_sets: []const []const []const u8 = &.{}, + descriptor_sets: []const []const []u8 = &.{}, expected_descriptor_sets: []const []const []const u8 = &.{}, }; @@ -54,11 +54,12 @@ pub const case = struct { for (config.descriptor_sets, 0..) |descriptor_set, set_index| { for (descriptor_set, 0..) |descriptor_binding, binding_index| { - try rt.writeDescriptorSet(allocator, descriptor_binding, @intCast(set_index), @intCast(binding_index)); + try rt.writeDescriptorSet(descriptor_binding, @intCast(set_index), @intCast(binding_index)); } } try rt.callEntryPoint(allocator, try rt.getEntryPointByName("main")); + try rt.flushDescriptorSets(allocator); for (config.expected_outputs, 0..) |expected, n| { const output = try allocator.alloc(u8, expected.len); @@ -68,13 +69,9 @@ pub const case = struct { try std.testing.expectEqualSlices(u8, expected, output); } - for (config.expected_descriptor_sets, 0..) |expected_descriptor_set, set_index| { - for (expected_descriptor_set, 0..) |expected_descriptor_binding, binding_index| { - const data = try allocator.alloc(u8, expected_descriptor_binding.len); - defer allocator.free(data); - - try rt.readDescriptorSet(data, @intCast(set_index), @intCast(binding_index)); - try std.testing.expectEqualSlices(u8, expected_descriptor_binding, data); + for (config.expected_descriptor_sets, config.descriptor_sets) |expected_descriptor_set, descriptor_set| { + for (expected_descriptor_set, descriptor_set) |expected_descriptor_binding, descriptor_binding| { + try std.testing.expectEqualSlices(u8, expected_descriptor_binding, descriptor_binding); } } } diff --git a/test/ssbo.zig b/test/ssbo.zig index 47f82b6..7f9bae7 100644 --- a/test/ssbo.zig +++ b/test/ssbo.zig @@ -33,6 +33,8 @@ test "Simple SSBO" { const code = try compileNzsl(allocator, shader); defer allocator.free(code); + var ssbo = [_]u32{0} ** 256; + var expected = [_]u32{0} ** 256; for (expected[0..], 0..) |*val, i| { val.* = @intCast(i); @@ -44,7 +46,7 @@ test "Simple SSBO" { // Set 0 &.{ // Binding 0 - std.mem.asBytes(&[_]u32{0} ** 256), + std.mem.asBytes(&ssbo), }, }, .expected_descriptor_sets = &.{