adding image gathering

This commit is contained in:
2026-06-18 16:45:18 +02:00
parent 42554a5cc1
commit 1f43dc95df
+143
View File
@@ -70,6 +70,7 @@ const BitOp = enum {
const ImageOp = enum { const ImageOp = enum {
Fetch, Fetch,
Gather,
QueryLevels, QueryLevels,
QueryLod, QueryLod,
QuerySamples, QuerySamples,
@@ -202,6 +203,7 @@ pub const SetupDispatcher = block: {
.ISubBorrow = autoSetupConstant, .ISubBorrow = autoSetupConstant,
.Image = autoSetupConstant, .Image = autoSetupConstant,
.ImageFetch = autoSetupConstant, .ImageFetch = autoSetupConstant,
.ImageGather = autoSetupConstant,
.ImageQueryLevels = autoSetupConstant, .ImageQueryLevels = autoSetupConstant,
.ImageQueryLod = opDerivativeSetup, .ImageQueryLod = opDerivativeSetup,
.ImageQuerySamples = autoSetupConstant, .ImageQuerySamples = autoSetupConstant,
@@ -379,6 +381,7 @@ pub fn initRuntimeDispatcher() void {
runtime_dispatcher[@intFromEnum(spv.SpvOp.ISubBorrow)] = opISubBorrow; runtime_dispatcher[@intFromEnum(spv.SpvOp.ISubBorrow)] = opISubBorrow;
runtime_dispatcher[@intFromEnum(spv.SpvOp.Image)] = ImageEngine(.Resolve).op; runtime_dispatcher[@intFromEnum(spv.SpvOp.Image)] = ImageEngine(.Resolve).op;
runtime_dispatcher[@intFromEnum(spv.SpvOp.ImageFetch)] = ImageEngine(.Fetch).op; runtime_dispatcher[@intFromEnum(spv.SpvOp.ImageFetch)] = ImageEngine(.Fetch).op;
runtime_dispatcher[@intFromEnum(spv.SpvOp.ImageGather)] = ImageEngine(.Gather).op;
runtime_dispatcher[@intFromEnum(spv.SpvOp.ImageQueryLevels)] = ImageEngine(.QueryLevels).op; runtime_dispatcher[@intFromEnum(spv.SpvOp.ImageQueryLevels)] = ImageEngine(.QueryLevels).op;
runtime_dispatcher[@intFromEnum(spv.SpvOp.ImageQueryLod)] = ImageEngine(.QueryLod).op; runtime_dispatcher[@intFromEnum(spv.SpvOp.ImageQueryLod)] = ImageEngine(.QueryLod).op;
runtime_dispatcher[@intFromEnum(spv.SpvOp.ImageQuerySamples)] = ImageEngine(.QuerySamples).op; runtime_dispatcher[@intFromEnum(spv.SpvOp.ImageQuerySamples)] = ImageEngine(.QuerySamples).op;
@@ -1655,6 +1658,120 @@ fn ImageEngine(comptime Op: ImageOp) type {
try writeFloatScalar(dst, try rt.image_api.sampleImageDref(driver_image, driver_sampler, dim, x, y, z, dref, lod, offset)); try writeFloatScalar(dst, try rt.image_api.sampleImageDref(driver_image, driver_sampler, dim, x, y, z, dref, lod, offset));
} }
fn gatherCoord(index: i32, extent: u32) f32 {
return (@as(f32, @floatFromInt(index)) + 0.5) / @as(f32, @floatFromInt(extent));
}
fn sampleImageGather(rt: *Runtime, dst: *Value, driver_image: *anyopaque, driver_sampler: *anyopaque, dim: spv.SpvDim, x: f32, y: f32, z: f32, component: usize, offset: Runtime.ImageOffset) RuntimeError!void {
const size = try rt.image_api.queryImageSize(driver_image, dim, false, 0);
if (size.x == 0) return RuntimeError.InvalidSpirV;
const width_f: f32 = @floatFromInt(size.x);
const height = if (size.y == 0) 1 else size.y;
const height_f: f32 = @floatFromInt(height);
const base_x: i32 = @intFromFloat(@floor(x * width_f - 0.5));
const base_y: i32 = @intFromFloat(@floor(y * height_f - 0.5));
const gather_x = [_]i32{ base_x, base_x + 1, base_x + 1, base_x };
const gather_y = [_]i32{ base_y + 1, base_y + 1, base_y, base_y };
switch (dst.*) {
.Vector4f32,
.Vector3f32,
.Vector2f32,
=> {
var result: Runtime.Vec4(f32) = undefined;
inline for (0..4) |i| {
const texel = try rt.image_api.sampleImageFloat4(
driver_image,
driver_sampler,
dim,
gatherCoord(gather_x[i], size.x),
gatherCoord(gather_y[i], height),
z,
0.0,
offset,
);
const values = [_]f32{ texel.x, texel.y, texel.z, texel.w };
@field(result, switch (i) {
0 => "x",
1 => "y",
2 => "z",
3 => "w",
else => unreachable,
}) = if (component < values.len) values[component] else return RuntimeError.InvalidSpirV;
}
try writeFloatTexel(dst, result);
},
.Vector4i32,
.Vector3i32,
.Vector2i32,
.Vector4u32,
.Vector3u32,
.Vector2u32,
=> {
var result: Runtime.Vec4(u32) = undefined;
inline for (0..4) |i| {
const texel = try rt.image_api.sampleImageInt4(
driver_image,
driver_sampler,
dim,
gatherCoord(gather_x[i], size.x),
gatherCoord(gather_y[i], height),
z,
0.0,
offset,
);
const values = [_]u32{ texel.x, texel.y, texel.z, texel.w };
@field(result, switch (i) {
0 => "x",
1 => "y",
2 => "z",
3 => "w",
else => unreachable,
}) = if (component < values.len) values[component] else return RuntimeError.InvalidSpirV;
}
try writeIntTexel(dst, result);
},
.Vector => |lanes| {
if (lanes.len == 0) return RuntimeError.InvalidSpirV;
switch (lanes[0]) {
.Float => {
var result: Runtime.Vec4(f32) = undefined;
inline for (0..4) |i| {
const texel = try rt.image_api.sampleImageFloat4(driver_image, driver_sampler, dim, gatherCoord(gather_x[i], size.x), gatherCoord(gather_y[i], height), z, 0.0, offset);
const values = [_]f32{ texel.x, texel.y, texel.z, texel.w };
@field(result, switch (i) {
0 => "x",
1 => "y",
2 => "z",
3 => "w",
else => unreachable,
}) = if (component < values.len) values[component] else return RuntimeError.InvalidSpirV;
}
try writeFloatTexel(dst, result);
},
.Int => {
var result: Runtime.Vec4(u32) = undefined;
inline for (0..4) |i| {
const texel = try rt.image_api.sampleImageInt4(driver_image, driver_sampler, dim, gatherCoord(gather_x[i], size.x), gatherCoord(gather_y[i], height), z, 0.0, offset);
const values = [_]u32{ texel.x, texel.y, texel.z, texel.w };
@field(result, switch (i) {
0 => "x",
1 => "y",
2 => "z",
3 => "w",
else => unreachable,
}) = if (component < values.len) values[component] else return RuntimeError.InvalidSpirV;
}
try writeIntTexel(dst, result);
},
else => return RuntimeError.InvalidValueType,
}
},
else => return RuntimeError.InvalidValueType,
}
}
fn writeImage(rt: *Runtime, texel: *const Value, driver_image: *anyopaque, dim: spv.SpvDim, x: i32, y: i32, z: i32) RuntimeError!void { fn writeImage(rt: *Runtime, texel: *const Value, driver_image: *anyopaque, dim: spv.SpvDim, x: i32, y: i32, z: i32) RuntimeError!void {
switch (texel.*) { switch (texel.*) {
.Float, .Float,
@@ -1980,6 +2097,32 @@ fn ImageEngine(comptime Op: ImageOp) type {
); );
}, },
.Gather => {
const sampled_image_operand = try resolveSampledImage(image, rt);
const coords = .{
.x = try readSampleCoordLane(coordinate, 0),
.y = readSampleCoordLane(coordinate, 1) catch 0,
.z = readSampleCoordLane(coordinate, 2) catch 0,
};
const component_value = try rt.results[try rt.it.next()].getValue();
const component: usize = @intCast(try readIntLane(component_value, 0));
const image_operands = if (word_count > 5) try rt.it.next() else 0;
const parsed_operands = try parseImageOperands(rt, image_operands);
try sampleImageGather(
rt,
dst,
sampled_image_operand.driver_image,
sampled_image_operand.driver_sampler,
sampled_image_operand.dim,
coords.x,
coords.y,
coords.z,
component,
parsed_operands.offset,
);
},
else => return RuntimeError.InvalidSpirV, else => return RuntimeError.InvalidSpirV,
} }
} }