adding derivatives support
Build / build (push) Successful in 47s
Test / build (push) Successful in 1m14s

This commit is contained in:
2026-06-07 23:27:49 +02:00
parent d66d1dd021
commit 291290f1b8
4 changed files with 246 additions and 21 deletions
+31
View File
@@ -93,6 +93,7 @@ pub fn initRuntimeDispatcher() void {
runtime_dispatcher[@intFromEnum(ext.GLSLOp.Log)] = MathEngine(.Float, .Log).opSingleOperator; runtime_dispatcher[@intFromEnum(ext.GLSLOp.Log)] = MathEngine(.Float, .Log).opSingleOperator;
runtime_dispatcher[@intFromEnum(ext.GLSLOp.Log2)] = MathEngine(.Float, .Log2).opSingleOperator; runtime_dispatcher[@intFromEnum(ext.GLSLOp.Log2)] = MathEngine(.Float, .Log2).opSingleOperator;
runtime_dispatcher[@intFromEnum(ext.GLSLOp.Normalize)] = opNormalize; runtime_dispatcher[@intFromEnum(ext.GLSLOp.Normalize)] = opNormalize;
runtime_dispatcher[@intFromEnum(ext.GLSLOp.Pow)] = MathEngine(.Float, .Pow).opDoubleOperators;
runtime_dispatcher[@intFromEnum(ext.GLSLOp.Round)] = MathEngine(.Float, .Round).opSingleOperator; runtime_dispatcher[@intFromEnum(ext.GLSLOp.Round)] = MathEngine(.Float, .Round).opSingleOperator;
runtime_dispatcher[@intFromEnum(ext.GLSLOp.SAbs)] = MathEngine(.SInt, .SAbs).opSingleOperator; runtime_dispatcher[@intFromEnum(ext.GLSLOp.SAbs)] = MathEngine(.SInt, .SAbs).opSingleOperator;
runtime_dispatcher[@intFromEnum(ext.GLSLOp.SClamp)] = MathEngine(.SInt, .SClamp).opTripleOperators; runtime_dispatcher[@intFromEnum(ext.GLSLOp.SClamp)] = MathEngine(.SInt, .SClamp).opTripleOperators;
@@ -102,6 +103,7 @@ pub fn initRuntimeDispatcher() void {
runtime_dispatcher[@intFromEnum(ext.GLSLOp.Tan)] = MathEngine(.Float, .Tan).opSingleOperator; runtime_dispatcher[@intFromEnum(ext.GLSLOp.Tan)] = MathEngine(.Float, .Tan).opSingleOperator;
runtime_dispatcher[@intFromEnum(ext.GLSLOp.Trunc)] = MathEngine(.Float, .Trunc).opSingleOperator; runtime_dispatcher[@intFromEnum(ext.GLSLOp.Trunc)] = MathEngine(.Float, .Trunc).opSingleOperator;
runtime_dispatcher[@intFromEnum(ext.GLSLOp.UClamp)] = MathEngine(.UInt, .UClamp).opTripleOperators; runtime_dispatcher[@intFromEnum(ext.GLSLOp.UClamp)] = MathEngine(.UInt, .UClamp).opTripleOperators;
runtime_dispatcher[@intFromEnum(ext.GLSLOp.Cross)] = opCross;
runtime_dispatcher[@intFromEnum(ext.GLSLOp.FindILsb)] = IntBitEngine(.FindILsb).op; runtime_dispatcher[@intFromEnum(ext.GLSLOp.FindILsb)] = IntBitEngine(.FindILsb).op;
runtime_dispatcher[@intFromEnum(ext.GLSLOp.FindSMsb)] = IntBitEngine(.FindSMsb).op; runtime_dispatcher[@intFromEnum(ext.GLSLOp.FindSMsb)] = IntBitEngine(.FindSMsb).op;
runtime_dispatcher[@intFromEnum(ext.GLSLOp.FindUMsb)] = IntBitEngine(.FindUMsb).op; runtime_dispatcher[@intFromEnum(ext.GLSLOp.FindUMsb)] = IntBitEngine(.FindUMsb).op;
@@ -205,6 +207,7 @@ fn MathEngine(comptime T: PrimitiveType, comptime Op: MathOp) type {
return switch (Op) { return switch (Op) {
.FMax => @max(l, r), .FMax => @max(l, r),
.FMin => @min(l, r), .FMin => @min(l, r),
.Pow => if (comptime isFloatOrF32Vector(TT)) @exp(@log(l) * r) else RuntimeError.InvalidSpirV,
else => RuntimeError.InvalidSpirV, else => RuntimeError.InvalidSpirV,
}; };
} }
@@ -397,6 +400,34 @@ fn IntBitEngine(comptime op_kind: IntBitOp) type {
}; };
} }
fn opCross(_: std.mem.Allocator, target_type_id: SpvWord, id: SpvWord, _: SpvWord, rt: *Runtime) RuntimeError!void {
const target_type = (try rt.results[target_type_id].getVariant()).Type;
const dst = try rt.results[id].getValue();
const lhs = try rt.results[try rt.it.next()].getValue();
const rhs = try rt.results[try rt.it.next()].getValue();
const lane_bits = try Result.resolveLaneBitWidth(target_type, rt);
if (try Result.resolveLaneCount(target_type) != 3)
return RuntimeError.InvalidSpirV;
switch (lane_bits) {
inline 16, 32, 64 => |bits| {
const FloatT = Value.getPrimitiveFieldType(.Float, bits);
const lx = try Value.readLane(.Float, bits, lhs, 0);
const ly = try Value.readLane(.Float, bits, lhs, 1);
const lz = try Value.readLane(.Float, bits, lhs, 2);
const rx = try Value.readLane(.Float, bits, rhs, 0);
const ry = try Value.readLane(.Float, bits, rhs, 1);
const rz = try Value.readLane(.Float, bits, rhs, 2);
try Value.writeLane(.Float, bits, dst, 0, @as(FloatT, ly * rz - lz * ry));
try Value.writeLane(.Float, bits, dst, 1, @as(FloatT, lz * rx - lx * rz));
try Value.writeLane(.Float, bits, dst, 2, @as(FloatT, lx * ry - ly * rx));
},
else => return RuntimeError.InvalidSpirV,
}
}
fn opLength(_: std.mem.Allocator, target_type_id: SpvWord, id: SpvWord, _: SpvWord, rt: *Runtime) RuntimeError!void { fn opLength(_: std.mem.Allocator, target_type_id: SpvWord, id: SpvWord, _: SpvWord, rt: *Runtime) RuntimeError!void {
const target_type = (try rt.results[target_type_id].getVariant()).Type; const target_type = (try rt.results[target_type_id].getVariant()).Type;
const dst = try rt.results[id].getValue(); const dst = try rt.results[id].getValue();
+87
View File
@@ -47,6 +47,23 @@ pub const SpecializationEntry = struct {
size: usize, size: usize,
}; };
pub const Derivative = struct {
dx: Value,
dy: Value,
pub fn dupe(self: *const @This(), allocator: std.mem.Allocator) RuntimeError!@This() {
return .{
.dx = try self.dx.dupe(allocator),
.dy = try self.dy.dupe(allocator),
};
}
pub fn deinit(self: *@This(), allocator: std.mem.Allocator) void {
self.dx.deinit(allocator);
self.dy.deinit(allocator);
}
};
pub const Function = struct { pub const Function = struct {
source_location: usize, source_location: usize,
result: *Result, result: *Result,
@@ -86,6 +103,7 @@ current_label: ?SpvWord,
previous_label: ?SpvWord, previous_label: ?SpvWord,
specialization_constants: std.AutoHashMapUnmanaged(u32, []const u8), specialization_constants: std.AutoHashMapUnmanaged(u32, []const u8),
derivatives: std.AutoHashMapUnmanaged(SpvWord, Derivative),
image_api: ImageAPI, image_api: ImageAPI,
@@ -116,6 +134,7 @@ pub fn init(allocator: std.mem.Allocator, module: *Module, image_api: ImageAPI)
.current_label = null, .current_label = null,
.previous_label = null, .previous_label = null,
.specialization_constants = .empty, .specialization_constants = .empty,
.derivatives = .empty,
.image_api = image_api, .image_api = image_api,
}; };
} }
@@ -131,6 +150,12 @@ pub fn deinit(self: *Self, allocator: std.mem.Allocator) void {
allocator.free(entry.value_ptr.*); allocator.free(entry.value_ptr.*);
} }
self.specialization_constants.deinit(allocator); self.specialization_constants.deinit(allocator);
var derivatives = self.derivatives.iterator();
while (derivatives.next()) |entry| {
entry.value_ptr.deinit(allocator);
}
self.derivatives.deinit(allocator);
} }
pub fn addSpecializationInfo(self: *Self, allocator: std.mem.Allocator, entry: SpecializationEntry, data: []const u8) RuntimeError!void { pub fn addSpecializationInfo(self: *Self, allocator: std.mem.Allocator, entry: SpecializationEntry, data: []const u8) RuntimeError!void {
@@ -149,6 +174,62 @@ pub fn copySpecializationConstantsFrom(self: *Self, allocator: std.mem.Allocator
} }
} }
pub fn setDerivative(self: *Self, allocator: std.mem.Allocator, result: SpvWord, dx: *const Value, dy: *const Value) RuntimeError!void {
const derivative: Derivative = .{
.dx = try dx.dupe(allocator),
.dy = try dy.dupe(allocator),
};
errdefer {
var tmp = derivative;
tmp.deinit(allocator);
}
const gop = self.derivatives.getOrPut(allocator, result) catch return RuntimeError.OutOfMemory;
if (gop.found_existing) {
gop.value_ptr.deinit(allocator);
}
gop.value_ptr.* = derivative;
}
pub fn setDerivativeFromMemory(self: *Self, allocator: std.mem.Allocator, result: SpvWord, dx: []const u8, dy: []const u8) RuntimeError!void {
const target_type = try self.getResultTargetTypeWord(result);
var dx_value = try Value.init(allocator, self.results, target_type, false);
defer dx_value.deinit(allocator);
_ = try dx_value.write(dx);
var dy_value = try Value.init(allocator, self.results, target_type, false);
defer dy_value.deinit(allocator);
_ = try dy_value.write(dy);
try self.setDerivative(allocator, result, &dx_value, &dy_value);
}
fn getResultTargetTypeWord(self: *const Self, result: SpvWord) RuntimeError!SpvWord {
return switch ((try self.results[result].getConstVariant()).*) {
.Variable => |v| v.type_word,
.Constant => |c| c.type_word,
.FunctionParameter => |p| p.type_word,
.AccessChain => |a| a.target,
else => return RuntimeError.InvalidSpirV,
};
}
pub fn clearDerivative(self: *Self, allocator: std.mem.Allocator, result: SpvWord) void {
if (self.derivatives.fetchRemove(result)) |kv| {
var derivative = kv.value;
derivative.deinit(allocator);
}
}
pub fn copyDerivative(self: *Self, allocator: std.mem.Allocator, dst: SpvWord, src: SpvWord) RuntimeError!void {
if (self.derivatives.get(src)) |derivative| {
try self.setDerivative(allocator, dst, &derivative.dx, &derivative.dy);
} else {
self.clearDerivative(allocator, dst);
}
}
pub fn getEntryPointByName(self: *const Self, name: []const u8) RuntimeError!SpvWord { pub fn getEntryPointByName(self: *const Self, name: []const u8) RuntimeError!SpvWord {
for (self.mod.entry_points.items, 0..) |entry_point, i| { for (self.mod.entry_points.items, 0..) |entry_point, i| {
if (blk: { if (blk: {
@@ -523,6 +604,12 @@ pub fn hasResultDecoration(self: *const Self, result: SpvWord, decoration: spv.S
} }
pub fn resetInvocation(self: *Self, allocator: std.mem.Allocator) void { pub fn resetInvocation(self: *Self, allocator: std.mem.Allocator) void {
var derivatives = self.derivatives.iterator();
while (derivatives.next()) |entry| {
entry.value_ptr.deinit(allocator);
}
self.derivatives.clearRetainingCapacity();
for (self.results) |*result| { for (self.results) |*result| {
if (result.variant) |*variant| { if (result.variant) |*variant| {
switch (variant.*) { switch (variant.*) {
+89 -19
View File
@@ -749,9 +749,41 @@ pub const Value = union(Type) {
return switch (v.*) { return switch (v.*) {
.Int => (try getPrimitiveField(T, bits, @constCast(v))).*, .Int => (try getPrimitiveField(T, bits, @constCast(v))).*,
.Float => (try getPrimitiveField(T, bits, @constCast(v))).*,
.Vector => |lanes| (try getPrimitiveField(T, bits, &lanes[lane_index])).*, .Vector => |lanes| (try getPrimitiveField(T, bits, &lanes[lane_index])).*,
.Vector2f32 => |*vec| switch (lane_index) {
inline 0...1 => |i| blk: {
if (comptime T == .Float and bits == 32) {
break :blk @as(TT, vec[i]);
} else {
return RuntimeError.InvalidSpirV;
}
},
else => return RuntimeError.InvalidSpirV,
},
.Vector3f32 => |*vec| switch (lane_index) {
inline 0...2 => |i| blk: {
if (comptime T == .Float and bits == 32) {
break :blk @as(TT, vec[i]);
} else {
return RuntimeError.InvalidSpirV;
}
},
else => return RuntimeError.InvalidSpirV,
},
.Vector4f32 => |*vec| switch (lane_index) {
inline 0...3 => |i| blk: {
if (comptime T == .Float and bits == 32) {
break :blk @as(TT, vec[i]);
} else {
return RuntimeError.InvalidSpirV;
}
},
else => return RuntimeError.InvalidSpirV,
},
.Vector2i32 => |*vec| switch (lane_index) { .Vector2i32 => |*vec| switch (lane_index) {
inline 0...1 => |i| blk: { inline 0...1 => |i| blk: {
if (bits == 32) { if (bits == 32) {
@@ -821,9 +853,35 @@ pub const Value = union(Type) {
pub inline fn writeLane(comptime T: PrimitiveType, comptime bits: u32, dst: *Value, lane_index: usize, value: getPrimitiveFieldType(T, bits)) RuntimeError!void { pub inline fn writeLane(comptime T: PrimitiveType, comptime bits: u32, dst: *Value, lane_index: usize, value: getPrimitiveFieldType(T, bits)) RuntimeError!void {
switch (dst.*) { switch (dst.*) {
.Int => (try getPrimitiveField(T, bits, dst)).* = value, .Int => (try getPrimitiveField(T, bits, dst)).* = value,
.Float => (try getPrimitiveField(T, bits, dst)).* = value,
.Vector => |lanes| try setScalarLaneValue(T, bits, &lanes[lane_index], value), .Vector => |lanes| try setScalarLaneValue(T, bits, &lanes[lane_index], value),
.Vector2f32 => |*vec| switch (lane_index) {
inline 0...1 => |i| if (comptime T == .Float and bits == 32) {
vec[i] = value;
} else {
return RuntimeError.InvalidSpirV;
},
else => return RuntimeError.InvalidSpirV,
},
.Vector3f32 => |*vec| switch (lane_index) {
inline 0...2 => |i| if (comptime T == .Float and bits == 32) {
vec[i] = value;
} else {
return RuntimeError.InvalidSpirV;
},
else => return RuntimeError.InvalidSpirV,
},
.Vector4f32 => |*vec| switch (lane_index) {
inline 0...3 => |i| if (comptime T == .Float and bits == 32) {
vec[i] = value;
} else {
return RuntimeError.InvalidSpirV;
},
else => return RuntimeError.InvalidSpirV,
},
.Vector2i32 => |*vec| switch (lane_index) { .Vector2i32 => |*vec| switch (lane_index) {
inline 0...1 => |i| if (bits == 32) { inline 0...1 => |i| if (bits == 32) {
vec[i] = @bitCast(value); vec[i] = @bitCast(value);
@@ -881,27 +939,39 @@ pub const Value = union(Type) {
fn setScalarLaneValue(comptime value_type: PrimitiveType, comptime bits: u32, dst: *Value, v: getPrimitiveFieldType(value_type, bits)) RuntimeError!void { fn setScalarLaneValue(comptime value_type: PrimitiveType, comptime bits: u32, dst: *Value, v: getPrimitiveFieldType(value_type, bits)) RuntimeError!void {
switch (bits) { switch (bits) {
inline 8, 16, 32, 64 => { inline 8, 16, 32, 64 => {
dst.* = .{ .Int = .{ dst.* = switch (value_type) {
.bit_count = bits, .Float => .{ .Float = .{
.is_signed = if (value_type == .SInt) true else false, .bit_count = bits,
.value = switch (value_type) { .value = switch (bits) {
.SInt => switch (bits) { 16 => .{ .float16 = v },
8 => .{ .sint8 = v }, 32 => .{ .float32 = v },
16 => .{ .sint16 = v }, 64 => .{ .float64 = v },
32 => .{ .sint32 = v }, else => return RuntimeError.InvalidSpirV,
64 => .{ .sint64 = v }, },
} },
.SInt, .UInt => .{ .Int = .{
.bit_count = bits,
.is_signed = if (value_type == .SInt) true else false,
.value = switch (value_type) {
.SInt => switch (bits) {
8 => .{ .sint8 = v },
16 => .{ .sint16 = v },
32 => .{ .sint32 = v },
64 => .{ .sint64 = v },
else => unreachable,
},
.UInt => switch (bits) {
8 => .{ .uint8 = v },
16 => .{ .uint16 = v },
32 => .{ .uint32 = v },
64 => .{ .uint64 = v },
else => unreachable,
},
else => unreachable, else => unreachable,
}, },
.UInt => switch (bits) { } },
8 => .{ .uint8 = v }, else => return RuntimeError.InvalidSpirV,
16 => .{ .uint16 = v }, };
32 => .{ .uint32 = v },
64 => .{ .uint64 = v },
else => unreachable,
},
else => return RuntimeError.InvalidSpirV,
},
} };
}, },
else => return RuntimeError.InvalidSpirV, else => return RuntimeError.InvalidSpirV,
} }
+39 -2
View File
@@ -141,6 +141,12 @@ pub const SetupDispatcher = block: {
.Decorate = opDecorate, .Decorate = opDecorate,
.DecorationGroup = opDecorationGroup, .DecorationGroup = opDecorationGroup,
.Dot = autoSetupConstant, .Dot = autoSetupConstant,
.DPdx = autoSetupConstant,
.DPdxCoarse = autoSetupConstant,
.DPdxFine = autoSetupConstant,
.DPdy = autoSetupConstant,
.DPdyCoarse = autoSetupConstant,
.DPdyFine = autoSetupConstant,
.EntryPoint = opEntryPoint, .EntryPoint = opEntryPoint,
.ExecutionMode = opExecutionMode, .ExecutionMode = opExecutionMode,
.ExtInst = autoSetupConstant, .ExtInst = autoSetupConstant,
@@ -303,6 +309,12 @@ pub fn initRuntimeDispatcher() void {
runtime_dispatcher[@intFromEnum(spv.SpvOp.ConvertSToF)] = ConversionEngine(.SInt, .Float).op; runtime_dispatcher[@intFromEnum(spv.SpvOp.ConvertSToF)] = ConversionEngine(.SInt, .Float).op;
runtime_dispatcher[@intFromEnum(spv.SpvOp.ConvertUToF)] = ConversionEngine(.UInt, .Float).op; runtime_dispatcher[@intFromEnum(spv.SpvOp.ConvertUToF)] = ConversionEngine(.UInt, .Float).op;
runtime_dispatcher[@intFromEnum(spv.SpvOp.CopyMemory)] = opCopyMemory; runtime_dispatcher[@intFromEnum(spv.SpvOp.CopyMemory)] = opCopyMemory;
runtime_dispatcher[@intFromEnum(spv.SpvOp.DPdx)] = DerivativeEngine(.x).op;
runtime_dispatcher[@intFromEnum(spv.SpvOp.DPdxCoarse)] = DerivativeEngine(.x).op;
runtime_dispatcher[@intFromEnum(spv.SpvOp.DPdxFine)] = DerivativeEngine(.x).op;
runtime_dispatcher[@intFromEnum(spv.SpvOp.DPdy)] = DerivativeEngine(.y).op;
runtime_dispatcher[@intFromEnum(spv.SpvOp.DPdyCoarse)] = DerivativeEngine(.y).op;
runtime_dispatcher[@intFromEnum(spv.SpvOp.DPdyFine)] = DerivativeEngine(.y).op;
runtime_dispatcher[@intFromEnum(spv.SpvOp.Dot)] = opDot; runtime_dispatcher[@intFromEnum(spv.SpvOp.Dot)] = opDot;
runtime_dispatcher[@intFromEnum(spv.SpvOp.ExtInst)] = opExtInst; runtime_dispatcher[@intFromEnum(spv.SpvOp.ExtInst)] = opExtInst;
runtime_dispatcher[@intFromEnum(spv.SpvOp.FAdd)] = MathEngine(.Float, .Add, false).op; runtime_dispatcher[@intFromEnum(spv.SpvOp.FAdd)] = MathEngine(.Float, .Add, false).op;
@@ -341,7 +353,6 @@ pub fn initRuntimeDispatcher() void {
runtime_dispatcher[@intFromEnum(spv.SpvOp.ImageSampleImplicitLod)] = ImageEngine(.SampleImplicitLod).op; runtime_dispatcher[@intFromEnum(spv.SpvOp.ImageSampleImplicitLod)] = ImageEngine(.SampleImplicitLod).op;
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.SampledImage)] = opSampledImage;
runtime_dispatcher[@intFromEnum(spv.SpvOp.InBoundsAccessChain)] = opAccessChain; runtime_dispatcher[@intFromEnum(spv.SpvOp.InBoundsAccessChain)] = opAccessChain;
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;
@@ -373,6 +384,7 @@ pub fn initRuntimeDispatcher() void {
runtime_dispatcher[@intFromEnum(spv.SpvOp.SMulExtended)] = opSMulExtended; runtime_dispatcher[@intFromEnum(spv.SpvOp.SMulExtended)] = opSMulExtended;
runtime_dispatcher[@intFromEnum(spv.SpvOp.SNegate)] = MathEngine(.SInt, .Negate, false).opSingle; runtime_dispatcher[@intFromEnum(spv.SpvOp.SNegate)] = MathEngine(.SInt, .Negate, false).opSingle;
runtime_dispatcher[@intFromEnum(spv.SpvOp.SRem)] = MathEngine(.SInt, .Rem, false).op; runtime_dispatcher[@intFromEnum(spv.SpvOp.SRem)] = MathEngine(.SInt, .Rem, false).op;
runtime_dispatcher[@intFromEnum(spv.SpvOp.SampledImage)] = opSampledImage;
runtime_dispatcher[@intFromEnum(spv.SpvOp.Select)] = opSelect; runtime_dispatcher[@intFromEnum(spv.SpvOp.Select)] = opSelect;
runtime_dispatcher[@intFromEnum(spv.SpvOp.ShiftLeftLogical)] = BitEngine(.UInt, .ShiftLeft).op; runtime_dispatcher[@intFromEnum(spv.SpvOp.ShiftLeftLogical)] = BitEngine(.UInt, .ShiftLeft).op;
runtime_dispatcher[@intFromEnum(spv.SpvOp.ShiftRightArithmetic)] = BitEngine(.SInt, .ShiftRightArithmetic).op; runtime_dispatcher[@intFromEnum(spv.SpvOp.ShiftRightArithmetic)] = BitEngine(.SInt, .ShiftRightArithmetic).op;
@@ -384,6 +396,7 @@ pub fn initRuntimeDispatcher() void {
runtime_dispatcher[@intFromEnum(spv.SpvOp.SpecConstantTrue)] = opSpecConstantTrue; runtime_dispatcher[@intFromEnum(spv.SpvOp.SpecConstantTrue)] = opSpecConstantTrue;
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.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;
runtime_dispatcher[@intFromEnum(spv.SpvOp.UGreaterThan)] = CondEngine(.UInt, .Greater).op; runtime_dispatcher[@intFromEnum(spv.SpvOp.UGreaterThan)] = CondEngine(.UInt, .Greater).op;
@@ -392,6 +405,7 @@ pub fn initRuntimeDispatcher() void {
runtime_dispatcher[@intFromEnum(spv.SpvOp.ULessThanEqual)] = CondEngine(.UInt, .LessEqual).op; runtime_dispatcher[@intFromEnum(spv.SpvOp.ULessThanEqual)] = CondEngine(.UInt, .LessEqual).op;
runtime_dispatcher[@intFromEnum(spv.SpvOp.UMod)] = MathEngine(.UInt, .Mod, false).op; runtime_dispatcher[@intFromEnum(spv.SpvOp.UMod)] = MathEngine(.UInt, .Mod, false).op;
runtime_dispatcher[@intFromEnum(spv.SpvOp.UMulExtended)] = opUMulExtended; runtime_dispatcher[@intFromEnum(spv.SpvOp.UMulExtended)] = opUMulExtended;
runtime_dispatcher[@intFromEnum(spv.SpvOp.Unreachable)] = opUnreachable;
runtime_dispatcher[@intFromEnum(spv.SpvOp.VectorShuffle)] = opVectorShuffle; runtime_dispatcher[@intFromEnum(spv.SpvOp.VectorShuffle)] = opVectorShuffle;
runtime_dispatcher[@intFromEnum(spv.SpvOp.VectorTimesMatrix)] = MathEngine(.Float, .VectorTimesMatrix, false).op; // TODO runtime_dispatcher[@intFromEnum(spv.SpvOp.VectorTimesMatrix)] = MathEngine(.Float, .VectorTimesMatrix, false).op; // TODO
runtime_dispatcher[@intFromEnum(spv.SpvOp.VectorTimesScalar)] = MathEngine(.Float, .VectorTimesScalar, false).op; runtime_dispatcher[@intFromEnum(spv.SpvOp.VectorTimesScalar)] = MathEngine(.Float, .VectorTimesScalar, false).op;
@@ -3197,6 +3211,10 @@ fn opSMulExtended(_: std.mem.Allocator, _: SpvWord, rt: *Runtime) RuntimeError!v
try opMulExtended(true, rt); try opMulExtended(true, rt);
} }
fn opUnreachable(_: std.mem.Allocator, _: SpvWord, _: *Runtime) RuntimeError!void {
return RuntimeError.Unreachable;
}
fn opSpecConstant(allocator: std.mem.Allocator, word_count: SpvWord, rt: *Runtime) RuntimeError!void { fn opSpecConstant(allocator: std.mem.Allocator, word_count: SpvWord, rt: *Runtime) RuntimeError!void {
const location = rt.it.emitSourceLocation(); const location = rt.it.emitSourceLocation();
_ = rt.it.skip(); _ = rt.it.skip();
@@ -3432,6 +3450,24 @@ fn opDot(_: std.mem.Allocator, _: SpvWord, rt: *Runtime) RuntimeError!void {
} }
} }
fn DerivativeEngine(comptime axis: enum { x, y }) type {
return struct {
fn op(allocator: std.mem.Allocator, _: SpvWord, rt: *Runtime) RuntimeError!void {
_ = try rt.it.next(); // result type
const id = try rt.it.next();
const operand = try rt.it.next();
const derivative = rt.derivatives.get(operand) orelse return RuntimeError.UnsupportedSpirV;
const src = switch (axis) {
.x => &derivative.dx,
.y => &derivative.dy,
};
try copyValue(try rt.results[id].getValue(), src);
try rt.copyDerivative(allocator, id, operand);
}
};
}
fn opEntryPoint(allocator: std.mem.Allocator, word_count: SpvWord, rt: *Runtime) RuntimeError!void { fn opEntryPoint(allocator: std.mem.Allocator, word_count: SpvWord, rt: *Runtime) RuntimeError!void {
const entry = rt.mod.entry_points.addOne(allocator) catch return RuntimeError.OutOfMemory; const entry = rt.mod.entry_points.addOne(allocator) catch return RuntimeError.OutOfMemory;
entry.exec_model = try rt.it.nextAs(spv.SpvExecutionModel); entry.exec_model = try rt.it.nextAs(spv.SpvExecutionModel);
@@ -3601,11 +3637,12 @@ fn opKill(_: std.mem.Allocator, _: SpvWord, _: *Runtime) RuntimeError!void {
return RuntimeError.Killed; return RuntimeError.Killed;
} }
fn opLoad(_: 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 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);
} }
fn opMemberName(allocator: std.mem.Allocator, word_count: SpvWord, rt: *Runtime) RuntimeError!void { fn opMemberName(allocator: std.mem.Allocator, word_count: SpvWord, rt: *Runtime) RuntimeError!void {