skip CI
All checks were successful
Build / build (push) Successful in 5m9s
Test / build (push) Successful in 10m43s

This commit is contained in:
2026-03-29 00:48:33 +01:00
parent 3c7054fae0
commit fbaf85a849
2 changed files with 95 additions and 61 deletions

View File

@@ -70,16 +70,22 @@ pub const Value = union(Type) {
stride: SpvWord,
data: []u8,
pub inline fn createValueFromIndex(self: *const @This(), allocator: std.mem.Allocator, results: []Result, index: usize) RuntimeError!*Value {
pub inline fn createValueFromIndex(self: *const @This(), allocator: std.mem.Allocator, results: []const Result, index: usize) RuntimeError!*Value {
const value = allocator.create(Value) catch return RuntimeError.OutOfMemory;
errdefer allocator.destroy(value);
value.* = try Value.init(allocator, results, self.type_word);
value.* = try Value.init(allocator, results, self.type_word, false);
_ = try value.writeConst(self.data[self.getOffsetOfIndex(index)..]);
return value;
}
pub inline fn createLocalValueFromIndex(self: *const @This(), allocator: std.mem.Allocator, results: []const Result, index: usize) RuntimeError!Value {
var value = try Value.init(allocator, results, self.type_word, false);
_ = try value.writeConst(self.data[self.getOffsetOfIndex(index)..]);
return value;
}
pub inline fn getOffsetOfIndex(self: *const @This(), index: usize) usize {
return self.stride * index;
}
@@ -116,7 +122,7 @@ pub const Value = union(Type) {
};
}
pub fn init(allocator: std.mem.Allocator, results: []const Result, target_type: SpvWord) RuntimeError!Self {
pub fn init(allocator: std.mem.Allocator, results: []const Result, target_type: SpvWord, is_externally_visible: bool) RuntimeError!Self {
const resolved = results[target_type].resolveType(results);
const member_count = resolved.getMemberCounts();
@@ -137,7 +143,7 @@ pub const Value = union(Type) {
errdefer self.deinit(allocator);
for (self.Vector) |*value| {
value.* = try Self.init(allocator, results, v.components_type_word);
value.* = try Self.init(allocator, results, v.components_type_word, is_externally_visible);
}
break :blk self;
},
@@ -155,11 +161,22 @@ pub const Value = union(Type) {
errdefer self.deinit(allocator);
for (self.Matrix) |*value| {
value.* = try Self.init(allocator, results, m.column_type_word);
value.* = try Self.init(allocator, results, m.column_type_word, is_externally_visible);
}
break :blk self;
},
.Array => |a| blk: {
// If an array is in externally visible storage we treat it as a runtime array
if (is_externally_visible) {
break :blk .{
.RuntimeArray = .{
.type_word = a.components_type_word,
.stride = a.stride,
.data = &.{},
},
};
}
var self: Self = .{
.Array = .{
.stride = a.stride,
@@ -169,7 +186,7 @@ pub const Value = union(Type) {
errdefer self.deinit(allocator);
for (self.Array.values) |*value| {
value.* = try Self.init(allocator, results, a.components_type_word);
value.* = try Self.init(allocator, results, a.components_type_word, is_externally_visible);
}
break :blk self;
},
@@ -183,7 +200,7 @@ pub const Value = union(Type) {
errdefer self.deinit(allocator);
for (self.Structure.values, s.members_type_word) |*value, type_word| {
value.* = try Self.init(allocator, results, type_word);
value.* = try Self.init(allocator, results, type_word, is_externally_visible);
}
break :blk self;
},

View File

@@ -1150,11 +1150,11 @@ fn opBitcast(allocator: std.mem.Allocator, _: SpvWord, rt: *Runtime) RuntimeErro
_ = try to_value.write(bytes);
}
fn copyValue(dst: *Value, src: *const Value) void {
fn copyValue(dst: *Value, src: *const Value) RuntimeError!void {
const helpers = struct {
inline fn copySlice(dst_slice: []Value, src_slice: []const Value) void {
inline fn copySlice(dst_slice: []Value, src_slice: []const Value) RuntimeError!void {
for (0..@min(dst_slice.len, src_slice.len)) |i| {
copyValue(&dst_slice[i], &src_slice[i]);
try copyValue(&dst_slice[i], &src_slice[i]);
}
}
@@ -1167,75 +1167,75 @@ fn copyValue(dst: *Value, src: *const Value) void {
};
}
inline fn writeF32(dst_f32_ptr: *f32, src_v: *const Value) void {
inline fn writeF32(dst_f32_ptr: *f32, src_v: *const Value) RuntimeError!void {
switch (src_v.*) {
.Pointer => |src_ptr| switch (src_ptr.ptr) {
.f32_ptr => |src_f32_ptr| dst_f32_ptr.* = src_f32_ptr.*,
.common => |src_val_ptr| dst_f32_ptr.* = src_val_ptr.Float.value.float32,
else => unreachable,
else => return RuntimeError.InvalidSpirV,
},
.Float => |f| dst_f32_ptr.* = f.value.float32,
else => unreachable,
else => return RuntimeError.InvalidSpirV,
}
}
inline fn writeI32(dst_i32_ptr: *i32, src_v: *const Value) void {
inline fn writeI32(dst_i32_ptr: *i32, src_v: *const Value) RuntimeError!void {
switch (src_v.*) {
.Pointer => |src_ptr| switch (src_ptr.ptr) {
.i32_ptr => |src_i32_ptr| dst_i32_ptr.* = src_i32_ptr.*,
.common => |src_val_ptr| dst_i32_ptr.* = src_val_ptr.Int.value.sint32,
else => unreachable,
else => return RuntimeError.InvalidSpirV,
},
.Int => |i| dst_i32_ptr.* = i.value.sint32,
else => unreachable,
else => return RuntimeError.InvalidSpirV,
}
}
inline fn writeU32(dst_u32_ptr: *u32, src_v: *const Value) void {
inline fn writeU32(dst_u32_ptr: *u32, src_v: *const Value) RuntimeError!void {
switch (src_v.*) {
.Pointer => |src_ptr| switch (src_ptr.ptr) {
.u32_ptr => |src_u32_ptr| dst_u32_ptr.* = src_u32_ptr.*,
.common => |src_val_ptr| dst_u32_ptr.* = src_val_ptr.Int.value.uint32,
else => unreachable,
else => return RuntimeError.InvalidSpirV,
},
.Int => |i| dst_u32_ptr.* = i.value.uint32,
else => unreachable,
else => return RuntimeError.InvalidSpirV,
}
}
inline fn readF32(dst_v: *Value, src_f32_ptr: *const f32) void {
inline fn readF32(dst_v: *Value, src_f32_ptr: *const f32) RuntimeError!void {
switch (dst_v.*) {
.Pointer => |dst_ptr| switch (dst_ptr.ptr) {
.f32_ptr => |dst_f32_ptr| dst_f32_ptr.* = src_f32_ptr.*,
.common => |dst_val_ptr| dst_val_ptr.Float.value.float32 = src_f32_ptr.*,
else => unreachable,
else => return RuntimeError.InvalidSpirV,
},
.Float => |*f| f.value.float32 = src_f32_ptr.*,
else => unreachable,
else => return RuntimeError.InvalidSpirV,
}
}
inline fn readI32(dst_v: *Value, src_i32_ptr: *const i32) void {
inline fn readI32(dst_v: *Value, src_i32_ptr: *const i32) RuntimeError!void {
switch (dst_v.*) {
.Pointer => |dst_ptr| switch (dst_ptr.ptr) {
.i32_ptr => |dst_i32_ptr| dst_i32_ptr.* = src_i32_ptr.*,
.common => |dst_val_ptr| dst_val_ptr.Int.value.sint32 = src_i32_ptr.*,
else => unreachable,
else => return RuntimeError.InvalidSpirV,
},
.Int => |*i| i.value.sint32 = src_i32_ptr.*,
else => unreachable,
else => return RuntimeError.InvalidSpirV,
}
}
inline fn readU32(dst_v: *Value, src_u32_ptr: *const u32) void {
inline fn readU32(dst_v: *Value, src_u32_ptr: *const u32) RuntimeError!void {
switch (dst_v.*) {
.Pointer => |dst_ptr| switch (dst_ptr.ptr) {
.u32_ptr => |dst_u32_ptr| dst_u32_ptr.* = src_u32_ptr.*,
.common => |dst_val_ptr| dst_val_ptr.Int.value.uint32 = src_u32_ptr.*,
else => unreachable,
else => return RuntimeError.InvalidSpirV,
},
.Int => |*i| i.value.uint32 = src_u32_ptr.*,
else => unreachable,
else => return RuntimeError.InvalidSpirV,
}
}
};
@@ -1244,35 +1244,46 @@ fn copyValue(dst: *Value, src: *const Value) void {
switch (dst.Pointer.ptr) {
.common => |dst_val_ptr| return switch (src.*) {
.Pointer => |src_ptr| switch (src_ptr.ptr) {
.common => |src_val_ptr| copyValue(dst_val_ptr, src_val_ptr),
.common => |src_val_ptr| try copyValue(dst_val_ptr, src_val_ptr),
else => dst_val_ptr.* = src.*,
},
else => copyValue(dst_val_ptr, src),
else => try copyValue(dst_val_ptr, src),
},
.f32_ptr => |dst_f32_ptr| helpers.writeF32(dst_f32_ptr, src),
.i32_ptr => |dst_i32_ptr| helpers.writeI32(dst_i32_ptr, src),
.u32_ptr => |dst_u32_ptr| helpers.writeU32(dst_u32_ptr, src),
.f32_ptr => |dst_f32_ptr| try helpers.writeF32(dst_f32_ptr, src),
.i32_ptr => |dst_i32_ptr| try helpers.writeI32(dst_i32_ptr, src),
.u32_ptr => |dst_u32_ptr| try helpers.writeU32(dst_u32_ptr, src),
}
}
switch (src.*) {
.Vector, .Matrix => |src_slice| {
const dst_slice = helpers.getDstSlice(dst);
helpers.copySlice(dst_slice.?, src_slice);
try helpers.copySlice(dst_slice.?, src_slice);
},
.Array => |a| {
const dst_slice = helpers.getDstSlice(dst);
helpers.copySlice(dst_slice.?, a.values);
try helpers.copySlice(dst_slice.?, a.values);
},
.Structure => |s| {
const dst_slice = helpers.getDstSlice(dst);
helpers.copySlice(dst_slice.?, s.values);
try helpers.copySlice(dst_slice.?, s.values);
},
.Pointer => |ptr| switch (ptr.ptr) {
.common => |src_val_ptr| copyValue(dst, src_val_ptr),
.f32_ptr => |src_f32_ptr| helpers.readF32(dst, src_f32_ptr),
.i32_ptr => |src_i32_ptr| helpers.readI32(dst, src_i32_ptr),
.u32_ptr => |src_u32_ptr| helpers.readU32(dst, src_u32_ptr),
.common => |src_val_ptr| try copyValue(dst, src_val_ptr),
.f32_ptr => |src_f32_ptr| try helpers.readF32(dst, src_f32_ptr),
.i32_ptr => |src_i32_ptr| try helpers.readI32(dst, src_i32_ptr),
.u32_ptr => |src_u32_ptr| try helpers.readU32(dst, src_u32_ptr),
},
.RuntimeArray => |src_arr| switch (dst.*) {
.RuntimeArray => |dst_arr| @memcpy(dst_arr.data, src_arr.data),
.Pointer => |dst_ptr| switch (dst_ptr.ptr) {
.common => |dst_ptr_ptr| switch (dst_ptr_ptr.*) {
.RuntimeArray => |dst_arr| @memcpy(dst_arr.data, src_arr.data),
else => return RuntimeError.InvalidSpirV,
},
else => return RuntimeError.InvalidSpirV,
},
else => return RuntimeError.InvalidSpirV,
},
else => dst.* = src.*,
}
@@ -1541,10 +1552,7 @@ fn opCompositeExtract(allocator: std.mem.Allocator, word_count: SpvWord, rt: *Ru
continue;
}
switch (composite) {
.RuntimeArray => |arr| {
composite = try Value.init(arena_allocator, rt.results, arr.type_word);
_ = try composite.writeConst(arr.data[arr.getOffsetOfIndex(member_id)..]);
},
.RuntimeArray => |arr| composite = try arr.createLocalValueFromIndex(arena_allocator, rt.results, 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] } } },
@@ -1571,7 +1579,7 @@ fn opCompositeInsert(allocator: std.mem.Allocator, word_count: SpvWord, rt: *Run
const target = try rt.results[id].getValue();
copyValue(target, composite);
try copyValue(target, composite);
const index_count = word_count - 4;
@@ -1587,7 +1595,7 @@ fn opCompositeInsert(allocator: std.mem.Allocator, word_count: SpvWord, rt: *Run
indices: []const SpvWord,
) RuntimeError!void {
if (indices.len == 0) {
copyValue(current, object_value);
try copyValue(current, object_value);
return;
}
@@ -1614,8 +1622,7 @@ fn opCompositeInsert(allocator: std.mem.Allocator, word_count: SpvWord, rt: *Run
return;
}
var elem_value = try Value.init(alloc, results, arr.type_word);
_ = try elem_value.writeConst(arr.data[elem_offset..]);
var elem_value = try arr.createLocalValueFromIndex(alloc, results, elem_offset);
try insertAt(alloc, results, &elem_value, object_value, indices[1..]);
_ = try elem_value.read(arr.data[elem_offset..]);
},
@@ -1700,7 +1707,7 @@ fn opConstantComposite(allocator: std.mem.Allocator, _: SpvWord, rt: *Runtime) R
const target_value = try target.getValue();
if (target_value.getCompositeDataOrNull()) |*values| {
for (values.*) |*element| {
copyValue(element, try rt.mod.results[try rt.it.next()].getValue());
try copyValue(element, try rt.mod.results[try rt.it.next()].getValue());
}
return;
}
@@ -1740,7 +1747,7 @@ fn opConstantComposite(allocator: std.mem.Allocator, _: 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());
try copyValue(try rt.results[target].getValue(), try rt.results[source].getValue());
}
fn opDecorate(allocator: std.mem.Allocator, _: SpvWord, rt: *Runtime) RuntimeError!void {
@@ -2000,7 +2007,7 @@ fn opLoad(_: std.mem.Allocator, _: SpvWord, rt: *Runtime) RuntimeError!void {
_ = rt.it.skip();
const id = try rt.it.next();
const ptr_id = try rt.it.next();
copyValue(try rt.results[id].getValue(), try rt.results[ptr_id].getValue());
try copyValue(try rt.results[id].getValue(), try rt.results[ptr_id].getValue());
}
fn opMemberName(allocator: std.mem.Allocator, word_count: SpvWord, rt: *Runtime) RuntimeError!void {
@@ -2061,7 +2068,7 @@ fn opReturn(_: std.mem.Allocator, _: SpvWord, rt: *Runtime) RuntimeError!void {
fn opReturnValue(_: std.mem.Allocator, _: SpvWord, rt: *Runtime) RuntimeError!void {
if (rt.function_stack.getLastOrNull()) |function| {
var ret_res = rt.results[try rt.it.next()];
copyValue(try function.ret.getValue(), try ret_res.getValue());
try copyValue(try function.ret.getValue(), try ret_res.getValue());
} else {
return RuntimeError.InvalidSpirV; // No current function ???
}
@@ -2095,13 +2102,13 @@ fn opSelect(_: std.mem.Allocator, _: SpvWord, rt: *Runtime) RuntimeError!void {
obj1_val.getCompositeDataOrNull().?,
obj2_val.getCompositeDataOrNull().?,
) |*t, c, o1, o2| {
copyValue(t, if (c.Bool) &o1 else &o2);
try copyValue(t, if (c.Bool) &o1 else &o2);
}
return;
}
switch (target_val.*) {
.Bool, .Int, .Float => copyValue(target_val, if (cond_val.Bool) obj1_val else obj2_val),
.Bool, .Int, .Float => try copyValue(target_val, if (cond_val.Bool) obj1_val else obj2_val),
.Vector4f32 => |*v| {
const cond_vec = @Vector(4, bool){
@@ -2188,7 +2195,7 @@ fn opSourceExtension(allocator: std.mem.Allocator, word_count: SpvWord, rt: *Run
fn opStore(_: std.mem.Allocator, _: SpvWord, rt: *Runtime) RuntimeError!void {
const ptr_id = try rt.it.next();
const val_id = try rt.it.next();
copyValue(try rt.results[ptr_id].getValue(), try rt.results[val_id].getValue());
try copyValue(try rt.results[ptr_id].getValue(), try rt.results[val_id].getValue());
}
fn opTypeArray(_: std.mem.Allocator, _: SpvWord, rt: *Runtime) RuntimeError!void {
@@ -2425,15 +2432,25 @@ fn opVariable(allocator: std.mem.Allocator, word_count: SpvWord, rt: *Runtime) R
const resolved_word = if (rt.mod.results[var_type].resolveTypeWordOrNull()) |word| word else var_type;
const resolved = &rt.mod.results[resolved_word];
const resolved_type = switch ((try resolved.getConstVariant()).*) {
.Type => |t| @as(Result.Type, t),
else => return RuntimeError.InvalidSpirV,
};
const externally_visible_data_storages = [_]spv.SpvStorageClass{
.Uniform,
.StorageBuffer,
};
const is_externally_visible = std.mem.containsAtLeastScalar(spv.SpvStorageClass, &externally_visible_data_storages, 1, storage_class);
target.variant = .{
.Variable = .{
.storage_class = storage_class,
.type_word = resolved_word,
.type = switch ((try resolved.getConstVariant()).*) {
.Type => |t| @as(Result.Type, t),
else => return RuntimeError.InvalidSpirV,
},
.value = try Value.init(allocator, rt.mod.results, resolved_word),
.type = resolved_type,
.value = try Value.init(allocator, rt.mod.results, resolved_word, is_externally_visible),
},
};
@@ -2476,7 +2493,7 @@ fn setupConstant(allocator: std.mem.Allocator, rt: *Runtime) RuntimeError!*Resul
const resolved = rt.mod.results[res_type].resolveType(rt.mod.results);
target.variant = .{
.Constant = .{
.value = try Value.init(allocator, rt.mod.results, res_type),
.value = try Value.init(allocator, rt.mod.results, res_type, false),
.type_word = res_type,
.type = switch ((try resolved.getConstVariant()).*) {
.Type => |t| @as(Result.Type, t),