182 lines
6.1 KiB
Zig
182 lines
6.1 KiB
Zig
const std = @import("std");
|
|
const base = @import("base");
|
|
const spv = @import("spv");
|
|
const zm = base.zm;
|
|
|
|
const blitter = @import("../blitter.zig");
|
|
const common = @import("common.zig");
|
|
const fragment = @import("../fragment.zig");
|
|
|
|
const Renderer = @import("../Renderer.zig");
|
|
const SoftImage = @import("../../SoftImage.zig");
|
|
|
|
const VkError = base.VkError;
|
|
const SpvRuntimeError = spv.Runtime.RuntimeError;
|
|
const F32x4 = zm.F32x4;
|
|
|
|
const RunData = struct {
|
|
allocator: std.mem.Allocator,
|
|
draw_call: *Renderer.DrawCall,
|
|
batch_id: usize,
|
|
x0: i32,
|
|
y0: i32,
|
|
d_x: i32,
|
|
d_err: i32,
|
|
y_step: i32,
|
|
steep: bool,
|
|
start_vertex: *Renderer.Vertex,
|
|
end_vertex: *Renderer.Vertex,
|
|
start_step: usize,
|
|
end_step: usize,
|
|
color_attachment_access: []const ?common.RenderTargetAccess,
|
|
depth_attachment_access: ?*common.RenderTargetAccess,
|
|
};
|
|
|
|
pub fn drawLine(
|
|
allocator: std.mem.Allocator,
|
|
draw_call: *Renderer.DrawCall,
|
|
v0: *Renderer.Vertex,
|
|
v1: *Renderer.Vertex,
|
|
color_attachment_access: []const ?common.RenderTargetAccess,
|
|
depth_attachment_access: ?*common.RenderTargetAccess,
|
|
) VkError!void {
|
|
const io = draw_call.renderer.device.interface.io();
|
|
|
|
var x0: i32 = @intFromFloat(v0.position[0]);
|
|
var y0: i32 = @intFromFloat(v0.position[1]);
|
|
var x1: i32 = @intFromFloat(v1.position[0]);
|
|
var y1: i32 = @intFromFloat(v1.position[1]);
|
|
|
|
const steep = blk: {
|
|
if (@abs(y1 - y0) > @abs(x1 - x0)) {
|
|
std.mem.swap(i32, &x0, &y0);
|
|
std.mem.swap(i32, &x1, &y1);
|
|
break :blk true;
|
|
}
|
|
break :blk false;
|
|
};
|
|
|
|
var start_vertex = v0;
|
|
var end_vertex = v1;
|
|
if (x0 > x1) {
|
|
std.mem.swap(i32, &x0, &x1);
|
|
std.mem.swap(i32, &y0, &y1);
|
|
std.mem.swap(*Renderer.Vertex, &start_vertex, &end_vertex);
|
|
}
|
|
|
|
const d_err: i32 = @intCast(@abs(y1 - y0));
|
|
const d_x = x1 - x0;
|
|
const y_step: i32 = if (y0 > y1) -1 else 1;
|
|
|
|
const pipeline = draw_call.renderer.state.pipeline orelse return;
|
|
|
|
const runtimes_count = (pipeline.stages.getPtr(.fragment) orelse return).runtimes.len;
|
|
if (runtimes_count == 0)
|
|
return;
|
|
|
|
const step_count: usize = @as(usize, @intCast(d_x)) + 1;
|
|
const runs_count = @min(runtimes_count, step_count);
|
|
const steps_per_run = @divTrunc(step_count + runs_count - 1, runs_count);
|
|
|
|
var batch_id: usize = 0;
|
|
for (0..runs_count) |run_index| {
|
|
defer batch_id = @mod(batch_id + 1, runtimes_count);
|
|
|
|
const start_step = run_index * steps_per_run;
|
|
if (start_step >= step_count)
|
|
continue;
|
|
|
|
const end_step = @min(start_step + steps_per_run - 1, step_count - 1);
|
|
|
|
const run_data: RunData = .{
|
|
.allocator = allocator,
|
|
.draw_call = draw_call,
|
|
.batch_id = batch_id,
|
|
.x0 = x0,
|
|
.y0 = y0,
|
|
.d_x = d_x,
|
|
.d_err = d_err,
|
|
.y_step = y_step,
|
|
.steep = steep,
|
|
.start_vertex = start_vertex,
|
|
.end_vertex = end_vertex,
|
|
.start_step = start_step,
|
|
.end_step = end_step,
|
|
.color_attachment_access = color_attachment_access,
|
|
.depth_attachment_access = depth_attachment_access,
|
|
};
|
|
|
|
draw_call.rasterizer_wait_group.async(io, runWrapper, .{run_data});
|
|
}
|
|
|
|
// Not syncing workers between triangles when rendering without depth buffer
|
|
// will lead to pixel rendering order issues between triangles.
|
|
if (depth_attachment_access == null)
|
|
draw_call.rasterizer_wait_group.await(io) catch return VkError.DeviceLost;
|
|
}
|
|
|
|
fn bresenhamYAtStep(y0: i32, d_x: i32, d_err: i32, y_step: i32, step: usize) i32 {
|
|
if (d_x == 0)
|
|
return y0;
|
|
|
|
const numerator = (@as(i64, @intCast(step)) * @as(i64, d_err)) + @as(i64, @divTrunc(d_x - 1, 2));
|
|
const y_offset: i32 = @intCast(@divTrunc(numerator, @as(i64, d_x)));
|
|
return y0 + (y_step * y_offset);
|
|
}
|
|
|
|
fn runWrapper(data: RunData) void {
|
|
@call(.always_inline, run, .{data}) catch |err| {
|
|
std.log.scoped(.@"Rasterization stage").err("line fill mode catched a '{s}'", .{@errorName(err)});
|
|
if (comptime base.config.logs == .verbose) {
|
|
if (@errorReturnTrace()) |trace| {
|
|
std.debug.dumpErrorReturnTrace(trace);
|
|
}
|
|
}
|
|
};
|
|
}
|
|
|
|
inline fn run(data: RunData) !void {
|
|
var step = data.start_step;
|
|
while (step <= data.end_step) : (step += 1) {
|
|
const x = data.x0 + @as(i32, @intCast(step));
|
|
const y = bresenhamYAtStep(data.y0, data.d_x, data.d_err, data.y_step, step);
|
|
|
|
const pixel_x = if (data.steep) y else x;
|
|
const pixel_y = if (data.steep) x else y;
|
|
|
|
if (!common.scissorContainsPixel(data.draw_call.scissor, pixel_x, pixel_y)) {
|
|
continue;
|
|
}
|
|
|
|
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]);
|
|
|
|
// Early depth test to avoid unnecesary computations
|
|
if (data.depth_attachment_access) |depth| {
|
|
const offset = @as(usize, @intCast(pixel_x)) * depth.texel_size + @as(usize, @intCast(pixel_y)) * depth.row_pitch;
|
|
const depth_value = blitter.readFloat4(depth.base[offset..], depth.format);
|
|
if (z >= depth_value[0])
|
|
continue;
|
|
}
|
|
|
|
const outputs = fragment.shaderInvocation(
|
|
data.allocator,
|
|
data.draw_call,
|
|
data.batch_id,
|
|
zm.f32x4(@floatFromInt(pixel_x), @floatFromInt(pixel_y), z, 1.0),
|
|
try common.interpolateLineOutputs(data.allocator, data.start_vertex, data.end_vertex, t),
|
|
) catch |err| {
|
|
std.log.scoped(.@"Fragment stage").err("catched a '{s}'", .{@errorName(err)});
|
|
if (comptime base.config.logs == .verbose) {
|
|
if (@errorReturnTrace()) |trace| {
|
|
std.debug.dumpErrorReturnTrace(trace);
|
|
}
|
|
}
|
|
|
|
return;
|
|
};
|
|
|
|
try common.writeToTargets(outputs, data.draw_call, data.color_attachment_access, data.depth_attachment_access, @intCast(pixel_x), @intCast(pixel_y), z);
|
|
}
|
|
}
|