adding helper invocation support
Build / build (push) Successful in 1m36s
Test / build (push) Successful in 10m21s

This commit is contained in:
2026-07-01 13:55:54 +02:00
parent 18f910f38a
commit 9b6f409bff
4 changed files with 113 additions and 18 deletions
+1 -1
View File
@@ -155,7 +155,7 @@ pub const TypeData = union(Type) {
.Int => |i| @divExact(i.bit_length, 8), .Int => |i| @divExact(i.bit_length, 8),
.Float => |f| @divExact(f.bit_length, 8), .Float => |f| @divExact(f.bit_length, 8),
.Vector => |v| results[v.components_type_word].variant.?.Type.getSize(results) * v.member_count, .Vector => |v| results[v.components_type_word].variant.?.Type.getSize(results) * v.member_count,
.Array => |a| a.stride, .Array => |a| a.stride * a.member_count,
.Matrix => |m| results[m.column_type_word].variant.?.Type.getSize(results) * m.member_count, .Matrix => |m| results[m.column_type_word].variant.?.Type.getSize(results) * m.member_count,
.RuntimeArray => |a| a.stride, .RuntimeArray => |a| a.stride,
.Structure => |s| blk: { .Structure => |s| blk: {
+13 -1
View File
@@ -127,6 +127,7 @@ active_entry_point: ?SpvWord,
specialization_constants: std.AutoHashMapUnmanaged(u32, []const u8), specialization_constants: std.AutoHashMapUnmanaged(u32, []const u8),
derivatives: std.AutoHashMapUnmanaged(SpvWord, Derivative), derivatives: std.AutoHashMapUnmanaged(SpvWord, Derivative),
phi_values: std.AutoHashMapUnmanaged(SpvWord, Value), phi_values: std.AutoHashMapUnmanaged(SpvWord, Value),
helper_invocation: bool,
image_api: ImageAPI, image_api: ImageAPI,
@@ -160,6 +161,7 @@ pub fn init(allocator: std.mem.Allocator, module: *Module, image_api: ImageAPI)
.specialization_constants = .empty, .specialization_constants = .empty,
.derivatives = .empty, .derivatives = .empty,
.phi_values = .empty, .phi_values = .empty,
.helper_invocation = false,
.image_api = image_api, .image_api = image_api,
}; };
errdefer self.deinit(allocator); errdefer self.deinit(allocator);
@@ -195,6 +197,7 @@ pub fn initFrom(allocator: std.mem.Allocator, other: *const Self, image_api: Ima
.specialization_constants = .empty, .specialization_constants = .empty,
.derivatives = .empty, .derivatives = .empty,
.phi_values = .empty, .phi_values = .empty,
.helper_invocation = other.helper_invocation,
.image_api = image_api, .image_api = image_api,
}; };
errdefer self.deinit(allocator); errdefer self.deinit(allocator);
@@ -300,6 +303,11 @@ fn refreshValueLayouts(self: *Self) RuntimeError!void {
} }
} }
pub fn refreshResultValueLayout(self: *Self, result: SpvWord) RuntimeError!void {
const type_word = try self.results[result].getValueTypeWord();
try applyValueLayout(self.results, try self.results[result].getValue(), type_word);
}
fn typePlainMemorySize(self: *const Self, type_word: SpvWord) RuntimeError!usize { fn typePlainMemorySize(self: *const Self, type_word: SpvWord) RuntimeError!usize {
const resolved_word = self.results[type_word].resolveTypeWordOrNull() orelse type_word; const resolved_word = self.results[type_word].resolveTypeWordOrNull() orelse type_word;
const target_type = (try self.results[resolved_word].getConstVariant()).Type; const target_type = (try self.results[resolved_word].getConstVariant()).Type;
@@ -769,7 +777,10 @@ pub fn populatePushConstants(self: *Self, blob: []const u8) RuntimeError!void {
pub fn writeDescriptorSet(self: *const Self, input: []const u8, set: SpvWord, binding: SpvWord, descriptor_index: SpvWord) RuntimeError!void { pub fn writeDescriptorSet(self: *const Self, input: []const u8, set: SpvWord, binding: SpvWord, descriptor_index: SpvWord) RuntimeError!void {
const result = self.mod.getBindingResult(set, binding) orelse return RuntimeError.NotFound; const result = self.mod.getBindingResult(set, binding) orelse return RuntimeError.NotFound;
const value = &(self.results[result].variant orelse return).Variable.value; const value = if (self.results[result].variant) |*variant| switch (variant.*) {
.Variable => |*variable| &variable.value,
else => return RuntimeError.InvalidSpirV,
} else return;
switch (value.*) { switch (value.*) {
.Array => |arr| { .Array => |arr| {
if (descriptor_index >= arr.values.len) if (descriptor_index >= arr.values.len)
@@ -1271,4 +1282,5 @@ fn reset(self: *Self) void {
self.current_function = null; self.current_function = null;
self.current_label = null; self.current_label = null;
self.previous_label = null; self.previous_label = null;
self.helper_invocation = false;
} }
+2 -2
View File
@@ -789,14 +789,14 @@ pub const Value = union(Type) {
.Vector => |*values| { .Vector => |*values| {
var offset: usize = 0; var offset: usize = 0;
for (values.*) |*v| { for (values.*) |*v| {
offset += try v.write(input[offset..]); offset += try v.write(input[@min(offset, input.len)..]);
} }
return offset; return offset;
}, },
.Matrix => |*values| { .Matrix => |*values| {
var offset: usize = 0; var offset: usize = 0;
for (values.*) |*v| { for (values.*) |*v| {
offset += try v.write(input[offset..]); offset += try v.write(input[@min(offset, input.len)..]);
} }
return offset; return offset;
}, },
+97 -14
View File
@@ -406,6 +406,7 @@ pub fn initRuntimeDispatcher() void {
runtime_dispatcher[@intFromEnum(spv.SpvOp.ImageTexelPointer)] = opImageTexelPointer; runtime_dispatcher[@intFromEnum(spv.SpvOp.ImageTexelPointer)] = opImageTexelPointer;
runtime_dispatcher[@intFromEnum(spv.SpvOp.ImageWrite)] = ImageEngine(.Write).op; runtime_dispatcher[@intFromEnum(spv.SpvOp.ImageWrite)] = ImageEngine(.Write).op;
runtime_dispatcher[@intFromEnum(spv.SpvOp.InBoundsAccessChain)] = opAccessChain; runtime_dispatcher[@intFromEnum(spv.SpvOp.InBoundsAccessChain)] = opAccessChain;
runtime_dispatcher[@intFromEnum(spv.SpvOp.IsHelperInvocationEXT)] = opIsHelperInvocationEXT;
runtime_dispatcher[@intFromEnum(spv.SpvOp.IsFinite)] = CondEngine(.Float, .IsNan).op; runtime_dispatcher[@intFromEnum(spv.SpvOp.IsFinite)] = CondEngine(.Float, .IsNan).op;
runtime_dispatcher[@intFromEnum(spv.SpvOp.IsInf)] = CondEngine(.Float, .IsInf).op; runtime_dispatcher[@intFromEnum(spv.SpvOp.IsInf)] = CondEngine(.Float, .IsInf).op;
runtime_dispatcher[@intFromEnum(spv.SpvOp.IsNan)] = CondEngine(.Float, .IsNan).op; runtime_dispatcher[@intFromEnum(spv.SpvOp.IsNan)] = CondEngine(.Float, .IsNan).op;
@@ -451,6 +452,7 @@ pub fn initRuntimeDispatcher() void {
runtime_dispatcher[@intFromEnum(spv.SpvOp.Store)] = opStore; runtime_dispatcher[@intFromEnum(spv.SpvOp.Store)] = opStore;
runtime_dispatcher[@intFromEnum(spv.SpvOp.Switch)] = opSwitch; runtime_dispatcher[@intFromEnum(spv.SpvOp.Switch)] = opSwitch;
runtime_dispatcher[@intFromEnum(spv.SpvOp.TerminateInvocation)] = opKill; runtime_dispatcher[@intFromEnum(spv.SpvOp.TerminateInvocation)] = opKill;
runtime_dispatcher[@intFromEnum(spv.SpvOp.DemoteToHelperInvocation)] = opDemoteToHelperInvocation;
runtime_dispatcher[@intFromEnum(spv.SpvOp.Transpose)] = opTranspose; runtime_dispatcher[@intFromEnum(spv.SpvOp.Transpose)] = opTranspose;
runtime_dispatcher[@intFromEnum(spv.SpvOp.UConvert)] = ConversionEngine(.UInt, .UInt).op; runtime_dispatcher[@intFromEnum(spv.SpvOp.UConvert)] = ConversionEngine(.UInt, .UInt).op;
runtime_dispatcher[@intFromEnum(spv.SpvOp.UDiv)] = MathEngine(.UInt, .Div, false).op; runtime_dispatcher[@intFromEnum(spv.SpvOp.UDiv)] = MathEngine(.UInt, .Div, false).op;
@@ -1056,11 +1058,13 @@ fn ConversionEngine(comptime from_kind: PrimitiveType, comptime to_kind: Primiti
} }
} }
fn op(_: std.mem.Allocator, _: SpvWord, rt: *Runtime) RuntimeError!void { fn op(allocator: std.mem.Allocator, _: SpvWord, rt: *Runtime) RuntimeError!void {
const target_type = (try rt.results[try rt.it.next()].getVariant()).Type; const target_type = (try rt.results[try rt.it.next()].getVariant()).Type;
const dst_value = try rt.results[try rt.it.next()].getValue(); const dst_id = try rt.it.next();
const dst_value = try rt.results[dst_id].getValue();
const src_value = try rt.results[try rt.it.next()].getValue(); const src_id = try rt.it.next();
const src_value = try rt.results[src_id].getValue();
const from_bits = try src_value.resolveLaneBitWidth(); const from_bits = try src_value.resolveLaneBitWidth();
const to_bits = try Result.resolveLaneBitWidth(target_type, rt); const to_bits = try Result.resolveLaneBitWidth(target_type, rt);
@@ -1072,6 +1076,7 @@ fn ConversionEngine(comptime from_kind: PrimitiveType, comptime to_kind: Primiti
for (0..dst_lane_count) |lane_index| { for (0..dst_lane_count) |lane_index| {
try applyLane(from_bits, to_bits, dst_value, src_value, lane_index); try applyLane(from_bits, to_bits, dst_value, src_value, lane_index);
} }
try rt.copyDerivative(allocator, dst_id, src_id);
} }
}; };
} }
@@ -2150,6 +2155,8 @@ fn ImageEngine(comptime Op: ImageOp) type {
const image = &rt.results[try rt.it.next()]; const image = &rt.results[try rt.it.next()];
const coordinate = try rt.results[try rt.it.next()].getValue(); const coordinate = try rt.results[try rt.it.next()].getValue();
const texel = try rt.results[try rt.it.next()].getValue(); const texel = try rt.results[try rt.it.next()].getValue();
if (rt.helper_invocation)
return;
const image_operand = try resolveImage(image, rt); const image_operand = try resolveImage(image, rt);
const x = try readStorageCoordLane(coordinate, 0); const x = try readStorageCoordLane(coordinate, 0);
@@ -2658,10 +2665,13 @@ fn AtomicEngine(comptime Op: AtomicOp) type {
const old = try readU32(ptr); const old = try readU32(ptr);
const new = apply(old, value, comparator); const new = apply(old, value, comparator);
try writeU32(ptr, new); if (!rt.helper_invocation)
try writeU32(ptr, new);
try writeU32(dst, old); try writeU32(dst, old);
try writeImageTexelPointer(rt, ptr); if (!rt.helper_invocation) {
try ptr.flushPtr(allocator); try writeImageTexelPointer(rt, ptr);
try ptr.flushPtr(allocator);
}
} }
}; };
} }
@@ -3425,8 +3435,23 @@ fn copyValue(dst: *Value, src: *const Value) RuntimeError!void {
.common => |dst_val_ptr| { .common => |dst_val_ptr| {
switch (src.*) { switch (src.*) {
.Pointer => |src_ptr| switch (src_ptr.ptr) { .Pointer => |src_ptr| switch (src_ptr.ptr) {
.common => |src_val_ptr| try copyValue(dst_val_ptr, src_val_ptr), .common => |src_val_ptr| {
else => dst_val_ptr.* = src.*, if (src_ptr.uniform_slice_window) |window| {
try helpers.readWindow(dst_val_ptr, window, src_ptr.matrix_stride, src_ptr.matrix_row_major);
} else {
try copyValue(dst_val_ptr, src_val_ptr);
}
},
.f32_ptr,
.i32_ptr,
.u32_ptr,
=> {
if (src_ptr.uniform_slice_window) |window| {
try helpers.readWindow(dst_val_ptr, window, src_ptr.matrix_stride, src_ptr.matrix_row_major);
} else {
dst_val_ptr.* = src.*;
}
},
}, },
else => try copyValue(dst_val_ptr, src), else => try copyValue(dst_val_ptr, src),
} }
@@ -3511,6 +3536,11 @@ fn copyValue(dst: *Value, src: *const Value) RuntimeError!void {
} }
const dst_slice = helpers.getDstSlice(dst) orelse return RuntimeError.InvalidSpirV; const dst_slice = helpers.getDstSlice(dst) orelse return RuntimeError.InvalidSpirV;
try helpers.copySlice(dst_slice, s.values); try helpers.copySlice(dst_slice, s.values);
if (dst.* == .Structure) {
if (dst.Structure.external_data) |dst_data| {
_ = try dst.read(dst_data);
}
}
}, },
.Pointer => |ptr| switch (ptr.ptr) { .Pointer => |ptr| switch (ptr.ptr) {
.common => |src_val_ptr| { .common => |src_val_ptr| {
@@ -3588,11 +3618,17 @@ fn opAccessChain(allocator: std.mem.Allocator, word_count: SpvWord, rt: *Runtime
var old_variant = variant; var old_variant = variant;
switch (old_variant) { switch (old_variant) {
.AccessChain => |*a| { .AccessChain => |*a| {
if (a.indexes.len != index_count)
return RuntimeError.InvalidSpirV;
try a.value.flushPtr(allocator); try a.value.flushPtr(allocator);
a.value.deinit(allocator); a.value.deinit(allocator);
break :blk .{ a.indexes, false }; if (a.indexes.len == index_count)
break :blk .{ a.indexes, false };
allocator.free(a.indexes);
},
.Constant => |*constant| {
constant.value.deinit(allocator);
},
.Variable => |*variable| {
variable.value.deinit(allocator);
}, },
else => {}, else => {},
} }
@@ -4008,6 +4044,8 @@ fn opAtomicStore(_: std.mem.Allocator, _: SpvWord, rt: *Runtime) RuntimeError!vo
_ = rt.it.skip(); // scope _ = rt.it.skip(); // scope
_ = rt.it.skip(); // semantic _ = rt.it.skip(); // semantic
const val_id = try rt.it.next(); const val_id = try rt.it.next();
if (rt.helper_invocation)
return;
try 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());
} }
@@ -4082,6 +4120,7 @@ fn opCompositeConstruct(allocator: std.mem.Allocator, word_count: SpvWord, rt: *
operand_id.* = try rt.it.next(); operand_id.* = try rt.it.next();
} }
try rt.refreshResultValueLayout(id);
const value = &(try rt.results[id].getVariant()).Constant.value; const value = &(try rt.results[id].getVariant()).Constant.value;
if (value.getCompositeDataOrNull()) |target| { if (value.getCompositeDataOrNull()) |target| {
for (target[0..index_count], operand_ids) |*elem, operand_id| { for (target[0..index_count], operand_ids) |*elem, operand_id| {
@@ -5300,10 +5339,11 @@ fn opSpecConstantOp(allocator: std.mem.Allocator, word_count: SpvWord, rt: *Runt
} }
} }
fn opCopyMemory(_: std.mem.Allocator, _: SpvWord, rt: *Runtime) RuntimeError!void { fn opCopyMemory(allocator: std.mem.Allocator, _: SpvWord, rt: *Runtime) RuntimeError!void {
const target = try rt.it.next(); const target = try rt.it.next();
const source = try rt.it.next(); const source = try rt.it.next();
try copyValue(try rt.results[target].getValue(), try rt.results[source].getValue()); try copyValue(try rt.results[target].getValue(), try rt.results[source].getValue());
try rt.copyDerivative(allocator, target, source);
} }
fn opCopyObject(_: std.mem.Allocator, _: SpvWord, rt: *Runtime) RuntimeError!void { fn opCopyObject(_: std.mem.Allocator, _: SpvWord, rt: *Runtime) RuntimeError!void {
@@ -5558,13 +5598,17 @@ fn opFunction(allocator: std.mem.Allocator, _: SpvWord, rt: *Runtime) RuntimeErr
const function_type_id = try rt.it.next(); const function_type_id = try rt.it.next();
const source_location = rt.it.emitSourceLocation(); const source_location = rt.it.emitSourceLocation();
const existing_params = if (rt.results[id].variant) |variant| switch (variant) {
.Function => |function| function.params,
else => return RuntimeError.InvalidSpirV,
} else null;
rt.results[id].variant = .{ rt.results[id].variant = .{
.Function = .{ .Function = .{
.source_location = source_location, .source_location = source_location,
.return_type = return_type, .return_type = return_type,
.function_type = function_type_id, .function_type = function_type_id,
.params = params: { .params = existing_params orelse params: {
if (rt.results[function_type_id].variant) |variant| { if (rt.results[function_type_id].variant) |variant| {
const params_count = switch (variant) { const params_count = switch (variant) {
.Type => |t| switch (t) { .Type => |t| switch (t) {
@@ -5607,6 +5651,7 @@ fn opFunctionCall(allocator: std.mem.Allocator, _: SpvWord, rt: *Runtime) Runtim
.previous_label = null, .previous_label = null,
}) catch return RuntimeError.OutOfMemory; }) catch return RuntimeError.OutOfMemory;
if (!rt.it.jumpToSourceLocation(source_location)) return RuntimeError.InvalidSpirV; if (!rt.it.jumpToSourceLocation(source_location)) return RuntimeError.InvalidSpirV;
rt.current_function = func;
rt.current_parameter_index = 0; rt.current_parameter_index = 0;
} }
@@ -5619,6 +5664,22 @@ fn opFunctionParameter(_: std.mem.Allocator, _: SpvWord, rt: *Runtime) RuntimeEr
const id = try rt.it.next(); const id = try rt.it.next();
const target = &rt.results[id]; const target = &rt.results[id];
if (rt.function_stack.items.len != 0) {
if (target.variant) |*variant| switch (variant.*) {
.FunctionParameter => |parameter| {
if (parameter.value_ptr != null) {
(try (rt.current_function orelse return RuntimeError.InvalidSpirV).getVariant()).Function.params[rt.current_parameter_index] = id;
rt.current_parameter_index += 1;
return;
}
},
else => {},
};
}
const value_ptr = if (target.variant) |*variant| switch (variant.*) {
.FunctionParameter => |parameter| parameter.value_ptr,
else => null,
} else null;
const resolved = rt.results[var_type].resolveType(rt.results); const resolved = rt.results[var_type].resolveType(rt.results);
target.variant = .{ target.variant = .{
@@ -5628,7 +5689,7 @@ fn opFunctionParameter(_: std.mem.Allocator, _: SpvWord, rt: *Runtime) RuntimeEr
.Type => |t| @as(Result.Type, t), .Type => |t| @as(Result.Type, t),
else => return RuntimeError.InvalidSpirV, else => return RuntimeError.InvalidSpirV,
}, },
.value_ptr = null, .value_ptr = value_ptr,
}, },
}; };
(try (rt.current_function orelse return RuntimeError.InvalidSpirV).getVariant()).Function.params[rt.current_parameter_index] = id; (try (rt.current_function orelse return RuntimeError.InvalidSpirV).getVariant()).Function.params[rt.current_parameter_index] = id;
@@ -5654,10 +5715,30 @@ fn opKill(_: std.mem.Allocator, _: SpvWord, _: *Runtime) RuntimeError!void {
return RuntimeError.Killed; return RuntimeError.Killed;
} }
fn opDemoteToHelperInvocation(allocator: std.mem.Allocator, _: SpvWord, rt: *Runtime) RuntimeError!void {
rt.helper_invocation = true;
const helper_invocation = true;
rt.writeBuiltIn(allocator, std.mem.asBytes(&helper_invocation), .HelperInvocation) catch |err| switch (err) {
RuntimeError.NotFound => {},
else => return err,
};
}
fn opIsHelperInvocationEXT(_: std.mem.Allocator, _: SpvWord, rt: *Runtime) RuntimeError!void {
_ = try rt.it.next();
const id = try rt.it.next();
const value = try rt.results[id].getValue();
switch (value.*) {
.Bool => |*b| b.* = rt.helper_invocation,
else => return RuntimeError.InvalidSpirV,
}
}
fn opLoad(allocator: std.mem.Allocator, _: SpvWord, rt: *Runtime) RuntimeError!void { fn opLoad(allocator: std.mem.Allocator, _: SpvWord, rt: *Runtime) RuntimeError!void {
_ = rt.it.skip(); _ = rt.it.skip();
const id = try rt.it.next(); const id = try rt.it.next();
const ptr_id = try rt.it.next(); const ptr_id = try rt.it.next();
try rt.refreshResultValueLayout(id);
try 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());
try rt.copyDerivative(allocator, id, ptr_id); try rt.copyDerivative(allocator, id, ptr_id);
} }
@@ -5912,6 +5993,8 @@ fn opSourceExtension(allocator: std.mem.Allocator, word_count: SpvWord, rt: *Run
fn opStore(allocator: std.mem.Allocator, _: SpvWord, rt: *Runtime) RuntimeError!void { fn opStore(allocator: std.mem.Allocator, _: SpvWord, rt: *Runtime) RuntimeError!void {
const ptr_id = try rt.it.next(); const ptr_id = try rt.it.next();
const val_id = try rt.it.next(); const val_id = try rt.it.next();
if (rt.helper_invocation)
return;
try 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());
try rt.copyDerivative(allocator, ptr_id, val_id); try rt.copyDerivative(allocator, ptr_id, val_id);
} }