Compare commits
3 Commits
42554a5cc1
...
f82e3e1629
| Author | SHA1 | Date | |
|---|---|---|---|
|
f82e3e1629
|
|||
|
391f4415d9
|
|||
|
1f43dc95df
|
+51
-13
@@ -274,7 +274,56 @@ fn applyStructMemberInterfaceDecorations(self: *Self, storage_class: spv.SpvStor
|
||||
}
|
||||
|
||||
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)
|
||||
continue;
|
||||
|
||||
@@ -323,18 +372,7 @@ fn applyDecorations(self: *Self, allocator: std.mem.Allocator) ModuleError!void
|
||||
.StorageBuffer,
|
||||
.Uniform,
|
||||
.PushConstant,
|
||||
=> if (v.value == .Structure) {
|
||||
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 => {},
|
||||
};
|
||||
},
|
||||
=> try helpers.applyValueLayout(self.results, &v.value, v.type_word),
|
||||
else => {},
|
||||
}
|
||||
},
|
||||
|
||||
+20
-12
@@ -501,6 +501,24 @@ pub const Value = union(Type) {
|
||||
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 {
|
||||
const vecRoutine = struct {
|
||||
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.*) {
|
||||
.Matrix => |*columns| blk: {
|
||||
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);
|
||||
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 - 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;
|
||||
_ = columns;
|
||||
break :blk try v.writeMatrixWithStride(input[member_offset..], matrix_stride);
|
||||
},
|
||||
else => try v.write(input[member_offset..]),
|
||||
};
|
||||
|
||||
+288
-201
@@ -70,6 +70,7 @@ const BitOp = enum {
|
||||
|
||||
const ImageOp = enum {
|
||||
Fetch,
|
||||
Gather,
|
||||
QueryLevels,
|
||||
QueryLod,
|
||||
QuerySamples,
|
||||
@@ -202,6 +203,7 @@ pub const SetupDispatcher = block: {
|
||||
.ISubBorrow = autoSetupConstant,
|
||||
.Image = autoSetupConstant,
|
||||
.ImageFetch = autoSetupConstant,
|
||||
.ImageGather = autoSetupConstant,
|
||||
.ImageQueryLevels = autoSetupConstant,
|
||||
.ImageQueryLod = opDerivativeSetup,
|
||||
.ImageQuerySamples = autoSetupConstant,
|
||||
@@ -379,6 +381,7 @@ pub fn initRuntimeDispatcher() void {
|
||||
runtime_dispatcher[@intFromEnum(spv.SpvOp.ISubBorrow)] = opISubBorrow;
|
||||
runtime_dispatcher[@intFromEnum(spv.SpvOp.Image)] = ImageEngine(.Resolve).op;
|
||||
runtime_dispatcher[@intFromEnum(spv.SpvOp.ImageFetch)] = ImageEngine(.Fetch).op;
|
||||
runtime_dispatcher[@intFromEnum(spv.SpvOp.ImageGather)] = ImageEngine(.Gather).op;
|
||||
runtime_dispatcher[@intFromEnum(spv.SpvOp.ImageQueryLevels)] = ImageEngine(.QueryLevels).op;
|
||||
runtime_dispatcher[@intFromEnum(spv.SpvOp.ImageQueryLod)] = ImageEngine(.QueryLod).op;
|
||||
runtime_dispatcher[@intFromEnum(spv.SpvOp.ImageQuerySamples)] = ImageEngine(.QuerySamples).op;
|
||||
@@ -969,36 +972,28 @@ fn CondEngine(comptime T: PrimitiveType, comptime Op: CondOp) type {
|
||||
|
||||
fn ConversionEngine(comptime from_kind: PrimitiveType, comptime to_kind: PrimitiveType) type {
|
||||
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) {
|
||||
inline 8, 16, 32, 64 => |bits| blk: {
|
||||
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);
|
||||
},
|
||||
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) {
|
||||
inline 8, 16, 32, 64 => |bits| {
|
||||
if (bits == 8 and to_kind == .Float) return RuntimeError.InvalidSpirV; // No f8
|
||||
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,
|
||||
}
|
||||
}
|
||||
|
||||
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 {
|
||||
const target_type = (try rt.results[try rt.it.next()].getVariant()).Type;
|
||||
const dst_value = try rt.results[try rt.it.next()].getValue();
|
||||
@@ -1010,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 to_bits = try Result.resolveLaneBitWidth(target_type, rt);
|
||||
|
||||
switch (dst_value.*) {
|
||||
.Float => {
|
||||
if (to_kind != .Float) 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);
|
||||
}
|
||||
},
|
||||
const dst_lane_count = try dst_value.resolveLaneCount();
|
||||
const src_lane_count = try src_value.resolveLaneCount();
|
||||
if (dst_lane_count != src_lane_count) return RuntimeError.InvalidSpirV;
|
||||
|
||||
.Vector4f32 => |*dst| switch (src_value.*) {
|
||||
.Vector4f32 => castSIMDVector(f32, 4, dst, &src_value.Vector4f32),
|
||||
.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,
|
||||
for (0..dst_lane_count) |lane_index| {
|
||||
try applyLane(from_bits, to_bits, dst_value, src_value, lane_index);
|
||||
}
|
||||
}
|
||||
};
|
||||
@@ -1655,6 +1581,120 @@ fn ImageEngine(comptime Op: ImageOp) type {
|
||||
try writeFloatScalar(dst, try rt.image_api.sampleImageDref(driver_image, driver_sampler, dim, x, y, z, dref, lod, offset));
|
||||
}
|
||||
|
||||
fn gatherCoord(index: i32, extent: u32) f32 {
|
||||
return (@as(f32, @floatFromInt(index)) + 0.5) / @as(f32, @floatFromInt(extent));
|
||||
}
|
||||
|
||||
fn sampleImageGather(rt: *Runtime, dst: *Value, driver_image: *anyopaque, driver_sampler: *anyopaque, dim: spv.SpvDim, x: f32, y: f32, z: f32, component: usize, offset: Runtime.ImageOffset) RuntimeError!void {
|
||||
const size = try rt.image_api.queryImageSize(driver_image, dim, false, 0);
|
||||
if (size.x == 0) return RuntimeError.InvalidSpirV;
|
||||
|
||||
const width_f: f32 = @floatFromInt(size.x);
|
||||
const height = if (size.y == 0) 1 else size.y;
|
||||
const height_f: f32 = @floatFromInt(height);
|
||||
const base_x: i32 = @intFromFloat(@floor(x * width_f - 0.5));
|
||||
const base_y: i32 = @intFromFloat(@floor(y * height_f - 0.5));
|
||||
const gather_x = [_]i32{ base_x, base_x + 1, base_x + 1, base_x };
|
||||
const gather_y = [_]i32{ base_y + 1, base_y + 1, base_y, base_y };
|
||||
|
||||
switch (dst.*) {
|
||||
.Vector4f32,
|
||||
.Vector3f32,
|
||||
.Vector2f32,
|
||||
=> {
|
||||
var result: Runtime.Vec4(f32) = undefined;
|
||||
inline for (0..4) |i| {
|
||||
const texel = try rt.image_api.sampleImageFloat4(
|
||||
driver_image,
|
||||
driver_sampler,
|
||||
dim,
|
||||
gatherCoord(gather_x[i], size.x),
|
||||
gatherCoord(gather_y[i], height),
|
||||
z,
|
||||
0.0,
|
||||
offset,
|
||||
);
|
||||
const values = [_]f32{ texel.x, texel.y, texel.z, texel.w };
|
||||
@field(result, switch (i) {
|
||||
0 => "x",
|
||||
1 => "y",
|
||||
2 => "z",
|
||||
3 => "w",
|
||||
else => unreachable,
|
||||
}) = if (component < values.len) values[component] else return RuntimeError.InvalidSpirV;
|
||||
}
|
||||
try writeFloatTexel(dst, result);
|
||||
},
|
||||
.Vector4i32,
|
||||
.Vector3i32,
|
||||
.Vector2i32,
|
||||
.Vector4u32,
|
||||
.Vector3u32,
|
||||
.Vector2u32,
|
||||
=> {
|
||||
var result: Runtime.Vec4(u32) = undefined;
|
||||
inline for (0..4) |i| {
|
||||
const texel = try rt.image_api.sampleImageInt4(
|
||||
driver_image,
|
||||
driver_sampler,
|
||||
dim,
|
||||
gatherCoord(gather_x[i], size.x),
|
||||
gatherCoord(gather_y[i], height),
|
||||
z,
|
||||
0.0,
|
||||
offset,
|
||||
);
|
||||
const values = [_]u32{ texel.x, texel.y, texel.z, texel.w };
|
||||
@field(result, switch (i) {
|
||||
0 => "x",
|
||||
1 => "y",
|
||||
2 => "z",
|
||||
3 => "w",
|
||||
else => unreachable,
|
||||
}) = if (component < values.len) values[component] else return RuntimeError.InvalidSpirV;
|
||||
}
|
||||
try writeIntTexel(dst, result);
|
||||
},
|
||||
.Vector => |lanes| {
|
||||
if (lanes.len == 0) return RuntimeError.InvalidSpirV;
|
||||
switch (lanes[0]) {
|
||||
.Float => {
|
||||
var result: Runtime.Vec4(f32) = undefined;
|
||||
inline for (0..4) |i| {
|
||||
const texel = try rt.image_api.sampleImageFloat4(driver_image, driver_sampler, dim, gatherCoord(gather_x[i], size.x), gatherCoord(gather_y[i], height), z, 0.0, offset);
|
||||
const values = [_]f32{ texel.x, texel.y, texel.z, texel.w };
|
||||
@field(result, switch (i) {
|
||||
0 => "x",
|
||||
1 => "y",
|
||||
2 => "z",
|
||||
3 => "w",
|
||||
else => unreachable,
|
||||
}) = if (component < values.len) values[component] else return RuntimeError.InvalidSpirV;
|
||||
}
|
||||
try writeFloatTexel(dst, result);
|
||||
},
|
||||
.Int => {
|
||||
var result: Runtime.Vec4(u32) = undefined;
|
||||
inline for (0..4) |i| {
|
||||
const texel = try rt.image_api.sampleImageInt4(driver_image, driver_sampler, dim, gatherCoord(gather_x[i], size.x), gatherCoord(gather_y[i], height), z, 0.0, offset);
|
||||
const values = [_]u32{ texel.x, texel.y, texel.z, texel.w };
|
||||
@field(result, switch (i) {
|
||||
0 => "x",
|
||||
1 => "y",
|
||||
2 => "z",
|
||||
3 => "w",
|
||||
else => unreachable,
|
||||
}) = if (component < values.len) values[component] else return RuntimeError.InvalidSpirV;
|
||||
}
|
||||
try writeIntTexel(dst, result);
|
||||
},
|
||||
else => return RuntimeError.InvalidValueType,
|
||||
}
|
||||
},
|
||||
else => return RuntimeError.InvalidValueType,
|
||||
}
|
||||
}
|
||||
|
||||
fn writeImage(rt: *Runtime, texel: *const Value, driver_image: *anyopaque, dim: spv.SpvDim, x: i32, y: i32, z: i32) RuntimeError!void {
|
||||
switch (texel.*) {
|
||||
.Float,
|
||||
@@ -1980,6 +2020,32 @@ fn ImageEngine(comptime Op: ImageOp) type {
|
||||
);
|
||||
},
|
||||
|
||||
.Gather => {
|
||||
const sampled_image_operand = try resolveSampledImage(image, rt);
|
||||
const coords = .{
|
||||
.x = try readSampleCoordLane(coordinate, 0),
|
||||
.y = readSampleCoordLane(coordinate, 1) catch 0,
|
||||
.z = readSampleCoordLane(coordinate, 2) catch 0,
|
||||
};
|
||||
const component_value = try rt.results[try rt.it.next()].getValue();
|
||||
const component: usize = @intCast(try readIntLane(component_value, 0));
|
||||
const image_operands = if (word_count > 5) try rt.it.next() else 0;
|
||||
const parsed_operands = try parseImageOperands(rt, image_operands);
|
||||
|
||||
try sampleImageGather(
|
||||
rt,
|
||||
dst,
|
||||
sampled_image_operand.driver_image,
|
||||
sampled_image_operand.driver_sampler,
|
||||
sampled_image_operand.dim,
|
||||
coords.x,
|
||||
coords.y,
|
||||
coords.z,
|
||||
component,
|
||||
parsed_operands.offset,
|
||||
);
|
||||
},
|
||||
|
||||
else => return RuntimeError.InvalidSpirV,
|
||||
}
|
||||
}
|
||||
@@ -2309,7 +2375,9 @@ fn MathEngine(comptime T: PrimitiveType, comptime Op: MathOp, comptime IsAtomic:
|
||||
.VectorTimesMatrix,
|
||||
=> if (comptime is_int) @mulWithOverflow(op1, op2)[0] else op1 * op2,
|
||||
.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;
|
||||
},
|
||||
.Mod => if (op2_is_zero) return RuntimeError.DivisionByZero else @mod(op1, op2),
|
||||
@@ -2481,7 +2549,7 @@ fn MathEngine(comptime T: PrimitiveType, comptime Op: MathOp, comptime IsAtomic:
|
||||
|
||||
fn operationSingle(comptime TT: type, ope: TT) RuntimeError!TT {
|
||||
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,
|
||||
};
|
||||
}
|
||||
@@ -2899,6 +2967,13 @@ fn copyValue(dst: *Value, src: *const Value) RuntimeError!void {
|
||||
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) {
|
||||
@@ -2957,14 +3032,43 @@ fn copyValue(dst: *Value, src: *const Value) RuntimeError!void {
|
||||
try helpers.copySlice(dst_slice.?, a.values);
|
||||
},
|
||||
.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);
|
||||
try helpers.copySlice(dst_slice.?, s.values);
|
||||
},
|
||||
.Pointer => |ptr| switch (ptr.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),
|
||||
.common => |src_val_ptr| {
|
||||
if (ptr.uniform_slice_window) |window| {
|
||||
try copyValue(dst, src_val_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 => |dst_arr| @memcpy(dst_arr.data, src_arr.data),
|
||||
@@ -3354,134 +3458,111 @@ fn opControlBarrier(_: std.mem.Allocator, _: SpvWord, rt: *Runtime) RuntimeError
|
||||
return RuntimeError.Barrier;
|
||||
}
|
||||
|
||||
fn opCompositeConstruct(_: std.mem.Allocator, word_count: SpvWord, rt: *Runtime) RuntimeError!void {
|
||||
_ = rt.it.skip();
|
||||
fn opCompositeConstruct(allocator: std.mem.Allocator, word_count: SpvWord, rt: *Runtime) RuntimeError!void {
|
||||
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 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;
|
||||
if (value.getCompositeDataOrNull()) |target| {
|
||||
for (target[0..index_count]) |*elem| {
|
||||
const elem_value = (try rt.results[try rt.it.next()].getVariant()).Constant.value;
|
||||
elem.* = elem_value;
|
||||
for (target[0..index_count], operand_ids) |*elem, operand_id| {
|
||||
try copyValue(elem, try rt.results[operand_id].getValue());
|
||||
}
|
||||
rt.clearDerivative(allocator, id);
|
||||
return;
|
||||
}
|
||||
|
||||
const vectorRoutines = struct {
|
||||
fn routines(value2: *Value, rt2: *Runtime) RuntimeError!void {
|
||||
switch (value2.*) {
|
||||
.Vector4f32 => |*vec| inline for (0..4) |i| {
|
||||
switch ((try rt2.results[try rt2.it.next()].getVariant()).Constant.value) {
|
||||
.Vector4f32 => |v| {
|
||||
vec.* = v;
|
||||
return;
|
||||
},
|
||||
.Float => |f| vec[i] = f.value.float32,
|
||||
else => return RuntimeError.InvalidValueType,
|
||||
}
|
||||
},
|
||||
const primitive_type: PrimitiveType = switch (target_type) {
|
||||
.Float, .Vector, .Vector2f32, .Vector3f32, .Vector4f32 => .Float,
|
||||
.Int, .Vector2i32, .Vector3i32, .Vector4i32 => .SInt,
|
||||
.Vector2u32, .Vector3u32, .Vector4u32 => .UInt,
|
||||
else => return RuntimeError.InvalidValueType,
|
||||
};
|
||||
|
||||
.Vector3f32 => |*vec| inline for (0..3) |i| {
|
||||
switch ((try rt2.results[try rt2.it.next()].getVariant()).Constant.value) {
|
||||
.Vector3f32 => |v| {
|
||||
vec.* = v;
|
||||
return;
|
||||
},
|
||||
.Float => |f| vec[i] = f.value.float32,
|
||||
else => return RuntimeError.InvalidValueType,
|
||||
}
|
||||
},
|
||||
const lane_bits = try Result.resolveLaneBitWidth(target_type, rt);
|
||||
const target_lane_count = try value.resolveLaneCount();
|
||||
|
||||
.Vector2f32 => |*vec| inline for (0..2) |i| {
|
||||
switch ((try rt2.results[try rt2.it.next()].getVariant()).Constant.value) {
|
||||
.Vector2f32 => |v| {
|
||||
vec.* = v;
|
||||
return;
|
||||
},
|
||||
.Float => |f| vec[i] = f.value.float32,
|
||||
else => return RuntimeError.InvalidValueType,
|
||||
}
|
||||
},
|
||||
var dx: ?Value = if (primitive_type == .Float)
|
||||
try Value.init(allocator, rt.results, target_type_word, false)
|
||||
else
|
||||
null;
|
||||
defer if (dx) |*dx_value| dx_value.deinit(allocator);
|
||||
var dy: ?Value = if (primitive_type == .Float)
|
||||
try Value.init(allocator, rt.results, target_type_word, false)
|
||||
else
|
||||
null;
|
||||
defer if (dy) |*dy_value| dy_value.deinit(allocator);
|
||||
|
||||
.Vector4i32 => |*vec| inline for (0..4) |i| {
|
||||
switch ((try rt2.results[try rt2.it.next()].getVariant()).Constant.value) {
|
||||
.Vector4i32 => |v| {
|
||||
vec.* = v;
|
||||
return;
|
||||
},
|
||||
.Int => |int| vec[i] = int.value.sint32,
|
||||
else => return RuntimeError.InvalidValueType,
|
||||
}
|
||||
},
|
||||
var has_derivative = false;
|
||||
var target_lane_index: usize = 0;
|
||||
for (operand_ids) |operand_id| {
|
||||
const operand_value = try rt.results[operand_id].getValue();
|
||||
const operand_lane_count = try operand_value.resolveLaneCount();
|
||||
const derivative = rt.derivatives.get(operand_id);
|
||||
|
||||
.Vector3i32 => |*vec| inline for (0..3) |i| {
|
||||
switch ((try rt2.results[try rt2.it.next()].getVariant()).Constant.value) {
|
||||
.Vector3i32 => |v| {
|
||||
vec.* = v;
|
||||
return;
|
||||
},
|
||||
.Int => |int| vec[i] = int.value.sint32,
|
||||
else => return RuntimeError.InvalidValueType,
|
||||
}
|
||||
},
|
||||
for (0..operand_lane_count) |operand_lane_index| {
|
||||
if (target_lane_index >= target_lane_count) return RuntimeError.InvalidSpirV;
|
||||
|
||||
.Vector2i32 => |*vec| inline for (0..2) |i| {
|
||||
switch ((try rt2.results[try rt2.it.next()].getVariant()).Constant.value) {
|
||||
.Vector2i32 => |v| {
|
||||
vec.* = v;
|
||||
return;
|
||||
},
|
||||
.Int => |int| vec[i] = int.value.sint32,
|
||||
else => return RuntimeError.InvalidValueType,
|
||||
}
|
||||
},
|
||||
switch (primitive_type) {
|
||||
.Float => switch (lane_bits) {
|
||||
inline 16, 32, 64 => |bits| {
|
||||
{
|
||||
const lane = try Value.readLane(.Float, bits, operand_value, operand_lane_index);
|
||||
try Value.writeLane(.Float, bits, value, target_lane_index, lane);
|
||||
|
||||
.Vector4u32 => |*vec| inline for (0..4) |i| {
|
||||
switch ((try rt2.results[try rt2.it.next()].getVariant()).Constant.value) {
|
||||
.Vector4u32 => |v| {
|
||||
vec.* = v;
|
||||
return;
|
||||
},
|
||||
.Int => |int| vec[i] = int.value.uint32,
|
||||
else => return RuntimeError.InvalidValueType,
|
||||
}
|
||||
const FloatT = Value.getPrimitiveFieldType(.Float, bits);
|
||||
const dx_lane: FloatT = if (derivative) |d| blk: {
|
||||
has_derivative = true;
|
||||
break :blk try Value.readLane(.Float, bits, &d.dx, operand_lane_index);
|
||||
} else 0;
|
||||
const dy_lane: FloatT = if (derivative) |d| blk: {
|
||||
has_derivative = true;
|
||||
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,
|
||||
},
|
||||
|
||||
.Vector3u32 => |*vec| inline for (0..3) |i| {
|
||||
switch ((try rt2.results[try rt2.it.next()].getVariant()).Constant.value) {
|
||||
.Vector3u32 => |v| {
|
||||
vec.* = v;
|
||||
return;
|
||||
},
|
||||
.Int => |int| vec[i] = int.value.uint32,
|
||||
else => return RuntimeError.InvalidValueType,
|
||||
}
|
||||
.SInt => switch (lane_bits) {
|
||||
inline 8, 16, 32, 64 => |bits| {
|
||||
{
|
||||
const lane = try Value.readLane(.SInt, bits, operand_value, operand_lane_index);
|
||||
try Value.writeLane(.SInt, bits, value, target_lane_index, lane);
|
||||
}
|
||||
},
|
||||
else => return RuntimeError.InvalidSpirV,
|
||||
},
|
||||
|
||||
.Vector2u32 => |*vec| inline for (0..2) |i| {
|
||||
switch ((try rt2.results[try rt2.it.next()].getVariant()).Constant.value) {
|
||||
.Vector2u32 => |v| {
|
||||
vec.* = v;
|
||||
return;
|
||||
},
|
||||
.Int => |int| vec[i] = int.value.uint32,
|
||||
else => return RuntimeError.InvalidValueType,
|
||||
}
|
||||
.UInt => switch (lane_bits) {
|
||||
inline 8, 16, 32, 64 => |bits| {
|
||||
{
|
||||
const lane = try Value.readLane(.UInt, bits, operand_value, operand_lane_index);
|
||||
try Value.writeLane(.UInt, bits, value, target_lane_index, lane);
|
||||
}
|
||||
},
|
||||
else => return RuntimeError.InvalidSpirV,
|
||||
},
|
||||
|
||||
else => return RuntimeError.InvalidValueType,
|
||||
}
|
||||
|
||||
target_lane_index += 1;
|
||||
}
|
||||
}.routines;
|
||||
}
|
||||
|
||||
switch (value.*) {
|
||||
.RuntimeArray => |arr| {
|
||||
_ = arr;
|
||||
return RuntimeError.ToDo;
|
||||
},
|
||||
if (target_lane_index != target_lane_count) return RuntimeError.InvalidSpirV;
|
||||
|
||||
else => try vectorRoutines(value, rt),
|
||||
if (has_derivative) {
|
||||
try rt.setDerivative(allocator, id, &dx.?, &dy.?);
|
||||
} else {
|
||||
rt.clearDerivative(allocator, id);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4401,17 +4482,23 @@ fn opFwidth(allocator: std.mem.Allocator, _: SpvWord, rt: *Runtime) RuntimeError
|
||||
const id = 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 lane_bits = try Result.resolveLaneBitWidth(target_type, rt);
|
||||
const lane_count = try Result.resolveLaneCount(target_type);
|
||||
const derivative = rt.derivatives.get(operand);
|
||||
|
||||
switch (lane_bits) {
|
||||
inline 16, 32, 64 => |bits| {
|
||||
const FloatT = Value.getPrimitiveFieldType(.Float, bits);
|
||||
for (0..lane_count) |lane_index| {
|
||||
const dx = try Value.readLane(.Float, bits, &derivative.dx, lane_index);
|
||||
const dy = try Value.readLane(.Float, bits, &derivative.dy, lane_index);
|
||||
const dx: FloatT = if (derivative) |d|
|
||||
try Value.readLane(.Float, bits, &d.dx, lane_index)
|
||||
else
|
||||
1;
|
||||
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)));
|
||||
}
|
||||
},
|
||||
|
||||
+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" {
|
||||
const allocator = std.testing.allocator;
|
||||
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
|
||||
test "Maths vectors with scalars" {
|
||||
const allocator = std.testing.allocator;
|
||||
|
||||
Reference in New Issue
Block a user