From 912b510c670d307accbe8ea0fe31fe99373a8b6e Mon Sep 17 00:00:00 2001 From: Kbz-8 Date: Fri, 3 Jul 2026 00:52:48 +0200 Subject: [PATCH] added per-output centroid flag to vertex outputs --- src/software/device/Renderer.zig | 1 + src/software/device/clip.zig | 1 + src/software/device/rasterizer.zig | 2 +- src/software/device/rasterizer/common.zig | 13 +++++-- .../device/rasterizer/edge_function.zig | 36 +++++++++++++++---- src/software/device/vertex_dispatcher.zig | 13 ++++--- 6 files changed, 52 insertions(+), 14 deletions(-) diff --git a/src/software/device/Renderer.zig b/src/software/device/Renderer.zig index 78e529c..953f838 100644 --- a/src/software/device/Renderer.zig +++ b/src/software/device/Renderer.zig @@ -73,6 +73,7 @@ pub const Vertex = struct { point_size: f32, outputs: [spv.SPIRV_MAX_OUTPUT_LOCATIONS][4]?struct { interpolation_type: InterpolationType, + centroid: bool, blob: []u8, size: usize, }, diff --git a/src/software/device/clip.zig b/src/software/device/clip.zig index c208937..a55312c 100644 --- a/src/software/device/clip.zig +++ b/src/software/device/clip.zig @@ -187,6 +187,7 @@ fn interpolateVertexForClipping(allocator: std.mem.Allocator, a: *const Vertex, result.outputs[location][component] = .{ .interpolation_type = out_a.interpolation_type, + .centroid = out_a.centroid, .blob = if (out_a.interpolation_type == .flat) allocator.dupe(u8, out_a.blob) catch return VkError.OutOfDeviceMemory else diff --git a/src/software/device/rasterizer.zig b/src/software/device/rasterizer.zig index 16497ec..03da27e 100644 --- a/src/software/device/rasterizer.zig +++ b/src/software/device/rasterizer.zig @@ -477,7 +477,7 @@ fn rasterizeTransformedPoint( point_coord, null, true, - try common.interpolateVertexOutputs(allocator, vertex, vertex, vertex, vertex, 1.0, 0.0, 0.0), + try common.interpolateVertexOutputs(allocator, vertex, vertex, vertex, vertex, 1.0, 0.0, 0.0, 1.0, 0.0, 0.0), null, ) catch |err| { if (err == SpvRuntimeError.Killed) diff --git a/src/software/device/rasterizer/common.zig b/src/software/device/rasterizer/common.zig index 3b6d975..505c120 100644 --- a/src/software/device/rasterizer/common.zig +++ b/src/software/device/rasterizer/common.zig @@ -262,6 +262,9 @@ pub fn interpolateVertexOutputs( b0: f32, b1: f32, b2: f32, + centroid_b0: f32, + centroid_b1: f32, + centroid_b2: f32, ) VkError![spv.SPIRV_MAX_OUTPUT_LOCATIONS]VertexInterpolationLocation { var inputs = [_]VertexInterpolationLocation{[_]VertexInterpolation{.{ .blob = &.{}, @@ -290,19 +293,23 @@ pub fn interpolateVertexOutputs( const input = allocator.alloc(u8, len + @sizeOf(F32x4)) catch return VkError.OutOfDeviceMemory; @memset(input, 0); + const input_b0 = if (out0.centroid) centroid_b0 else b0; + const input_b1 = if (out0.centroid) centroid_b1 else b1; + const input_b2 = if (out0.centroid) centroid_b2 else b2; + var byte_index: usize = 0; while (byte_index + @sizeOf(F32x4) <= len) : (byte_index += @sizeOf(F32x4)) { const value0 = std.mem.bytesToValue(F32x4, out0.blob[byte_index..]); const value1 = std.mem.bytesToValue(F32x4, out1.blob[byte_index..]); const value2 = std.mem.bytesToValue(F32x4, out2.blob[byte_index..]); - base.utils.writePacked(F32x4, input[byte_index..], interpolateF32x4(out0.interpolation_type, value0, value1, value2, v0, v1, v2, b0, b1, b2)); + base.utils.writePacked(F32x4, input[byte_index..], interpolateF32x4(out0.interpolation_type, value0, value1, value2, v0, v1, v2, input_b0, input_b1, input_b2)); } while (byte_index + @sizeOf(f32) <= len) : (byte_index += @sizeOf(f32)) { const value0 = std.mem.bytesToValue(f32, out0.blob[byte_index..]); const value1 = std.mem.bytesToValue(f32, out1.blob[byte_index..]); const value2 = std.mem.bytesToValue(f32, out2.blob[byte_index..]); - base.utils.writePacked(f32, input[byte_index..], interpolateF32(out0.interpolation_type, value0, value1, value2, v0, v1, v2, b0, b1, b2)); + base.utils.writePacked(f32, input[byte_index..], interpolateF32(out0.interpolation_type, value0, value1, value2, v0, v1, v2, input_b0, input_b1, input_b2)); } if (byte_index < len) @@ -322,7 +329,7 @@ pub fn interpolateLineOutputs( provoking_vertex: *const Renderer.Vertex, t: f32, ) VkError![spv.SPIRV_MAX_OUTPUT_LOCATIONS]VertexInterpolationLocation { - return interpolateVertexOutputs(allocator, v0, v1, v0, provoking_vertex, 1.0 - t, t, 0.0); + return interpolateVertexOutputs(allocator, v0, v1, v0, provoking_vertex, 1.0 - t, t, 0.0, 1.0 - t, t, 0.0); } pub fn interpolateVertexOutputDerivatives( diff --git a/src/software/device/rasterizer/edge_function.zig b/src/software/device/rasterizer/edge_function.zig index 21597de..554f8a8 100644 --- a/src/software/device/rasterizer/edge_function.zig +++ b/src/software/device/rasterizer/edge_function.zig @@ -381,7 +381,7 @@ inline fn run(data: RunData) !void { if (early_depth.mask == 0) continue; - const interpolation_barycentrics = if (data.sample_count > 1 and data.fragment_uses_centroid) blk: { + const centroid_barycentrics = if (data.sample_count > 1 and data.fragment_uses_centroid) blk: { const sample_pos = firstCoveredSamplePosition(data.sample_count, early_depth.mask); const centroid_p = zm.f32x4( @as(f32, @floatFromInt(x)) + sample_pos.x, @@ -398,9 +398,9 @@ inline fn run(data: RunData) !void { centroid_w2 / data.area, }; } else .{ b0, b1, b2 }; - const input_b0 = interpolation_barycentrics[0]; - const input_b1 = interpolation_barycentrics[1]; - const input_b2 = interpolation_barycentrics[2]; + const centroid_b0 = centroid_barycentrics[0]; + const centroid_b1 = centroid_barycentrics[1]; + const centroid_b2 = centroid_barycentrics[2]; var fragment_result: fragment.InvocationResult = .{ .outputs = std.mem.zeroes([spv.SPIRV_MAX_OUTPUT_LOCATIONS][@sizeOf(F32x4)]u8), @@ -417,7 +417,19 @@ inline fn run(data: RunData) !void { if ((early_depth.mask & sample_coverage_mask) == 0) continue; - const inputs = try common.interpolateVertexOutputs(data.allocator, &data.v0, &data.v1, &data.v2, &data.provoking_vertex, input_b0, input_b1, input_b2); + const inputs = try common.interpolateVertexOutputs( + data.allocator, + &data.v0, + &data.v1, + &data.v2, + &data.provoking_vertex, + b0, + b1, + b2, + centroid_b0, + centroid_b1, + centroid_b2, + ); const sample_result = fragment.shaderInvocation( data.allocator, data.draw_call, @@ -459,7 +471,19 @@ inline fn run(data: RunData) !void { continue; } if (data.has_fragment_shader) { - const inputs = try common.interpolateVertexOutputs(data.allocator, &data.v0, &data.v1, &data.v2, &data.provoking_vertex, input_b0, input_b1, input_b2); + const inputs = try common.interpolateVertexOutputs( + data.allocator, + &data.v0, + &data.v1, + &data.v2, + &data.provoking_vertex, + b0, + b1, + b2, + centroid_b0, + centroid_b1, + centroid_b2, + ); const derivative_inputs: ?fragment.DerivativeInputs = if (data.fragment_uses_derivatives) blk: { var derivatives: fragment.DerivativeInputs = undefined; diff --git a/src/software/device/vertex_dispatcher.zig b/src/software/device/vertex_dispatcher.zig index fb5d29a..ec63c47 100644 --- a/src/software/device/vertex_dispatcher.zig +++ b/src/software/device/vertex_dispatcher.zig @@ -202,9 +202,10 @@ fn readVertexOutput(data: RunData, output: *Renderer.Vertex, rt: *spv.Runtime, l const value = try rt.results[result_word].getConstValue(); const memory_size = try rt.getResultMemorySize(result_word); const interpolation_type = vertexOutputInterpolationType(data, rt, location, component, result_word); + const centroid = fragmentInputHasDecoration(data, location, component, .Centroid); switch (value.*) { .Array, .Matrix => { - try readVertexOutputAggregate(data, output, value, location, component, interpolation_type); + try readVertexOutputAggregate(data, output, value, location, component, interpolation_type, centroid); return; }, else => {}, @@ -212,6 +213,7 @@ fn readVertexOutput(data: RunData, output: *Renderer.Vertex, rt: *spv.Runtime, l output.outputs[location][component] = .{ .interpolation_type = interpolation_type, + .centroid = centroid, .blob = data.allocator.alloc(u8, memory_size + INTERFACE_BLOB_PADDING) catch return VkError.OutOfDeviceMemory, .size = memory_size, }; @@ -226,22 +228,23 @@ fn readVertexOutputAggregate( location: usize, component: usize, interpolation_type: Renderer.InterpolationType, + centroid: bool, ) !void { var target_location = location; switch (value.*) { .Array => |array| { for (array.values) |*element| { - try readVertexOutputAggregate(data, output, element, target_location, component, interpolation_type); + try readVertexOutputAggregate(data, output, element, target_location, component, interpolation_type, centroid); target_location += vertexOutputValueLocationCount(element); } }, .Matrix => |columns| { for (columns) |*column| { - try writeVertexOutputValue(data, output, column, target_location, component, interpolation_type); + try writeVertexOutputValue(data, output, column, target_location, component, interpolation_type, centroid); target_location += 1; } }, - else => try writeVertexOutputValue(data, output, value, location, component, interpolation_type), + else => try writeVertexOutputValue(data, output, value, location, component, interpolation_type, centroid), } } @@ -252,6 +255,7 @@ fn writeVertexOutputValue( location: usize, component: usize, interpolation_type: Renderer.InterpolationType, + centroid: bool, ) !void { if (location >= spv.SPIRV_MAX_OUTPUT_LOCATIONS or component >= 4) return VkError.ValidationFailed; @@ -259,6 +263,7 @@ fn writeVertexOutputValue( const memory_size = try value.getPlainMemorySize(); output.outputs[location][component] = .{ .interpolation_type = interpolation_type, + .centroid = centroid, .blob = data.allocator.alloc(u8, memory_size + INTERFACE_BLOB_PADDING) catch return VkError.OutOfDeviceMemory, .size = memory_size, };