fixing synchronization issues, implementing missing entrypoints
Test / build_and_test (push) Successful in 2m13s
Build / build (push) Successful in 2m30s

This commit is contained in:
2026-06-30 14:36:20 +02:00
parent b83ddd52a4
commit 7933bea68a
15 changed files with 547 additions and 130 deletions
+5 -5
View File
@@ -89,11 +89,11 @@ vkCmdResetEvent | ✅ Implemented
vkCmdResetQueryPool | ✅ Implemented
vkCmdResolveImage | ✅ Implemented
vkCmdSetBlendConstants | ✅ Implemented
vkCmdSetDepthBias | ⚙️ WIP
vkCmdSetDepthBounds | ⚙️ WIP
vkCmdSetDepthBias | ✅ Implemented
vkCmdSetDepthBounds | ✅ Implemented
vkCmdSetDeviceMaskKHR | ✅ Implemented
vkCmdSetEvent | ✅ Implemented
vkCmdSetLineWidth | ⚙️ WIP
vkCmdSetLineWidth | ✅ Implemented
vkCmdSetScissor | ✅ Implemented
vkCmdSetStencilCompareMask | ✅ Implemented
vkCmdSetStencilReference | ✅ Implemented
@@ -101,7 +101,7 @@ vkCmdSetStencilWriteMask | ✅ Implemented
vkCmdSetViewport | ✅ Implemented
vkCmdUpdateBuffer | ✅ Implemented
vkCmdWaitEvents | ✅ Implemented
vkCmdWriteTimestamp | ⚙️ WIP
vkCmdWriteTimestamp | ✅ Implemented
vkCreateBuffer | ✅ Implemented
vkCreateBufferView | ✅ Implemented
vkCreateCommandPool | ✅ Implemented
@@ -169,7 +169,7 @@ vkGetBufferMemoryRequirements | ✅ Implemented
vkGetDeviceGroupPeerMemoryFeaturesKHR | ✅ Implemented
vkGetDeviceGroupPresentCapabilitiesKHR | ✅ Implemented
vkGetDeviceGroupSurfacePresentModesKHR | ✅ Implemented
vkGetDeviceMemoryCommitment | ⚙️ WIP
vkGetDeviceMemoryCommitment | ✅ Implemented
vkGetDeviceProcAddr | ✅ Implemented
vkGetDeviceQueue | ✅ Implemented
vkGetEventStatus | ✅ Implemented
+28
View File
@@ -53,7 +53,10 @@ pub fn create(device: *base.Device, allocator: std.mem.Allocator, info: *const v
.resolveImage = resolveImage,
.setEvent = setEvent,
.setBlendConstants = setBlendConstants,
.setDepthBias = setDepthBias,
.setDepthBounds = setDepthBounds,
.setDeviceMask = setDeviceMask,
.setLineWidth = setLineWidth,
.setScissor = setScissor,
.setStencilCompareMask = setStencilCompareMask,
.setStencilReference = setStencilReference,
@@ -61,6 +64,7 @@ pub fn create(device: *base.Device, allocator: std.mem.Allocator, info: *const v
.setViewport = setViewport,
.updateBuffer = updateBuffer,
.waitEvent = waitEvent,
.writeTimestamp = writeTimestamp,
};
self.* = .{ .interface = interface };
@@ -363,6 +367,24 @@ pub fn setBlendConstants(interface: *Interface, constants: [4]f32) VkError!void
_ = constants;
}
pub fn setDepthBias(interface: *Interface, constant_factor: f32, clamp: f32, slope_factor: f32) VkError!void {
_ = interface;
_ = constant_factor;
_ = clamp;
_ = slope_factor;
}
pub fn setDepthBounds(interface: *Interface, min: f32, max: f32) VkError!void {
_ = interface;
_ = min;
_ = max;
}
pub fn setLineWidth(interface: *Interface, width: f32) VkError!void {
_ = interface;
_ = width;
}
pub fn setStencilCompareMask(interface: *Interface, face_mask: vk.StencilFaceFlags, compare_mask: u32) VkError!void {
_ = interface;
_ = face_mask;
@@ -390,3 +412,9 @@ pub fn waitEvent(interface: *Interface, event: *base.Event, src_stage: vk.Pipeli
_ = buffer_barriers;
_ = image_barriers;
}
pub fn writeTimestamp(interface: *Interface, stage: vk.PipelineStageFlags, pool: *base.QueryPool, query: u32) VkError!void {
_ = interface;
_ = stage;
try pool.writeTimestamp(query, 0);
}
+103
View File
@@ -113,7 +113,10 @@ pub fn create(device: *base.Device, allocator: std.mem.Allocator, info: *const v
.resolveImage = resolveImage,
.setEvent = setEvent,
.setBlendConstants = setBlendConstants,
.setDepthBias = setDepthBias,
.setDepthBounds = setDepthBounds,
.setDeviceMask = setDeviceMask,
.setLineWidth = setLineWidth,
.setScissor = setScissor,
.setStencilCompareMask = setStencilCompareMask,
.setStencilReference = setStencilReference,
@@ -121,6 +124,7 @@ pub fn create(device: *base.Device, allocator: std.mem.Allocator, info: *const v
.setViewport = setViewport,
.updateBuffer = updateBuffer,
.waitEvent = waitEvent,
.writeTimestamp = writeTimestamp,
};
self.* = .{
@@ -283,6 +287,36 @@ pub fn resetQueryPool(interface: *Interface, pool: *base.QueryPool, first: u32,
self.commands.append(allocator, .{ .ptr = cmd, .vtable = &.{ .execute = CommandImpl.execute } }) catch return VkError.OutOfHostMemory;
}
pub fn writeTimestamp(interface: *Interface, stage: vk.PipelineStageFlags, pool: *base.QueryPool, query: u32) VkError!void {
const self: *Self = @alignCast(@fieldParentPtr("interface", interface));
const allocator = self.command_allocator.allocator();
const CommandImpl = struct {
const Impl = @This();
stage: vk.PipelineStageFlags,
pool: *base.QueryPool,
query: u32,
pub fn execute(context: *anyopaque, device: *ExecutionDevice) VkError!void {
const impl: *Impl = @ptrCast(@alignCast(context));
_ = impl.stage;
const io = device.renderer.device.interface.io();
const now = std.Io.Timestamp.now(io, .real).toNanoseconds();
try impl.pool.writeTimestamp(impl.query, if (now > 0) @intCast(now) else 0);
}
};
const cmd = allocator.create(CommandImpl) catch return VkError.OutOfHostMemory;
errdefer allocator.destroy(cmd);
cmd.* = .{
.stage = stage,
.pool = pool,
.query = query,
};
self.commands.append(allocator, .{ .ptr = cmd, .vtable = &.{ .execute = CommandImpl.execute } }) catch return VkError.OutOfHostMemory;
}
pub fn beginRenderPass(interface: *Interface, render_pass: *base.RenderPass, framebuffer: *base.Framebuffer, render_area: vk.Rect2D, clear_values: ?[]const vk.ClearValue) VkError!void {
const self: *Self = @alignCast(@fieldParentPtr("interface", interface));
const allocator = self.command_allocator.allocator();
@@ -1330,6 +1364,75 @@ pub fn setBlendConstants(interface: *Interface, constants: [4]f32) VkError!void
self.commands.append(allocator, .{ .ptr = cmd, .vtable = &.{ .execute = CommandImpl.execute } }) catch return VkError.OutOfHostMemory;
}
pub fn setDepthBias(interface: *Interface, constant_factor: f32, clamp: f32, slope_factor: f32) VkError!void {
const self: *Self = @alignCast(@fieldParentPtr("interface", interface));
const allocator = self.command_allocator.allocator();
const CommandImpl = struct {
const Impl = @This();
depth_bias: @import("device/Renderer.zig").DepthBias,
pub fn execute(context: *anyopaque, device: *ExecutionDevice) VkError!void {
const impl: *Impl = @ptrCast(@alignCast(context));
device.renderer.dynamic_state.depth_bias = impl.depth_bias;
}
};
const cmd = allocator.create(CommandImpl) catch return VkError.OutOfHostMemory;
errdefer allocator.destroy(cmd);
cmd.* = .{
.depth_bias = .{
.constant_factor = constant_factor,
.clamp = clamp,
.slope_factor = slope_factor,
},
};
self.commands.append(allocator, .{ .ptr = cmd, .vtable = &.{ .execute = CommandImpl.execute } }) catch return VkError.OutOfHostMemory;
}
pub fn setDepthBounds(interface: *Interface, min: f32, max: f32) VkError!void {
const self: *Self = @alignCast(@fieldParentPtr("interface", interface));
const allocator = self.command_allocator.allocator();
const CommandImpl = struct {
const Impl = @This();
depth_bounds: @import("device/Renderer.zig").DepthBounds,
pub fn execute(context: *anyopaque, device: *ExecutionDevice) VkError!void {
const impl: *Impl = @ptrCast(@alignCast(context));
device.renderer.dynamic_state.depth_bounds = impl.depth_bounds;
}
};
const cmd = allocator.create(CommandImpl) catch return VkError.OutOfHostMemory;
errdefer allocator.destroy(cmd);
cmd.* = .{ .depth_bounds = .{ .min = min, .max = max } };
self.commands.append(allocator, .{ .ptr = cmd, .vtable = &.{ .execute = CommandImpl.execute } }) catch return VkError.OutOfHostMemory;
}
pub fn setLineWidth(interface: *Interface, width: f32) VkError!void {
const self: *Self = @alignCast(@fieldParentPtr("interface", interface));
const allocator = self.command_allocator.allocator();
const CommandImpl = struct {
const Impl = @This();
width: f32,
pub fn execute(context: *anyopaque, device: *ExecutionDevice) VkError!void {
const impl: *Impl = @ptrCast(@alignCast(context));
device.renderer.dynamic_state.line_width = impl.width;
}
};
const cmd = allocator.create(CommandImpl) catch return VkError.OutOfHostMemory;
errdefer allocator.destroy(cmd);
cmd.* = .{ .width = width };
self.commands.append(allocator, .{ .ptr = cmd, .vtable = &.{ .execute = CommandImpl.execute } }) catch return VkError.OutOfHostMemory;
}
pub fn setStencilCompareMask(interface: *Interface, face_mask: vk.StencilFaceFlags, compare_mask: u32) VkError!void {
try setStencilDynamicState(interface, face_mask, compare_mask, .compare_mask);
}
+35 -9
View File
@@ -43,6 +43,11 @@ pub fn destroy(interface: *Interface, allocator: std.mem.Allocator) void {
pub fn getStatus(interface: *Interface) VkError!void {
const self: *Self = @alignCast(@fieldParentPtr("interface", interface));
const io = interface.owner.io();
self.mutex.lock(io) catch return VkError.DeviceLost;
defer self.mutex.unlock(io);
if (!self.is_signaled) {
return VkError.NotReady;
}
@@ -50,6 +55,11 @@ pub fn getStatus(interface: *Interface) VkError!void {
pub fn reset(interface: *Interface) VkError!void {
const self: *Self = @alignCast(@fieldParentPtr("interface", interface));
const io = interface.owner.io();
self.mutex.lock(io) catch return VkError.DeviceLost;
defer self.mutex.unlock(io);
self.is_signaled = false;
}
@@ -68,18 +78,34 @@ pub fn wait(interface: *Interface, timeout: u64) VkError!void {
const self: *Self = @alignCast(@fieldParentPtr("interface", interface));
const io = interface.owner.io();
if (timeout == std.math.maxInt(@TypeOf(timeout))) {
self.mutex.lock(io) catch return VkError.DeviceLost;
defer self.mutex.unlock(io);
while (!self.is_signaled) {
self.condition.wait(io, &self.mutex) catch return VkError.DeviceLost;
}
return;
}
const deadline = std.Io.Clock.Timestamp.fromNow(io, .{
.raw = .fromNanoseconds(@intCast(timeout)),
.clock = .awake,
});
while (true) {
{
self.mutex.lock(io) catch return VkError.DeviceLost;
defer self.mutex.unlock(io);
if (self.is_signaled) return;
if (timeout == 0) return VkError.Timeout;
if (timeout != std.math.maxInt(@TypeOf(timeout))) {
const duration: std.Io.Clock.Duration = .{
.raw = .fromNanoseconds(@intCast(timeout)),
.clock = .cpu_process,
};
duration.sleep(io) catch return VkError.DeviceLost;
}
self.condition.wait(io, &self.mutex) catch return VkError.DeviceLost;
const remaining = deadline.durationFromNow(io);
if (remaining.raw.nanoseconds <= 0) return VkError.Timeout;
(std.Io.Clock.Duration{
.raw = .fromNanoseconds(@min(remaining.raw.nanoseconds, std.time.ns_per_ms)),
.clock = .awake,
}).sleep(io) catch return VkError.DeviceLost;
}
}
+3 -1
View File
@@ -184,7 +184,6 @@ inline fn run(data: RunData) !void {
const workgroup_memory = try rt.createWorkgroupMemory(allocator);
defer rt.destroyWorkgroupMemory(allocator, workgroup_memory);
for (0..data.invocations_per_workgroup) |i| {
rt.resetInvocation(allocator);
if (rt.specialization_constants.count() != 0)
try rt.applySpecializationInvocationLayout(allocator);
@@ -193,6 +192,9 @@ inline fn run(data: RunData) !void {
try rt.bindWorkgroupMemory(workgroup_memory);
try setupWorkgroupBuiltins(data.self, rt, data.local_size, group_count_vec, group_id_vec);
for (0..data.invocations_per_workgroup) |i| {
rt.resetInvocation(allocator);
const invocation_index = data.self.invocation_index.fetchAdd(1, .monotonic);
try setupSubgroupBuiltins(data.self, rt, data.local_size, .{
+15
View File
@@ -45,6 +45,8 @@ pub const DynamicState = struct {
viewports: ?[]const vk.Viewport,
scissor: ?[]const vk.Rect2D,
line_width: ?f32,
depth_bias: ?DepthBias,
depth_bounds: ?DepthBounds,
blend_constants: ?[4]f32,
stencil_front_compare_mask: ?u32,
stencil_back_compare_mask: ?u32,
@@ -54,6 +56,17 @@ pub const DynamicState = struct {
stencil_back_reference: ?u32,
};
pub const DepthBias = struct {
constant_factor: f32,
clamp: f32,
slope_factor: f32,
};
pub const DepthBounds = struct {
min: f32,
max: f32,
};
pub const Vertex = struct {
primitive_restart: bool,
position: F32x4,
@@ -158,6 +171,8 @@ pub fn init(device: *SoftDevice, state: *PipelineState, active_occlusion_queries
.viewports = null,
.scissor = null,
.line_width = null,
.depth_bias = null,
.depth_bounds = null,
.blend_constants = null,
.stencil_front_compare_mask = null,
.stencil_back_compare_mask = null,
+1 -1
View File
@@ -271,7 +271,7 @@ pub fn processThenFragmentStage(renderer: *Renderer, allocator: std.mem.Allocato
v0,
v1,
v2,
v0,
v1,
color_attachment_access,
if (depth_attachment_access) |*access| access else null,
if (stencil_attachment_access) |*access| access else null,
+167 -61
View File
@@ -26,6 +26,7 @@ const RunData = struct {
steep: bool,
start_vertex: *Renderer.Vertex,
end_vertex: *Renderer.Vertex,
provoking_vertex: *Renderer.Vertex,
start_step: usize,
end_step: usize,
color_attachment_access: []const ?common.RenderTargetAccess,
@@ -68,78 +69,183 @@ fn drawLineWithEndpointMode(
stencil_attachment_access: ?*common.RenderTargetAccess,
include_last_endpoint: bool,
) VkError!void {
const io = draw_call.renderer.device.interface.io();
var x0: i32 = @intFromFloat(v0.position[0]);
var y0: i32 = @intFromFloat(@floor(v0.position[1] - 0.5));
var x1: i32 = @intFromFloat(v1.position[0]);
var y1: i32 = @intFromFloat(@floor(v1.position[1] - 0.5));
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;
try drawLineDiamond(
allocator,
draw_call,
v0,
v1,
color_attachment_access,
depth_attachment_access,
stencil_attachment_access,
include_last_endpoint,
);
}
fn drawLineDiamond(
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,
stencil_attachment_access: ?*common.RenderTargetAccess,
include_last_endpoint: bool,
) VkError!void {
const pipeline = draw_call.renderer.state.pipeline orelse return;
const fragment_stage = pipeline.stages.getPtr(.fragment);
const runtimes_count = if (fragment_stage) |stage| stage.runtimes.len else 1;
if (runtimes_count == 0)
return;
const has_fragment_shader = fragment_stage != null;
const batch_id: usize = 0;
const step_count: usize = @intCast(if (include_last_endpoint) @max(d_x, 0) + 1 else @max(d_x, 1));
const runs_count = @min(runtimes_count, step_count);
const steps_per_run = @divTrunc(step_count + runs_count - 1, runs_count);
const min_x: i32 = @intFromFloat(@floor(@min(v0.position[0], v1.position[0]) - 1.0));
const max_x: i32 = @intFromFloat(@ceil(@max(v0.position[0], v1.position[0]) + 1.0));
const min_y: i32 = @intFromFloat(@floor(@min(v0.position[1], v1.position[1]) - 1.0));
const max_y: i32 = @intFromFloat(@ceil(@max(v0.position[1], v1.position[1]) + 1.0));
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)
var y = min_y;
while (y <= max_y) : (y += 1) {
var x = min_x;
while (x <= max_x) : (x += 1) {
if (!common.scissorContainsPixel(draw_call.scissor, x, y))
continue;
const end_step = @min(start_step + steps_per_run - 1, step_count - 1);
const coverage = diamondCoverage(v0.position, v1.position, x, y, include_last_endpoint);
const t = coverage orelse continue;
const z = ((1.0 - t) * v0.position[2]) + (t * v1.position[2]);
const frag_w = ((1.0 - t) / v0.position[3]) + (t / v1.position[3]);
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,
.stencil_attachment_access = stencil_attachment_access,
.has_fragment_shader = fragment_stage != null,
var fragment_result: fragment.InvocationResult = .{
.outputs = std.mem.zeroes([spv.SPIRV_MAX_OUTPUT_LOCATIONS][@sizeOf(F32x4)]u8),
.depth = null,
.sample_mask = null,
};
if (has_fragment_shader) {
fragment_result = fragment.shaderInvocation(
allocator,
draw_call,
batch_id,
zm.f32x4(@as(f32, @floatFromInt(x)) + 0.5, @as(f32, @floatFromInt(y)) + 0.5, z, frag_w),
null,
null,
true,
try common.interpolateLineOutputs(allocator, v0, v1, v0, t),
null,
) catch |err| {
if (err == SpvRuntimeError.Killed)
continue;
draw_call.rasterizer_wait_group.async(io, runWrapper, .{run_data});
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;
};
}
draw_call.rasterizer_wait_group.await(io) catch return VkError.DeviceLost;
try common.writeToTargets(
fragment_result.outputs,
draw_call,
color_attachment_access,
depth_attachment_access,
stencil_attachment_access,
true,
@intCast(x),
@intCast(y),
fragment_result.depth orelse z,
lineCoverageMaskFloat(
v0.position,
v1.position,
x,
y,
pipeline.interface.mode.graphics.multisample.rasterization_samples.toInt(),
),
fragment_result.sample_mask,
false,
);
}
}
}
fn diamondCoverage(a: F32x4, b: F32x4, pixel_x: i32, pixel_y: i32, include_last_endpoint: bool) ?f32 {
const cx = @as(f32, @floatFromInt(pixel_x)) + 0.5;
const cy = @as(f32, @floatFromInt(pixel_y)) + 0.5;
const clipped = clipSegmentToDiamond(a[0], a[1], b[0], b[1], cx, cy) orelse return null;
if (!include_last_endpoint and a[3] == 1.0 and b[3] == 1.0 and clipped.min_t <= 0.0)
return null;
if (!include_last_endpoint and clipped.min_t >= 1.0)
return null;
return std.math.clamp((clipped.min_t + clipped.max_t) * 0.5, 0.0, 1.0);
}
fn clipSegmentToDiamond(ax: f32, ay: f32, bx: f32, by: f32, cx: f32, cy: f32) ?struct { min_t: f32, max_t: f32 } {
var min_t: f32 = 0.0;
var max_t: f32 = 1.0;
const dx = bx - ax;
const dy = by - ay;
const normals = [_]struct { nx: f32, ny: f32 }{
.{ .nx = 1.0, .ny = 1.0 },
.{ .nx = 1.0, .ny = -1.0 },
.{ .nx = -1.0, .ny = 1.0 },
.{ .nx = -1.0, .ny = -1.0 },
};
for (normals) |normal| {
const origin_distance = normal.nx * (ax - cx) + normal.ny * (ay - cy) - 0.5;
const delta_distance = normal.nx * dx + normal.ny * dy;
if (delta_distance == 0.0) {
if (origin_distance > 0.0)
return null;
continue;
}
const t = -origin_distance / delta_distance;
if (delta_distance > 0.0) {
max_t = @min(max_t, t);
} else {
min_t = @max(min_t, t);
}
if (min_t > max_t)
return null;
}
return .{ .min_t = min_t, .max_t = max_t };
}
fn lineCoverageMaskFloat(a: F32x4, b: F32x4, pixel_x: i32, pixel_y: i32, sample_count: usize) vk.SampleMask {
if (sample_count <= 1)
return 1;
const ab_x = b[0] - a[0];
const ab_y = b[1] - a[1];
const ab_len2 = ab_x * ab_x + ab_y * ab_y;
if (ab_len2 == 0.0)
return 1;
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 sample_x = @as(f32, @floatFromInt(pixel_x)) + sample_pos.x;
const sample_y = @as(f32, @floatFromInt(pixel_y)) + sample_pos.y;
const ap_x = sample_x - a[0];
const ap_y = sample_y - a[1];
const t = std.math.clamp(((ap_x * ab_x) + (ap_y * ab_y)) / ab_len2, 0.0, 1.0);
const closest_x = a[0] + ab_x * t;
const closest_y = a[1] + ab_y * t;
const dx = sample_x - closest_x;
const dy = sample_y - closest_y;
if (dx * dx + dy * dy <= 0.25) {
const bit_index: u5 = @intCast(sample_index);
mask |= @as(vk.SampleMask, 1) << bit_index;
}
}
return if (mask == 0) 1 else mask;
}
fn bresenhamYAtStep(y0: i32, d_x: i32, d_err: i32, y_step: i32, step: usize) i32 {
@@ -249,7 +355,7 @@ inline fn run(data: RunData) !void {
null,
null,
true,
try common.interpolateLineOutputs(data.allocator, data.start_vertex, data.end_vertex, t),
try common.interpolateLineOutputs(data.allocator, data.start_vertex, data.end_vertex, data.provoking_vertex, t),
null,
) catch |err| {
if (err == SpvRuntimeError.Killed)
+43 -3
View File
@@ -31,6 +31,27 @@ pub const VertexInterpolation = struct {
pub const VertexInterpolationLocation = [4]VertexInterpolation;
pub fn depthBiasConstantUnit(format: vk.Format, z: f32) f32 {
return switch (format) {
.d16_unorm => 1.0 / @as(f32, @floatFromInt(std.math.maxInt(u16))),
.x8_d24_unorm_pack32,
.d24_unorm_s8_uint,
=> 1.0 / @as(f32, @floatFromInt(0x00ff_ffff)),
.d32_sfloat,
.d32_sfloat_s8_uint,
=> if (z > 0.0) std.math.pow(f32, 2.0, @floor(@log2(z)) - 23.0) else 0.0,
else => 0.0,
};
}
pub fn clampDepthBias(bias: f32, clamp: f32) f32 {
if (clamp > 0.0)
return @min(bias, clamp);
if (clamp < 0.0)
return @max(bias, clamp);
return bias;
}
pub fn scissorContainsPixel(scissor: vk.Rect2D, x: i32, y: i32) bool {
const min_x: i64 = @as(i64, scissor.offset.x);
const min_y: i64 = @as(i64, scissor.offset.y);
@@ -172,6 +193,20 @@ pub fn depthTestAndUpdate(depth: *RenderTargetAccess, x: usize, y: usize, z: f32
return depthTestAndUpdateAtOffset(depth, offset, z, state);
}
pub fn resolveDepthStencilState(draw_call: *Renderer.DrawCall, state: vk.PipelineDepthStencilStateCreateInfo) vk.PipelineDepthStencilStateCreateInfo {
var resolved = state;
const pipeline_data = draw_call.renderer.state.pipeline.?.interface.mode.graphics;
if (pipeline_data.dynamic_state.depth_bounds) {
const bounds = draw_call.renderer.dynamic_state.depth_bounds orelse Renderer.DepthBounds{
.min = 0.0,
.max = 1.0,
};
resolved.min_depth_bounds = bounds.min;
resolved.max_depth_bounds = bounds.max;
}
return resolved;
}
pub fn depthTestSampleAndUpdate(
io: std.Io,
depth: *RenderTargetAccess,
@@ -198,10 +233,14 @@ pub fn depthTestSampleAndUpdate(
}
fn depthTestAndUpdateAtOffset(depth: *RenderTargetAccess, offset: usize, z: f32, state: vk.PipelineDepthStencilStateCreateInfo) bool {
const reference = quantizeDepthForFormat(depth.format, z);
if (state.depth_bounds_test_enable == .true and
(reference < state.min_depth_bounds or reference > state.max_depth_bounds))
return false;
if (state.depth_test_enable == .false)
return true;
const reference = quantizeDepthForFormat(depth.format, z);
const depth_value = blitter.readFloat4(depth.base[offset..], depth.format);
const passed = compare(f32, state.depth_compare_op, reference, depth_value[0]);
if (passed and state.depth_write_enable == .true)
@@ -287,9 +326,10 @@ pub fn interpolateLineOutputs(
allocator: std.mem.Allocator,
v0: *const Renderer.Vertex,
v1: *const Renderer.Vertex,
provoking_vertex: *const Renderer.Vertex,
t: f32,
) VkError![spv.SPIRV_MAX_OUTPUT_LOCATIONS]VertexInterpolationLocation {
return interpolateVertexOutputs(allocator, v0, v1, v0, v0, 1.0 - t, t, 0.0);
return interpolateVertexOutputs(allocator, v0, v1, v0, provoking_vertex, 1.0 - t, t, 0.0);
}
pub fn interpolateVertexOutputDerivatives(
@@ -506,7 +546,7 @@ pub fn writeToTargets(
) VkError!void {
const io = draw_call.renderer.device.interface.io();
const pipeline_data = draw_call.renderer.state.pipeline.?.interface.mode.graphics;
const depth_stencil_state = pipeline_data.depth_stencil;
const depth_stencil_state = if (pipeline_data.depth_stencil) |state| resolveDepthStencilState(draw_call, state) else null;
const effective_fragment_sample_mask = alphaToCoverageMask(
pipeline_data.multisample,
outputs,
@@ -40,6 +40,7 @@ const RunData = struct {
fragment_uses_derivatives: bool,
fragment_uses_sample_id: bool,
fragment_uses_centroid: bool,
depth_bias_slope: f32,
};
pub fn drawTriangle(
@@ -64,6 +65,16 @@ pub fn drawTriangle(
const area = edgeFunction(v0.position, v1.position, v2.position);
if (area == 0.0)
return;
const inv_area = 1.0 / area;
const dz_dx =
(v0.position[2] * ((v1.position[1] - v2.position[1]) * inv_area)) +
(v1.position[2] * ((v2.position[1] - v0.position[1]) * inv_area)) +
(v2.position[2] * ((v0.position[1] - v1.position[1]) * inv_area));
const dz_dy =
(v0.position[2] * ((v2.position[0] - v1.position[0]) * inv_area)) +
(v1.position[2] * ((v0.position[0] - v2.position[0]) * inv_area)) +
(v2.position[2] * ((v1.position[0] - v0.position[0]) * inv_area));
const depth_bias_slope = @max(@abs(dz_dx), @abs(dz_dy));
const pipeline = draw_call.renderer.state.pipeline orelse return;
const fragment_stage = pipeline.stages.getPtr(.fragment);
@@ -139,6 +150,7 @@ pub fn drawTriangle(
.fragment_uses_derivatives = fragment_uses_derivatives,
.fragment_uses_sample_id = fragment_uses_sample_id,
.fragment_uses_centroid = fragment_uses_centroid,
.depth_bias_slope = depth_bias_slope,
};
draw_call.rasterizer_wait_group.async(io, runWrapper, .{run_data});
@@ -250,6 +262,7 @@ fn applyEarlyDepth(data: RunData, coverage_sample_mask: vk.SampleMask, x: i32, y
const depth = data.depth_attachment_access orelse return .{ .mask = coverage_sample_mask, .applied = false };
const pipeline_data = data.draw_call.renderer.state.pipeline.?.interface.mode.graphics;
const depth_stencil_state = if (pipeline_data.depth_stencil) |state| common.resolveDepthStencilState(data.draw_call, state) else null;
const io = data.draw_call.renderer.device.interface.io();
var passed_mask: vk.SampleMask = 0;
@@ -261,13 +274,38 @@ fn applyEarlyDepth(data: RunData, coverage_sample_mask: vk.SampleMask, x: i32, y
if ((coverage_sample_mask & bit) == 0)
continue;
if (try common.depthTestSampleAndUpdate(io, depth, @intCast(x), @intCast(y), sample_index, z, pipeline_data.depth_stencil))
if (try common.depthTestSampleAndUpdate(io, depth, @intCast(x), @intCast(y), sample_index, z, depth_stencil_state))
passed_mask |= bit;
}
return .{ .mask = passed_mask, .applied = true };
}
fn biasedDepth(data: RunData, z: f32) f32 {
const pipeline_data = data.draw_call.renderer.state.pipeline.?.interface.mode.graphics;
if (pipeline_data.rasterization.depth_bias_enable == .false)
return z;
const depth = data.depth_attachment_access orelse return z;
const bias_state: Renderer.DepthBias = if (pipeline_data.dynamic_state.depth_bias)
data.draw_call.renderer.dynamic_state.depth_bias orelse Renderer.DepthBias{
.constant_factor = 0.0,
.clamp = 0.0,
.slope_factor = 0.0,
}
else
Renderer.DepthBias{
.constant_factor = pipeline_data.rasterization.depth_bias_constant_factor,
.clamp = pipeline_data.rasterization.depth_bias_clamp,
.slope_factor = pipeline_data.rasterization.depth_bias_slope_factor,
};
const bias =
bias_state.constant_factor * common.depthBiasConstantUnit(depth.format, z) +
bias_state.slope_factor * data.depth_bias_slope;
return z + common.clampDepthBias(bias, bias_state.clamp);
}
fn runWrapper(data: RunData) void {
@call(.always_inline, run, .{data}) catch |err| {
std.log.scoped(.@"Rasterization stage").err("triangle fill mode catched a '{s}'", .{@errorName(err)});
@@ -304,8 +342,9 @@ 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 depth_z = biasedDepth(data, z);
const frag_w = (b0 / data.v0.position[3]) + (b1 / data.v1.position[3]) + (b2 / data.v2.position[3]);
const early_depth = try applyEarlyDepth(data, coverage_sample_mask, x, y, z, sample_count);
const early_depth = try applyEarlyDepth(data, coverage_sample_mask, x, y, depth_z, sample_count);
if (early_depth.mask == 0)
continue;
@@ -378,7 +417,7 @@ inline fn run(data: RunData) !void {
data.front_face,
@intCast(x),
@intCast(y),
sample_result.depth orelse z,
sample_result.depth orelse depth_z,
sample_coverage_mask,
sample_result.sample_mask,
early_depth.applied,
@@ -460,7 +499,7 @@ inline fn run(data: RunData) !void {
data.front_face,
@intCast(x),
@intCast(y),
fragment_result.depth orelse z,
fragment_result.depth orelse depth_z,
early_depth.mask,
fragment_result.sample_mask,
early_depth.applied,
+20
View File
@@ -78,7 +78,10 @@ pub const DispatchTable = struct {
resolveImage: *const fn (*Self, *Image, vk.ImageLayout, *Image, vk.ImageLayout, vk.ImageResolve) VkError!void,
setEvent: *const fn (*Self, *Event, vk.PipelineStageFlags) VkError!void,
setBlendConstants: *const fn (*Self, [4]f32) VkError!void,
setDepthBias: *const fn (*Self, f32, f32, f32) VkError!void,
setDepthBounds: *const fn (*Self, f32, f32) VkError!void,
setDeviceMask: *const fn (*Self, u32) VkError!void,
setLineWidth: *const fn (*Self, f32) VkError!void,
setScissor: *const fn (*Self, u32, []const vk.Rect2D) VkError!void,
setStencilCompareMask: *const fn (*Self, vk.StencilFaceFlags, u32) VkError!void,
setStencilReference: *const fn (*Self, vk.StencilFaceFlags, u32) VkError!void,
@@ -86,6 +89,7 @@ pub const DispatchTable = struct {
setViewport: *const fn (*Self, u32, []const vk.Viewport) VkError!void,
updateBuffer: *const fn (*Self, *Buffer, vk.DeviceSize, []const u8) VkError!void,
waitEvent: *const fn (*Self, *Event, vk.PipelineStageFlags, vk.PipelineStageFlags, []const vk.MemoryBarrier, []const vk.BufferMemoryBarrier, []const vk.ImageMemoryBarrier) VkError!void,
writeTimestamp: *const fn (*Self, vk.PipelineStageFlags, *QueryPool, u32) VkError!void,
};
pub const VTable = struct {
@@ -338,6 +342,14 @@ pub inline fn setBlendConstants(self: *Self, constants: [4]f32) VkError!void {
try self.dispatch_table.setBlendConstants(self, constants);
}
pub inline fn setDepthBias(self: *Self, constant_factor: f32, clamp: f32, slope_factor: f32) VkError!void {
try self.dispatch_table.setDepthBias(self, constant_factor, clamp, slope_factor);
}
pub inline fn setDepthBounds(self: *Self, min: f32, max: f32) VkError!void {
try self.dispatch_table.setDepthBounds(self, min, max);
}
pub inline fn setDeviceMask(self: *Self, device_mask: u32) VkError!void {
if (device_mask != 1)
return VkError.ValidationFailed;
@@ -345,6 +357,10 @@ pub inline fn setDeviceMask(self: *Self, device_mask: u32) VkError!void {
try self.dispatch_table.setDeviceMask(self, device_mask);
}
pub inline fn setLineWidth(self: *Self, width: f32) VkError!void {
try self.dispatch_table.setLineWidth(self, width);
}
pub inline fn setScissor(self: *Self, first: u32, scissor: []const vk.Rect2D) VkError!void {
try self.dispatch_table.setScissor(self, first, scissor);
}
@@ -376,3 +392,7 @@ pub inline fn waitEvent(
) VkError!void {
try self.dispatch_table.waitEvent(self, event, src_stage, dst_stage, memory_barriers, buffer_barriers, image_barriers);
}
pub inline fn writeTimestamp(self: *Self, stage: vk.PipelineStageFlags, pool: *QueryPool, query: u32) VkError!void {
try self.dispatch_table.writeTimestamp(self, stage, pool, query);
}
+38
View File
@@ -48,3 +48,41 @@ pub inline fn signal(self: *Self) VkError!void {
pub inline fn wait(self: *Self, timeout: u64) VkError!void {
try self.vtable.wait(self, timeout);
}
pub fn waitMany(device: *Device, fences: []const *Self, wait_for_all: vk.Bool32, timeout: u64) VkError!void {
const forever = timeout == std.math.maxInt(@TypeOf(timeout));
const io = device.io();
const deadline = if (forever) null else std.Io.Clock.Timestamp.fromNow(io, .{
.raw = .fromNanoseconds(@intCast(timeout)),
.clock = .awake,
});
while (true) {
var signaled_count: usize = 0;
for (fences) |fence| {
if (fence.owner != device) return VkError.InvalidHandleDrv;
fence.getStatus() catch |err| switch (err) {
VkError.NotReady => continue,
else => return err,
};
if (wait_for_all == .false) return;
signaled_count += 1;
}
if (signaled_count == fences.len) return;
if (timeout == 0) return VkError.Timeout;
const sleep_ns = if (deadline) |value| blk: {
const remaining = value.durationFromNow(io);
if (remaining.raw.nanoseconds <= 0) return VkError.Timeout;
break :blk @min(remaining.raw.nanoseconds, std.time.ns_per_ms);
} else std.time.ns_per_ms;
(std.Io.Clock.Duration{
.raw = .fromNanoseconds(sleep_ns),
.clock = .awake,
}).sleep(io) catch return VkError.DeviceLost;
}
}
+8
View File
@@ -49,6 +49,10 @@ mode: union(enum) {
cull_mode: vk.CullModeFlags,
front_face: vk.FrontFace,
line_width: f32,
depth_bias_enable: vk.Bool32,
depth_bias_constant_factor: f32,
depth_bias_clamp: f32,
depth_bias_slope_factor: f32,
},
multisample: struct {
rasterization_samples: vk.SampleCountFlags,
@@ -194,6 +198,10 @@ pub fn initGraphics(device: *Device, allocator: std.mem.Allocator, cache: ?*Pipe
.cull_mode = if (info.p_rasterization_state) |state| state.cull_mode else return VkError.ValidationFailed,
.front_face = if (info.p_rasterization_state) |state| state.front_face else return VkError.ValidationFailed,
.line_width = if (info.p_rasterization_state) |state| state.line_width else return VkError.ValidationFailed,
.depth_bias_enable = if (info.p_rasterization_state) |state| state.depth_bias_enable else return VkError.ValidationFailed,
.depth_bias_constant_factor = if (info.p_rasterization_state) |state| state.depth_bias_constant_factor else return VkError.ValidationFailed,
.depth_bias_clamp = if (info.p_rasterization_state) |state| state.depth_bias_clamp else return VkError.ValidationFailed,
.depth_bias_slope_factor = if (info.p_rasterization_state) |state| state.depth_bias_slope_factor else return VkError.ValidationFailed,
},
.multisample = blk: {
if (rasterizer_discard_enable) {
+10
View File
@@ -69,6 +69,16 @@ pub fn end(self: *Self, query: u32) VkError!void {
q.available = true;
}
pub fn writeTimestamp(self: *Self, query: u32, value: u64) VkError!void {
if (self.query_type != .timestamp)
return VkError.ValidationFailed;
const q = try self.queryAt(query);
q.value.store(value, .seq_cst);
q.available = true;
q.active = false;
}
pub fn addSamples(self: *Self, query: u32, samples: u64) VkError!void {
const q = try self.queryAt(query);
if (q.active)
+18 -36
View File
@@ -1434,13 +1434,11 @@ pub export fn apeGetDeviceMemoryCommitment(p_device: vk.Device, p_memory: vk.Dev
defer entryPointEndLogTrace();
const device = Dispatchable(Device).fromHandleObject(p_device) catch |err| return errorLogger(err);
const memory = Dispatchable(DeviceMemory).fromHandleObject(p_memory) catch |err| return errorLogger(err);
const memory = NonDispatchable(DeviceMemory).fromHandleObject(p_memory) catch |err| return errorLogger(err);
if (memory.owner != device)
return errorLogger(VkError.InvalidHandleDrv);
notImplementedWarning();
_ = device;
_ = memory;
_ = committed_memory;
committed_memory.* = memory.size;
}
pub export fn apeGetDeviceGroupPeerMemoryFeatures(
@@ -1759,13 +1757,17 @@ pub export fn apeWaitForFences(p_device: vk.Device, count: u32, p_fences: [*]con
entryPointBeginLogTrace(.vkWaitForFences);
defer entryPointEndLogTrace();
Dispatchable(Device).checkHandleValidity(p_device) catch |err| return toVkResult(err);
const device = Dispatchable(Device).fromHandleObject(p_device) catch |err| return toVkResult(err);
for (p_fences, 0..count) |p_fence, _| {
const fence = NonDispatchable(Fence).fromHandleObject(p_fence) catch |err| return toVkResult(err);
fence.wait(timeout) catch |err| return toVkResult(err);
if (waitForAll == .false) break;
const allocator = VulkanAllocator.init(null, .command).allocator();
const fences = allocator.alloc(*Fence, count) catch return toVkResult(VkError.OutOfHostMemory);
defer allocator.free(fences);
for (p_fences[0..count], fences) |p_fence, *fence| {
fence.* = NonDispatchable(Fence).fromHandleObject(p_fence) catch |err| return toVkResult(err);
}
Fence.waitMany(device, fences, waitForAll, timeout) catch |err| return toVkResult(err);
return .success;
}
@@ -2152,13 +2154,7 @@ pub export fn apeCmdSetDepthBias(p_cmd: vk.CommandBuffer, constant_factor: f32,
defer entryPointEndLogTrace();
const cmd = Dispatchable(CommandBuffer).fromHandleObject(p_cmd) catch |err| return errorLogger(err);
notImplementedWarning();
_ = cmd;
_ = constant_factor;
_ = clamp;
_ = slope_factor;
cmd.setDepthBias(constant_factor, clamp, slope_factor) catch |err| return errorLogger(err);
}
pub export fn apeCmdSetDepthBounds(p_cmd: vk.CommandBuffer, min: f32, max: f32) callconv(vk.vulkan_call_conv) void {
@@ -2166,12 +2162,7 @@ pub export fn apeCmdSetDepthBounds(p_cmd: vk.CommandBuffer, min: f32, max: f32)
defer entryPointEndLogTrace();
const cmd = Dispatchable(CommandBuffer).fromHandleObject(p_cmd) catch |err| return errorLogger(err);
notImplementedWarning();
_ = cmd;
_ = min;
_ = max;
cmd.setDepthBounds(min, max) catch |err| return errorLogger(err);
}
pub export fn apeCmdSetDeviceMask(p_cmd: vk.CommandBuffer, device_mask: u32) callconv(vk.vulkan_call_conv) void {
@@ -2200,11 +2191,7 @@ pub export fn apeCmdSetLineWidth(p_cmd: vk.CommandBuffer, width: f32) callconv(v
defer entryPointEndLogTrace();
const cmd = Dispatchable(CommandBuffer).fromHandleObject(p_cmd) catch |err| return errorLogger(err);
notImplementedWarning();
_ = cmd;
_ = width;
cmd.setLineWidth(width) catch |err| return errorLogger(err);
}
pub export fn apeCmdSetScissor(p_cmd: vk.CommandBuffer, first: u32, count: u32, scissors: [*]const vk.Rect2D) callconv(vk.vulkan_call_conv) void {
@@ -2292,13 +2279,8 @@ pub export fn apeCmdWriteTimestamp(p_cmd: vk.CommandBuffer, stage: vk.PipelineSt
defer entryPointEndLogTrace();
const cmd = Dispatchable(CommandBuffer).fromHandleObject(p_cmd) catch |err| return errorLogger(err);
notImplementedWarning();
_ = cmd;
_ = stage;
_ = p_pool;
_ = query;
const pool = NonDispatchable(QueryPool).fromHandleObject(p_pool) catch |err| return errorLogger(err);
cmd.writeTimestamp(stage, pool, query) catch |err| return errorLogger(err);
}
pub export fn apeEndCommandBuffer(p_cmd: vk.CommandBuffer) callconv(vk.vulkan_call_conv) vk.Result {