ugly implementation of new runtime arrays management
All checks were successful
Build / build (push) Successful in 56s
Test / build (push) Successful in 4m54s

This commit is contained in:
2026-03-08 02:25:25 +01:00
parent 16eb184808
commit ad013d23fc
6 changed files with 367 additions and 337 deletions

View File

@@ -35,12 +35,12 @@ pub fn main() !void {
}; };
try rt.writeBuiltIn(std.mem.asBytes(&global_invocation_indices), .GlobalInvocationId); 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) { rt.callEntryPoint(allocator, entry) catch |err| switch (err) {
spv.Runtime.RuntimeError.OutOfBounds => continue, spv.Runtime.RuntimeError.OutOfBounds => continue,
else => return err, else => return err,
}; };
try rt.readDescriptorSet(std.mem.asBytes(&ssbo), 0, 0); try rt.flushDescriptorSets(allocator);
} }
} }

View File

@@ -113,28 +113,33 @@ pub const Value = union(Type) {
Vector2u32: Vec2u32, Vector2u32: Vec2u32,
Matrix: []Value, Matrix: []Value,
Array: []Value, Array: []Value,
RuntimeArray: ?[]Value, RuntimeArray: struct {
type_word: SpvWord,
data: []u8,
},
Structure: []Value, Structure: []Value,
Function: noreturn, Function: noreturn,
Image: struct {}, Image: struct {},
Sampler: struct {}, Sampler: struct {},
SampledImage: struct {}, SampledImage: struct {},
Pointer: union(enum) { Pointer: struct {
ptr: union(enum) {
common: *Value, common: *Value,
f32_ptr: *f32, f32_ptr: *f32,
i32_ptr: *i32, //< For vector specializations i32_ptr: *i32, //< For vector specializations
u32_ptr: *u32, u32_ptr: *u32,
}, },
runtime_array_window: ?[]u8 = null,
},
pub inline fn getCompositeDataOrNull(self: *const Value) ?[]Value { pub inline fn getCompositeDataOrNull(self: *const Value) ?[]Value {
return switch (self.*) { return switch (self.*) {
.Vector, .Matrix, .Array, .Structure => |v| v, .Vector, .Matrix, .Array, .Structure => |v| v,
.RuntimeArray => |v| v,
else => null, 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 resolved = results[target].resolveType(results);
const member_count = resolved.getMemberCounts(); const member_count = resolved.getMemberCounts();
@@ -194,7 +199,12 @@ pub const Value = union(Type) {
} }
break :blk self; break :blk self;
}, },
.RuntimeArray => .{ .RuntimeArray = null }, .RuntimeArray => |a| .{
.RuntimeArray = .{
.type_word = a.components_type_word,
.data = &.{},
},
},
else => unreachable, else => unreachable,
}, },
else => unreachable, else => unreachable,
@@ -225,17 +235,6 @@ pub const Value = union(Type) {
break :blk values; 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 => |s| .{
.Structure = blk: { .Structure = blk: {
const values = allocator.dupe(Value, s) catch return RuntimeError.OutOfMemory; 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 { fn deinit(self: *Value, allocator: std.mem.Allocator) void {
switch (self.*) { switch (self.*) {
.Vector, .Matrix, .Array, .Structure => |values| { .Vector, .Matrix, .Array, .Structure => |values| {
for (values) |*value| value.deinit(allocator); for (values) |*value| value.deinit(allocator);
allocator.free(values); allocator.free(values);
}, },
.RuntimeArray => |opt_values| if (opt_values) |values| {
for (values) |*value| value.deinit(allocator);
allocator.free(values);
},
else => {}, else => {},
} }
} }
@@ -412,6 +639,7 @@ pub fn deinit(self: *Self, allocator: std.mem.Allocator) void {
}, },
.Constant => |*c| c.value.deinit(allocator), .Constant => |*c| c.value.deinit(allocator),
.Variable => |*v| v.value.deinit(allocator), .Variable => |*v| v.value.deinit(allocator),
.AccessChain => |*a| a.value.deinit(allocator),
.Function => |f| allocator.free(f.params), .Function => |f| allocator.free(f.params),
else => {}, else => {},
} }
@@ -649,16 +877,11 @@ pub fn initValue(allocator: std.mem.Allocator, member_count: usize, results: []c
} }
break :blk value; break :blk value;
}, },
.RuntimeArray => |a| blk: { .RuntimeArray => |a| .{
if (member_count == 0) { .RuntimeArray = .{
break :blk Value{ .RuntimeArray = null }; .type_word = a.components_type_word,
} .data = &.{},
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;
}, },
.Structure => |s| blk: { .Structure => |s| blk: {
const value: Value = .{ .Structure = allocator.alloc(Value, member_count) catch return RuntimeError.OutOfMemory }; 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, 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 => {},
}
}
}

