fixing vector convertions and derivative propagation through composite construct
This commit is contained in:
+51
-13
@@ -274,7 +274,56 @@ fn applyStructMemberInterfaceDecorations(self: *Self, storage_class: spv.SpvStor
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn applyDecorations(self: *Self, allocator: std.mem.Allocator) ModuleError!void {
|
fn applyDecorations(self: *Self, allocator: std.mem.Allocator) ModuleError!void {
|
||||||
for (self.results, 0..) |result, id| {
|
const helpers = struct {
|
||||||
|
fn applyValueLayout(results: []Result, value: *Value, type_word: SpvWord) ModuleError!void {
|
||||||
|
const type_variant = results[type_word].variant orelse return;
|
||||||
|
const type_data = switch (type_variant) {
|
||||||
|
.Type => |type_data| type_data,
|
||||||
|
else => return,
|
||||||
|
};
|
||||||
|
|
||||||
|
switch (value.*) {
|
||||||
|
.Structure => |*structure| switch (type_data) {
|
||||||
|
.Structure => |type_structure| {
|
||||||
|
@memcpy(@constCast(structure.offsets), type_structure.members_offsets);
|
||||||
|
@memcpy(@constCast(structure.matrix_strides), type_structure.members_matrix_strides);
|
||||||
|
|
||||||
|
for (structure.values, type_structure.members_type_word) |*member_value, member_type_word| {
|
||||||
|
try applyValueLayout(results, member_value, member_type_word);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
else => {},
|
||||||
|
},
|
||||||
|
.Array => |array| switch (type_data) {
|
||||||
|
.Array => |type_array| {
|
||||||
|
for (array.values) |*element| {
|
||||||
|
try applyValueLayout(results, element, type_array.components_type_word);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
else => {},
|
||||||
|
},
|
||||||
|
.Matrix => |columns| switch (type_data) {
|
||||||
|
.Matrix => |type_matrix| {
|
||||||
|
for (columns) |*column| {
|
||||||
|
try applyValueLayout(results, column, type_matrix.column_type_word);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
else => {},
|
||||||
|
},
|
||||||
|
.Vector => |elements| switch (type_data) {
|
||||||
|
.Vector => |type_vector| {
|
||||||
|
for (elements) |*element| {
|
||||||
|
try applyValueLayout(results, element, type_vector.components_type_word);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
else => {},
|
||||||
|
},
|
||||||
|
else => {},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
for (self.results, 0..) |*result, id| {
|
||||||
if (result.variant == null)
|
if (result.variant == null)
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
@@ -323,18 +372,7 @@ fn applyDecorations(self: *Self, allocator: std.mem.Allocator) ModuleError!void
|
|||||||
.StorageBuffer,
|
.StorageBuffer,
|
||||||
.Uniform,
|
.Uniform,
|
||||||
.PushConstant,
|
.PushConstant,
|
||||||
=> if (v.value == .Structure) {
|
=> try helpers.applyValueLayout(self.results, &v.value, v.type_word),
|
||||||
if (self.results[v.type_word].variant) |type_variant| switch (type_variant) {
|
|
||||||
.Type => |type_data| switch (type_data) {
|
|
||||||
.Structure => |s| {
|
|
||||||
@memcpy(@constCast(v.value.Structure.offsets), s.members_offsets);
|
|
||||||
@memcpy(@constCast(v.value.Structure.matrix_strides), s.members_matrix_strides);
|
|
||||||
},
|
|
||||||
else => {},
|
|
||||||
},
|
|
||||||
else => {},
|
|
||||||
};
|
|
||||||
},
|
|
||||||
else => {},
|
else => {},
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|||||||
+20
-12
@@ -501,6 +501,24 @@ pub const Value = union(Type) {
|
|||||||
return matrix_size;
|
return matrix_size;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn writeMatrixWithStride(self: *Self, input: []const u8, matrix_stride: SpvWord) RuntimeError!usize {
|
||||||
|
const columns = switch (self.*) {
|
||||||
|
.Matrix => |columns| columns,
|
||||||
|
else => return RuntimeError.InvalidValueType,
|
||||||
|
};
|
||||||
|
if (columns.len == 0) return 0;
|
||||||
|
|
||||||
|
const matrix_stride_usize: usize = @intCast(matrix_stride);
|
||||||
|
const column_size = try columns[0].getPlainMemorySize();
|
||||||
|
const matrix_size = (columns.len - 1) * matrix_stride_usize + column_size;
|
||||||
|
if (input.len < matrix_size) return RuntimeError.OutOfBounds;
|
||||||
|
|
||||||
|
for (columns, 0..) |*column, column_index| {
|
||||||
|
_ = try column.write(input[column_index * matrix_stride_usize ..]);
|
||||||
|
}
|
||||||
|
return matrix_size;
|
||||||
|
}
|
||||||
|
|
||||||
pub fn write(self: *Self, input: []const u8) RuntimeError!usize {
|
pub fn write(self: *Self, input: []const u8) RuntimeError!usize {
|
||||||
const vecRoutine = struct {
|
const vecRoutine = struct {
|
||||||
inline fn routine(comptime T: type, vec: *T, in: []const u8) RuntimeError!usize {
|
inline fn routine(comptime T: type, vec: *T, in: []const u8) RuntimeError!usize {
|
||||||
@@ -605,18 +623,8 @@ pub const Value = union(Type) {
|
|||||||
const write_size = switch (v.*) {
|
const write_size = switch (v.*) {
|
||||||
.Matrix => |*columns| blk: {
|
.Matrix => |*columns| blk: {
|
||||||
const matrix_stride = s.matrix_strides[i] orelse break :blk try v.write(input[member_offset..]);
|
const matrix_stride = s.matrix_strides[i] orelse break :blk try v.write(input[member_offset..]);
|
||||||
if (columns.len == 0) break :blk @as(usize, 0);
|
_ = columns;
|
||||||
const matrix_stride_usize: usize = @intCast(matrix_stride);
|
break :blk try v.writeMatrixWithStride(input[member_offset..], matrix_stride);
|
||||||
|
|
||||||
const column_size = try columns.*[0].getPlainMemorySize();
|
|
||||||
const matrix_size = (columns.len - 1) * matrix_stride_usize + column_size;
|
|
||||||
if (input.len - member_offset < matrix_size)
|
|
||||||
return RuntimeError.OutOfBounds;
|
|
||||||
|
|
||||||
for (columns.*, 0..) |*column, column_index| {
|
|
||||||
_ = try column.write(input[member_offset + (column_index * matrix_stride_usize) ..]);
|
|
||||||
}
|
|
||||||
break :blk matrix_size;
|
|
||||||
},
|
},
|
||||||
else => try v.write(input[member_offset..]),
|
else => try v.write(input[member_offset..]),
|
||||||
};
|
};
|
||||||
|
|||||||
+145
-201
@@ -972,36 +972,28 @@ fn CondEngine(comptime T: PrimitiveType, comptime Op: CondOp) type {
|
|||||||
|
|
||||||
fn ConversionEngine(comptime from_kind: PrimitiveType, comptime to_kind: PrimitiveType) type {
|
fn ConversionEngine(comptime from_kind: PrimitiveType, comptime to_kind: PrimitiveType) type {
|
||||||
return struct {
|
return struct {
|
||||||
fn castLane(comptime ToT: type, from_bit_count: SpvWord, from: *Value) RuntimeError!ToT {
|
fn castLane(comptime ToT: type, from_bit_count: SpvWord, from: *const Value, lane_index: usize) RuntimeError!ToT {
|
||||||
return switch (from_bit_count) {
|
return switch (from_bit_count) {
|
||||||
inline 8, 16, 32, 64 => |bits| blk: {
|
inline 8, 16, 32, 64 => |bits| blk: {
|
||||||
if (bits == 8 and from_kind == .Float) return RuntimeError.InvalidSpirV; // No f8
|
if (bits == 8 and from_kind == .Float) return RuntimeError.InvalidSpirV; // No f8
|
||||||
const v = (try Value.getPrimitiveField(from_kind, bits, from)).*;
|
const v = try Value.readLane(from_kind, bits, from, lane_index);
|
||||||
break :blk std.math.lossyCast(ToT, v);
|
break :blk std.math.lossyCast(ToT, v);
|
||||||
},
|
},
|
||||||
else => return RuntimeError.InvalidSpirV,
|
else => return RuntimeError.InvalidSpirV,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
fn applyScalar(from_bit_count: SpvWord, to_bit_count: SpvWord, dst: *Value, from: *Value) RuntimeError!void {
|
fn applyLane(from_bit_count: SpvWord, to_bit_count: SpvWord, dst: *Value, from: *const Value, lane_index: usize) RuntimeError!void {
|
||||||
switch (to_bit_count) {
|
switch (to_bit_count) {
|
||||||
inline 8, 16, 32, 64 => |bits| {
|
inline 8, 16, 32, 64 => |bits| {
|
||||||
if (bits == 8 and to_kind == .Float) return RuntimeError.InvalidSpirV; // No f8
|
if (bits == 8 and to_kind == .Float) return RuntimeError.InvalidSpirV; // No f8
|
||||||
const ToT = Value.getPrimitiveFieldType(to_kind, bits);
|
const ToT = Value.getPrimitiveFieldType(to_kind, bits);
|
||||||
(try Value.getPrimitiveField(to_kind, bits, dst)).* = try castLane(ToT, from_bit_count, from);
|
try Value.writeLane(to_kind, bits, dst, lane_index, try castLane(ToT, from_bit_count, from, lane_index));
|
||||||
},
|
},
|
||||||
else => return RuntimeError.InvalidSpirV,
|
else => return RuntimeError.InvalidSpirV,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn castSIMDVector(comptime ToT: type, comptime N: usize, dst_arr: *@Vector(N, ToT), src_arr: *const @Vector(N, ToT)) void {
|
|
||||||
inline for (0..N) |i| dst_arr[i] = std.math.lossyCast(ToT, src_arr[i]);
|
|
||||||
}
|
|
||||||
|
|
||||||
fn castSIMDVectorFromOther(comptime ToT: type, comptime FromT: type, comptime N: usize, dst_arr: *@Vector(N, ToT), src_arr: *const @Vector(N, FromT)) void {
|
|
||||||
inline for (0..N) |i| dst_arr[i] = std.math.lossyCast(ToT, src_arr[i]);
|
|
||||||
}
|
|
||||||
|
|
||||||
fn op(_: std.mem.Allocator, _: SpvWord, rt: *Runtime) RuntimeError!void {
|
fn op(_: 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_value = try rt.results[try rt.it.next()].getValue();
|
||||||
@@ -1013,81 +1005,12 @@ fn ConversionEngine(comptime from_kind: PrimitiveType, comptime to_kind: Primiti
|
|||||||
const from_bits = try Result.resolveLaneBitWidth((try rt.results[src_type_word].getVariant()).Type, rt);
|
const from_bits = try Result.resolveLaneBitWidth((try rt.results[src_type_word].getVariant()).Type, rt);
|
||||||
const to_bits = try Result.resolveLaneBitWidth(target_type, rt);
|
const to_bits = try Result.resolveLaneBitWidth(target_type, rt);
|
||||||
|
|
||||||
switch (dst_value.*) {
|
const dst_lane_count = try dst_value.resolveLaneCount();
|
||||||
.Float => {
|
const src_lane_count = try src_value.resolveLaneCount();
|
||||||
if (to_kind != .Float) return RuntimeError.InvalidSpirV;
|
if (dst_lane_count != src_lane_count) return RuntimeError.InvalidSpirV;
|
||||||
try applyScalar(from_bits, to_bits, dst_value, src_value);
|
|
||||||
},
|
|
||||||
.Int => {
|
|
||||||
if (to_kind != .SInt and to_kind != .UInt) return RuntimeError.InvalidSpirV;
|
|
||||||
try applyScalar(from_bits, to_bits, dst_value, src_value);
|
|
||||||
},
|
|
||||||
.Vector => |dst_vec| {
|
|
||||||
const src_vec = src_value.Vector;
|
|
||||||
if (dst_vec.len != src_vec.len) return RuntimeError.InvalidSpirV;
|
|
||||||
for (dst_vec, src_vec) |*d_lane, *s_lane| {
|
|
||||||
try applyScalar(from_bits, to_bits, d_lane, s_lane);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
.Vector4f32 => |*dst| switch (src_value.*) {
|
for (0..dst_lane_count) |lane_index| {
|
||||||
.Vector4f32 => castSIMDVector(f32, 4, dst, &src_value.Vector4f32),
|
try applyLane(from_bits, to_bits, dst_value, src_value, lane_index);
|
||||||
.Vector4i32 => castSIMDVectorFromOther(f32, i32, 4, dst, &src_value.Vector4i32),
|
|
||||||
.Vector4u32 => castSIMDVectorFromOther(f32, u32, 4, dst, &src_value.Vector4u32),
|
|
||||||
else => return RuntimeError.InvalidSpirV,
|
|
||||||
},
|
|
||||||
.Vector3f32 => |*dst| switch (src_value.*) {
|
|
||||||
.Vector3f32 => castSIMDVector(f32, 3, dst, &src_value.Vector3f32),
|
|
||||||
.Vector3i32 => castSIMDVectorFromOther(f32, i32, 3, dst, &src_value.Vector3i32),
|
|
||||||
.Vector3u32 => castSIMDVectorFromOther(f32, u32, 3, dst, &src_value.Vector3u32),
|
|
||||||
else => return RuntimeError.InvalidSpirV,
|
|
||||||
},
|
|
||||||
.Vector2f32 => |*dst| switch (src_value.*) {
|
|
||||||
.Vector2f32 => castSIMDVector(f32, 2, dst, &src_value.Vector2f32),
|
|
||||||
.Vector2i32 => castSIMDVectorFromOther(f32, i32, 2, dst, &src_value.Vector2i32),
|
|
||||||
.Vector2u32 => castSIMDVectorFromOther(f32, u32, 2, dst, &src_value.Vector2u32),
|
|
||||||
else => return RuntimeError.InvalidSpirV,
|
|
||||||
},
|
|
||||||
|
|
||||||
.Vector4i32 => |*dst| switch (src_value.*) {
|
|
||||||
.Vector4f32 => castSIMDVectorFromOther(i32, f32, 4, dst, &src_value.Vector4f32),
|
|
||||||
.Vector4i32 => castSIMDVector(i32, 4, dst, &src_value.Vector4i32),
|
|
||||||
.Vector4u32 => castSIMDVectorFromOther(i32, u32, 4, dst, &src_value.Vector4u32),
|
|
||||||
else => return RuntimeError.InvalidSpirV,
|
|
||||||
},
|
|
||||||
.Vector3i32 => |*dst| switch (src_value.*) {
|
|
||||||
.Vector3f32 => castSIMDVectorFromOther(i32, f32, 3, dst, &src_value.Vector3f32),
|
|
||||||
.Vector3i32 => castSIMDVector(i32, 3, dst, &src_value.Vector3i32),
|
|
||||||
.Vector3u32 => castSIMDVectorFromOther(i32, u32, 3, dst, &src_value.Vector3u32),
|
|
||||||
else => return RuntimeError.InvalidSpirV,
|
|
||||||
},
|
|
||||||
.Vector2i32 => |*dst| switch (src_value.*) {
|
|
||||||
.Vector2f32 => castSIMDVectorFromOther(i32, f32, 2, dst, &src_value.Vector2f32),
|
|
||||||
.Vector2i32 => castSIMDVector(i32, 2, dst, &src_value.Vector2i32),
|
|
||||||
.Vector2u32 => castSIMDVectorFromOther(i32, u32, 2, dst, &src_value.Vector2u32),
|
|
||||||
else => return RuntimeError.InvalidSpirV,
|
|
||||||
},
|
|
||||||
|
|
||||||
.Vector4u32 => |*dst| switch (src_value.*) {
|
|
||||||
.Vector4f32 => castSIMDVectorFromOther(u32, f32, 4, dst, &src_value.Vector4f32),
|
|
||||||
.Vector4i32 => castSIMDVectorFromOther(u32, i32, 4, dst, &src_value.Vector4i32),
|
|
||||||
.Vector4u32 => castSIMDVector(u32, 4, dst, &src_value.Vector4u32),
|
|
||||||
else => return RuntimeError.InvalidSpirV,
|
|
||||||
},
|
|
||||||
.Vector3u32 => |*dst| switch (src_value.*) {
|
|
||||||
.Vector3f32 => castSIMDVectorFromOther(u32, f32, 3, dst, &src_value.Vector3f32),
|
|
||||||
.Vector3i32 => castSIMDVectorFromOther(u32, i32, 3, dst, &src_value.Vector3i32),
|
|
||||||
.Vector3u32 => castSIMDVector(u32, 3, dst, &src_value.Vector3u32),
|
|
||||||
else => return RuntimeError.InvalidSpirV,
|
|
||||||
},
|
|
||||||
.Vector2u32 => |*dst| switch (src_value.*) {
|
|
||||||
.Vector2f32 => castSIMDVectorFromOther(u32, f32, 2, dst, &src_value.Vector2f32),
|
|
||||||
.Vector2i32 => castSIMDVectorFromOther(u32, i32, 2, dst, &src_value.Vector2i32),
|
|
||||||
.Vector2u32 => castSIMDVector(u32, 2, dst, &src_value.Vector2u32),
|
|
||||||
else => return RuntimeError.InvalidSpirV,
|
|
||||||
},
|
|
||||||
|
|
||||||
else => return RuntimeError.InvalidSpirV,
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@@ -2452,7 +2375,9 @@ fn MathEngine(comptime T: PrimitiveType, comptime Op: MathOp, comptime IsAtomic:
|
|||||||
.VectorTimesMatrix,
|
.VectorTimesMatrix,
|
||||||
=> if (comptime is_int) @mulWithOverflow(op1, op2)[0] else op1 * op2,
|
=> if (comptime is_int) @mulWithOverflow(op1, op2)[0] else op1 * op2,
|
||||||
.Div => blk: {
|
.Div => blk: {
|
||||||
if (op2_is_zero) return RuntimeError.DivisionByZero;
|
if (comptime is_int) {
|
||||||
|
if (op2_is_zero) return RuntimeError.DivisionByZero;
|
||||||
|
}
|
||||||
break :blk if (comptime is_int) @divTrunc(op1, op2) else op1 / op2;
|
break :blk if (comptime is_int) @divTrunc(op1, op2) else op1 / op2;
|
||||||
},
|
},
|
||||||
.Mod => if (op2_is_zero) return RuntimeError.DivisionByZero else @mod(op1, op2),
|
.Mod => if (op2_is_zero) return RuntimeError.DivisionByZero else @mod(op1, op2),
|
||||||
@@ -2624,7 +2549,7 @@ fn MathEngine(comptime T: PrimitiveType, comptime Op: MathOp, comptime IsAtomic:
|
|||||||
|
|
||||||
fn operationSingle(comptime TT: type, ope: TT) RuntimeError!TT {
|
fn operationSingle(comptime TT: type, ope: TT) RuntimeError!TT {
|
||||||
return switch (Op) {
|
return switch (Op) {
|
||||||
.Negate => if (@typeInfo(TT) == .int) std.math.negate(ope) catch return RuntimeError.InvalidSpirV else -ope,
|
.Negate => if (@typeInfo(TT) == .int) @subWithOverflow(@as(TT, 0), ope)[0] else -ope,
|
||||||
else => return RuntimeError.InvalidSpirV,
|
else => return RuntimeError.InvalidSpirV,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
@@ -3042,6 +2967,13 @@ fn copyValue(dst: *Value, src: *const Value) RuntimeError!void {
|
|||||||
else => return RuntimeError.InvalidSpirV,
|
else => return RuntimeError.InvalidSpirV,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
inline fn readWindow(dst_v: *Value, window: []const u8, matrix_stride: ?SpvWord) RuntimeError!void {
|
||||||
|
_ = if (matrix_stride) |stride|
|
||||||
|
try dst_v.writeMatrixWithStride(window, stride)
|
||||||
|
else
|
||||||
|
try dst_v.write(window);
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
if (std.meta.activeTag(dst.*) == .Pointer) {
|
if (std.meta.activeTag(dst.*) == .Pointer) {
|
||||||
@@ -3100,14 +3032,43 @@ fn copyValue(dst: *Value, src: *const Value) RuntimeError!void {
|
|||||||
try helpers.copySlice(dst_slice.?, a.values);
|
try helpers.copySlice(dst_slice.?, a.values);
|
||||||
},
|
},
|
||||||
.Structure => |s| {
|
.Structure => |s| {
|
||||||
|
if (dst.* == .Structure) {
|
||||||
|
@memcpy(@constCast(dst.Structure.offsets), s.offsets);
|
||||||
|
@memcpy(@constCast(dst.Structure.matrix_strides), s.matrix_strides);
|
||||||
|
}
|
||||||
const dst_slice = helpers.getDstSlice(dst);
|
const dst_slice = helpers.getDstSlice(dst);
|
||||||
try helpers.copySlice(dst_slice.?, s.values);
|
try helpers.copySlice(dst_slice.?, s.values);
|
||||||
},
|
},
|
||||||
.Pointer => |ptr| switch (ptr.ptr) {
|
.Pointer => |ptr| switch (ptr.ptr) {
|
||||||
.common => |src_val_ptr| try copyValue(dst, src_val_ptr),
|
.common => |src_val_ptr| {
|
||||||
.f32_ptr => |src_f32_ptr| try helpers.readF32(dst, src_f32_ptr),
|
if (ptr.uniform_slice_window) |window| {
|
||||||
.i32_ptr => |src_i32_ptr| try helpers.readI32(dst, src_i32_ptr),
|
try copyValue(dst, src_val_ptr);
|
||||||
.u32_ptr => |src_u32_ptr| try helpers.readU32(dst, src_u32_ptr),
|
try helpers.readWindow(dst, window, ptr.matrix_stride);
|
||||||
|
} else {
|
||||||
|
try copyValue(dst, src_val_ptr);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
.f32_ptr => |src_f32_ptr| {
|
||||||
|
if (ptr.uniform_slice_window) |window| {
|
||||||
|
try helpers.readWindow(dst, window, ptr.matrix_stride);
|
||||||
|
} else {
|
||||||
|
try helpers.readF32(dst, src_f32_ptr);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
.i32_ptr => |src_i32_ptr| {
|
||||||
|
if (ptr.uniform_slice_window) |window| {
|
||||||
|
try helpers.readWindow(dst, window, ptr.matrix_stride);
|
||||||
|
} else {
|
||||||
|
try helpers.readI32(dst, src_i32_ptr);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
.u32_ptr => |src_u32_ptr| {
|
||||||
|
if (ptr.uniform_slice_window) |window| {
|
||||||
|
try helpers.readWindow(dst, window, ptr.matrix_stride);
|
||||||
|
} else {
|
||||||
|
try helpers.readU32(dst, src_u32_ptr);
|
||||||
|
}
|
||||||
|
},
|
||||||
},
|
},
|
||||||
.RuntimeArray => |src_arr| switch (dst.*) {
|
.RuntimeArray => |src_arr| switch (dst.*) {
|
||||||
.RuntimeArray => |dst_arr| @memcpy(dst_arr.data, src_arr.data),
|
.RuntimeArray => |dst_arr| @memcpy(dst_arr.data, src_arr.data),
|
||||||
@@ -3497,134 +3458,111 @@ fn opControlBarrier(_: std.mem.Allocator, _: SpvWord, rt: *Runtime) RuntimeError
|
|||||||
return RuntimeError.Barrier;
|
return RuntimeError.Barrier;
|
||||||
}
|
}
|
||||||
|
|
||||||
fn opCompositeConstruct(_: std.mem.Allocator, word_count: SpvWord, rt: *Runtime) RuntimeError!void {
|
fn opCompositeConstruct(allocator: std.mem.Allocator, word_count: SpvWord, rt: *Runtime) RuntimeError!void {
|
||||||
_ = rt.it.skip();
|
const target_type_word = try rt.it.next();
|
||||||
|
const target_type = (try rt.results[target_type_word].getVariant()).Type;
|
||||||
const id = try rt.it.next();
|
const id = try rt.it.next();
|
||||||
|
|
||||||
const index_count = word_count - 2;
|
const index_count: usize = @intCast(word_count - 2);
|
||||||
|
const operand_ids = allocator.alloc(SpvWord, index_count) catch return RuntimeError.OutOfMemory;
|
||||||
|
defer allocator.free(operand_ids);
|
||||||
|
for (operand_ids) |*operand_id| {
|
||||||
|
operand_id.* = try rt.it.next();
|
||||||
|
}
|
||||||
|
|
||||||
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]) |*elem| {
|
for (target[0..index_count], operand_ids) |*elem, operand_id| {
|
||||||
const elem_value = (try rt.results[try rt.it.next()].getVariant()).Constant.value;
|
try copyValue(elem, try rt.results[operand_id].getValue());
|
||||||
elem.* = elem_value;
|
|
||||||
}
|
}
|
||||||
|
rt.clearDerivative(allocator, id);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const vectorRoutines = struct {
|
const primitive_type: PrimitiveType = switch (target_type) {
|
||||||
fn routines(value2: *Value, rt2: *Runtime) RuntimeError!void {
|
.Float, .Vector, .Vector2f32, .Vector3f32, .Vector4f32 => .Float,
|
||||||
switch (value2.*) {
|
.Int, .Vector2i32, .Vector3i32, .Vector4i32 => .SInt,
|
||||||
.Vector4f32 => |*vec| inline for (0..4) |i| {
|
.Vector2u32, .Vector3u32, .Vector4u32 => .UInt,
|
||||||
switch ((try rt2.results[try rt2.it.next()].getVariant()).Constant.value) {
|
else => return RuntimeError.InvalidValueType,
|
||||||
.Vector4f32 => |v| {
|
};
|
||||||
vec.* = v;
|
|
||||||
return;
|
|
||||||
},
|
|
||||||
.Float => |f| vec[i] = f.value.float32,
|
|
||||||
else => return RuntimeError.InvalidValueType,
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
.Vector3f32 => |*vec| inline for (0..3) |i| {
|
const lane_bits = try Result.resolveLaneBitWidth(target_type, rt);
|
||||||
switch ((try rt2.results[try rt2.it.next()].getVariant()).Constant.value) {
|
const target_lane_count = try value.resolveLaneCount();
|
||||||
.Vector3f32 => |v| {
|
|
||||||
vec.* = v;
|
|
||||||
return;
|
|
||||||
},
|
|
||||||
.Float => |f| vec[i] = f.value.float32,
|
|
||||||
else => return RuntimeError.InvalidValueType,
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
.Vector2f32 => |*vec| inline for (0..2) |i| {
|
var dx: ?Value = if (primitive_type == .Float)
|
||||||
switch ((try rt2.results[try rt2.it.next()].getVariant()).Constant.value) {
|
try Value.init(allocator, rt.results, target_type_word, false)
|
||||||
.Vector2f32 => |v| {
|
else
|
||||||
vec.* = v;
|
null;
|
||||||
return;
|
defer if (dx) |*dx_value| dx_value.deinit(allocator);
|
||||||
},
|
var dy: ?Value = if (primitive_type == .Float)
|
||||||
.Float => |f| vec[i] = f.value.float32,
|
try Value.init(allocator, rt.results, target_type_word, false)
|
||||||
else => return RuntimeError.InvalidValueType,
|
else
|
||||||
}
|
null;
|
||||||
},
|
defer if (dy) |*dy_value| dy_value.deinit(allocator);
|
||||||
|
|
||||||
.Vector4i32 => |*vec| inline for (0..4) |i| {
|
var has_derivative = false;
|
||||||
switch ((try rt2.results[try rt2.it.next()].getVariant()).Constant.value) {
|
var target_lane_index: usize = 0;
|
||||||
.Vector4i32 => |v| {
|
for (operand_ids) |operand_id| {
|
||||||
vec.* = v;
|
const operand_value = try rt.results[operand_id].getValue();
|
||||||
return;
|
const operand_lane_count = try operand_value.resolveLaneCount();
|
||||||
},
|
const derivative = rt.derivatives.get(operand_id);
|
||||||
.Int => |int| vec[i] = int.value.sint32,
|
|
||||||
else => return RuntimeError.InvalidValueType,
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
.Vector3i32 => |*vec| inline for (0..3) |i| {
|
for (0..operand_lane_count) |operand_lane_index| {
|
||||||
switch ((try rt2.results[try rt2.it.next()].getVariant()).Constant.value) {
|
if (target_lane_index >= target_lane_count) return RuntimeError.InvalidSpirV;
|
||||||
.Vector3i32 => |v| {
|
|
||||||
vec.* = v;
|
|
||||||
return;
|
|
||||||
},
|
|
||||||
.Int => |int| vec[i] = int.value.sint32,
|
|
||||||
else => return RuntimeError.InvalidValueType,
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
.Vector2i32 => |*vec| inline for (0..2) |i| {
|
switch (primitive_type) {
|
||||||
switch ((try rt2.results[try rt2.it.next()].getVariant()).Constant.value) {
|
.Float => switch (lane_bits) {
|
||||||
.Vector2i32 => |v| {
|
inline 16, 32, 64 => |bits| {
|
||||||
vec.* = v;
|
{
|
||||||
return;
|
const lane = try Value.readLane(.Float, bits, operand_value, operand_lane_index);
|
||||||
},
|
try Value.writeLane(.Float, bits, value, target_lane_index, lane);
|
||||||
.Int => |int| vec[i] = int.value.sint32,
|
|
||||||
else => return RuntimeError.InvalidValueType,
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
.Vector4u32 => |*vec| inline for (0..4) |i| {
|
const FloatT = Value.getPrimitiveFieldType(.Float, bits);
|
||||||
switch ((try rt2.results[try rt2.it.next()].getVariant()).Constant.value) {
|
const dx_lane: FloatT = if (derivative) |d| blk: {
|
||||||
.Vector4u32 => |v| {
|
has_derivative = true;
|
||||||
vec.* = v;
|
break :blk try Value.readLane(.Float, bits, &d.dx, operand_lane_index);
|
||||||
return;
|
} else 0;
|
||||||
},
|
const dy_lane: FloatT = if (derivative) |d| blk: {
|
||||||
.Int => |int| vec[i] = int.value.uint32,
|
has_derivative = true;
|
||||||
else => return RuntimeError.InvalidValueType,
|
break :blk try Value.readLane(.Float, bits, &d.dy, operand_lane_index);
|
||||||
}
|
} else 0;
|
||||||
|
try Value.writeLane(.Float, bits, &dx.?, target_lane_index, dx_lane);
|
||||||
|
try Value.writeLane(.Float, bits, &dy.?, target_lane_index, dy_lane);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
else => return RuntimeError.InvalidSpirV,
|
||||||
},
|
},
|
||||||
|
.SInt => switch (lane_bits) {
|
||||||
.Vector3u32 => |*vec| inline for (0..3) |i| {
|
inline 8, 16, 32, 64 => |bits| {
|
||||||
switch ((try rt2.results[try rt2.it.next()].getVariant()).Constant.value) {
|
{
|
||||||
.Vector3u32 => |v| {
|
const lane = try Value.readLane(.SInt, bits, operand_value, operand_lane_index);
|
||||||
vec.* = v;
|
try Value.writeLane(.SInt, bits, value, target_lane_index, lane);
|
||||||
return;
|
}
|
||||||
},
|
},
|
||||||
.Int => |int| vec[i] = int.value.uint32,
|
else => return RuntimeError.InvalidSpirV,
|
||||||
else => return RuntimeError.InvalidValueType,
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
|
.UInt => switch (lane_bits) {
|
||||||
.Vector2u32 => |*vec| inline for (0..2) |i| {
|
inline 8, 16, 32, 64 => |bits| {
|
||||||
switch ((try rt2.results[try rt2.it.next()].getVariant()).Constant.value) {
|
{
|
||||||
.Vector2u32 => |v| {
|
const lane = try Value.readLane(.UInt, bits, operand_value, operand_lane_index);
|
||||||
vec.* = v;
|
try Value.writeLane(.UInt, bits, value, target_lane_index, lane);
|
||||||
return;
|
}
|
||||||
},
|
},
|
||||||
.Int => |int| vec[i] = int.value.uint32,
|
else => return RuntimeError.InvalidSpirV,
|
||||||
else => return RuntimeError.InvalidValueType,
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
|
|
||||||
else => return RuntimeError.InvalidValueType,
|
else => return RuntimeError.InvalidValueType,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
target_lane_index += 1;
|
||||||
}
|
}
|
||||||
}.routines;
|
}
|
||||||
|
|
||||||
switch (value.*) {
|
if (target_lane_index != target_lane_count) return RuntimeError.InvalidSpirV;
|
||||||
.RuntimeArray => |arr| {
|
|
||||||
_ = arr;
|
|
||||||
return RuntimeError.ToDo;
|
|
||||||
},
|
|
||||||
|
|
||||||
else => try vectorRoutines(value, rt),
|
if (has_derivative) {
|
||||||
|
try rt.setDerivative(allocator, id, &dx.?, &dy.?);
|
||||||
|
} else {
|
||||||
|
rt.clearDerivative(allocator, id);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -4544,17 +4482,23 @@ fn opFwidth(allocator: std.mem.Allocator, _: SpvWord, rt: *Runtime) RuntimeError
|
|||||||
const id = try rt.it.next();
|
const id = try rt.it.next();
|
||||||
const operand = try rt.it.next();
|
const operand = try rt.it.next();
|
||||||
|
|
||||||
const derivative = rt.derivatives.get(operand) orelse return RuntimeError.UnsupportedSpirV;
|
|
||||||
const dst = try rt.results[id].getValue();
|
const dst = try rt.results[id].getValue();
|
||||||
const lane_bits = try Result.resolveLaneBitWidth(target_type, rt);
|
const lane_bits = try Result.resolveLaneBitWidth(target_type, rt);
|
||||||
const lane_count = try Result.resolveLaneCount(target_type);
|
const lane_count = try Result.resolveLaneCount(target_type);
|
||||||
|
const derivative = rt.derivatives.get(operand);
|
||||||
|
|
||||||
switch (lane_bits) {
|
switch (lane_bits) {
|
||||||
inline 16, 32, 64 => |bits| {
|
inline 16, 32, 64 => |bits| {
|
||||||
const FloatT = Value.getPrimitiveFieldType(.Float, bits);
|
const FloatT = Value.getPrimitiveFieldType(.Float, bits);
|
||||||
for (0..lane_count) |lane_index| {
|
for (0..lane_count) |lane_index| {
|
||||||
const dx = try Value.readLane(.Float, bits, &derivative.dx, lane_index);
|
const dx: FloatT = if (derivative) |d|
|
||||||
const dy = try Value.readLane(.Float, bits, &derivative.dy, lane_index);
|
try Value.readLane(.Float, bits, &d.dx, lane_index)
|
||||||
|
else
|
||||||
|
0;
|
||||||
|
const dy: FloatT = if (derivative) |d|
|
||||||
|
try Value.readLane(.Float, bits, &d.dy, lane_index)
|
||||||
|
else
|
||||||
|
0;
|
||||||
try Value.writeLane(.Float, bits, dst, lane_index, @as(FloatT, @abs(dx) + @abs(dy)));
|
try Value.writeLane(.Float, bits, dst, lane_index, @as(FloatT, @abs(dx) + @abs(dy)));
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|||||||
+101
@@ -64,6 +64,107 @@ test "Primitives casts" {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
test "Vector float casts" {
|
||||||
|
const allocator = std.testing.allocator;
|
||||||
|
const cases = [_]struct {
|
||||||
|
len: usize,
|
||||||
|
source_type: []const u8,
|
||||||
|
target_type: []const u8,
|
||||||
|
values: []const f64,
|
||||||
|
}{
|
||||||
|
.{ .len = 2, .source_type = "f32", .target_type = "f64", .values = &.{ 1.25, -2.5 } },
|
||||||
|
.{ .len = 3, .source_type = "f32", .target_type = "f64", .values = &.{ 1.25, -2.5, 3.75 } },
|
||||||
|
.{ .len = 4, .source_type = "f32", .target_type = "f64", .values = &.{ 1.25, -2.5, 3.75, -4.5 } },
|
||||||
|
.{ .len = 2, .source_type = "f64", .target_type = "f32", .values = &.{ 1.25, -2.5 } },
|
||||||
|
.{ .len = 3, .source_type = "f64", .target_type = "f32", .values = &.{ 1.25, -2.5, 3.75 } },
|
||||||
|
.{ .len = 4, .source_type = "f64", .target_type = "f32", .values = &.{ 1.25, -2.5, 3.75, -4.5 } },
|
||||||
|
};
|
||||||
|
|
||||||
|
inline for (cases) |c| {
|
||||||
|
const constructor_values = try std.fmt.allocPrint(
|
||||||
|
allocator,
|
||||||
|
switch (c.len) {
|
||||||
|
2 => "{d}, {d}",
|
||||||
|
3 => "{d}, {d}, {d}",
|
||||||
|
4 => "{d}, {d}, {d}, {d}",
|
||||||
|
else => unreachable,
|
||||||
|
},
|
||||||
|
switch (c.len) {
|
||||||
|
2 => .{ c.values[0], c.values[1] },
|
||||||
|
3 => .{ c.values[0], c.values[1], c.values[2] },
|
||||||
|
4 => .{ c.values[0], c.values[1], c.values[2], c.values[3] },
|
||||||
|
else => unreachable,
|
||||||
|
},
|
||||||
|
);
|
||||||
|
defer allocator.free(constructor_values);
|
||||||
|
|
||||||
|
const output_values = switch (c.len) {
|
||||||
|
2 => "casted.x, casted.y, 0.0, 1.0",
|
||||||
|
3 => "casted.x, casted.y, casted.z, 1.0",
|
||||||
|
4 => "casted.x, casted.y, casted.z, casted.w",
|
||||||
|
else => unreachable,
|
||||||
|
};
|
||||||
|
|
||||||
|
const shader = try std.fmt.allocPrint(
|
||||||
|
allocator,
|
||||||
|
\\ [nzsl_version("1.1")]
|
||||||
|
\\ [feature(float64)]
|
||||||
|
\\ module;
|
||||||
|
\\
|
||||||
|
\\ struct FragOut
|
||||||
|
\\ {{
|
||||||
|
\\ [location(0)] color: vec4[{s}]
|
||||||
|
\\ }}
|
||||||
|
\\
|
||||||
|
\\ [entry(frag)]
|
||||||
|
\\ fn main() -> FragOut
|
||||||
|
\\ {{
|
||||||
|
\\ let base = vec{d}[{s}]({s});
|
||||||
|
\\ let casted = vec{d}[{s}](base);
|
||||||
|
\\
|
||||||
|
\\ let output: FragOut;
|
||||||
|
\\ output.color = vec4[{s}]({s});
|
||||||
|
\\ return output;
|
||||||
|
\\ }}
|
||||||
|
,
|
||||||
|
.{
|
||||||
|
c.target_type,
|
||||||
|
c.len,
|
||||||
|
c.source_type,
|
||||||
|
constructor_values,
|
||||||
|
c.len,
|
||||||
|
c.target_type,
|
||||||
|
c.target_type,
|
||||||
|
output_values,
|
||||||
|
},
|
||||||
|
);
|
||||||
|
defer allocator.free(shader);
|
||||||
|
|
||||||
|
const code = try compileNzsl(allocator, shader);
|
||||||
|
defer allocator.free(code);
|
||||||
|
|
||||||
|
if (std.mem.eql(u8, c.target_type, "f32")) {
|
||||||
|
var expected = [_]f32{ 0.0, 0.0, 0.0, 1.0 };
|
||||||
|
for (c.values, 0..) |value, i| expected[i] = @floatCast(value);
|
||||||
|
try case.expect(.{
|
||||||
|
.source = code,
|
||||||
|
.expected_outputs = &.{
|
||||||
|
std.mem.asBytes(&expected),
|
||||||
|
},
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
var expected = [_]f64{ 0.0, 0.0, 0.0, 1.0 };
|
||||||
|
for (c.values, 0..) |value, i| expected[i] = @floatCast(value);
|
||||||
|
try case.expect(.{
|
||||||
|
.source = code,
|
||||||
|
.expected_outputs = &.{
|
||||||
|
std.mem.asBytes(&expected),
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
test "Primitives bitcasts" {
|
test "Primitives bitcasts" {
|
||||||
const allocator = std.testing.allocator;
|
const allocator = std.testing.allocator;
|
||||||
const types = [_][2]type{
|
const types = [_][2]type{
|
||||||
|
|||||||
@@ -156,6 +156,38 @@ test "Maths vectors" {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
test "Unsigned vector negate wraps" {
|
||||||
|
const allocator = std.testing.allocator;
|
||||||
|
const shader =
|
||||||
|
\\ [nzsl_version("1.1")]
|
||||||
|
\\ module;
|
||||||
|
\\
|
||||||
|
\\ struct FragOut
|
||||||
|
\\ {
|
||||||
|
\\ [location(0)] color: vec4[u32]
|
||||||
|
\\ }
|
||||||
|
\\
|
||||||
|
\\ [entry(frag)]
|
||||||
|
\\ fn main() -> FragOut
|
||||||
|
\\ {
|
||||||
|
\\ let v = vec4[u32](1, 2, 2147483648, 4294967295);
|
||||||
|
\\ let negated = -v;
|
||||||
|
\\ let output: FragOut;
|
||||||
|
\\ output.color = negated;
|
||||||
|
\\ return output;
|
||||||
|
\\ }
|
||||||
|
;
|
||||||
|
const code = try compileNzsl(allocator, shader);
|
||||||
|
defer allocator.free(code);
|
||||||
|
|
||||||
|
try case.expect(.{
|
||||||
|
.source = code,
|
||||||
|
.expected_outputs = &.{
|
||||||
|
std.mem.asBytes(&[_]u32{ 4294967295, 4294967294, 2147483648, 1 }),
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
// Tests all mathematical operation on vec2/3/4 with scalars with all NZSL supported primitive types
|
// Tests all mathematical operation on vec2/3/4 with scalars with all NZSL supported primitive types
|
||||||
test "Maths vectors with scalars" {
|
test "Maths vectors with scalars" {
|
||||||
const allocator = std.testing.allocator;
|
const allocator = std.testing.allocator;
|
||||||
|
|||||||
Reference in New Issue
Block a user