adding point rasterization

This commit is contained in:
2026-06-19 00:42:46 +02:00
parent b8367ac2bb
commit aaf6653b3f
8 changed files with 105 additions and 71 deletions
+2 -2
View File
@@ -32,8 +32,8 @@
// Soft dependencies // Soft dependencies
.SPIRV_Interpreter = .{ .SPIRV_Interpreter = .{
.url = "git+https://git.kbz8.me/kbz_8/SPIRV-Interpreter#42554a5cc1069e8174b377e95ce2ac2d802a44ff", .url = "git+https://github.com/Kbz-8/SPIRV-Interpreter#391f4415d9a77d15455f4d7efab65fc8cc931bc5",
.hash = "SPIRV_Interpreter-0.0.1-ajmpn0GXBwD01BnmV_Kf8EeNqTDoyuq7UvHVaq-WoBP0", .hash = "SPIRV_Interpreter-0.0.1-ajmpn6mrBwDO21UuGIRs3iP-0Z9GyhivF3N8V_X4tLzX",
.lazy = true, .lazy = true,
}, },
//.SPIRV_Interpreter = .{ //.SPIRV_Interpreter = .{
+2
View File
@@ -55,6 +55,7 @@ pub const DynamicState = struct {
pub const Vertex = struct { pub const Vertex = struct {
primitive_restart: bool, primitive_restart: bool,
position: F32x4, position: F32x4,
point_size: f32,
outputs: [spv.SPIRV_MAX_OUTPUT_LOCATIONS][4]?struct { outputs: [spv.SPIRV_MAX_OUTPUT_LOCATIONS][4]?struct {
interpolation_type: enum { smooth, flat, noperspective }, interpolation_type: enum { smooth, flat, noperspective },
blob: []u8, blob: []u8,
@@ -106,6 +107,7 @@ pub const DrawCall = struct {
for (self.vertices) |*vertex| { for (self.vertices) |*vertex| {
vertex.primitive_restart = false; vertex.primitive_restart = false;
vertex.point_size = 1.0;
for (&vertex.outputs) |*location| { for (&vertex.outputs) |*location| {
@memset(location, null); @memset(location, null);
} }
+1
View File
@@ -171,6 +171,7 @@ fn interpolateVertexForClipping(allocator: std.mem.Allocator, a: *const Vertex,
var result: Vertex = .{ var result: Vertex = .{
.primitive_restart = false, .primitive_restart = false,
.position = a.position + ((b.position - a.position) * zm.f32x4s(t)), .position = a.position + ((b.position - a.position) * zm.f32x4s(t)),
.point_size = a.point_size + ((b.point_size - a.point_size) * t),
.outputs = undefined, .outputs = undefined,
}; };
+7
View File
@@ -23,6 +23,7 @@ pub fn shaderInvocation(
draw_call: *Renderer.DrawCall, draw_call: *Renderer.DrawCall,
batch_id: usize, batch_id: usize,
position: zm.F32x4, position: zm.F32x4,
point_coord: ?@Vector(2, f32),
front_face: bool, front_face: bool,
inputs: [spv.SPIRV_MAX_OUTPUT_LOCATIONS]VertexInterpolationLocation, inputs: [spv.SPIRV_MAX_OUTPUT_LOCATIONS]VertexInterpolationLocation,
derivative_inputs: ?DerivativeInputs, derivative_inputs: ?DerivativeInputs,
@@ -54,6 +55,12 @@ pub fn shaderInvocation(
SpvRuntimeError.NotFound => {}, SpvRuntimeError.NotFound => {},
else => return err, else => return err,
}; };
if (point_coord) |coord| {
rt.writeBuiltIn(std.mem.asBytes(&coord), .PointCoord) catch |err| switch (err) {
SpvRuntimeError.NotFound => {},
else => return err,
};
}
rt.writeBuiltIn(std.mem.asBytes(&front_face), .FrontFacing) catch |err| switch (err) { rt.writeBuiltIn(std.mem.asBytes(&front_face), .FrontFacing) catch |err| switch (err) {
SpvRuntimeError.NotFound => {}, SpvRuntimeError.NotFound => {},
else => return err, else => return err,
+74 -64
View File
@@ -165,25 +165,25 @@ pub fn processThenFragmentStage(renderer: *Renderer, allocator: std.mem.Allocato
var segment_start = firstNonRestart(draw_call, range.start, range.end); var segment_start = firstNonRestart(draw_call, range.start, range.end);
while (segment_start < range.end) { while (segment_start < range.end) {
const segment_end = nextRestart(draw_call, segment_start, range.end); const segment_end = nextRestart(draw_call, segment_start, range.end);
if (segment_end - segment_start >= 3) { if (segment_end - segment_start >= 3) {
const v0 = &draw_call.vertices[segment_start]; const v0 = &draw_call.vertices[segment_start];
for ((segment_start + 1)..(segment_end - 1)) |vertex_index| { for ((segment_start + 1)..(segment_end - 1)) |vertex_index| {
const v1 = &draw_call.vertices[vertex_index]; const v1 = &draw_call.vertices[vertex_index];
const v2 = &draw_call.vertices[vertex_index + 1]; const v2 = &draw_call.vertices[vertex_index + 1];
try clipTransformAndRasterizeTriangle( try clipTransformAndRasterizeTriangle(
renderer, renderer,
allocator, allocator,
draw_call, draw_call,
v0, v0,
v1, v1,
v2, v2,
color_attachment_access, color_attachment_access,
if (depth_attachment_access) |*access| access else null, if (depth_attachment_access) |*access| access else null,
if (stencil_attachment_access) |*access| access else null, if (stencil_attachment_access) |*access| access else null,
); );
}
} }
}
segment_start = firstNonRestart(draw_call, segment_end + 1, range.end); segment_start = firstNonRestart(draw_call, segment_end + 1, range.end);
} }
} }
@@ -194,40 +194,40 @@ pub fn processThenFragmentStage(renderer: *Renderer, allocator: std.mem.Allocato
var segment_start = firstNonRestart(draw_call, range.start, range.end); var segment_start = firstNonRestart(draw_call, range.start, range.end);
while (segment_start < range.end) { while (segment_start < range.end) {
const segment_end = nextRestart(draw_call, segment_start, range.end); const segment_end = nextRestart(draw_call, segment_start, range.end);
if (segment_end - segment_start >= 3) { if (segment_end - segment_start >= 3) {
for (segment_start..(segment_end - 2)) |vertex_index| { for (segment_start..(segment_end - 2)) |vertex_index| {
const local_index = vertex_index - segment_start; const local_index = vertex_index - segment_start;
const v0 = &draw_call.vertices[vertex_index + 0]; const v0 = &draw_call.vertices[vertex_index + 0];
const v1 = &draw_call.vertices[vertex_index + 1]; const v1 = &draw_call.vertices[vertex_index + 1];
const v2 = &draw_call.vertices[vertex_index + 2]; const v2 = &draw_call.vertices[vertex_index + 2];
if ((local_index & 1) == 0) { if ((local_index & 1) == 0) {
try clipTransformAndRasterizeTriangle( try clipTransformAndRasterizeTriangle(
renderer, renderer,
allocator, allocator,
draw_call, draw_call,
v0, v0,
v1, v1,
v2, v2,
color_attachment_access, color_attachment_access,
if (depth_attachment_access) |*access| access else null, if (depth_attachment_access) |*access| access else null,
if (stencil_attachment_access) |*access| access else null, if (stencil_attachment_access) |*access| access else null,
); );
} else { } else {
try clipTransformAndRasterizeTriangle( try clipTransformAndRasterizeTriangle(
renderer, renderer,
allocator, allocator,
draw_call, draw_call,
v1, v1,
v0, v0,
v2, v2,
color_attachment_access, color_attachment_access,
if (depth_attachment_access) |*access| access else null, if (depth_attachment_access) |*access| access else null,
if (stencil_attachment_access) |*access| access else null, if (stencil_attachment_access) |*access| access else null,
); );
}
} }
} }
}
segment_start = firstNonRestart(draw_call, segment_end + 1, range.end); segment_start = firstNonRestart(draw_call, segment_end + 1, range.end);
} }
} }
@@ -257,22 +257,22 @@ pub fn processThenFragmentStage(renderer: *Renderer, allocator: std.mem.Allocato
var segment_start = firstNonRestart(draw_call, range.start, range.end); var segment_start = firstNonRestart(draw_call, range.start, range.end);
while (segment_start < range.end) { while (segment_start < range.end) {
const segment_end = nextRestart(draw_call, segment_start, range.end); const segment_end = nextRestart(draw_call, segment_start, range.end);
if (segment_end - segment_start >= 2) { if (segment_end - segment_start >= 2) {
for (segment_start..(segment_end - 1)) |vertex_index| { for (segment_start..(segment_end - 1)) |vertex_index| {
const v0 = &draw_call.vertices[vertex_index + 0]; const v0 = &draw_call.vertices[vertex_index + 0];
const v1 = &draw_call.vertices[vertex_index + 1]; const v1 = &draw_call.vertices[vertex_index + 1];
try clipTransformAndRasterizeLine( try clipTransformAndRasterizeLine(
allocator, allocator,
draw_call, draw_call,
v0, v0,
v1, v1,
color_attachment_access, color_attachment_access,
if (depth_attachment_access) |*access| access else null, if (depth_attachment_access) |*access| access else null,
if (stencil_attachment_access) |*access| access else null, if (stencil_attachment_access) |*access| access else null,
); );
}
} }
}
segment_start = firstNonRestart(draw_call, segment_end + 1, range.end); segment_start = firstNonRestart(draw_call, segment_end + 1, range.end);
} }
} }
@@ -323,11 +323,13 @@ fn clipTransformAndRasterizePoint(
var transformed = vertex.*; var transformed = vertex.*;
clip.viewportTransformVertex(draw_call.viewport, &transformed); clip.viewportTransformVertex(draw_call.viewport, &transformed);
const point_size = 1.0; const point_size = transformed.point_size;
const min_x: i32 = @intFromFloat(@ceil(transformed.position[0] - (point_size / 2.0) - 0.5)); const min_x: i32 = @intFromFloat(@ceil(transformed.position[0] - (point_size / 2.0) - 0.5));
const max_x: i32 = @intFromFloat(@ceil(transformed.position[0] + (point_size / 2.0) - 0.5) - 1.0); const max_x: i32 = @intFromFloat(@ceil(transformed.position[0] + (point_size / 2.0) - 0.5) - 1.0);
const min_y: i32 = @intFromFloat(@ceil(transformed.position[1] - (point_size / 2.0) - 0.5)); const min_y: i32 = @intFromFloat(@ceil(transformed.position[1] - (point_size / 2.0) - 0.5));
const max_y: i32 = @intFromFloat(@ceil(transformed.position[1] + (point_size / 2.0) - 0.5) - 1.0); const max_y: i32 = @intFromFloat(@ceil(transformed.position[1] + (point_size / 2.0) - 0.5) - 1.0);
const point_min_x = transformed.position[0] - (point_size / 2.0);
const point_min_y = transformed.position[1] - (point_size / 2.0);
const pipeline = draw_call.renderer.state.pipeline orelse return; const pipeline = draw_call.renderer.state.pipeline orelse return;
const has_fragment_shader = pipeline.stages.getPtr(.fragment) != null; const has_fragment_shader = pipeline.stages.getPtr(.fragment) != null;
@@ -340,11 +342,19 @@ fn clipTransformAndRasterizePoint(
var outputs = std.mem.zeroes([spv.SPIRV_MAX_OUTPUT_LOCATIONS][@sizeOf(zm.F32x4)]u8); var outputs = std.mem.zeroes([spv.SPIRV_MAX_OUTPUT_LOCATIONS][@sizeOf(zm.F32x4)]u8);
if (has_fragment_shader) { if (has_fragment_shader) {
const frag_x = @as(f32, @floatFromInt(px)) + 0.5;
const frag_y = @as(f32, @floatFromInt(py)) + 0.5;
const point_coord = @Vector(2, f32){
(frag_x - point_min_x) / point_size,
(frag_y - point_min_y) / point_size,
};
outputs = fragment.shaderInvocation( outputs = fragment.shaderInvocation(
allocator, allocator,
draw_call, draw_call,
0, 0,
zm.f32x4(@floatFromInt(px), @floatFromInt(py), transformed.position[2], 1.0), zm.f32x4(frag_x, frag_y, transformed.position[2], 1.0 / transformed.position[3]),
point_coord,
true, true,
try common.interpolateVertexOutputs(allocator, &transformed, &transformed, &transformed, 1.0, 0.0, 0.0), try common.interpolateVertexOutputs(allocator, &transformed, &transformed, &transformed, 1.0, 0.0, 0.0),
null, null,
+6 -4
View File
@@ -45,9 +45,9 @@ pub fn drawLine(
const io = draw_call.renderer.device.interface.io(); const io = draw_call.renderer.device.interface.io();
var x0: i32 = @intFromFloat(v0.position[0]); var x0: i32 = @intFromFloat(v0.position[0]);
var y0: i32 = @intFromFloat(v0.position[1]); var y0: i32 = @intFromFloat(@floor(v0.position[1] - 0.5));
var x1: i32 = @intFromFloat(v1.position[0]); var x1: i32 = @intFromFloat(v1.position[0]);
var y1: i32 = @intFromFloat(v1.position[1]); var y1: i32 = @intFromFloat(@floor(v1.position[1] - 0.5));
const steep = blk: { const steep = blk: {
if (@abs(y1 - y0) > @abs(x1 - x0)) { if (@abs(y1 - y0) > @abs(x1 - x0)) {
@@ -76,7 +76,7 @@ pub fn drawLine(
if (runtimes_count == 0) if (runtimes_count == 0)
return; return;
const step_count: usize = @as(usize, @intCast(d_x)) + 1; const step_count: usize = if (d_x == 0) 1 else @intCast(d_x);
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);
@@ -151,6 +151,7 @@ inline fn run(data: RunData) !void {
const t = @as(f32, @floatFromInt(step)) / @as(f32, @floatFromInt(@max(data.d_x, 1))); const t = @as(f32, @floatFromInt(step)) / @as(f32, @floatFromInt(@max(data.d_x, 1)));
const z = ((1.0 - t) * data.start_vertex.position[2]) + (t * data.end_vertex.position[2]); const z = ((1.0 - t) * data.start_vertex.position[2]) + (t * data.end_vertex.position[2]);
const frag_w = ((1.0 - t) / data.start_vertex.position[3]) + (t / data.end_vertex.position[3]);
var outputs = std.mem.zeroes([spv.SPIRV_MAX_OUTPUT_LOCATIONS][@sizeOf(F32x4)]u8); var outputs = std.mem.zeroes([spv.SPIRV_MAX_OUTPUT_LOCATIONS][@sizeOf(F32x4)]u8);
if (data.has_fragment_shader) { if (data.has_fragment_shader) {
@@ -158,7 +159,8 @@ inline fn run(data: RunData) !void {
data.allocator, data.allocator,
data.draw_call, data.draw_call,
data.batch_id, data.batch_id,
zm.f32x4(@floatFromInt(pixel_x), @floatFromInt(pixel_y), z, 1.0), zm.f32x4(@as(f32, @floatFromInt(pixel_x)) + 0.5, @as(f32, @floatFromInt(pixel_y)) + 0.5, z, frag_w),
null,
true, true,
try common.interpolateLineOutputs(data.allocator, data.start_vertex, data.end_vertex, t), try common.interpolateLineOutputs(data.allocator, data.start_vertex, data.end_vertex, t),
null, null,
@@ -177,6 +177,7 @@ inline fn run(data: RunData) !void {
const b1 = w1 / data.area; const b1 = w1 / data.area;
const b2 = w2 / data.area; const b2 = w2 / data.area;
const z = (b0 * data.v0.position[2]) + (b1 * data.v1.position[2]) + (b2 * data.v2.position[2]); const z = (b0 * data.v0.position[2]) + (b1 * data.v1.position[2]) + (b2 * data.v2.position[2]);
const frag_w = (b0 / data.v0.position[3]) + (b1 / data.v1.position[3]) + (b2 / data.v2.position[3]);
var outputs = std.mem.zeroes([spv.SPIRV_MAX_OUTPUT_LOCATIONS][@sizeOf(F32x4)]u8); var outputs = std.mem.zeroes([spv.SPIRV_MAX_OUTPUT_LOCATIONS][@sizeOf(F32x4)]u8);
if (data.has_fragment_shader) { if (data.has_fragment_shader) {
@@ -218,7 +219,8 @@ inline fn run(data: RunData) !void {
data.allocator, data.allocator,
data.draw_call, data.draw_call,
data.batch_id, data.batch_id,
zm.f32x4(@floatFromInt(x), @floatFromInt(y), z, 1.0), zm.f32x4(@as(f32, @floatFromInt(x)) + 0.5, @as(f32, @floatFromInt(y)) + 0.5, z, frag_w),
null,
data.front_face, data.front_face,
inputs, inputs,
derivative_inputs, derivative_inputs,
+10
View File
@@ -107,6 +107,7 @@ inline fn run(data: RunData) !void {
}; };
try readPosition(rt, std.mem.asBytes(&output.position)); try readPosition(rt, std.mem.asBytes(&output.position));
try readPointSize(rt, &output.point_size);
for (0..spv.SPIRV_MAX_OUTPUT_LOCATIONS) |location| { for (0..spv.SPIRV_MAX_OUTPUT_LOCATIONS) |location| {
for (0..4) |component| { for (0..4) |component| {
@@ -187,6 +188,15 @@ fn readPosition(rt: *spv.Runtime, output: []u8) !void {
return SpvRuntimeError.InvalidSpirV; return SpvRuntimeError.InvalidSpirV;
} }
fn readPointSize(rt: *spv.Runtime, output: *f32) !void {
if (rt.readBuiltIn(std.mem.asBytes(output), .PointSize)) {
return;
} else |err| switch (err) {
SpvRuntimeError.InvalidSpirV, SpvRuntimeError.NotFound => {},
else => return err,
}
}
fn isConstantZero(rt: *spv.Runtime, result_word: spv.SpvWord) bool { fn isConstantZero(rt: *spv.Runtime, result_word: spv.SpvWord) bool {
if (result_word >= rt.results.len) if (result_word >= rt.results.len)
return false; return false;