improving access chain queries
Build / build (push) Failing after 0s
Test / build (push) Failing after 0s

This commit is contained in:
2026-06-28 13:08:12 +02:00
parent cba8a4723d
commit b9b6087fef
2 changed files with 366 additions and 39 deletions
+191 -6
View File
@@ -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 { pub fn getEntryPointByName(self: *const Self, name: []const u8) RuntimeError!SpvWord {
for (self.mod.entry_points.items, 0..) |entry_point, i| { for (self.mod.entry_points.items, 0..) |entry_point, i| {
if (blk: { 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 { fn writeResultValue(self: *const Self, allocator: std.mem.Allocator, input: []const u8, result: SpvWord) RuntimeError!void {
if (self.results[result].variant) |*variant| { if (self.results[result].variant) |*variant| {
switch (variant.*) { switch (variant.*) {
@@ -559,18 +627,41 @@ fn writeResultValue(self: *const Self, allocator: std.mem.Allocator, input: []co
}, },
.AccessChain => |*a| switch (a.value) { .AccessChain => |*a| switch (a.value) {
.Pointer => |ptr| switch (ptr.ptr) { .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| { .f32_ptr => |value_ptr| {
if (input.len < @sizeOf(f32)) return RuntimeError.OutOfBounds; if (input.len < @sizeOf(f32)) return RuntimeError.OutOfBounds;
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)]); std.mem.copyForwards(u8, std.mem.asBytes(value_ptr), input[0..@sizeOf(f32)]);
}
}, },
.i32_ptr => |value_ptr| { .i32_ptr => |value_ptr| {
if (input.len < @sizeOf(i32)) return RuntimeError.OutOfBounds; if (input.len < @sizeOf(i32)) return RuntimeError.OutOfBounds;
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)]); std.mem.copyForwards(u8, std.mem.asBytes(value_ptr), input[0..@sizeOf(i32)]);
}
}, },
.u32_ptr => |value_ptr| { .u32_ptr => |value_ptr| {
if (input.len < @sizeOf(u32)) return RuntimeError.OutOfBounds; if (input.len < @sizeOf(u32)) return RuntimeError.OutOfBounds;
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)]); std.mem.copyForwards(u8, std.mem.asBytes(value_ptr), input[0..@sizeOf(u32)]);
}
}, },
}, },
else => _ = try a.value.write(input), else => _ = try a.value.write(input),
@@ -584,19 +675,63 @@ fn writeResultValue(self: *const Self, allocator: std.mem.Allocator, input: []co
const InputLocationTarget = struct { const InputLocationTarget = struct {
result: SpvWord, result: SpvWord,
struct_member: ?usize = null,
array_element: ?usize = null,
matrix_column: ?usize = null, matrix_column: ?usize = null,
}; };
fn resolveInputLocationTarget(self: *const Self, location: SpvWord) RuntimeError!InputLocationTarget { 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 result = self.mod.input_locations[location][0];
const value = try self.results[result].getConstValue(); const value = try self.results[result].getConstValue();
switch (value.*) { 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 }, .Matrix => return .{ .result = result, .matrix_column = 0 },
else => return .{ .result = result }, 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; var base_location = location;
while (base_location > 0) { while (base_location > 0) {
base_location -= 1; base_location -= 1;
@@ -610,6 +745,25 @@ fn resolveInputLocationTarget(self: *const Self, location: SpvWord) RuntimeError
const location_offset: usize = @intCast(location - base_location); const location_offset: usize = @intCast(location - base_location);
const value = try self.results[result].getConstValue(); const value = try self.results[result].getConstValue();
switch (value.*) { 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| { .Matrix => |columns| {
if (location_offset < columns.len) { if (location_offset < columns.len) {
return .{ return .{
@@ -632,8 +786,28 @@ fn getInputLocationTargetValue(self: *const Self, target: InputLocationTarget) R
else => return RuntimeError.InvalidSpirV, else => return RuntimeError.InvalidSpirV,
}; };
if (target.matrix_column) |column| { const element_value = if (target.array_element) |element| blk: {
switch (value.*) { 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| { .Matrix => |columns| {
if (column >= columns.len) return RuntimeError.OutOfBounds; if (column >= columns.len) return RuntimeError.OutOfBounds;
return &columns[column]; 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 { 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 { pub fn getResultMemorySize(self: *const Self, result: SpvWord) RuntimeError!usize {
const value = try self.results[result].getConstValue(); const variant = self.results[result].variant orelse return RuntimeError.InvalidSpirV;
return value.getPlainMemorySize(); 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 { pub fn hasResultDecoration(self: *const Self, result: SpvWord, decoration: spv.SpvDecoration) bool {
+169 -27
View File
@@ -1334,8 +1334,10 @@ fn ImageEngine(comptime Op: ImageOp) type {
} }
const ParsedImageOperands = struct { const ParsedImageOperands = struct {
bias: f32 = 0.0,
lod: ?f32 = null, lod: ?f32 = null,
image_lod: ?i32 = null, image_lod: ?i32 = null,
grad: ?Runtime.ImageDerivatives = null,
sample: ?i32 = null, sample: ?i32 = null,
offset: Runtime.ImageOffset = .{}, offset: Runtime.ImageOffset = .{},
}; };
@@ -1344,7 +1346,7 @@ fn ImageEngine(comptime Op: ImageOp) type {
var parsed: ParsedImageOperands = .{}; var parsed: ParsedImageOperands = .{};
if (imageOperandPresent(image_operands, .BiasMask)) { 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)) { if (imageOperandPresent(image_operands, .LodMask)) {
const lod_value = try rt.results[try rt.it.next()].getValue(); 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; parsed.image_lod = readImageQueryLod(lod_value) catch null;
} }
if (imageOperandPresent(image_operands, .GradMask)) { if (imageOperandPresent(image_operands, .GradMask)) {
_ = try rt.it.next(); const dx = try rt.results[try rt.it.next()].getValue();
_ = try rt.it.next(); 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)) { if (imageOperandPresent(image_operands, .ConstOffsetMask) or imageOperandPresent(image_operands, .OffsetMask)) {
parsed.offset = try readImageOffset(rt, try rt.it.next()); 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 ax = @abs(x);
const ay = @abs(y); const ay = @abs(y);
const az = @abs(z); const az = @abs(z);
@@ -1503,31 +1544,38 @@ fn ImageEngine(comptime Op: ImageOp) type {
var sc: f32 = 0.0; var sc: f32 = 0.0;
var tc: f32 = 0.0; var tc: f32 = 0.0;
var ma: f32 = 1.0; var ma: f32 = 1.0;
var face: u32 = 0;
if (ax >= ay and ax >= az) { if (ax >= ay and ax >= az) {
ma = ax; ma = ax;
if (x >= 0.0) { if (x >= 0.0) {
face = 0;
sc = -z; sc = -z;
tc = -y; tc = -y;
} else { } else {
face = 1;
sc = z; sc = z;
tc = -y; tc = -y;
} }
} else if (ay >= ax and ay >= az) { } else if (ay >= ax and ay >= az) {
ma = ay; ma = ay;
if (y >= 0.0) { if (y >= 0.0) {
face = 2;
sc = x; sc = x;
tc = z; tc = z;
} else { } else {
face = 3;
sc = x; sc = x;
tc = -z; tc = -z;
} }
} else { } else {
ma = az; ma = az;
if (z >= 0.0) { if (z >= 0.0) {
face = 4;
sc = x; sc = x;
tc = -y; tc = -y;
} else { } else {
face = 5;
sc = -x; sc = -x;
tc = -y; 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; const inv_ma = if (ma == 0.0) 0.0 else 1.0 / ma;
return .{ return .{
.face = face,
.u = (sc * inv_ma + 1.0) * 0.5, .u = (sc * inv_ma + 1.0) * 0.5,
.v = (tc * 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 { fn projectedSampleDerivatives(rt: *Runtime, coordinate_id: SpvWord, coordinate: *const Value) RuntimeError!?Runtime.ImageDerivatives {
if (dim == .Cube)
return 0.0;
const coord_derivative = rt.derivatives.get(coordinate_id) orelse return null; const coord_derivative = rt.derivatives.get(coordinate_id) orelse return null;
const coord_dx_x = try readSampleCoordLane(&coord_derivative.dx, 0); const lane_count = try valueLaneCount(coordinate);
const coord_dx_y = readSampleCoordLane(&coord_derivative.dx, 1) catch 0.0; if (lane_count < 2)
const coord_dx_z = readSampleCoordLane(&coord_derivative.dx, 2) catch 0.0; return RuntimeError.InvalidSpirV;
const coord_dy_x = try readSampleCoordLane(&coord_derivative.dy, 0);
const coord_dy_y = readSampleCoordLane(&coord_derivative.dy, 1) catch 0.0; const q_lane = lane_count - 1;
const coord_dy_z = readSampleCoordLane(&coord_derivative.dy, 2) catch 0.0; 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 lod_dim, const derivatives = if (dim == .Cube) blk: {
const center = cubeFaceCoord(x, y, z); const center = cubeFaceCoord(x, y, z);
const dx = cubeFaceCoord(x + coord_dx_x, y + coord_dx_y, z + coord_dx_z); const dx = cubeFaceCoordForFace(center.face, 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 dy = cubeFaceCoordForFace(center.face, x + coord_dy_x, y + coord_dy_y, z + coord_dy_z);
break :blk .{ break :blk .{
spv.SpvDim.@"2D", spv.SpvDim.@"2D",
Runtime.ImageDerivatives{ 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); const lod = try rt.image_api.queryImageLod(driver_image, driver_sampler, lod_dim, derivatives);
return lod.y; return lod.y + bias;
} }
fn setImplicitSampleDerivative( fn setImplicitSampleDerivative(
@@ -1579,6 +1685,8 @@ fn ImageEngine(comptime Op: ImageOp) type {
result_type_word: SpvWord, result_type_word: SpvWord,
result_id: SpvWord, result_id: SpvWord,
coordinate_id: SpvWord, coordinate_id: SpvWord,
coordinate: *const Value,
projected: bool,
dst: *const Value, dst: *const Value,
driver_image: *anyopaque, driver_image: *anyopaque,
driver_sampler: *anyopaque, driver_sampler: *anyopaque,
@@ -1586,9 +1694,16 @@ fn ImageEngine(comptime Op: ImageOp) type {
x: f32, x: f32,
y: f32, y: f32,
z: f32, z: f32,
bias: f32,
offset: Runtime.ImageOffset, offset: Runtime.ImageOffset,
) RuntimeError!void { ) RuntimeError!void {
const coord_derivative = rt.derivatives.get(coordinate_id) orelse { 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); rt.clearDerivative(allocator, result_id);
return; return;
}; };
@@ -1598,13 +1713,13 @@ fn ImageEngine(comptime Op: ImageOp) type {
var dy_sample = try Value.init(allocator, rt.results, result_type_word, false); var dy_sample = try Value.init(allocator, rt.results, result_type_word, false);
defer dy_sample.deinit(allocator); defer dy_sample.deinit(allocator);
const coord_dx_x = try readSampleCoordLane(&coord_derivative.dx, 0); const coord_dx_x = coord_derivatives.dx.x;
const coord_dx_y = readSampleCoordLane(&coord_derivative.dx, 1) catch 0.0; const coord_dx_y = coord_derivatives.dx.y;
const coord_dx_z = readSampleCoordLane(&coord_derivative.dx, 2) catch 0.0; const coord_dx_z = coord_derivatives.dx.z;
const coord_dy_x = try readSampleCoordLane(&coord_derivative.dy, 0); const coord_dy_x = coord_derivatives.dy.x;
const coord_dy_y = readSampleCoordLane(&coord_derivative.dy, 1) catch 0.0; const coord_dy_y = coord_derivatives.dy.y;
const coord_dy_z = readSampleCoordLane(&coord_derivative.dy, 2) catch 0.0; const coord_dy_z = coord_derivatives.dy.z;
const sample_lod = try implicitSampleLod(rt, coordinate_id, driver_image, driver_sampler, dim, x, y, 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, &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); 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 { 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)); 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 image_operands = if (word_count > 4) try rt.it.next() else 0;
const parsed_operands = try parseImageOperands(rt, image_operands); const parsed_operands = try parseImageOperands(rt, image_operands);
const projected = comptime Op == .SampleProjImplicitLod;
const lod = try implicitSampleLod( const lod = try implicitSampleLod(
rt, rt,
coordinate_id, coordinate_id,
coordinate,
projected,
sampled_image_operand.driver_image, sampled_image_operand.driver_image,
sampled_image_operand.driver_sampler, sampled_image_operand.driver_sampler,
sampled_image_operand.dim, sampled_image_operand.dim,
coords.x, coords.x,
coords.y, coords.y,
coords.z, coords.z,
parsed_operands.bias,
); );
try sampleImageImplicitLod( try sampleImageImplicitLod(
@@ -2006,6 +2133,8 @@ fn ImageEngine(comptime Op: ImageOp) type {
result_type_word, result_type_word,
result_id, result_id,
coordinate_id, coordinate_id,
coordinate,
projected,
dst, dst,
sampled_image_operand.driver_image, sampled_image_operand.driver_image,
sampled_image_operand.driver_sampler, sampled_image_operand.driver_sampler,
@@ -2013,6 +2142,7 @@ fn ImageEngine(comptime Op: ImageOp) type {
coords.x, coords.x,
coords.y, coords.y,
coords.z, coords.z,
parsed_operands.bias,
parsed_operands.offset, parsed_operands.offset,
); );
}, },
@@ -2076,7 +2206,13 @@ fn ImageEngine(comptime Op: ImageOp) type {
coords.x, coords.x,
coords.y, coords.y,
coords.z, 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, parsed_operands.offset,
); );
}, },
@@ -2111,7 +2247,13 @@ fn ImageEngine(comptime Op: ImageOp) type {
coords.y, coords.y,
coords.z, coords.z,
dref, 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, parsed_operands.offset,
); );
}, },