diff --git a/src/Runtime.zig b/src/Runtime.zig index 29126d0..cf2fee8 100644 --- a/src/Runtime.zig +++ b/src/Runtime.zig @@ -281,6 +281,19 @@ pub fn copyDerivative(self: *Self, allocator: std.mem.Allocator, dst: SpvWord, s } } +pub fn applySpecializationLayout(self: *Self, allocator: std.mem.Allocator) RuntimeError!void { + self.reset(); + try self.pass(allocator, .initMany(&.{ + .SpecConstantTrue, + .SpecConstantFalse, + .SpecConstantComposite, + .SpecConstant, + .SpecConstantOp, + .TypeArray, + .Variable, + })); +} + pub fn getEntryPointByName(self: *const Self, name: []const u8) RuntimeError!SpvWord { for (self.mod.entry_points.items, 0..) |entry_point, i| { if (blk: { @@ -523,6 +536,61 @@ fn readResultValue(self: *const Self, output: []u8, result: SpvWord) RuntimeErro } } +fn readConstantIndex(self: *const Self, result: SpvWord) RuntimeError!usize { + const value = try self.results[result].getConstValue(); + return switch (value.*) { + .Int => |i| switch (i.bit_count) { + 8 => if (i.is_signed) std.math.cast(usize, i.value.sint8) orelse RuntimeError.OutOfBounds else @as(usize, i.value.uint8), + 16 => if (i.is_signed) std.math.cast(usize, i.value.sint16) orelse RuntimeError.OutOfBounds else @as(usize, i.value.uint16), + 32 => if (i.is_signed) std.math.cast(usize, i.value.sint32) orelse RuntimeError.OutOfBounds else @as(usize, i.value.uint32), + 64 => if (i.is_signed) std.math.cast(usize, i.value.sint64) orelse RuntimeError.OutOfBounds else std.math.cast(usize, i.value.uint64) orelse RuntimeError.OutOfBounds, + else => RuntimeError.InvalidSpirV, + }, + else => RuntimeError.InvalidSpirV, + }; +} + +fn accessChainPrefixValue(self: *const Self, result: SpvWord, prefix_len: usize) RuntimeError!*Value { + const access_chain = switch ((self.results[result].variant orelse return RuntimeError.InvalidSpirV)) { + .AccessChain => |*a| a, + else => return RuntimeError.InvalidSpirV, + }; + if (prefix_len > access_chain.indexes.len) return RuntimeError.OutOfBounds; + + var value = switch ((self.results[access_chain.base].variant orelse return RuntimeError.InvalidSpirV)) { + .Variable => |*v| &v.value, + .AccessChain => |*a| switch (a.value) { + .Pointer => |ptr| switch (ptr.ptr) { + .common => |value_ptr| value_ptr, + else => return RuntimeError.InvalidSpirV, + }, + else => &a.value, + }, + else => return RuntimeError.InvalidSpirV, + }; + + for (access_chain.indexes[0..prefix_len]) |index_id| { + const index = try self.readConstantIndex(index_id); + value = switch (value.*) { + .Vector, .Matrix => |values| blk: { + if (index >= values.len) return RuntimeError.OutOfBounds; + break :blk &values[index]; + }, + .Array => |arr| blk: { + if (index >= arr.values.len) return RuntimeError.OutOfBounds; + break :blk &arr.values[index]; + }, + .Structure => |structure| blk: { + if (index >= structure.values.len) return RuntimeError.OutOfBounds; + break :blk &structure.values[index]; + }, + else => return RuntimeError.InvalidValueType, + }; + } + + return value; +} + fn writeResultValue(self: *const Self, allocator: std.mem.Allocator, input: []const u8, result: SpvWord) RuntimeError!void { if (self.results[result].variant) |*variant| { switch (variant.*) { @@ -559,18 +627,41 @@ fn writeResultValue(self: *const Self, allocator: std.mem.Allocator, input: []co }, .AccessChain => |*a| switch (a.value) { .Pointer => |ptr| switch (ptr.ptr) { - .common => |value_ptr| _ = try value_ptr.write(input), + .common => |value_ptr| { + const value_size = try value_ptr.getPlainMemorySize(); + if (a.indexes.len > 1 and input.len > value_size) { + const parent = try self.accessChainPrefixValue(result, a.indexes.len - 1); + _ = try parent.write(input); + } else { + _ = try value_ptr.write(input); + } + }, .f32_ptr => |value_ptr| { if (input.len < @sizeOf(f32)) return RuntimeError.OutOfBounds; - std.mem.copyForwards(u8, std.mem.asBytes(value_ptr), input[0..@sizeOf(f32)]); + if (a.indexes.len > 1 and input.len > @sizeOf(f32)) { + const parent = try self.accessChainPrefixValue(result, a.indexes.len - 1); + _ = try parent.write(input); + } else { + std.mem.copyForwards(u8, std.mem.asBytes(value_ptr), input[0..@sizeOf(f32)]); + } }, .i32_ptr => |value_ptr| { if (input.len < @sizeOf(i32)) return RuntimeError.OutOfBounds; - std.mem.copyForwards(u8, std.mem.asBytes(value_ptr), input[0..@sizeOf(i32)]); + if (a.indexes.len > 1 and input.len > @sizeOf(i32)) { + const parent = try self.accessChainPrefixValue(result, a.indexes.len - 1); + _ = try parent.write(input); + } else { + std.mem.copyForwards(u8, std.mem.asBytes(value_ptr), input[0..@sizeOf(i32)]); + } }, .u32_ptr => |value_ptr| { if (input.len < @sizeOf(u32)) return RuntimeError.OutOfBounds; - std.mem.copyForwards(u8, std.mem.asBytes(value_ptr), input[0..@sizeOf(u32)]); + if (a.indexes.len > 1 and input.len > @sizeOf(u32)) { + const parent = try self.accessChainPrefixValue(result, a.indexes.len - 1); + _ = try parent.write(input); + } else { + std.mem.copyForwards(u8, std.mem.asBytes(value_ptr), input[0..@sizeOf(u32)]); + } }, }, else => _ = try a.value.write(input), @@ -584,19 +675,63 @@ fn writeResultValue(self: *const Self, allocator: std.mem.Allocator, input: []co const InputLocationTarget = struct { result: SpvWord, + struct_member: ?usize = null, + array_element: ?usize = null, matrix_column: ?usize = null, }; fn resolveInputLocationTarget(self: *const Self, location: SpvWord) RuntimeError!InputLocationTarget { - if (location < self.mod.input_locations.len and self.mod.input_locations[location][0] != 0) { + if (location < self.mod.input_locations.len and + self.mod.input_locations[location][0] != 0 and + self.results[self.mod.input_locations[location][0]].variant != null) + { const result = self.mod.input_locations[location][0]; const value = try self.results[result].getConstValue(); switch (value.*) { + .Array => |arr| { + if (arr.values.len == 0) return RuntimeError.OutOfBounds; + return .{ .result = result, .array_element = 0 }; + }, .Matrix => return .{ .result = result, .matrix_column = 0 }, else => return .{ .result = result }, } } + for (self.results, 0..) |*result, id| { + const variant = result.variant orelse continue; + const variable = switch (variant) { + .Variable => |v| v, + else => continue, + }; + if (variable.storage_class != .Input) continue; + + const type_word = switch ((self.results[variable.type_word].variant orelse continue)) { + .Type => |t| switch (t) { + .Pointer => |ptr| ptr.target, + else => variable.type_word, + }, + else => continue, + }; + const type_result = &self.results[type_word]; + const type_variant = type_result.variant orelse continue; + switch (type_variant) { + .Type => |t| switch (t) { + .Structure => { + for (type_result.decorations.items) |decoration| { + if (decoration.rtype == .Location and decoration.literal_1 == location) { + return .{ + .result = @intCast(id), + .struct_member = @intCast(decoration.index), + }; + } + } + }, + else => {}, + }, + else => {}, + } + } + var base_location = location; while (base_location > 0) { base_location -= 1; @@ -610,6 +745,25 @@ fn resolveInputLocationTarget(self: *const Self, location: SpvWord) RuntimeError const location_offset: usize = @intCast(location - base_location); const value = try self.results[result].getConstValue(); switch (value.*) { + .Array => |arr| { + if (arr.values.len == 0) continue; + + const element_locations = switch (arr.values[0]) { + .Matrix => |columns| columns.len, + else => 1, + }; + if (element_locations == 0) continue; + + const element_index = location_offset / element_locations; + if (element_index >= arr.values.len) continue; + + const element_location = location_offset % element_locations; + return .{ + .result = result, + .array_element = element_index, + .matrix_column = if (std.meta.activeTag(arr.values[element_index]) == .Matrix) element_location else null, + }; + }, .Matrix => |columns| { if (location_offset < columns.len) { return .{ @@ -632,8 +786,28 @@ fn getInputLocationTargetValue(self: *const Self, target: InputLocationTarget) R else => return RuntimeError.InvalidSpirV, }; - if (target.matrix_column) |column| { + const element_value = if (target.array_element) |element| blk: { switch (value.*) { + .Array => |arr| { + if (element >= arr.values.len) return RuntimeError.OutOfBounds; + break :blk &arr.values[element]; + }, + else => return RuntimeError.InvalidValueType, + } + } else value; + + const member_value = if (target.struct_member) |member| blk: { + switch (element_value.*) { + .Structure => |structure| { + if (member >= structure.values.len) return RuntimeError.OutOfBounds; + break :blk &structure.values[member]; + }, + else => return RuntimeError.InvalidValueType, + } + } else element_value; + + if (target.matrix_column) |column| { + switch (member_value.*) { .Matrix => |columns| { if (column >= columns.len) return RuntimeError.OutOfBounds; return &columns[column]; @@ -642,7 +816,7 @@ fn getInputLocationTargetValue(self: *const Self, target: InputLocationTarget) R } } - return value; + return member_value; } pub fn readOutput(self: *const Self, output: []u8, result: SpvWord) RuntimeError!void { @@ -705,8 +879,19 @@ pub fn flushDescriptorSets(self: *const Self, allocator: std.mem.Allocator) Runt } pub fn getResultMemorySize(self: *const Self, result: SpvWord) RuntimeError!usize { - const value = try self.results[result].getConstValue(); - return value.getPlainMemorySize(); + const variant = self.results[result].variant orelse return RuntimeError.InvalidSpirV; + return switch (variant) { + .AccessChain => |a| switch (a.value) { + .Pointer => |ptr| switch (ptr.ptr) { + .common => |value_ptr| value_ptr.getPlainMemorySize(), + .f32_ptr => @sizeOf(f32), + .i32_ptr => @sizeOf(i32), + .u32_ptr => @sizeOf(u32), + }, + else => a.value.getPlainMemorySize(), + }, + else => (try self.results[result].getConstValue()).getPlainMemorySize(), + }; } pub fn hasResultDecoration(self: *const Self, result: SpvWord, decoration: spv.SpvDecoration) bool { diff --git a/src/opcodes.zig b/src/opcodes.zig index d4ad7ea..1fd946b 100644 --- a/src/opcodes.zig +++ b/src/opcodes.zig @@ -1334,8 +1334,10 @@ fn ImageEngine(comptime Op: ImageOp) type { } const ParsedImageOperands = struct { + bias: f32 = 0.0, lod: ?f32 = null, image_lod: ?i32 = null, + grad: ?Runtime.ImageDerivatives = null, sample: ?i32 = null, offset: Runtime.ImageOffset = .{}, }; @@ -1344,7 +1346,7 @@ fn ImageEngine(comptime Op: ImageOp) type { var parsed: ParsedImageOperands = .{}; if (imageOperandPresent(image_operands, .BiasMask)) { - _ = try rt.it.next(); + parsed.bias = try readSampleCoordLane(try rt.results[try rt.it.next()].getValue(), 0); } if (imageOperandPresent(image_operands, .LodMask)) { const lod_value = try rt.results[try rt.it.next()].getValue(); @@ -1352,8 +1354,22 @@ fn ImageEngine(comptime Op: ImageOp) type { parsed.image_lod = readImageQueryLod(lod_value) catch null; } if (imageOperandPresent(image_operands, .GradMask)) { - _ = try rt.it.next(); - _ = try rt.it.next(); + const dx = try rt.results[try rt.it.next()].getValue(); + const dy = try rt.results[try rt.it.next()].getValue(); + parsed.grad = .{ + .dx = .{ + .x = try readSampleCoordLane(dx, 0), + .y = if (try valueLaneCount(dx) > 1) try readSampleCoordLane(dx, 1) else 0.0, + .z = if (try valueLaneCount(dx) > 2) try readSampleCoordLane(dx, 2) else 0.0, + .w = if (try valueLaneCount(dx) > 3) try readSampleCoordLane(dx, 3) else 0.0, + }, + .dy = .{ + .x = try readSampleCoordLane(dy, 0), + .y = if (try valueLaneCount(dy) > 1) try readSampleCoordLane(dy, 1) else 0.0, + .z = if (try valueLaneCount(dy) > 2) try readSampleCoordLane(dy, 2) else 0.0, + .w = if (try valueLaneCount(dy) > 3) try readSampleCoordLane(dy, 3) else 0.0, + }, + }; } if (imageOperandPresent(image_operands, .ConstOffsetMask) or imageOperandPresent(image_operands, .OffsetMask)) { parsed.offset = try readImageOffset(rt, try rt.it.next()); @@ -1495,7 +1511,32 @@ fn ImageEngine(comptime Op: ImageOp) type { } } - fn cubeFaceCoord(x: f32, y: f32, z: f32) struct { u: f32, v: f32 } { + const CubeFaceCoord = struct { + face: u32, + u: f32, + v: f32, + }; + + fn cubeFaceCoordForFace(face: u32, x: f32, y: f32, z: f32) CubeFaceCoord { + const sc, const tc, const ma = switch (face) { + 0 => .{ -z, -y, @abs(x) }, + 1 => .{ z, -y, @abs(x) }, + 2 => .{ x, z, @abs(y) }, + 3 => .{ x, -z, @abs(y) }, + 4 => .{ x, -y, @abs(z) }, + 5 => .{ -x, -y, @abs(z) }, + else => .{ 0.0, 0.0, 1.0 }, + }; + + const inv_ma = if (ma == 0.0) 0.0 else 1.0 / ma; + return .{ + .face = face, + .u = (sc * inv_ma + 1.0) * 0.5, + .v = (tc * inv_ma + 1.0) * 0.5, + }; + } + + fn cubeFaceCoord(x: f32, y: f32, z: f32) CubeFaceCoord { const ax = @abs(x); const ay = @abs(y); const az = @abs(z); @@ -1503,31 +1544,38 @@ fn ImageEngine(comptime Op: ImageOp) type { var sc: f32 = 0.0; var tc: f32 = 0.0; var ma: f32 = 1.0; + var face: u32 = 0; if (ax >= ay and ax >= az) { ma = ax; if (x >= 0.0) { + face = 0; sc = -z; tc = -y; } else { + face = 1; sc = z; tc = -y; } } else if (ay >= ax and ay >= az) { ma = ay; if (y >= 0.0) { + face = 2; sc = x; tc = z; } else { + face = 3; sc = x; tc = -z; } } else { ma = az; if (z >= 0.0) { + face = 4; sc = x; tc = -y; } else { + face = 5; sc = -x; tc = -y; } @@ -1535,26 +1583,84 @@ fn ImageEngine(comptime Op: ImageOp) type { const inv_ma = if (ma == 0.0) 0.0 else 1.0 / ma; return .{ + .face = face, .u = (sc * inv_ma + 1.0) * 0.5, .v = (tc * inv_ma + 1.0) * 0.5, }; } - fn implicitSampleLod(rt: *Runtime, coordinate_id: SpvWord, driver_image: *anyopaque, driver_sampler: *anyopaque, dim: spv.SpvDim, x: f32, y: f32, z: f32) RuntimeError!?f32 { - if (dim == .Cube) - return 0.0; - + fn projectedSampleDerivatives(rt: *Runtime, coordinate_id: SpvWord, coordinate: *const Value) RuntimeError!?Runtime.ImageDerivatives { const coord_derivative = rt.derivatives.get(coordinate_id) orelse return null; - const coord_dx_x = try readSampleCoordLane(&coord_derivative.dx, 0); - const coord_dx_y = readSampleCoordLane(&coord_derivative.dx, 1) catch 0.0; - const coord_dx_z = readSampleCoordLane(&coord_derivative.dx, 2) catch 0.0; - const coord_dy_x = try readSampleCoordLane(&coord_derivative.dy, 0); - const coord_dy_y = readSampleCoordLane(&coord_derivative.dy, 1) catch 0.0; - const coord_dy_z = readSampleCoordLane(&coord_derivative.dy, 2) catch 0.0; + const lane_count = try valueLaneCount(coordinate); + if (lane_count < 2) + return RuntimeError.InvalidSpirV; + + const q_lane = lane_count - 1; + const q = try readProjectionDivisor(coordinate); + const inv_q_squared = 1.0 / (q * q); + const dq_dx = try readSampleCoordLane(&coord_derivative.dx, q_lane); + const dq_dy = try readSampleCoordLane(&coord_derivative.dy, q_lane); + + const coord_x = try readSampleCoordLane(coordinate, 0); + const coord_y = readSampleCoordLane(coordinate, 1) catch 0.0; + const coord_z = readSampleCoordLane(coordinate, 2) catch 0.0; + const dx_x = try readSampleCoordLane(&coord_derivative.dx, 0); + const dx_y = readSampleCoordLane(&coord_derivative.dx, 1) catch 0.0; + const dx_z = readSampleCoordLane(&coord_derivative.dx, 2) catch 0.0; + const dy_x = try readSampleCoordLane(&coord_derivative.dy, 0); + const dy_y = readSampleCoordLane(&coord_derivative.dy, 1) catch 0.0; + const dy_z = readSampleCoordLane(&coord_derivative.dy, 2) catch 0.0; + + return .{ + .dx = .{ + .x = (dx_x * q - coord_x * dq_dx) * inv_q_squared, + .y = if (q_lane > 1) (dx_y * q - coord_y * dq_dx) * inv_q_squared else 0.0, + .z = if (q_lane > 2) (dx_z * q - coord_z * dq_dx) * inv_q_squared else 0.0, + .w = 0.0, + }, + .dy = .{ + .x = (dy_x * q - coord_x * dq_dy) * inv_q_squared, + .y = if (q_lane > 1) (dy_y * q - coord_y * dq_dy) * inv_q_squared else 0.0, + .z = if (q_lane > 2) (dy_z * q - coord_z * dq_dy) * inv_q_squared else 0.0, + .w = 0.0, + }, + }; + } + + fn sampleDerivativesFromRuntime(rt: *Runtime, coordinate_id: SpvWord) RuntimeError!?Runtime.ImageDerivatives { + const coord_derivative = rt.derivatives.get(coordinate_id) orelse return null; + return .{ + .dx = .{ + .x = try readSampleCoordLane(&coord_derivative.dx, 0), + .y = readSampleCoordLane(&coord_derivative.dx, 1) catch 0.0, + .z = readSampleCoordLane(&coord_derivative.dx, 2) catch 0.0, + .w = readSampleCoordLane(&coord_derivative.dx, 3) catch 0.0, + }, + .dy = .{ + .x = try readSampleCoordLane(&coord_derivative.dy, 0), + .y = readSampleCoordLane(&coord_derivative.dy, 1) catch 0.0, + .z = readSampleCoordLane(&coord_derivative.dy, 2) catch 0.0, + .w = readSampleCoordLane(&coord_derivative.dy, 3) catch 0.0, + }, + }; + } + + fn implicitSampleLod(rt: *Runtime, coordinate_id: SpvWord, coordinate: *const Value, projected: bool, driver_image: *anyopaque, driver_sampler: *anyopaque, dim: spv.SpvDim, x: f32, y: f32, z: f32, bias: f32) RuntimeError!?f32 { + const fallback_lod = if (bias != 0.0) bias else null; + const coord_derivatives = if (projected) + (try projectedSampleDerivatives(rt, coordinate_id, coordinate)) orelse return fallback_lod + else + (try sampleDerivativesFromRuntime(rt, coordinate_id)) orelse return fallback_lod; + const coord_dx_x = coord_derivatives.dx.x; + const coord_dx_y = coord_derivatives.dx.y; + const coord_dx_z = coord_derivatives.dx.z; + const coord_dy_x = coord_derivatives.dy.x; + const coord_dy_y = coord_derivatives.dy.y; + const coord_dy_z = coord_derivatives.dy.z; const lod_dim, const derivatives = if (dim == .Cube) blk: { const center = cubeFaceCoord(x, y, z); - const dx = cubeFaceCoord(x + coord_dx_x, y + coord_dx_y, z + coord_dx_z); - const dy = cubeFaceCoord(x + coord_dy_x, y + coord_dy_y, z + coord_dy_z); + const dx = cubeFaceCoordForFace(center.face, x + coord_dx_x, y + coord_dx_y, z + coord_dx_z); + const dy = cubeFaceCoordForFace(center.face, x + coord_dy_x, y + coord_dy_y, z + coord_dy_z); break :blk .{ spv.SpvDim.@"2D", Runtime.ImageDerivatives{ @@ -1570,7 +1676,7 @@ fn ImageEngine(comptime Op: ImageOp) type { }, }; const lod = try rt.image_api.queryImageLod(driver_image, driver_sampler, lod_dim, derivatives); - return lod.y; + return lod.y + bias; } fn setImplicitSampleDerivative( @@ -1579,6 +1685,8 @@ fn ImageEngine(comptime Op: ImageOp) type { result_type_word: SpvWord, result_id: SpvWord, coordinate_id: SpvWord, + coordinate: *const Value, + projected: bool, dst: *const Value, driver_image: *anyopaque, driver_sampler: *anyopaque, @@ -1586,25 +1694,32 @@ fn ImageEngine(comptime Op: ImageOp) type { x: f32, y: f32, z: f32, + bias: f32, offset: Runtime.ImageOffset, ) RuntimeError!void { - const coord_derivative = rt.derivatives.get(coordinate_id) orelse { - rt.clearDerivative(allocator, result_id); - return; - }; + const coord_derivatives = if (projected) + (try projectedSampleDerivatives(rt, coordinate_id, coordinate)) orelse { + rt.clearDerivative(allocator, result_id); + return; + } + else + (try sampleDerivativesFromRuntime(rt, coordinate_id)) orelse { + rt.clearDerivative(allocator, result_id); + return; + }; var dx_sample = try Value.init(allocator, rt.results, result_type_word, false); defer dx_sample.deinit(allocator); var dy_sample = try Value.init(allocator, rt.results, result_type_word, false); defer dy_sample.deinit(allocator); - const coord_dx_x = try readSampleCoordLane(&coord_derivative.dx, 0); - const coord_dx_y = readSampleCoordLane(&coord_derivative.dx, 1) catch 0.0; - const coord_dx_z = readSampleCoordLane(&coord_derivative.dx, 2) catch 0.0; - const coord_dy_x = try readSampleCoordLane(&coord_derivative.dy, 0); - const coord_dy_y = readSampleCoordLane(&coord_derivative.dy, 1) catch 0.0; - const coord_dy_z = readSampleCoordLane(&coord_derivative.dy, 2) catch 0.0; - const sample_lod = try implicitSampleLod(rt, coordinate_id, driver_image, driver_sampler, dim, x, y, z); + const coord_dx_x = coord_derivatives.dx.x; + const coord_dx_y = coord_derivatives.dx.y; + const coord_dx_z = coord_derivatives.dx.z; + const coord_dy_x = coord_derivatives.dy.x; + const coord_dy_y = coord_derivatives.dy.y; + const coord_dy_z = coord_derivatives.dy.z; + const sample_lod = try implicitSampleLod(rt, coordinate_id, coordinate, projected, driver_image, driver_sampler, dim, x, y, z, bias); try sampleImageImplicitLod(rt, &dx_sample, driver_image, driver_sampler, dim, x + coord_dx_x, y + coord_dx_y, z + coord_dx_z, sample_lod, offset); try sampleImageImplicitLod(rt, &dy_sample, driver_image, driver_sampler, dim, x + coord_dy_x, y + coord_dy_y, z + coord_dy_z, sample_lod, offset); @@ -1661,6 +1776,14 @@ fn ImageEngine(comptime Op: ImageOp) type { } } + fn explicitSampleLod(rt: *Runtime, driver_image: *anyopaque, driver_sampler: *anyopaque, dim: spv.SpvDim, parsed: ParsedImageOperands) RuntimeError!?f32 { + if (parsed.grad) |derivatives| { + const lod = try rt.image_api.queryImageLod(driver_image, driver_sampler, dim, derivatives); + return lod.y; + } + 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)); } @@ -1977,15 +2100,19 @@ fn ImageEngine(comptime Op: ImageOp) type { }; const image_operands = if (word_count > 4) try rt.it.next() else 0; const parsed_operands = try parseImageOperands(rt, image_operands); + const projected = comptime Op == .SampleProjImplicitLod; 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 sampleImageImplicitLod( @@ -2006,6 +2133,8 @@ fn ImageEngine(comptime Op: ImageOp) type { result_type_word, result_id, coordinate_id, + coordinate, + projected, dst, sampled_image_operand.driver_image, sampled_image_operand.driver_sampler, @@ -2013,6 +2142,7 @@ fn ImageEngine(comptime Op: ImageOp) type { coords.x, coords.y, coords.z, + parsed_operands.bias, parsed_operands.offset, ); }, @@ -2076,7 +2206,13 @@ fn ImageEngine(comptime Op: ImageOp) type { coords.x, coords.y, coords.z, - parsed_operands.lod, + try explicitSampleLod( + rt, + sampled_image_operand.driver_image, + sampled_image_operand.driver_sampler, + sampled_image_operand.dim, + parsed_operands, + ), parsed_operands.offset, ); }, @@ -2111,7 +2247,13 @@ fn ImageEngine(comptime Op: ImageOp) type { coords.y, coords.z, dref, - parsed_operands.lod, + try explicitSampleLod( + rt, + sampled_image_operand.driver_image, + sampled_image_operand.driver_sampler, + sampled_image_operand.dim, + parsed_operands, + ), parsed_operands.offset, ); },