View File

@@ -151,43 +151,9 @@ pub fn callEntryPoint(self: *Self, allocator: std.mem.Allocator, entry_point_ind
//}) catch return RuntimeError.OutOfMemory; //}) 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) { 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); _ = try self.results[self.mod.bindings[set][binding]].variant.?.Variable.value.write(input);
} 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);
} else { } else {
return RuntimeError.NotFound; 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 { pub fn readOutput(self: *const Self, output: []u8, result: SpvWord) RuntimeError!void {
if (std.mem.indexOfScalar(SpvWord, &self.mod.output_locations, result)) |_| { 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 { } else {
return RuntimeError.NotFound; 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 { pub fn writeInput(self: *const Self, input: []const u8, result: SpvWord) RuntimeError!void {
if (std.mem.indexOfScalar(SpvWord, &self.mod.input_locations, result)) |_| { 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 { } else {
return RuntimeError.NotFound; 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 { pub fn writeBuiltIn(self: *const Self, input: []const u8, builtin: spv.SpvBuiltIn) RuntimeError!void {
if (self.mod.builtins.get(builtin)) |result| { 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 { } else {
return RuntimeError.NotFound; 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 { fn reset(self: *Self) void {
self.function_stack.clearRetainingCapacity(); self.function_stack.clearRetainingCapacity();
self.current_function = null; 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;
}

View File

@@ -945,14 +945,13 @@ fn copyValue(dst: *Result.Value, src: *const Result.Value) void {
fn getDstSlice(v: *Result.Value) ?[]Result.Value { fn getDstSlice(v: *Result.Value) ?[]Result.Value {
return switch (v.*) { return switch (v.*) {
.Vector, .Matrix, .Array, .Structure => |s| s, .Vector, .Matrix, .Array, .Structure => |s| s,
.RuntimeArray => |s| s,
else => null, else => null,
}; };
} }
fn writeF32(dst_f32_ptr: *f32, src_v: *const Result.Value) void { fn writeF32(dst_f32_ptr: *f32, src_v: *const Result.Value) void {
switch (src_v.*) { 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.*, .f32_ptr => |src_f32_ptr| dst_f32_ptr.* = src_f32_ptr.*,
.common => |src_val_ptr| switch (src_val_ptr.*) { .common => |src_val_ptr| switch (src_val_ptr.*) {
.Float => |f| dst_f32_ptr.* = f.value.float32, .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 { fn writeI32(dst_i32_ptr: *i32, src_v: *const Result.Value) void {
switch (src_v.*) { 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.*, .i32_ptr => |src_i32_ptr| dst_i32_ptr.* = src_i32_ptr.*,
.common => |src_val_ptr| switch (src_val_ptr.*) { .common => |src_val_ptr| switch (src_val_ptr.*) {
.Int => |i| dst_i32_ptr.* = i.value.sint32, .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 { fn writeU32(dst_u32_ptr: *u32, src_v: *const Result.Value) void {
switch (src_v.*) { 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.*, .u32_ptr => |src_u32_ptr| dst_u32_ptr.* = src_u32_ptr.*,
.common => |src_val_ptr| switch (src_val_ptr.*) { .common => |src_val_ptr| switch (src_val_ptr.*) {
.Int => |i| dst_u32_ptr.* = i.value.uint32, .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) { if (std.meta.activeTag(dst.*) == .Pointer) {
switch (dst.Pointer) { switch (dst.Pointer.ptr) {
.common => |dst_val_ptr| return switch (src.*) { .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), .common => |src_val_ptr| copyValue(dst_val_ptr, src_val_ptr),
else => dst_val_ptr.* = src.*, 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) { if (std.meta.activeTag(src.*) == .Pointer) {
switch (src.Pointer) { switch (src.Pointer.ptr) {
.common => |src_val_ptr| { .common => |src_val_ptr| {
copyValue(dst, src_val_ptr); copyValue(dst, src_val_ptr);
return; return;
@@ -1036,16 +1035,13 @@ fn copyValue(dst: *Result.Value, src: *const Result.Value) void {
.Vector, .Matrix, .Array, .Structure => |src_slice| { .Vector, .Matrix, .Array, .Structure => |src_slice| {
helpers.copySlice(dst_slice.?, 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.*, else => dst.* = src.*,
} }
} }
pub fn getValuePrimitiveField(comptime T: ValueType, comptime BitCount: SpvWord, v: *Result.Value) RuntimeError!*getValuePrimitiveFieldType(T, BitCount) { pub fn getValuePrimitiveField(comptime T: ValueType, comptime BitCount: SpvWord, v: *Result.Value) RuntimeError!*getValuePrimitiveFieldType(T, BitCount) {
if (std.meta.activeTag(v.*) == .Pointer) { if (std.meta.activeTag(v.*) == .Pointer) {
return switch (v.Pointer) { return switch (v.Pointer.ptr) {
.common => |value| getValuePrimitiveField(T, BitCount, value), .common => |value| getValuePrimitiveField(T, BitCount, value),
.f32_ptr => |ptr| @ptrCast(@alignCast(ptr)), .f32_ptr => |ptr| @ptrCast(@alignCast(ptr)),
.u32_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 var_type = try rt.it.next();
const id = try rt.it.next(); const id = try rt.it.next();
const base_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]; const base = &rt.results[base_id];
var value_ptr = try base.getValue(); 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; 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 = .{ rt.results[id].variant = .{
.AccessChain = .{ .AccessChain = .{
.target = var_type, .target = var_type,
.value = blk: { .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 = &rt.results[try rt.it.next()];
const member_value = switch ((try member.getVariant()).*) { const member_value = switch ((try member.getVariant()).*) {
.Constant => |c| &c.value, .Constant => |c| &c.value,
@@ -1102,7 +1115,7 @@ fn opAccessChain(_: std.mem.Allocator, word_count: SpvWord, rt: *Runtime) Runtim
switch (member_value.*) { switch (member_value.*) {
.Int => |i| { .Int => |i| {
if (std.meta.activeTag(value_ptr.*) == .Pointer) { 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.*) { 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; if (i.value.uint32 >= v.len) return RuntimeError.OutOfBounds;
value_ptr = &v[i.value.uint32]; value_ptr = &v[i.value.uint32];
}, },
.RuntimeArray => |opt_a| if (opt_a) |a| { .RuntimeArray => |*arr| {
if (i.value.uint32 >= a.len) return RuntimeError.OutOfBounds; const concrete_allocator = if (is_last) allocator else arena_allocator;
value_ptr = &a[i.value.uint32]; const type_size = (try rt.results[arr.type_word].getVariant()).Type.getSize(rt.results);
} else return RuntimeError.InvalidSpirV,
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| { .Vector4f32 => |*v| {
if (i.value.uint32 > 4) return RuntimeError.OutOfBounds; 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| { .Vector3f32 => |*v| {
if (i.value.uint32 > 3) return RuntimeError.OutOfBounds; 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| { .Vector2f32 => |*v| {
if (i.value.uint32 > 2) return RuntimeError.OutOfBounds; 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| { .Vector4i32 => |*v| {
if (i.value.uint32 > 4) return RuntimeError.OutOfBounds; 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| { .Vector3i32 => |*v| {
if (i.value.uint32 > 3) return RuntimeError.OutOfBounds; 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| { .Vector2i32 => |*v| {
if (i.value.uint32 > 2) return RuntimeError.OutOfBounds; 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| { .Vector4u32 => |*v| {
if (i.value.uint32 > 4) return RuntimeError.OutOfBounds; 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| { .Vector3u32 => |*v| {
if (i.value.uint32 > 3) return RuntimeError.OutOfBounds; 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| { .Vector2u32 => |*v| {
if (i.value.uint32 > 2) return RuntimeError.OutOfBounds; 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, else => return RuntimeError.InvalidSpirV,
} }
@@ -1156,7 +1178,12 @@ fn opAccessChain(_: std.mem.Allocator, word_count: SpvWord, rt: *Runtime) Runtim
else => return RuntimeError.InvalidSpirV, 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.*) { 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| { .Vector4f32 => |*vec| inline for (0..4) |i| {
vec[i] = (try rt.results[try rt.it.next()].getVariant()).Constant.value.Float.value.float32; 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| { .Vector2u32 => |*vec| inline for (0..2) |i| {
vec[i] = (try rt.results[try rt.it.next()].getVariant()).Constant.value.Int.value.uint32; 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 composite_id = try rt.it.next();
const index_count = word_count - 3; 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 = .{ rt.results[id].variant = .{
.Constant = .{ .Constant = .{
.type_word = res_type, .type_word = res_type,
@@ -1259,6 +1301,11 @@ fn opCompositeExtract(allocator: std.mem.Allocator, word_count: SpvWord, rt: *Ru
continue; continue;
} }
switch (composite) { 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] } } }, .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] } } }, .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] } } }, .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] } } }, .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] } } }, .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] } } }, .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); break :blk try composite.dupe(allocator);

View File

@@ -24,7 +24,7 @@ pub const case = struct {
source: []const u32, source: []const u32,
inputs: []const []const u8 = &.{}, inputs: []const []const u8 = &.{},
expected_outputs: []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 = &.{}, 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 (config.descriptor_sets, 0..) |descriptor_set, set_index| {
for (descriptor_set, 0..) |descriptor_binding, binding_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.callEntryPoint(allocator, try rt.getEntryPointByName("main"));
try rt.flushDescriptorSets(allocator);
for (config.expected_outputs, 0..) |expected, n| { for (config.expected_outputs, 0..) |expected, n| {
const output = try allocator.alloc(u8, expected.len); const output = try allocator.alloc(u8, expected.len);
@@ -68,13 +69,9 @@ pub const case = struct {
try std.testing.expectEqualSlices(u8, expected, output); try std.testing.expectEqualSlices(u8, expected, output);
} }
for (config.expected_descriptor_sets, 0..) |expected_descriptor_set, set_index| { for (config.expected_descriptor_sets, config.descriptor_sets) |expected_descriptor_set, descriptor_set| {
for (expected_descriptor_set, 0..) |expected_descriptor_binding, binding_index| { for (expected_descriptor_set, descriptor_set) |expected_descriptor_binding, descriptor_binding| {
const data = try allocator.alloc(u8, expected_descriptor_binding.len); try std.testing.expectEqualSlices(u8, expected_descriptor_binding, descriptor_binding);
defer allocator.free(data);
try rt.readDescriptorSet(data, @intCast(set_index), @intCast(binding_index));
try std.testing.expectEqualSlices(u8, expected_descriptor_binding, data);
} }
} }
} }

View File

@@ -33,6 +33,8 @@ test "Simple SSBO" {
const code = try compileNzsl(allocator, shader); const code = try compileNzsl(allocator, shader);
defer allocator.free(code); defer allocator.free(code);
var ssbo = [_]u32{0} ** 256;
var expected = [_]u32{0} ** 256; var expected = [_]u32{0} ** 256;
for (expected[0..], 0..) |*val, i| { for (expected[0..], 0..) |*val, i| {
val.* = @intCast(i); val.* = @intCast(i);
@@ -44,7 +46,7 @@ test "Simple SSBO" {
// Set 0 // Set 0
&.{ &.{
// Binding 0 // Binding 0
std.mem.asBytes(&[_]u32{0} ** 256), std.mem.asBytes(&ssbo),
}, },
}, },
.expected_descriptor_sets = &.{ .expected_descriptor_sets = &.{