improving robust buffer access
Build / build (push) Failing after 1m3s
Test / build (push) Failing after 9m8s

This commit is contained in:
2026-06-29 23:21:38 +02:00
parent fcf303a0c6
commit e49383f07e
3 changed files with 320 additions and 178 deletions
+1 -1
View File
@@ -104,7 +104,7 @@ pub const ImageAPI = struct {
writeImageInt4: *const fn (driver_image: *anyopaque, dim: spv.SpvDim, x: i32, y: i32, z: i32, pixel: Vec4(u32)) RuntimeError!void,
sampleImageFloat4: *const fn (driver_image: *anyopaque, driver_sampler: *anyopaque, dim: spv.SpvDim, x: f32, y: f32, z: f32, lod: ?f32, offset: ImageOffset) RuntimeError!Vec4(f32),
sampleImageInt4: *const fn (driver_image: *anyopaque, driver_sampler: *anyopaque, dim: spv.SpvDim, x: f32, y: f32, z: f32, lod: ?f32, offset: ImageOffset) RuntimeError!Vec4(u32),
sampleImageDref: *const fn (driver_image: *anyopaque, driver_sampler: *anyopaque, dim: spv.SpvDim, x: f32, y: f32, z: f32, dref: f32, lod: ?f32, offset: ImageOffset) RuntimeError!f32,
sampleImageDref: *const fn (driver_image: *anyopaque, driver_sampler: *anyopaque, dim: spv.SpvDim, x: f32, y: f32, z: f32, w: f32, dref: f32, lod: ?f32, offset: ImageOffset) RuntimeError!f32,
queryImageSize: *const fn (driver_image: *anyopaque, dim: spv.SpvDim, arrayed: bool, lod: ?i32) RuntimeError!Vec4(u32),
queryImageLevels: *const fn (driver_image: *anyopaque) RuntimeError!u32,
queryImageSamples: *const fn (driver_image: *anyopaque) RuntimeError!u32,
+80 -36
View File
@@ -96,6 +96,30 @@ pub const Value = union(Type) {
return value;
}
pub inline fn createRobustValueFromIndex(self: *const @This(), allocator: std.mem.Allocator, results: []const Result, index: usize) RuntimeError!*Value {
const value = allocator.create(Value) catch return RuntimeError.OutOfMemory;
errdefer allocator.destroy(value);
value.* = try Value.init(allocator, results, self.type_word, false);
errdefer value.deinit(allocator);
const bytes = allocator.alloc(u8, self.stride) catch return RuntimeError.OutOfMemory;
defer allocator.free(bytes);
@memset(bytes, 0);
if (self.getRobustOffsetOfIndex(index)) |offset| {
const available = @min(self.stride, self.data.len - offset);
@memcpy(bytes[0..available], self.data[offset .. offset + available]);
}
_ = if (self.matrix_stride) |matrix_stride|
try value.writeWithMatrixLayout(bytes, matrix_stride, self.row_major)
else
try value.write(bytes);
return value;
}
pub inline fn createLocalValueFromIndex(self: *const @This(), allocator: std.mem.Allocator, results: []const Result, index: usize) RuntimeError!Value {
const offset = try self.getCheckedOffsetOfIndex(index);
var value = try Value.init(allocator, results, self.type_word, false);
@@ -111,6 +135,12 @@ pub const Value = union(Type) {
return self.stride * index;
}
pub inline fn getRobustOffsetOfIndex(self: *const @This(), index: usize) ?usize {
if (self.stride == 0) return null;
const offset = std.math.mul(usize, self.stride, index) catch return null;
return if (offset < self.data.len) offset else null;
}
pub inline fn getCheckedOffsetOfIndex(self: *const @This(), index: usize) RuntimeError!usize {
const offset = self.getOffsetOfIndex(index);
if (self.stride == 0 or offset > self.data.len or self.stride > self.data.len - offset)
@@ -540,18 +570,18 @@ pub const Value = union(Type) {
return values[lane_index].write(input);
},
.Vector2f32, .Vector3f32, .Vector4f32 => {
if (input.len < @sizeOf(f32)) return RuntimeError.OutOfBounds;
try writeLane(.Float, 32, self, lane_index, std.mem.bytesToValue(f32, input[0..@sizeOf(f32)]));
const value: f32 = if (input.len >= @sizeOf(f32)) std.mem.bytesToValue(f32, input[0..@sizeOf(f32)]) else 0;
try writeLane(.Float, 32, self, lane_index, value);
return @sizeOf(f32);
},
.Vector2i32, .Vector3i32, .Vector4i32 => {
if (input.len < @sizeOf(i32)) return RuntimeError.OutOfBounds;
try writeLane(.SInt, 32, self, lane_index, std.mem.bytesToValue(i32, input[0..@sizeOf(i32)]));
const value: i32 = if (input.len >= @sizeOf(i32)) std.mem.bytesToValue(i32, input[0..@sizeOf(i32)]) else 0;
try writeLane(.SInt, 32, self, lane_index, value);
return @sizeOf(i32);
},
.Vector2u32, .Vector3u32, .Vector4u32 => {
if (input.len < @sizeOf(u32)) return RuntimeError.OutOfBounds;
try writeLane(.UInt, 32, self, lane_index, std.mem.bytesToValue(u32, input[0..@sizeOf(u32)]));
const value: u32 = if (input.len >= @sizeOf(u32)) std.mem.bytesToValue(u32, input[0..@sizeOf(u32)]) else 0;
try writeLane(.UInt, 32, self, lane_index, value);
return @sizeOf(u32);
},
else => return RuntimeError.InvalidValueType,
@@ -651,17 +681,17 @@ pub const Value = union(Type) {
(row_count - 1) * matrix_stride_usize + columns.len * scalar_size
else
(columns.len - 1) * matrix_stride_usize + try columns[0].getPlainMemorySize();
if (input.len < matrix_size) return RuntimeError.OutOfBounds;
if (row_major) {
for (columns, 0..) |*column, column_index| {
for (0..row_count) |row_index| {
_ = try column.writeVectorLane(row_index, input[row_index * matrix_stride_usize + column_index * scalar_size ..]);
const offset = row_index * matrix_stride_usize + column_index * scalar_size;
_ = try column.writeVectorLane(row_index, input[@min(offset, input.len)..]);
}
}
} else {
for (columns, 0..) |*column, column_index| {
_ = try column.write(input[column_index * matrix_stride_usize ..]);
const offset = column_index * matrix_stride_usize;
_ = try column.write(input[@min(offset, input.len)..]);
}
}
return matrix_size;
@@ -673,7 +703,7 @@ pub const Value = union(Type) {
.Array => |*arr| blk: {
var offset: usize = 0;
for (arr.values) |*value| {
_ = try value.writeWithMatrixLayout(input[offset..], matrix_stride, row_major);
_ = try value.writeWithMatrixLayout(input[@min(offset, input.len)..], matrix_stride, row_major);
offset += if (arr.stride != 0) arr.stride else try value.getPlainMemorySize();
}
break :blk offset;
@@ -688,10 +718,12 @@ pub const Value = union(Type) {
const info = @typeInfo(T).vector;
const Lane = info.child;
const size = info.len * @sizeOf(Lane);
if (in.len < size) return RuntimeError.OutOfBounds;
inline for (0..info.len) |i| {
const offset = i * @sizeOf(Lane);
vec[i] = std.mem.bytesToValue(Lane, in[offset..][0..@sizeOf(Lane)]);
vec[i] = if (offset + @sizeOf(Lane) <= in.len)
std.mem.bytesToValue(Lane, in[offset..][0..@sizeOf(Lane)])
else
0;
}
return size;
}
@@ -699,27 +731,25 @@ pub const Value = union(Type) {
switch (self.*) {
.Bool => |*b| {
if (input.len < 1) return RuntimeError.OutOfBounds;
b.* = if (input[0] != 0) true else false;
b.* = if (input.len >= 1 and input[0] != 0) true else false;
return 1;
},
.Int => |*i| {
switch (i.bit_count) {
8 => {
if (input.len < 1) return RuntimeError.OutOfBounds;
i.value.uint8 = @bitCast(input[0]);
i.value.uint8 = if (input.len >= 1) @bitCast(input[0]) else 0;
},
16 => {
if (input.len < 2) return RuntimeError.OutOfBounds;
@memcpy(std.mem.asBytes(&i.value.uint16), input[0..2]);
i.value.uint16 = 0;
if (input.len >= 2) @memcpy(std.mem.asBytes(&i.value.uint16), input[0..2]);
},
32 => {
if (input.len < 4) return RuntimeError.OutOfBounds;
@memcpy(std.mem.asBytes(&i.value.uint32), input[0..4]);
i.value.uint32 = 0;
if (input.len >= 4) @memcpy(std.mem.asBytes(&i.value.uint32), input[0..4]);
},
64 => {
if (input.len < 8) return RuntimeError.OutOfBounds;
@memcpy(std.mem.asBytes(&i.value.uint64), input[0..8]);
i.value.uint64 = 0;
if (input.len >= 8) @memcpy(std.mem.asBytes(&i.value.uint64), input[0..8]);
},
else => return RuntimeError.InvalidValueType,
}
@@ -728,16 +758,16 @@ pub const Value = union(Type) {
.Float => |*f| {
switch (f.bit_count) {
16 => {
if (input.len < 2) return RuntimeError.OutOfBounds;
@memcpy(std.mem.asBytes(&f.value.float16), input[0..2]);
f.value.float16 = 0;
if (input.len >= 2) @memcpy(std.mem.asBytes(&f.value.float16), input[0..2]);
},
32 => {
if (input.len < 4) return RuntimeError.OutOfBounds;
@memcpy(std.mem.asBytes(&f.value.float32), input[0..4]);
f.value.float32 = 0;
if (input.len >= 4) @memcpy(std.mem.asBytes(&f.value.float32), input[0..4]);
},
64 => {
if (input.len < 8) return RuntimeError.OutOfBounds;
@memcpy(std.mem.asBytes(&f.value.float64), input[0..8]);
f.value.float64 = 0;
if (input.len >= 8) @memcpy(std.mem.asBytes(&f.value.float64), input[0..8]);
},
else => return RuntimeError.InvalidValueType,
}
@@ -773,7 +803,12 @@ pub const Value = union(Type) {
.Array => |*arr| {
var offset: usize = 0;
for (arr.values) |*v| {
_ = try v.write(input[offset..]);
if (v.* == .RuntimeArray) {
v.RuntimeArray.data = @constCast(input[@min(offset, input.len)..]);
offset += input.len - @min(offset, input.len);
continue;
}
_ = try v.write(input[@min(offset, input.len)..]);
offset += if (arr.stride != 0) arr.stride else try v.getPlainMemorySize();
}
return offset;
@@ -782,20 +817,29 @@ pub const Value = union(Type) {
var end_offset: usize = 0;
for (s.values, 0..) |*v, i| {
const member_offset: usize = @intCast(s.offsets[i] orelse end_offset);
if (member_offset > input.len) return RuntimeError.OutOfBounds;
if (member_offset > input.len) {
if (v.* == .RuntimeArray) {
v.RuntimeArray.data = &.{};
continue;
}
}
const member_input = if (v.* == .RuntimeArray and i + 1 < s.values.len and s.offsets[i + 1] != null) blk: {
const next_offset: usize = @intCast(s.offsets[i + 1].?);
if (next_offset < member_offset or next_offset > input.len) return RuntimeError.OutOfBounds;
break :blk input[member_offset..next_offset];
} else input[member_offset..];
if (next_offset < member_offset) return RuntimeError.OutOfBounds;
break :blk input[member_offset..@min(next_offset, input.len)];
} else input[@min(member_offset, input.len)..];
if (v.* == .RuntimeArray) {
v.RuntimeArray.data = @constCast(member_input);
end_offset = @max(end_offset, input.len);
continue;
}
const write_size = if (s.matrix_strides[i]) |matrix_stride|
try v.writeWithMatrixLayout(member_input, matrix_stride, s.row_major[i])
else
try v.write(member_input);
end_offset = @max(end_offset, member_offset + write_size);
}
if (end_offset > input.len) return RuntimeError.OutOfBounds;
s.external_data = @constCast(input[0..end_offset]);
s.external_data = @constCast(input[0..@min(end_offset, input.len)]);
return end_offset;
},
.RuntimeArray => |*arr| {
+239 -141
View File
@@ -1367,7 +1367,7 @@ fn ImageEngine(comptime Op: ImageOp) type {
};
}
fn readProjectedSampleCoords(coordinate: *const Value) RuntimeError!struct { x: f32, y: f32, z: f32 } {
fn readProjectedSampleCoords(coordinate: *const Value) RuntimeError!struct { x: f32, y: f32, z: f32, w: f32 } {
const lane_count = try valueLaneCount(coordinate);
if (lane_count < 2)
return RuntimeError.InvalidSpirV;
@@ -1378,6 +1378,7 @@ fn ImageEngine(comptime Op: ImageOp) type {
.x = try readSampleCoordLane(coordinate, 0) / q,
.y = if (q_lane > 1) (readSampleCoordLane(coordinate, 1) catch 0.0) / q else 0.0,
.z = if (q_lane > 2) (readSampleCoordLane(coordinate, 2) catch 0.0) / q else 0.0,
.w = if (q_lane > 3) (readSampleCoordLane(coordinate, 3) catch 0.0) / q else 0.0,
};
}
@@ -1856,8 +1857,8 @@ fn ImageEngine(comptime Op: ImageOp) type {
return parsed.lod;
}
fn sampleImageDref(rt: *Runtime, dst: *Value, driver_image: *anyopaque, driver_sampler: *anyopaque, dim: spv.SpvDim, x: f32, y: f32, z: f32, dref: f32, lod: ?f32, offset: Runtime.ImageOffset) RuntimeError!void {
try writeFloatScalar(dst, try rt.image_api.sampleImageDref(driver_image, driver_sampler, dim, x, y, z, dref, lod, offset));
fn sampleImageDref(rt: *Runtime, dst: *Value, driver_image: *anyopaque, driver_sampler: *anyopaque, dim: spv.SpvDim, x: f32, y: f32, z: f32, w: f32, dref: f32, lod: ?f32, offset: Runtime.ImageOffset) RuntimeError!void {
try writeFloatScalar(dst, try rt.image_api.sampleImageDref(driver_image, driver_sampler, dim, x, y, z, w, dref, lod, offset));
}
fn gatherCoord(index: i32, extent: u32) f32 {
@@ -2169,6 +2170,7 @@ fn ImageEngine(comptime Op: ImageOp) type {
.x = try readSampleCoordLane(coordinate, 0),
.y = readSampleCoordLane(coordinate, 1) catch 0,
.z = readSampleCoordLane(coordinate, 2) catch 0,
.w = readSampleCoordLane(coordinate, 3) catch 0,
};
const image_operands = if (word_count > 4) try rt.it.next() else 0;
const parsed_operands = try parseImageOperands(rt, image_operands);
@@ -2230,6 +2232,7 @@ fn ImageEngine(comptime Op: ImageOp) type {
.x = try readSampleCoordLane(coordinate, 0),
.y = readSampleCoordLane(coordinate, 1) catch 0,
.z = readSampleCoordLane(coordinate, 2) catch 0,
.w = readSampleCoordLane(coordinate, 3) catch 0,
};
const raw_dref = try readFloatLane(try rt.results[try rt.it.next()].getValue(), 0);
const dref = if (comptime Op == .SampleProjDrefImplicitLod)
@@ -2238,6 +2241,20 @@ fn ImageEngine(comptime Op: ImageOp) type {
raw_dref;
const image_operands = if (word_count > 5) try rt.it.next() else 0;
const parsed_operands = try parseImageOperands(rt, image_operands);
const projected = comptime Op == .SampleProjDrefImplicitLod;
const lod = try implicitSampleLod(
rt,
coordinate_id,
coordinate,
projected,
sampled_image_operand.driver_image,
sampled_image_operand.driver_sampler,
sampled_image_operand.dim,
coords.x,
coords.y,
coords.z,
parsed_operands.bias,
);
try sampleImageDref(
rt,
@@ -2248,8 +2265,9 @@ fn ImageEngine(comptime Op: ImageOp) type {
coords.x,
coords.y,
coords.z,
coords.w,
dref,
null,
lod,
parsed_operands.offset,
);
},
@@ -2265,6 +2283,7 @@ fn ImageEngine(comptime Op: ImageOp) type {
.x = try readSampleCoordLane(coordinate, 0),
.y = readSampleCoordLane(coordinate, 1) catch 0,
.z = readSampleCoordLane(coordinate, 2) catch 0,
.w = readSampleCoordLane(coordinate, 3) catch 0,
};
const image_operands = if (word_count > 4) try rt.it.next() else 0;
const parsed_operands = try parseImageOperands(rt, image_operands);
@@ -2300,6 +2319,7 @@ fn ImageEngine(comptime Op: ImageOp) type {
.x = try readSampleCoordLane(coordinate, 0),
.y = readSampleCoordLane(coordinate, 1) catch 0,
.z = readSampleCoordLane(coordinate, 2) catch 0,
.w = readSampleCoordLane(coordinate, 3) catch 0,
};
const raw_dref = try readFloatLane(try rt.results[try rt.it.next()].getValue(), 0);
const dref = if (comptime Op == .SampleProjDrefExplicitLod)
@@ -2318,6 +2338,7 @@ fn ImageEngine(comptime Op: ImageOp) type {
coords.x,
coords.y,
coords.z,
coords.w,
dref,
try explicitSampleLod(
rt,
@@ -3514,9 +3535,60 @@ fn opAccessChain(allocator: std.mem.Allocator, word_count: SpvWord, rt: *Runtime
.indexes = indexes,
.value = blk: {
const helpers = struct {
const F32Pointer = struct {
ptr: *f32,
backing: ?*Value,
owns_backing: bool,
};
const I32Pointer = struct {
ptr: *i32,
backing: ?*Value,
owns_backing: bool,
};
const U32Pointer = struct {
ptr: *u32,
backing: ?*Value,
owns_backing: bool,
};
fn destroyBacking(gpa: std.mem.Allocator, backing: ?*Value, owns_backing: bool) void {
if (!owns_backing) return;
if (backing) |value| {
value.deinit(gpa);
gpa.destroy(value);
}
}
fn robustF32Pointer(gpa: std.mem.Allocator, ptr: ?*f32, window: ?[]u8, descriptor_backed: bool, backing: ?*Value, owns_backing: bool) RuntimeError!F32Pointer {
if (window != null or !descriptor_backed) return .{ .ptr = ptr orelse return RuntimeError.InvalidSpirV, .backing = backing, .owns_backing = owns_backing };
destroyBacking(gpa, backing, owns_backing);
const value = gpa.create(Value) catch return RuntimeError.OutOfMemory;
value.* = .{ .Float = .{ .bit_count = 32, .value = .{ .float32 = 0 } } };
return .{ .ptr = &value.Float.value.float32, .backing = value, .owns_backing = true };
}
fn robustI32Pointer(gpa: std.mem.Allocator, ptr: ?*i32, window: ?[]u8, descriptor_backed: bool, backing: ?*Value, owns_backing: bool) RuntimeError!I32Pointer {
if (window != null or !descriptor_backed) return .{ .ptr = ptr orelse return RuntimeError.InvalidSpirV, .backing = backing, .owns_backing = owns_backing };
destroyBacking(gpa, backing, owns_backing);
const value = gpa.create(Value) catch return RuntimeError.OutOfMemory;
value.* = .{ .Int = .{ .bit_count = 32, .is_signed = true, .value = .{ .sint32 = 0 } } };
return .{ .ptr = &value.Int.value.sint32, .backing = value, .owns_backing = true };
}
fn robustU32Pointer(gpa: std.mem.Allocator, ptr: ?*u32, window: ?[]u8, descriptor_backed: bool, backing: ?*Value, owns_backing: bool) RuntimeError!U32Pointer {
if (window != null or !descriptor_backed) return .{ .ptr = ptr orelse return RuntimeError.InvalidSpirV, .backing = backing, .owns_backing = owns_backing };
destroyBacking(gpa, backing, owns_backing);
const value = gpa.create(Value) catch return RuntimeError.OutOfMemory;
value.* = .{ .Int = .{ .bit_count = 32, .is_signed = false, .value = .{ .uint32 = 0 } } };
return .{ .ptr = &value.Int.value.uint32, .backing = value, .owns_backing = true };
}
fn advanceWindow(window: ?[]u8, offset: usize) RuntimeError!?[]u8 {
if (window) |w| {
if (offset > w.len) return RuntimeError.OutOfBounds;
if (offset > w.len) return null;
return w[offset..];
}
return null;
@@ -3524,7 +3596,7 @@ fn opAccessChain(allocator: std.mem.Allocator, word_count: SpvWord, rt: *Runtime
fn advanceWindowSized(window: ?[]u8, offset: usize, size: usize) RuntimeError!?[]u8 {
if (window) |w| {
if (offset > w.len or size > w.len - offset) return RuntimeError.OutOfBounds;
if (offset > w.len or size > w.len - offset) return null;
return w[offset .. offset + size];
}
return null;
@@ -3545,6 +3617,7 @@ fn opAccessChain(allocator: std.mem.Allocator, word_count: SpvWord, rt: *Runtime
var owns_uniform_backing_value = false;
var matrix_stride: ?SpvWord = null;
var matrix_row_major = false;
var descriptor_backed = false;
if (std.meta.activeTag(value_ptr.*) == .Pointer) {
const ptr = value_ptr.Pointer;
@@ -3553,6 +3626,7 @@ fn opAccessChain(allocator: std.mem.Allocator, word_count: SpvWord, rt: *Runtime
owns_uniform_backing_value = false;
matrix_stride = ptr.matrix_stride;
matrix_row_major = ptr.matrix_row_major;
descriptor_backed = uniform_slice_window != null or uniform_backing_value != null;
switch (ptr.ptr) {
.common => |common| value_ptr = common,
else => return RuntimeError.InvalidSpirV,
@@ -3579,6 +3653,7 @@ fn opAccessChain(allocator: std.mem.Allocator, word_count: SpvWord, rt: *Runtime
owns_uniform_backing_value = false;
matrix_stride = ptr.matrix_stride;
matrix_row_major = ptr.matrix_row_major;
descriptor_backed = uniform_slice_window != null or uniform_backing_value != null;
switch (ptr.ptr) {
.common => |common| value_ptr = common,
else => return RuntimeError.InvalidSpirV,
@@ -3639,8 +3714,10 @@ fn opAccessChain(allocator: std.mem.Allocator, word_count: SpvWord, rt: *Runtime
const member_offset: usize = @intCast(s.offsets[component_index] orelse end_offset);
if (uniform_slice_window != null) {
descriptor_backed = true;
uniform_slice_window = try helpers.advanceWindow(uniform_slice_window, member_offset);
} else if (s.external_data) |data| {
descriptor_backed = true;
uniform_slice_window = try helpers.advanceWindow(data, member_offset);
}
@@ -3658,13 +3735,7 @@ fn opAccessChain(allocator: std.mem.Allocator, word_count: SpvWord, rt: *Runtime
}
},
.RuntimeArray => |*arr| {
if (component_index >= arr.getLen()) return RuntimeError.OutOfBounds;
const element_offset = arr.getOffsetOfIndex(component_index);
if (element_offset > arr.data.len or arr.stride > arr.data.len - element_offset)
return RuntimeError.OutOfBounds;
const backing = try arr.createValueFromIndex(allocator, rt.results, component_index);
const backing = try arr.createRobustValueFromIndex(allocator, rt.results, component_index);
errdefer {
backing.deinit(allocator);
allocator.destroy(backing);
@@ -3678,7 +3749,11 @@ fn opAccessChain(allocator: std.mem.Allocator, word_count: SpvWord, rt: *Runtime
value_ptr = backing;
uniform_backing_value = backing;
owns_uniform_backing_value = true;
uniform_slice_window = arr.data[element_offset .. element_offset + arr.stride];
descriptor_backed = true;
uniform_slice_window = if (arr.getRobustOffsetOfIndex(component_index)) |element_offset|
arr.data[element_offset..]
else
null;
if (arr.matrix_stride) |stride| {
matrix_stride = stride;
matrix_row_major = arr.row_major;
@@ -3692,94 +3767,103 @@ fn opAccessChain(allocator: std.mem.Allocator, word_count: SpvWord, rt: *Runtime
}
},
.Vector4f32 => |*v| switch (component_index) {
inline 0...3 => |idx| break :blk .{ .Pointer = .{
.ptr = .{ .f32_ptr = &v[idx] },
.uniform_slice_window = try helpers.advanceWindowSized(uniform_slice_window, helpers.laneOffset(matrix_stride, matrix_row_major, idx, @sizeOf(f32)), @sizeOf(f32)),
.uniform_backing_value = uniform_backing_value,
.owns_uniform_backing_value = owns_uniform_backing_value,
.matrix_stride = null,
} },
else => return RuntimeError.InvalidSpirV,
inline 0...3 => |idx| {
const lane_window = try helpers.advanceWindowSized(uniform_slice_window, helpers.laneOffset(matrix_stride, matrix_row_major, idx, @sizeOf(f32)), @sizeOf(f32));
const ptr = try helpers.robustF32Pointer(allocator, &v[idx], lane_window, descriptor_backed, uniform_backing_value, owns_uniform_backing_value);
break :blk .{ .Pointer = .{ .ptr = .{ .f32_ptr = ptr.ptr }, .uniform_slice_window = lane_window, .uniform_backing_value = ptr.backing, .owns_uniform_backing_value = ptr.owns_backing, .matrix_stride = null } };
},
else => {
const ptr = try helpers.robustF32Pointer(allocator, null, null, descriptor_backed, uniform_backing_value, owns_uniform_backing_value);
break :blk .{ .Pointer = .{ .ptr = .{ .f32_ptr = ptr.ptr }, .uniform_slice_window = null, .uniform_backing_value = ptr.backing, .owns_uniform_backing_value = ptr.owns_backing, .matrix_stride = null } };
},
},
.Vector3f32 => |*v| switch (component_index) {
inline 0...2 => |idx| break :blk .{ .Pointer = .{
.ptr = .{ .f32_ptr = &v[idx] },
.uniform_slice_window = try helpers.advanceWindowSized(uniform_slice_window, helpers.laneOffset(matrix_stride, matrix_row_major, idx, @sizeOf(f32)), @sizeOf(f32)),
.uniform_backing_value = uniform_backing_value,
.owns_uniform_backing_value = owns_uniform_backing_value,
.matrix_stride = null,
} },
else => return RuntimeError.InvalidSpirV,
inline 0...2 => |idx| {
const lane_window = try helpers.advanceWindowSized(uniform_slice_window, helpers.laneOffset(matrix_stride, matrix_row_major, idx, @sizeOf(f32)), @sizeOf(f32));
const ptr = try helpers.robustF32Pointer(allocator, &v[idx], lane_window, descriptor_backed, uniform_backing_value, owns_uniform_backing_value);
break :blk .{ .Pointer = .{ .ptr = .{ .f32_ptr = ptr.ptr }, .uniform_slice_window = lane_window, .uniform_backing_value = ptr.backing, .owns_uniform_backing_value = ptr.owns_backing, .matrix_stride = null } };
},
else => {
const ptr = try helpers.robustF32Pointer(allocator, null, null, descriptor_backed, uniform_backing_value, owns_uniform_backing_value);
break :blk .{ .Pointer = .{ .ptr = .{ .f32_ptr = ptr.ptr }, .uniform_slice_window = null, .uniform_backing_value = ptr.backing, .owns_uniform_backing_value = ptr.owns_backing, .matrix_stride = null } };
},
},
.Vector2f32 => |*v| switch (component_index) {
inline 0...1 => |idx| break :blk .{ .Pointer = .{
.ptr = .{ .f32_ptr = &v[idx] },
.uniform_slice_window = try helpers.advanceWindowSized(uniform_slice_window, helpers.laneOffset(matrix_stride, matrix_row_major, idx, @sizeOf(f32)), @sizeOf(f32)),
.uniform_backing_value = uniform_backing_value,
.owns_uniform_backing_value = owns_uniform_backing_value,
.matrix_stride = null,
} },
else => return RuntimeError.InvalidSpirV,
inline 0...1 => |idx| {
const lane_window = try helpers.advanceWindowSized(uniform_slice_window, helpers.laneOffset(matrix_stride, matrix_row_major, idx, @sizeOf(f32)), @sizeOf(f32));
const ptr = try helpers.robustF32Pointer(allocator, &v[idx], lane_window, descriptor_backed, uniform_backing_value, owns_uniform_backing_value);
break :blk .{ .Pointer = .{ .ptr = .{ .f32_ptr = ptr.ptr }, .uniform_slice_window = lane_window, .uniform_backing_value = ptr.backing, .owns_uniform_backing_value = ptr.owns_backing, .matrix_stride = null } };
},
else => {
const ptr = try helpers.robustF32Pointer(allocator, null, null, descriptor_backed, uniform_backing_value, owns_uniform_backing_value);
break :blk .{ .Pointer = .{ .ptr = .{ .f32_ptr = ptr.ptr }, .uniform_slice_window = null, .uniform_backing_value = ptr.backing, .owns_uniform_backing_value = ptr.owns_backing, .matrix_stride = null } };
},
},
.Vector4i32 => |*v| switch (component_index) {
inline 0...3 => |idx| break :blk .{ .Pointer = .{
.ptr = .{ .i32_ptr = &v[idx] },
.uniform_slice_window = try helpers.advanceWindowSized(uniform_slice_window, helpers.laneOffset(matrix_stride, matrix_row_major, idx, @sizeOf(i32)), @sizeOf(i32)),
.uniform_backing_value = uniform_backing_value,
.owns_uniform_backing_value = owns_uniform_backing_value,
.matrix_stride = null,
} },
else => return RuntimeError.InvalidSpirV,
inline 0...3 => |idx| {
const lane_window = try helpers.advanceWindowSized(uniform_slice_window, helpers.laneOffset(matrix_stride, matrix_row_major, idx, @sizeOf(i32)), @sizeOf(i32));
const ptr = try helpers.robustI32Pointer(allocator, &v[idx], lane_window, descriptor_backed, uniform_backing_value, owns_uniform_backing_value);
break :blk .{ .Pointer = .{ .ptr = .{ .i32_ptr = ptr.ptr }, .uniform_slice_window = lane_window, .uniform_backing_value = ptr.backing, .owns_uniform_backing_value = ptr.owns_backing, .matrix_stride = null } };
},
else => {
const ptr = try helpers.robustI32Pointer(allocator, null, null, descriptor_backed, uniform_backing_value, owns_uniform_backing_value);
break :blk .{ .Pointer = .{ .ptr = .{ .i32_ptr = ptr.ptr }, .uniform_slice_window = null, .uniform_backing_value = ptr.backing, .owns_uniform_backing_value = ptr.owns_backing, .matrix_stride = null } };
},
},
.Vector3i32 => |*v| switch (component_index) {
inline 0...2 => |idx| break :blk .{ .Pointer = .{
.ptr = .{ .i32_ptr = &v[idx] },
.uniform_slice_window = try helpers.advanceWindowSized(uniform_slice_window, helpers.laneOffset(matrix_stride, matrix_row_major, idx, @sizeOf(i32)), @sizeOf(i32)),
.uniform_backing_value = uniform_backing_value,
.owns_uniform_backing_value = owns_uniform_backing_value,
.matrix_stride = null,
} },
else => return RuntimeError.InvalidSpirV,
inline 0...2 => |idx| {
const lane_window = try helpers.advanceWindowSized(uniform_slice_window, helpers.laneOffset(matrix_stride, matrix_row_major, idx, @sizeOf(i32)), @sizeOf(i32));
const ptr = try helpers.robustI32Pointer(allocator, &v[idx], lane_window, descriptor_backed, uniform_backing_value, owns_uniform_backing_value);
break :blk .{ .Pointer = .{ .ptr = .{ .i32_ptr = ptr.ptr }, .uniform_slice_window = lane_window, .uniform_backing_value = ptr.backing, .owns_uniform_backing_value = ptr.owns_backing, .matrix_stride = null } };
},
else => {
const ptr = try helpers.robustI32Pointer(allocator, null, null, descriptor_backed, uniform_backing_value, owns_uniform_backing_value);
break :blk .{ .Pointer = .{ .ptr = .{ .i32_ptr = ptr.ptr }, .uniform_slice_window = null, .uniform_backing_value = ptr.backing, .owns_uniform_backing_value = ptr.owns_backing, .matrix_stride = null } };
},
},
.Vector2i32 => |*v| switch (component_index) {
inline 0...1 => |idx| break :blk .{ .Pointer = .{
.ptr = .{ .i32_ptr = &v[idx] },
.uniform_slice_window = try helpers.advanceWindowSized(uniform_slice_window, helpers.laneOffset(matrix_stride, matrix_row_major, idx, @sizeOf(i32)), @sizeOf(i32)),
.uniform_backing_value = uniform_backing_value,
.owns_uniform_backing_value = owns_uniform_backing_value,
.matrix_stride = null,
} },
else => return RuntimeError.InvalidSpirV,
inline 0...1 => |idx| {
const lane_window = try helpers.advanceWindowSized(uniform_slice_window, helpers.laneOffset(matrix_stride, matrix_row_major, idx, @sizeOf(i32)), @sizeOf(i32));
const ptr = try helpers.robustI32Pointer(allocator, &v[idx], lane_window, descriptor_backed, uniform_backing_value, owns_uniform_backing_value);
break :blk .{ .Pointer = .{ .ptr = .{ .i32_ptr = ptr.ptr }, .uniform_slice_window = lane_window, .uniform_backing_value = ptr.backing, .owns_uniform_backing_value = ptr.owns_backing, .matrix_stride = null } };
},
else => {
const ptr = try helpers.robustI32Pointer(allocator, null, null, descriptor_backed, uniform_backing_value, owns_uniform_backing_value);
break :blk .{ .Pointer = .{ .ptr = .{ .i32_ptr = ptr.ptr }, .uniform_slice_window = null, .uniform_backing_value = ptr.backing, .owns_uniform_backing_value = ptr.owns_backing, .matrix_stride = null } };
},
},
.Vector4u32 => |*v| switch (component_index) {
inline 0...3 => |idx| break :blk .{ .Pointer = .{
.ptr = .{ .u32_ptr = &v[idx] },
.uniform_slice_window = try helpers.advanceWindowSized(uniform_slice_window, helpers.laneOffset(matrix_stride, matrix_row_major, idx, @sizeOf(u32)), @sizeOf(u32)),
.uniform_backing_value = uniform_backing_value,
.owns_uniform_backing_value = owns_uniform_backing_value,
.matrix_stride = null,
} },
else => return RuntimeError.InvalidSpirV,
inline 0...3 => |idx| {
const lane_window = try helpers.advanceWindowSized(uniform_slice_window, helpers.laneOffset(matrix_stride, matrix_row_major, idx, @sizeOf(u32)), @sizeOf(u32));
const ptr = try helpers.robustU32Pointer(allocator, &v[idx], lane_window, descriptor_backed, uniform_backing_value, owns_uniform_backing_value);
break :blk .{ .Pointer = .{ .ptr = .{ .u32_ptr = ptr.ptr }, .uniform_slice_window = lane_window, .uniform_backing_value = ptr.backing, .owns_uniform_backing_value = ptr.owns_backing, .matrix_stride = null } };
},
else => {
const ptr = try helpers.robustU32Pointer(allocator, null, null, descriptor_backed, uniform_backing_value, owns_uniform_backing_value);
break :blk .{ .Pointer = .{ .ptr = .{ .u32_ptr = ptr.ptr }, .uniform_slice_window = null, .uniform_backing_value = ptr.backing, .owns_uniform_backing_value = ptr.owns_backing, .matrix_stride = null } };
},
},
.Vector3u32 => |*v| switch (component_index) {
inline 0...2 => |idx| break :blk .{ .Pointer = .{
.ptr = .{ .u32_ptr = &v[idx] },
.uniform_slice_window = try helpers.advanceWindowSized(uniform_slice_window, helpers.laneOffset(matrix_stride, matrix_row_major, idx, @sizeOf(u32)), @sizeOf(u32)),
.uniform_backing_value = uniform_backing_value,
.owns_uniform_backing_value = owns_uniform_backing_value,
.matrix_stride = null,
} },
else => return RuntimeError.InvalidSpirV,
inline 0...2 => |idx| {
const lane_window = try helpers.advanceWindowSized(uniform_slice_window, helpers.laneOffset(matrix_stride, matrix_row_major, idx, @sizeOf(u32)), @sizeOf(u32));
const ptr = try helpers.robustU32Pointer(allocator, &v[idx], lane_window, descriptor_backed, uniform_backing_value, owns_uniform_backing_value);
break :blk .{ .Pointer = .{ .ptr = .{ .u32_ptr = ptr.ptr }, .uniform_slice_window = lane_window, .uniform_backing_value = ptr.backing, .owns_uniform_backing_value = ptr.owns_backing, .matrix_stride = null } };
},
else => {
const ptr = try helpers.robustU32Pointer(allocator, null, null, descriptor_backed, uniform_backing_value, owns_uniform_backing_value);
break :blk .{ .Pointer = .{ .ptr = .{ .u32_ptr = ptr.ptr }, .uniform_slice_window = null, .uniform_backing_value = ptr.backing, .owns_uniform_backing_value = ptr.owns_backing, .matrix_stride = null } };
},
},
.Vector2u32 => |*v| switch (component_index) {
inline 0...1 => |idx| break :blk .{ .Pointer = .{
.ptr = .{ .u32_ptr = &v[idx] },
.uniform_slice_window = try helpers.advanceWindowSized(uniform_slice_window, helpers.laneOffset(matrix_stride, matrix_row_major, idx, @sizeOf(u32)), @sizeOf(u32)),
.uniform_backing_value = uniform_backing_value,
.owns_uniform_backing_value = owns_uniform_backing_value,
.matrix_stride = null,
} },
else => return RuntimeError.InvalidSpirV,
inline 0...1 => |idx| {
const lane_window = try helpers.advanceWindowSized(uniform_slice_window, helpers.laneOffset(matrix_stride, matrix_row_major, idx, @sizeOf(u32)), @sizeOf(u32));
const ptr = try helpers.robustU32Pointer(allocator, &v[idx], lane_window, descriptor_backed, uniform_backing_value, owns_uniform_backing_value);
break :blk .{ .Pointer = .{ .ptr = .{ .u32_ptr = ptr.ptr }, .uniform_slice_window = lane_window, .uniform_backing_value = ptr.backing, .owns_uniform_backing_value = ptr.owns_backing, .matrix_stride = null } };
},
else => {
const ptr = try helpers.robustU32Pointer(allocator, null, null, descriptor_backed, uniform_backing_value, owns_uniform_backing_value);
break :blk .{ .Pointer = .{ .ptr = .{ .u32_ptr = ptr.ptr }, .uniform_slice_window = null, .uniform_backing_value = ptr.backing, .owns_uniform_backing_value = ptr.owns_backing, .matrix_stride = null } };
},
},
else => return RuntimeError.InvalidSpirV,
}
@@ -4017,17 +4101,71 @@ fn opCompositeConstruct(allocator: std.mem.Allocator, word_count: SpvWord, rt: *
}
}
fn extractCompositeValue(allocator: std.mem.Allocator, results: []Result, value: *const Value, member_ids: []const SpvWord) RuntimeError!Value {
var composite = value.*;
for (member_ids) |member_id_word| {
const member_id: usize = @intCast(member_id_word);
if (composite.getCompositeDataOrNull()) |v| {
composite = v[member_id];
continue;
}
switch (composite) {
.RuntimeArray => |arr| composite = try arr.createLocalValueFromIndex(allocator, results, member_id_word),
.Vector4f32 => |v| return .{ .Float = .{ .bit_count = 32, .value = .{ .float32 = switch (member_id) {
inline 0...3 => |idx| v[idx],
else => return RuntimeError.OutOfBounds,
} } } },
.Vector3f32 => |v| return .{ .Float = .{ .bit_count = 32, .value = .{ .float32 = switch (member_id) {
inline 0...2 => |idx| v[idx],
else => return RuntimeError.OutOfBounds,
} } } },
.Vector2f32 => |v| return .{ .Float = .{ .bit_count = 32, .value = .{ .float32 = switch (member_id) {
inline 0...1 => |idx| v[idx],
else => return RuntimeError.OutOfBounds,
} } } },
.Vector4i32 => |v| return .{ .Int = .{ .bit_count = 32, .is_signed = true, .value = .{ .sint32 = switch (member_id) {
inline 0...3 => |idx| v[idx],
else => return RuntimeError.OutOfBounds,
} } } },
.Vector3i32 => |v| return .{ .Int = .{ .bit_count = 32, .is_signed = true, .value = .{ .sint32 = switch (member_id) {
inline 0...2 => |idx| v[idx],
else => return RuntimeError.OutOfBounds,
} } } },
.Vector2i32 => |v| return .{ .Int = .{ .bit_count = 32, .is_signed = true, .value = .{ .sint32 = switch (member_id) {
inline 0...1 => |idx| v[idx],
else => return RuntimeError.OutOfBounds,
} } } },
.Vector4u32 => |v| return .{ .Int = .{ .bit_count = 32, .is_signed = false, .value = .{ .uint32 = switch (member_id) {
inline 0...3 => |idx| v[idx],
else => return RuntimeError.OutOfBounds,
} } } },
.Vector3u32 => |v| return .{ .Int = .{ .bit_count = 32, .is_signed = false, .value = .{ .uint32 = switch (member_id) {
inline 0...2 => |idx| v[idx],
else => return RuntimeError.OutOfBounds,
} } } },
.Vector2u32 => |v| return .{ .Int = .{ .bit_count = 32, .is_signed = false, .value = .{ .uint32 = switch (member_id) {
inline 0...1 => |idx| v[idx],
else => return RuntimeError.OutOfBounds,
} } } },
else => return RuntimeError.InvalidValueType,
}
}
return composite.dupe(allocator);
}
fn opCompositeExtract(allocator: std.mem.Allocator, word_count: SpvWord, rt: *Runtime) RuntimeError!void {
const res_type = try rt.it.next();
const id = try rt.it.next();
const composite_id = try rt.it.next();
const index_count: usize = @intCast(word_count - 3);
var arena = std.heap.ArenaAllocator.init(allocator);
defer arena.deinit();
const arena_allocator = arena.allocator();
const member_ids = allocator.alloc(SpvWord, index_count) catch return RuntimeError.OutOfMemory;
defer allocator.free(member_ids);
for (member_ids) |*member_id| {
member_id.* = try rt.it.next();
}
const value = try extractCompositeValue(allocator, rt.results, try rt.results[composite_id].getValue(), member_ids);
rt.results[id].variant = .{
.Constant = .{
.type_word = res_type,
@@ -4035,59 +4173,19 @@ fn opCompositeExtract(allocator: std.mem.Allocator, word_count: SpvWord, rt: *Ru
.Type => |t| @as(Result.Type, t),
else => return RuntimeError.InvalidSpirV,
},
.value = blk: {
var composite = (try rt.results[composite_id].getValue()).*;
for (0..index_count) |_| {
const member_id = try rt.it.next();
if (composite.getCompositeDataOrNull()) |v| {
composite = v[member_id];
continue;
}
switch (composite) {
.RuntimeArray => |arr| composite = try arr.createLocalValueFromIndex(arena_allocator, rt.results, member_id),
.Vector4f32 => |v| break :blk .{ .Float = .{ .bit_count = 32, .value = .{ .float32 = switch (member_id) {
inline 0...3 => |idx| v[idx],
else => return RuntimeError.OutOfBounds,
} } } },
.Vector3f32 => |v| break :blk .{ .Float = .{ .bit_count = 32, .value = .{ .float32 = switch (member_id) {
inline 0...2 => |idx| v[idx],
else => return RuntimeError.OutOfBounds,
} } } },
.Vector2f32 => |v| break :blk .{ .Float = .{ .bit_count = 32, .value = .{ .float32 = switch (member_id) {
inline 0...1 => |idx| v[idx],
else => return RuntimeError.OutOfBounds,
} } } },
.Vector4i32 => |v| break :blk .{ .Int = .{ .bit_count = 32, .is_signed = true, .value = .{ .sint32 = switch (member_id) {
inline 0...3 => |idx| v[idx],
else => return RuntimeError.OutOfBounds,
} } } },
.Vector3i32 => |v| break :blk .{ .Int = .{ .bit_count = 32, .is_signed = true, .value = .{ .sint32 = switch (member_id) {
inline 0...2 => |idx| v[idx],
else => return RuntimeError.OutOfBounds,
} } } },
.Vector2i32 => |v| break :blk .{ .Int = .{ .bit_count = 32, .is_signed = true, .value = .{ .sint32 = switch (member_id) {
inline 0...1 => |idx| v[idx],
else => return RuntimeError.OutOfBounds,
} } } },
.Vector4u32 => |v| break :blk .{ .Int = .{ .bit_count = 32, .is_signed = false, .value = .{ .uint32 = switch (member_id) {
inline 0...3 => |idx| v[idx],
else => return RuntimeError.OutOfBounds,
} } } },
.Vector3u32 => |v| break :blk .{ .Int = .{ .bit_count = 32, .is_signed = false, .value = .{ .uint32 = switch (member_id) {
inline 0...2 => |idx| v[idx],
else => return RuntimeError.OutOfBounds,
} } } },
.Vector2u32 => |v| break :blk .{ .Int = .{ .bit_count = 32, .is_signed = false, .value = .{ .uint32 = switch (member_id) {
inline 0...1 => |idx| v[idx],
else => return RuntimeError.OutOfBounds,
} } } },
else => return RuntimeError.InvalidValueType,
}
}
break :blk try composite.dupe(allocator);
},
.value = value,
},
};
if (rt.derivatives.get(composite_id)) |derivative| {
var dx = try extractCompositeValue(allocator, rt.results, &derivative.dx, member_ids);
defer dx.deinit(allocator);
var dy = try extractCompositeValue(allocator, rt.results, &derivative.dy, member_ids);
defer dy.deinit(allocator);
try rt.setDerivative(allocator, id, &dx, &dy);
} else {
rt.clearDerivative(allocator, id);
}
}
fn opCompositeInsert(allocator: std.mem.Allocator, word_count: SpvWord, rt: *Runtime) RuntimeError!void {