From e49383f07ec20d194811a48986f9ffd3483aed2b Mon Sep 17 00:00:00 2001 From: Kbz-8 Date: Mon, 29 Jun 2026 23:21:38 +0200 Subject: [PATCH] improving robust buffer access --- src/Runtime.zig | 2 +- src/Value.zig | 116 ++++++++++----- src/opcodes.zig | 380 ++++++++++++++++++++++++++++++------------------ 3 files changed, 320 insertions(+), 178 deletions(-) diff --git a/src/Runtime.zig b/src/Runtime.zig index d44078c..37b9cc2 100644 --- a/src/Runtime.zig +++ b/src/Runtime.zig @@ -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, diff --git a/src/Value.zig b/src/Value.zig index aa619f2..c887949 100644 --- a/src/Value.zig +++ b/src/Value.zig @@ -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| { diff --git a/src/opcodes.zig b/src/opcodes.zig index c19bcdc..d19074c 100644 --- a/src/opcodes.zig +++ b/src/opcodes.zig @@ -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 {