diff --git a/build.zig.zon b/build.zig.zon index b2406cb..45ceeca 100644 --- a/build.zig.zon +++ b/build.zig.zon @@ -32,8 +32,8 @@ // Soft dependencies .SPIRV_Interpreter = .{ - .url = "git+https://git.kbz8.me/kbz_8/SPIRV-Interpreter#42554a5cc1069e8174b377e95ce2ac2d802a44ff", - .hash = "SPIRV_Interpreter-0.0.1-ajmpn0GXBwD01BnmV_Kf8EeNqTDoyuq7UvHVaq-WoBP0", + .url = "git+https://github.com/Kbz-8/SPIRV-Interpreter#391f4415d9a77d15455f4d7efab65fc8cc931bc5", + .hash = "SPIRV_Interpreter-0.0.1-ajmpn6mrBwDO21UuGIRs3iP-0Z9GyhivF3N8V_X4tLzX", .lazy = true, }, //.SPIRV_Interpreter = .{ diff --git a/src/software/device/Renderer.zig b/src/software/device/Renderer.zig index 3668c5d..63bae1c 100644 --- a/src/software/device/Renderer.zig +++ b/src/software/device/Renderer.zig @@ -55,6 +55,7 @@ pub const DynamicState = struct { pub const Vertex = struct { primitive_restart: bool, position: F32x4, + point_size: f32, outputs: [spv.SPIRV_MAX_OUTPUT_LOCATIONS][4]?struct { interpolation_type: enum { smooth, flat, noperspective }, blob: []u8, @@ -106,6 +107,7 @@ pub const DrawCall = struct { for (self.vertices) |*vertex| { vertex.primitive_restart = false; + vertex.point_size = 1.0; for (&vertex.outputs) |*location| { @memset(location, null); } diff --git a/src/software/device/clip.zig b/src/software/device/clip.zig index 7224806..90db196 100644 --- a/src/software/device/clip.zig +++ b/src/software/device/clip.zig @@ -171,6 +171,7 @@ fn interpolateVertexForClipping(allocator: std.mem.Allocator, a: *const Vertex, var result: Vertex = .{ .primitive_restart = false, .position = a.position + ((b.position - a.position) * zm.f32x4s(t)), + .point_size = a.point_size + ((b.point_size - a.point_size) * t), .outputs = undefined, }; diff --git a/src/software/device/fragment.zig b/src/software/device/fragment.zig index 91388b2..925f464 100644 --- a/src/software/device/fragment.zig +++ b/src/software/device/fragment.zig @@ -23,6 +23,7 @@ pub fn shaderInvocation( draw_call: *Renderer.DrawCall, batch_id: usize, position: zm.F32x4, + point_coord: ?@Vector(2, f32), front_face: bool, inputs: [spv.SPIRV_MAX_OUTPUT_LOCATIONS]VertexInterpolationLocation, derivative_inputs: ?DerivativeInputs, @@ -54,6 +55,12 @@ pub fn shaderInvocation( SpvRuntimeError.NotFound => {}, 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) { SpvRuntimeError.NotFound => {}, else => return err, diff --git a/src/software/device/rasterizer.zig b/src/software/device/rasterizer.zig index e30e1b7..a354324 100644 --- a/src/software/device/rasterizer.zig +++ b/src/software/device/rasterizer.zig @@ -165,25 +165,25 @@ pub fn processThenFragmentStage(renderer: *Renderer, allocator: std.mem.Allocato var segment_start = firstNonRestart(draw_call, range.start, range.end); while (segment_start < range.end) { const segment_end = nextRestart(draw_call, segment_start, range.end); - if (segment_end - segment_start >= 3) { - const v0 = &draw_call.vertices[segment_start]; - for ((segment_start + 1)..(segment_end - 1)) |vertex_index| { - const v1 = &draw_call.vertices[vertex_index]; - const v2 = &draw_call.vertices[vertex_index + 1]; + if (segment_end - segment_start >= 3) { + const v0 = &draw_call.vertices[segment_start]; + for ((segment_start + 1)..(segment_end - 1)) |vertex_index| { + const v1 = &draw_call.vertices[vertex_index]; + const v2 = &draw_call.vertices[vertex_index + 1]; - try clipTransformAndRasterizeTriangle( - renderer, - allocator, - draw_call, - v0, - v1, - v2, - color_attachment_access, - if (depth_attachment_access) |*access| access else null, - if (stencil_attachment_access) |*access| access else null, - ); + try clipTransformAndRasterizeTriangle( + renderer, + allocator, + draw_call, + v0, + v1, + v2, + color_attachment_access, + if (depth_attachment_access) |*access| access else null, + if (stencil_attachment_access) |*access| access else null, + ); + } } - } 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); while (segment_start < range.end) { const segment_end = nextRestart(draw_call, segment_start, range.end); - if (segment_end - segment_start >= 3) { - for (segment_start..(segment_end - 2)) |vertex_index| { - const local_index = vertex_index - segment_start; - const v0 = &draw_call.vertices[vertex_index + 0]; - const v1 = &draw_call.vertices[vertex_index + 1]; - const v2 = &draw_call.vertices[vertex_index + 2]; + if (segment_end - segment_start >= 3) { + for (segment_start..(segment_end - 2)) |vertex_index| { + const local_index = vertex_index - segment_start; + const v0 = &draw_call.vertices[vertex_index + 0]; + const v1 = &draw_call.vertices[vertex_index + 1]; + const v2 = &draw_call.vertices[vertex_index + 2]; - if ((local_index & 1) == 0) { - try clipTransformAndRasterizeTriangle( - renderer, - allocator, - draw_call, - v0, - v1, - v2, - color_attachment_access, - if (depth_attachment_access) |*access| access else null, - if (stencil_attachment_access) |*access| access else null, - ); - } else { - try clipTransformAndRasterizeTriangle( - renderer, - allocator, - draw_call, - v1, - v0, - v2, - color_attachment_access, - if (depth_attachment_access) |*access| access else null, - if (stencil_attachment_access) |*access| access else null, - ); + if ((local_index & 1) == 0) { + try clipTransformAndRasterizeTriangle( + renderer, + allocator, + draw_call, + v0, + v1, + v2, + color_attachment_access, + if (depth_attachment_access) |*access| access else null, + if (stencil_attachment_access) |*access| access else null, + ); + } else { + try clipTransformAndRasterizeTriangle( + renderer, + allocator, + draw_call, + v1, + v0, + v2, + color_attachment_access, + if (depth_attachment_access) |*access| access else null, + if (stencil_attachment_access) |*access| access else null, + ); + } } } - } 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); while (segment_start < range.end) { const segment_end = nextRestart(draw_call, segment_start, range.end); - if (segment_end - segment_start >= 2) { - for (segment_start..(segment_end - 1)) |vertex_index| { - const v0 = &draw_call.vertices[vertex_index + 0]; - const v1 = &draw_call.vertices[vertex_index + 1]; + if (segment_end - segment_start >= 2) { + for (segment_start..(segment_end - 1)) |vertex_index| { + const v0 = &draw_call.vertices[vertex_index + 0]; + const v1 = &draw_call.vertices[vertex_index + 1]; - try clipTransformAndRasterizeLine( - allocator, - draw_call, - v0, - v1, - color_attachment_access, - if (depth_attachment_access) |*access| access else null, - if (stencil_attachment_access) |*access| access else null, - ); + try clipTransformAndRasterizeLine( + allocator, + draw_call, + v0, + v1, + color_attachment_access, + if (depth_attachment_access) |*access| access else null, + if (stencil_attachment_access) |*access| access else null, + ); + } } - } segment_start = firstNonRestart(draw_call, segment_end + 1, range.end); } } @@ -323,11 +323,13 @@ fn clipTransformAndRasterizePoint( var transformed = vertex.*; 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 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 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 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); 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( allocator, draw_call, 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, try common.interpolateVertexOutputs(allocator, &transformed, &transformed, &transformed, 1.0, 0.0, 0.0), null, diff --git a/src/software/device/rasterizer/bresenham.zig b/src/software/device/rasterizer/bresenham.zig index c46df15..7cd477b 100644 --- a/src/software/device/rasterizer/bresenham.zig +++ b/src/software/device/rasterizer/bresenham.zig @@ -45,9 +45,9 @@ pub fn drawLine( const io = draw_call.renderer.device.interface.io(); 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 y1: i32 = @intFromFloat(v1.position[1]); + var y1: i32 = @intFromFloat(@floor(v1.position[1] - 0.5)); const steep = blk: { if (@abs(y1 - y0) > @abs(x1 - x0)) { @@ -76,7 +76,7 @@ pub fn drawLine( if (runtimes_count == 0) 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 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 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); if (data.has_fragment_shader) { @@ -158,7 +159,8 @@ inline fn run(data: RunData) !void { data.allocator, data.draw_call, 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, try common.interpolateLineOutputs(data.allocator, data.start_vertex, data.end_vertex, t), null, diff --git a/src/software/device/rasterizer/edge_function.zig b/src/software/device/rasterizer/edge_function.zig index 0c8f3c0..0900a7c 100644 --- a/src/software/device/rasterizer/edge_function.zig +++ b/src/software/device/rasterizer/edge_function.zig @@ -177,6 +177,7 @@ inline fn run(data: RunData) !void { const b1 = w1 / 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 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); if (data.has_fragment_shader) { @@ -218,7 +219,8 @@ inline fn run(data: RunData) !void { data.allocator, data.draw_call, 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, inputs, derivative_inputs, diff --git a/src/software/device/vertex_dispatcher.zig b/src/software/device/vertex_dispatcher.zig index 3a607de..3c032d1 100644 --- a/src/software/device/vertex_dispatcher.zig +++ b/src/software/device/vertex_dispatcher.zig @@ -107,6 +107,7 @@ inline fn run(data: RunData) !void { }; try readPosition(rt, std.mem.asBytes(&output.position)); + try readPointSize(rt, &output.point_size); for (0..spv.SPIRV_MAX_OUTPUT_LOCATIONS) |location| { for (0..4) |component| { @@ -187,6 +188,15 @@ fn readPosition(rt: *spv.Runtime, output: []u8) !void { 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 { if (result_word >= rt.results.len) return false;