fixing spirv interpreter image API functions
Build / build (push) Failing after 0s
Test / build_and_test (push) Failing after 0s

This commit is contained in:
2026-06-21 14:26:32 +02:00
parent d1a6728864
commit 47c63705e2
8 changed files with 158 additions and 49 deletions
+2 -2
View File
@@ -32,8 +32,8 @@
// Soft dependencies // Soft dependencies
.SPIRV_Interpreter = .{ .SPIRV_Interpreter = .{
.url = "git+https://github.com/Kbz-8/SPIRV-Interpreter#25ef7751c6c0610179e65c7a0d1b8eff2165acbe", .url = "git+https://github.com/Kbz-8/SPIRV-Interpreter#ae0914647bbc48683dd10108b5cae9acc54fe759",
.hash = "SPIRV_Interpreter-0.0.1-ajmpn6HuBwDtrbf_UHTbmbSCTgPBuNimCMt-l5NlU6tR", .hash = "SPIRV_Interpreter-0.0.1-ajmpn9_vBwCUxGCNVFXUgZXaDv0OyNz8sqhzuyFD7hTU",
.lazy = true, .lazy = true,
}, },
//.SPIRV_Interpreter = .{ //.SPIRV_Interpreter = .{
+44 -14
View File
@@ -251,12 +251,7 @@ fn createShader(
}; };
} }
fn initRuntime( fn initRuntime(allocator: std.mem.Allocator, module: *SoftShaderModule, stage: *const vk.PipelineShaderStageCreateInfo, image_api: spv.Runtime.ImageAPI) VkError!spv.Runtime {
allocator: std.mem.Allocator,
module: *SoftShaderModule,
stage: *const vk.PipelineShaderStageCreateInfo,
image_api: spv.Runtime.ImageAPI,
) VkError!spv.Runtime {
var runtime = spv.Runtime.init(allocator, &module.module, image_api) catch |err| { var runtime = spv.Runtime.init(allocator, &module.module, image_api) catch |err| {
std.log.scoped(.SpvRuntimeInit).err("SPIR-V Runtime failed to initialize, {s}", .{@errorName(err)}); std.log.scoped(.SpvRuntimeInit).err("SPIR-V Runtime failed to initialize, {s}", .{@errorName(err)});
return VkError.Unknown; return VkError.Unknown;
@@ -311,6 +306,33 @@ fn imageReadAspect(image_view: *SoftImageView, comptime int_read: bool) vk.Image
return aspect; return aspect;
} }
fn sampledTexelOffset(image: *SoftImage, offset: vk.Offset3D, subresource: vk.ImageSubresource, sample_index: u32) VkError!usize {
const sample_count = image.interface.samples.toInt();
if (sample_index >= sample_count)
return VkError.ValidationFailed;
return try image.getTexelMemoryOffset(offset, subresource) +
@as(usize, sample_index) * image.getMipLevelSize(subresource.aspect_mask, subresource.mip_level);
}
fn readImageFloat4Sample(image: *SoftImage, offset: vk.Offset3D, subresource: vk.ImageSubresource, format: vk.Format, sample_index: u32) VkError!zm.F32x4 {
if (image.interface.samples.toInt() == 1)
return image.readFloat4(offset, subresource, format);
const texel_size = base.format.texelSize(format);
const texel_offset = try sampledTexelOffset(image, offset, subresource, sample_index);
return blitter.readFloat4(try image.mapAsSliceWithAddedOffset(u8, texel_offset, texel_size), format);
}
fn readImageInt4Sample(image: *SoftImage, offset: vk.Offset3D, subresource: vk.ImageSubresource, format: vk.Format, sample_index: u32) VkError!@Vector(4, u32) {
if (image.interface.samples.toInt() == 1)
return image.readInt4(offset, subresource, format);
const texel_size = base.format.texelSize(format);
const texel_offset = try sampledTexelOffset(image, offset, subresource, sample_index);
return blitter.readInt4(try image.mapAsSliceWithAddedOffset(u8, texel_offset, texel_size), format);
}
fn subpassDataCoord(x: i32, y: i32, z: i32) SpvRuntimeError!vk.Offset3D { fn subpassDataCoord(x: i32, y: i32, z: i32) SpvRuntimeError!vk.Offset3D {
const coord = current_fragment_coord orelse return SpvRuntimeError.Unknown; const coord = current_fragment_coord orelse return SpvRuntimeError.Unknown;
return .{ .x = coord.x + x, .y = coord.y + y, .z = coord.z + z }; return .{ .x = coord.x + x, .y = coord.y + y, .z = coord.z + z };
@@ -340,14 +362,18 @@ fn readImageFloat4(context: *anyopaque, dim: spv.SpvDim, x: i32, y: i32, z: i32,
else => 0, else => 0,
}; };
const aspect_mask = imageReadAspect(image_view, false); const aspect_mask = imageReadAspect(image_view, false);
pixel = SoftSampler.swizzleFloat4(image.readFloat4( const subresource = vk.ImageSubresource{
image_coord,
.{
.aspect_mask = aspect_mask, .aspect_mask = aspect_mask,
.mip_level = mip_level, .mip_level = mip_level,
.array_layer = array_layer, .array_layer = array_layer,
}, };
const sample_index: u32 = if (image.interface.samples.toInt() > 1) @intCast(z) else 0;
pixel = SoftSampler.swizzleFloat4(readImageFloat4Sample(
image,
image_coord,
subresource,
base.format.fromAspect(image_view.interface.format, aspect_mask), base.format.fromAspect(image_view.interface.format, aspect_mask),
sample_index,
) catch return SpvRuntimeError.Unknown, image_view.interface.components); ) catch return SpvRuntimeError.Unknown, image_view.interface.components);
} }
return .{ return .{
@@ -382,14 +408,18 @@ fn readImageInt4(context: *anyopaque, dim: spv.SpvDim, x: i32, y: i32, z: i32, l
else => 0, else => 0,
}; };
const aspect_mask = imageReadAspect(image_view, true); const aspect_mask = imageReadAspect(image_view, true);
pixel = SoftSampler.swizzleInt4(image.readInt4( const subresource = vk.ImageSubresource{
image_coord,
.{
.aspect_mask = aspect_mask, .aspect_mask = aspect_mask,
.mip_level = mip_level, .mip_level = mip_level,
.array_layer = array_layer, .array_layer = array_layer,
}, };
const sample_index: u32 = if (image.interface.samples.toInt() > 1) @intCast(z) else 0;
pixel = SoftSampler.swizzleInt4(readImageInt4Sample(
image,
image_coord,
subresource,
base.format.fromAspect(image_view.interface.format, aspect_mask), base.format.fromAspect(image_view.interface.format, aspect_mask),
sample_index,
) catch return SpvRuntimeError.Unknown, image_view.interface.components); ) catch return SpvRuntimeError.Unknown, image_view.interface.components);
} }
return .{ return .{
+10 -4
View File
@@ -383,10 +383,13 @@ fn readSampledFloat4(
std.math.clamp(@as(i32, @intFromFloat(texel.u * width_f)), 0, @as(i32, @intCast(extent.width)) - 1) std.math.clamp(@as(i32, @intFromFloat(texel.u * width_f)), 0, @as(i32, @intCast(extent.width)) - 1)
else else
sampleAddressOrBorder(ix, extent.width, sampler.interface.address_mode_u) orelse return samplerBorderColor(sampler, format); sampleAddressOrBorder(ix, extent.width, sampler.interface.address_mode_u) orelse return samplerBorderColor(sampler, format);
const sy = if (dim == .Cube) const sy = switch (image_view.interface.view_type) {
.@"1d", .@"1d_array" => 0,
else => if (dim == .Cube)
std.math.clamp(@as(i32, @intFromFloat(texel.v * height_f)), 0, @as(i32, @intCast(extent.height)) - 1) std.math.clamp(@as(i32, @intFromFloat(texel.v * height_f)), 0, @as(i32, @intCast(extent.height)) - 1)
else else
sampleAddressOrBorder(iy, extent.height, sampler.interface.address_mode_v) orelse return samplerBorderColor(sampler, format); sampleAddressOrBorder(iy, extent.height, sampler.interface.address_mode_v) orelse return samplerBorderColor(sampler, format),
};
const color = try image.readFloat4( const color = try image.readFloat4(
.{ .{
@@ -458,10 +461,13 @@ fn readSampledInt4(
std.math.clamp(@as(i32, @intFromFloat(texel.u * width_f)), 0, @as(i32, @intCast(extent.width)) - 1) std.math.clamp(@as(i32, @intFromFloat(texel.u * width_f)), 0, @as(i32, @intCast(extent.width)) - 1)
else else
sampleAddressOrBorder(ix, extent.width, sampler.interface.address_mode_u) orelse return samplerBorderColorInt(sampler, format); sampleAddressOrBorder(ix, extent.width, sampler.interface.address_mode_u) orelse return samplerBorderColorInt(sampler, format);
const sy = if (dim == .Cube) const sy = switch (image_view.interface.view_type) {
.@"1d", .@"1d_array" => 0,
else => if (dim == .Cube)
std.math.clamp(@as(i32, @intFromFloat(texel.v * height_f)), 0, @as(i32, @intCast(extent.height)) - 1) std.math.clamp(@as(i32, @intFromFloat(texel.v * height_f)), 0, @as(i32, @intCast(extent.height)) - 1)
else else
sampleAddressOrBorder(iy, extent.height, sampler.interface.address_mode_v) orelse return samplerBorderColorInt(sampler, format); sampleAddressOrBorder(iy, extent.height, sampler.interface.address_mode_v) orelse return samplerBorderColorInt(sampler, format),
};
return image.readInt4( return image.readInt4(
.{ .{
@@ -286,6 +286,9 @@ fn setupSubgroupBuiltins(self: *Self, rt: *spv.Runtime, group_id: @Vector(3, u32
local_invocation[0] = idx; local_invocation[0] = idx;
const global_invocation_index = local_base + local_invocation; const global_invocation_index = local_base + local_invocation;
const local_invocation_index_u32: u32 = @intCast(local_invocation_index);
rt.writeBuiltIn(std.mem.asBytes(&local_invocation), .LocalInvocationId) catch {};
rt.writeBuiltIn(std.mem.asBytes(&local_invocation_index_u32), .LocalInvocationIndex) catch {};
rt.writeBuiltIn(std.mem.asBytes(&global_invocation_index), .GlobalInvocationId) catch {}; rt.writeBuiltIn(std.mem.asBytes(&global_invocation_index), .GlobalInvocationId) catch {};
} }
+13 -1
View File
@@ -394,7 +394,19 @@ fn clipTransformAndRasterizePoint(
}; };
} }
try common.writeToTargets(fragment_result.outputs, draw_call, color_attachment_access, depth_attachment_access, stencil_attachment_access, true, @intCast(px), @intCast(py), fragment_result.depth orelse transformed.position[2], fragment_result.sample_mask); try common.writeToTargets(
fragment_result.outputs,
draw_call,
color_attachment_access,
depth_attachment_access,
stencil_attachment_access,
true,
@intCast(px),
@intCast(py),
fragment_result.depth orelse transformed.position[2],
null,
fragment_result.sample_mask,
);
} }
} }
} }
+2 -1
View File
@@ -76,7 +76,7 @@ pub fn drawLine(
if (runtimes_count == 0) if (runtimes_count == 0)
return; return;
const step_count: usize = if (d_x == 0) 1 else @intCast(d_x); const step_count: usize = @intCast(@max(d_x, 0) + 1);
const runs_count = @min(runtimes_count, step_count); const runs_count = @min(runtimes_count, step_count);
const steps_per_run = @divTrunc(step_count + runs_count - 1, runs_count); const steps_per_run = @divTrunc(step_count + runs_count - 1, runs_count);
@@ -193,6 +193,7 @@ inline fn run(data: RunData) !void {
@intCast(pixel_x), @intCast(pixel_x),
@intCast(pixel_y), @intCast(pixel_y),
fragment_result.depth orelse z, fragment_result.depth orelse z,
null,
fragment_result.sample_mask, fragment_result.sample_mask,
); );
} }
+18 -10
View File
@@ -223,6 +223,11 @@ pub fn interpolateVertexOutputs(
} }
const len = @min(out0.size, out1.size, out2.size); const len = @min(out0.size, out1.size, out2.size);
if (std.mem.eql(u8, out0.blob[0..len], out1.blob[0..len]) and std.mem.eql(u8, out0.blob[0..len], out2.blob[0..len])) {
inputs[location][component] = .{ .blob = out0.blob, .size = len, .free_responsability = false };
continue;
}
const input = allocator.alloc(u8, len + @sizeOf(F32x4)) catch return VkError.OutOfDeviceMemory; const input = allocator.alloc(u8, len + @sizeOf(F32x4)) catch return VkError.OutOfDeviceMemory;
@memset(input, 0); @memset(input, 0);
@@ -407,13 +412,14 @@ pub fn writeToTargets(
x: usize, x: usize,
y: usize, y: usize,
z: f32, z: f32,
coverage_sample_mask: ?vk.SampleMask,
fragment_sample_mask: ?vk.SampleMask, fragment_sample_mask: ?vk.SampleMask,
) VkError!void { ) VkError!void {
const io = draw_call.renderer.device.interface.io(); const io = draw_call.renderer.device.interface.io();
const pipeline_data = draw_call.renderer.state.pipeline.?.interface.mode.graphics; const pipeline_data = draw_call.renderer.state.pipeline.?.interface.mode.graphics;
const depth_stencil_state = pipeline_data.depth_stencil; const depth_stencil_state = pipeline_data.depth_stencil;
if (!sampleMaskEnablesAnySample(pipeline_data.multisample, fragment_sample_mask)) if (!sampleMaskEnablesAnySample(pipeline_data.multisample, coverage_sample_mask, fragment_sample_mask))
return; return;
if (x >= draw_call.framebuffer.interface.width or y >= draw_call.framebuffer.interface.height) if (x >= draw_call.framebuffer.interface.width or y >= draw_call.framebuffer.interface.height)
@@ -426,7 +432,7 @@ pub fn writeToTargets(
const sample_count = pipeline_data.multisample.rasterization_samples.toInt(); const sample_count = pipeline_data.multisample.rasterization_samples.toInt();
for (0..sample_count) |sample_index| { for (0..sample_count) |sample_index| {
if (!sampleMaskEnablesSample(pipeline_data.multisample, fragment_sample_mask, sample_index)) if (!sampleMaskEnablesSample(pipeline_data.multisample, coverage_sample_mask, fragment_sample_mask, sample_index))
continue; continue;
var stencil_state: ?vk.StencilOpState = null; var stencil_state: ?vk.StencilOpState = null;
@@ -505,23 +511,23 @@ pub fn writeToTargets(
} }
} }
fn sampleMaskEnablesAnySample(multisample: anytype, fragment_sample_mask: ?vk.SampleMask) bool { fn sampleMaskEnablesAnySample(multisample: anytype, coverage_sample_mask: ?vk.SampleMask, fragment_sample_mask: ?vk.SampleMask) bool {
const sample_count = multisample.rasterization_samples.toInt(); const sample_count = multisample.rasterization_samples.toInt();
if (multisample.sample_mask == null and fragment_sample_mask == null) if (multisample.sample_mask == null and coverage_sample_mask == null and fragment_sample_mask == null)
return true; return true;
if (multisample.sample_mask) |pipeline_sample_mask| { if (multisample.sample_mask) |pipeline_sample_mask| {
for (pipeline_sample_mask, 0..) |word, word_index| { for (pipeline_sample_mask, 0..) |word, word_index| {
if (sampleMaskWordEnablesAnySample(sample_count, word_index, word, fragment_sample_mask)) if (sampleMaskWordEnablesAnySample(sample_count, word_index, word, coverage_sample_mask, fragment_sample_mask))
return true; return true;
} }
return false; return false;
} }
return sampleMaskWordEnablesAnySample(sample_count, 0, std.math.maxInt(vk.SampleMask), fragment_sample_mask); return sampleMaskWordEnablesAnySample(sample_count, 0, std.math.maxInt(vk.SampleMask), coverage_sample_mask, fragment_sample_mask);
} }
fn sampleMaskWordEnablesAnySample(sample_count: usize, word_index: usize, pipeline_word: vk.SampleMask, fragment_sample_mask: ?vk.SampleMask) bool { fn sampleMaskWordEnablesAnySample(sample_count: usize, word_index: usize, pipeline_word: vk.SampleMask, coverage_sample_mask: ?vk.SampleMask, fragment_sample_mask: ?vk.SampleMask) bool {
const remaining_samples = sample_count -| (word_index * 32); const remaining_samples = sample_count -| (word_index * 32);
const active_bits = @min(remaining_samples, 32); const active_bits = @min(remaining_samples, 32);
if (active_bits == 0) if (active_bits == 0)
@@ -532,11 +538,12 @@ fn sampleMaskWordEnablesAnySample(sample_count: usize, word_index: usize, pipeli
else else
(@as(vk.SampleMask, 1) << @intCast(active_bits)) - 1; (@as(vk.SampleMask, 1) << @intCast(active_bits)) - 1;
const coverage_word = if (word_index == 0) coverage_sample_mask orelse std.math.maxInt(vk.SampleMask) else std.math.maxInt(vk.SampleMask);
const fragment_word = if (word_index == 0) fragment_sample_mask orelse std.math.maxInt(vk.SampleMask) else std.math.maxInt(vk.SampleMask); const fragment_word = if (word_index == 0) fragment_sample_mask orelse std.math.maxInt(vk.SampleMask) else std.math.maxInt(vk.SampleMask);
return ((pipeline_word & fragment_word) & active_mask) != 0; return ((pipeline_word & coverage_word & fragment_word) & active_mask) != 0;
} }
fn sampleMaskEnablesSample(multisample: anytype, fragment_sample_mask: ?vk.SampleMask, sample_index: usize) bool { fn sampleMaskEnablesSample(multisample: anytype, coverage_sample_mask: ?vk.SampleMask, fragment_sample_mask: ?vk.SampleMask, sample_index: usize) bool {
if (sample_index >= multisample.rasterization_samples.toInt()) if (sample_index >= multisample.rasterization_samples.toInt())
return false; return false;
@@ -549,6 +556,7 @@ fn sampleMaskEnablesSample(multisample: anytype, fragment_sample_mask: ?vk.Sampl
else else
std.math.maxInt(vk.SampleMask); std.math.maxInt(vk.SampleMask);
const coverage_word = if (word_index == 0) coverage_sample_mask orelse std.math.maxInt(vk.SampleMask) else std.math.maxInt(vk.SampleMask);
const fragment_word = if (word_index == 0) fragment_sample_mask orelse std.math.maxInt(vk.SampleMask) else std.math.maxInt(vk.SampleMask); const fragment_word = if (word_index == 0) fragment_sample_mask orelse std.math.maxInt(vk.SampleMask) else std.math.maxInt(vk.SampleMask);
return (pipeline_word & fragment_word & bit) != 0; return (pipeline_word & coverage_word & fragment_word & bit) != 0;
} }
@@ -129,7 +129,7 @@ inline fn edgeFunction(a: F32x4, b: F32x4, p: F32x4) f32 {
inline fn isInclusiveEdge(a: F32x4, b: F32x4) bool { inline fn isInclusiveEdge(a: F32x4, b: F32x4) bool {
const dx = b[0] - a[0]; const dx = b[0] - a[0];
const dy = b[1] - a[1]; const dy = b[1] - a[1];
return dy < 0.0 or (dy == 0.0 and dx > 0.0); return dy > 0.0 or (dy == 0.0 and dx < 0.0);
} }
inline fn edgeContainsPixel(a: F32x4, b: F32x4, edge_value: f32, area: f32) bool { inline fn edgeContainsPixel(a: F32x4, b: F32x4, edge_value: f32, area: f32) bool {
@@ -139,6 +139,56 @@ inline fn edgeContainsPixel(a: F32x4, b: F32x4, edge_value: f32, area: f32) bool
edge_value < 0.0 or (edge_value == 0.0 and isInclusiveEdge(b, a)); edge_value < 0.0 or (edge_value == 0.0 and isInclusiveEdge(b, a));
} }
inline fn standardSamplePosition(sample_count: usize, sample_index: usize) struct { x: f32, y: f32 } {
return switch (sample_count) {
1 => .{ .x = 0.5, .y = 0.5 },
2 => switch (sample_index) {
0 => .{ .x = 0.75, .y = 0.75 },
1 => .{ .x = 0.25, .y = 0.25 },
else => .{ .x = 0.5, .y = 0.5 },
},
4 => switch (sample_index) {
0 => .{ .x = 0.375, .y = 0.125 },
1 => .{ .x = 0.875, .y = 0.375 },
2 => .{ .x = 0.125, .y = 0.625 },
3 => .{ .x = 0.625, .y = 0.875 },
else => .{ .x = 0.5, .y = 0.5 },
},
else => .{ .x = 0.5, .y = 0.5 },
};
}
fn triangleCoverageMask(data: RunData, x: i32, y: i32, sample_count: usize) vk.SampleMask {
var mask: vk.SampleMask = 0;
for (0..sample_count) |sample_index| {
if (sample_index >= @bitSizeOf(vk.SampleMask))
break;
const sample_pos = standardSamplePosition(sample_count, sample_index);
const p = zm.f32x4(
@as(f32, @floatFromInt(x)) + sample_pos.x,
@as(f32, @floatFromInt(y)) + sample_pos.y,
0.0,
1.0,
);
const w0 = edgeFunction(data.v1.position, data.v2.position, p);
const w1 = edgeFunction(data.v2.position, data.v0.position, p);
const w2 = edgeFunction(data.v0.position, data.v1.position, p);
const inside =
edgeContainsPixel(data.v1.position, data.v2.position, w0, data.area) and
edgeContainsPixel(data.v2.position, data.v0.position, w1, data.area) and
edgeContainsPixel(data.v0.position, data.v1.position, w2, data.area);
if (inside) {
const bit_index: u5 = @intCast(sample_index);
mask |= @as(vk.SampleMask, 1) << bit_index;
}
}
return mask;
}
fn runWrapper(data: RunData) void { fn runWrapper(data: RunData) void {
@call(.always_inline, run, .{data}) catch |err| { @call(.always_inline, run, .{data}) catch |err| {
std.log.scoped(.@"Rasterization stage").err("triangle fill mode catched a '{s}'", .{@errorName(err)}); std.log.scoped(.@"Rasterization stage").err("triangle fill mode catched a '{s}'", .{@errorName(err)});
@@ -159,20 +209,18 @@ inline fn run(data: RunData) !void {
continue; continue;
} }
const pipeline_data = data.draw_call.renderer.state.pipeline.?.interface.mode.graphics;
const sample_count = pipeline_data.multisample.rasterization_samples.toInt();
const coverage_sample_mask = triangleCoverageMask(data, x, y, sample_count);
if (coverage_sample_mask == 0)
continue;
const p = zm.f32x4(@as(f32, @floatFromInt(x)) + 0.5, @as(f32, @floatFromInt(y)) + 0.5, 0.0, 1.0); const p = zm.f32x4(@as(f32, @floatFromInt(x)) + 0.5, @as(f32, @floatFromInt(y)) + 0.5, 0.0, 1.0);
const w0 = edgeFunction(data.v1.position, data.v2.position, p); const w0 = edgeFunction(data.v1.position, data.v2.position, p);
const w1 = edgeFunction(data.v2.position, data.v0.position, p); const w1 = edgeFunction(data.v2.position, data.v0.position, p);
const w2 = edgeFunction(data.v0.position, data.v1.position, p); const w2 = edgeFunction(data.v0.position, data.v1.position, p);
const inside =
edgeContainsPixel(data.v1.position, data.v2.position, w0, data.area) and
edgeContainsPixel(data.v2.position, data.v0.position, w1, data.area) and
edgeContainsPixel(data.v0.position, data.v1.position, w2, data.area);
if (!inside)
continue;
const b0 = w0 / data.area; const b0 = w0 / data.area;
const b1 = w1 / data.area; const b1 = w1 / data.area;
const b2 = w2 / data.area; const b2 = w2 / data.area;
@@ -252,6 +300,7 @@ inline fn run(data: RunData) !void {
@intCast(x), @intCast(x),
@intCast(y), @intCast(y),
fragment_result.depth orelse z, fragment_result.depth orelse z,
coverage_sample_mask,
fragment_result.sample_mask, fragment_result.sample_mask,
); );
} }