implementing queries
Build / build (push) Successful in 1m6s
Test / build_and_test (push) Successful in 50s

This commit is contained in:
2026-06-05 18:18:38 +02:00
parent 9100db5bc4
commit 90b93a69e0
18 changed files with 707 additions and 347 deletions
+12 -1
View File
@@ -21,6 +21,11 @@ pub const GRAPHICS_PIPELINE_STATE = 0;
pub const COMPUTE_PIPELINE_STATE = 1;
pub const MAX_DYNAMIC_DESCRIPTORS_PER_SET = 64;
pub const ActiveOcclusionQuery = struct {
pool: *base.QueryPool,
query: u32,
};
pub const PipelineState = struct {
pipeline: ?*SoftPipeline,
sets: [base.VULKAN_MAX_DESCRIPTOR_SETS]?*SoftDescriptorSet,
@@ -39,6 +44,7 @@ compute: ComputeDispatcher,
renderer: Renderer,
pipeline_states: [2]PipelineState,
active_occlusion_queries: std.ArrayList(ActiveOcclusionQuery),
/// Initializating an execution device and
/// not creating one to avoid dangling pointers
@@ -61,8 +67,13 @@ pub fn setup(self: *Self, device: *SoftDevice) void {
},
};
}
self.active_occlusion_queries = .empty;
self.compute = .init(device, &self.pipeline_states[@intFromEnum(vk.PipelineBindPoint.compute)]);
self.renderer = .init(device, &self.pipeline_states[@intFromEnum(vk.PipelineBindPoint.graphics)]);
self.renderer = .init(device, &self.pipeline_states[@intFromEnum(vk.PipelineBindPoint.graphics)], &self.active_occlusion_queries);
}
pub fn deinit(self: *Self, allocator: std.mem.Allocator) void {
self.active_occlusion_queries.deinit(allocator);
}
pub fn writeDescriptorSets(state: *PipelineState, rt: *spv.Runtime) !void {
+3 -3
View File
@@ -74,7 +74,6 @@ pub const DrawCall = struct {
render_pass: *SoftRenderPass,
framebuffer: *SoftFramebuffer,
allocator_mutex: std.Io.Mutex,
rasterizer_wait_group: std.Io.Group,
stats: struct {
@@ -94,7 +93,6 @@ pub const DrawCall = struct {
.depth_attachment = if (render_pass.interface.subpasses[renderer.subpass_index].depth_stencil_attachments) |desc| framebuffer.interface.attachments[desc.attachment] else null,
.render_pass = render_pass,
.framebuffer = framebuffer,
.allocator_mutex = .init,
.rasterizer_wait_group = .init,
.stats = .{
.polygons_drawn = 0,
@@ -132,8 +130,9 @@ framebuffer: ?*SoftFramebuffer,
dynamic_state: DynamicState,
subpass_index: usize,
active_occlusion_queries: *std.ArrayList(ExecutionDevice.ActiveOcclusionQuery),
pub fn init(device: *SoftDevice, state: *PipelineState) Self {
pub fn init(device: *SoftDevice, state: *PipelineState, active_occlusion_queries: *std.ArrayList(ExecutionDevice.ActiveOcclusionQuery)) Self {
return .{
.device = device,
.state = state,
@@ -152,6 +151,7 @@ pub fn init(device: *SoftDevice, state: *PipelineState) Self {
.stencil_back_reference = null,
},
.subpass_index = 0,
.active_occlusion_queries = active_occlusion_queries,
};
}
+14 -1
View File
@@ -574,7 +574,12 @@ pub fn readFloat4(map: []const u8, src_format: vk.Format) F32x4 {
=> c[0] = normalizedI8(map[0]),
.r16_snorm => c[0] = normalizedI16(std.mem.bytesToValue(u16, map)),
.r16_unorm => c[0] = @as(f32, @floatFromInt(std.mem.bytesToValue(u16, map))) / std.math.maxInt(u16),
.r16_unorm,
.d16_unorm,
=> c[0] = @as(f32, @floatFromInt(std.mem.bytesToValue(u16, map))) / std.math.maxInt(u16),
.x8_d24_unorm_pack32,
.d24_unorm_s8_uint,
=> c[0] = @as(f32, @floatFromInt(std.mem.bytesToValue(u32, map) & 0x00ff_ffff)) / @as(f32, @floatFromInt(0x00ff_ffff)),
.r8g8b8a8_sint,
.r8g8b8a8_uint,
@@ -856,6 +861,14 @@ pub fn writeFloat4(c: F32x4, map: []u8, dst_format: vk.Format) void {
.d16_unorm,
=> std.mem.bytesAsValue(u16, map).* = @intFromFloat(@round(color[0] * std.math.maxInt(u16))),
.x8_d24_unorm_pack32,
.d24_unorm_s8_uint,
=> {
const depth: u32 = @intFromFloat(@round(color[0] * @as(f32, @floatFromInt(0x00ff_ffff))));
const preserved: u32 = std.mem.bytesToValue(u32, map) & 0xff00_0000;
std.mem.bytesAsValue(u32, map).* = preserved | depth;
},
.r16_sfloat => std.mem.bytesAsValue(f16, map).* = @floatCast(color[0]),
.r32_sint,
+6 -3
View File
@@ -72,9 +72,12 @@ pub fn shaderInvocation(
rt.callEntryPoint(allocator, entry) catch |err| switch (err) {
// Some errors can be safely ignored
SpvRuntimeError.OutOfBounds,
SpvRuntimeError.Killed,
=> {},
SpvRuntimeError.OutOfBounds => {},
SpvRuntimeError.Killed => {
try rt.flushDescriptorSets(allocator);
freeOwnedInputs(allocator, fragment_inputs);
return undefined; // FIXME
},
else => return err,
};
+11 -1
View File
@@ -173,6 +173,16 @@ inline fn run(data: RunData) !void {
};
}
try common.writeToTargets(outputs, data.draw_call, data.color_attachment_access, data.depth_attachment_access, data.stencil_attachment_access, true, @intCast(pixel_x), @intCast(pixel_y), z);
try common.writeToTargets(
outputs,
data.draw_call,
data.color_attachment_access,
data.depth_attachment_access,
data.stencil_attachment_access,
true,
@intCast(pixel_x),
@intCast(pixel_y),
z,
);
}
}
+22 -9
View File
@@ -98,13 +98,7 @@ fn updateStencilValue(stencil: *RenderTargetAccess, offset: usize, state: vk.Ste
blitter.writeInt4(@splat(new_value), stencil.base[offset..], stencil.format);
}
pub fn stencilTestAndUpdate(
stencil: *RenderTargetAccess,
x: usize,
y: usize,
state: vk.StencilOpState,
depth_passed: ?bool,
) bool {
pub fn stencilTestAndUpdate(stencil: *RenderTargetAccess, x: usize, y: usize, state: vk.StencilOpState, depth_passed: ?bool) bool {
const offset = targetOffset(stencil.*, x, y) orelse return false;
const current = blitter.readInt4(stencil.base[offset..], stencil.format)[0] & std.math.maxInt(u8);
const reference = state.reference & std.math.maxInt(u8);
@@ -132,15 +126,27 @@ fn stencilTest(stencil: *RenderTargetAccess, offset: usize, state: vk.StencilOpS
return compare(u32, state.compare_op, reference & compare_mask, current & compare_mask);
}
fn quantizeDepthForFormat(format: vk.Format, z: f32) f32 {
const clamped = std.math.clamp(z, 0.0, 1.0);
return switch (format) {
.d16_unorm => @as(f32, @floatFromInt(@as(u16, @intFromFloat(@round(clamped * std.math.maxInt(u16)))))) / std.math.maxInt(u16),
.x8_d24_unorm_pack32,
.d24_unorm_s8_uint,
=> @as(f32, @floatFromInt(@as(u32, @intFromFloat(@round(clamped * @as(f32, @floatFromInt(0x00ff_ffff))))))) / @as(f32, @floatFromInt(0x00ff_ffff)),
else => z,
};
}
pub fn depthTestAndUpdate(depth: *RenderTargetAccess, x: usize, y: usize, z: f32, state: vk.PipelineDepthStencilStateCreateInfo) bool {
if (state.depth_test_enable == .false)
return true;
const offset = targetOffset(depth.*, x, y) orelse return false;
const reference = quantizeDepthForFormat(depth.format, z);
const depth_value = blitter.readFloat4(depth.base[offset..], depth.format);
const passed = compare(f32, state.depth_compare_op, z, depth_value[0]);
const passed = compare(f32, state.depth_compare_op, reference, depth_value[0]);
if (passed and state.depth_write_enable == .true)
blitter.writeFloat4(zm.f32x4s(z), depth.base[offset..], depth.format);
blitter.writeFloat4(zm.f32x4s(reference), depth.base[offset..], depth.format);
return passed;
}
@@ -312,6 +318,9 @@ pub fn writeToTargets(
const io = draw_call.renderer.device.interface.io();
const depth_stencil_state = draw_call.renderer.state.pipeline.?.interface.mode.graphics.depth_stencil;
if (x >= draw_call.framebuffer.interface.width or y >= draw_call.framebuffer.interface.height)
return;
var stencil_state: ?vk.StencilOpState = null;
var stencil_offset: ?usize = null;
if (stencil_attachment_access) |stencil| {
@@ -361,6 +370,10 @@ pub fn writeToTargets(
}
}
for (draw_call.renderer.active_occlusion_queries.items) |active| {
try active.pool.addSamples(active.query, 1);
}
for (color_attachment_access, 0..) |maybe_color, location| {
const color = maybe_color orelse continue;
const color_offset = targetOffset(color, x, y) orelse continue;
+11 -1
View File
@@ -191,7 +191,17 @@ inline fn run(data: RunData) !void {
};
}
try common.writeToTargets(outputs, data.draw_call, data.color_attachment_access, data.depth_attachment_access, data.stencil_attachment_access, data.front_face, @intCast(x), @intCast(y), z);
try common.writeToTargets(
outputs,
data.draw_call,
data.color_attachment_access,
data.depth_attachment_access,
data.stencil_attachment_access,
data.front_face,
@intCast(x),
@intCast(y),
z,
);
}
}
}
+14 -35
View File
@@ -39,16 +39,18 @@ pub fn runWrapper(data: RunData) void {
inline fn run(data: RunData) !void {
const shader = data.pipeline.stages.getPtrAssertContains(.vertex);
const rt = &shader.runtimes[data.batch_id].rt;
const runtime = &shader.runtimes[data.batch_id];
const mutex = &runtime.mutex;
const rt = &runtime.rt;
const io = data.draw_call.renderer.device.interface.io();
mutex.lock(io) catch return VkError.DeviceLost;
defer mutex.unlock(io);
const entry = try rt.getEntryPointByName(shader.entry);
var invocation_index: usize = data.batch_id;
while (invocation_index < data.vertex_count) : (invocation_index += data.batch_size) {
const io = data.draw_call.renderer.device.interface.io();
data.draw_call.allocator_mutex.lock(io) catch return VkError.DeviceLost;
defer data.draw_call.allocator_mutex.unlock(io);
rt.resetInvocation(data.allocator);
try rt.populatePushConstants(data.draw_call.renderer.state.push_constant_blob[0..]);
@@ -78,9 +80,11 @@ inline fn run(data: RunData) !void {
rt.callEntryPoint(data.allocator, entry) catch |err| switch (err) {
// Some errors can be safely ignored
SpvRuntimeError.OutOfBounds,
SpvRuntimeError.Killed,
=> {},
SpvRuntimeError.OutOfBounds => {},
SpvRuntimeError.Killed => {
try rt.flushDescriptorSets(data.allocator);
return;
},
else => return err,
};
@@ -95,7 +99,7 @@ inline fn run(data: RunData) !void {
};
const memory_size = try rt.getResultMemorySize(result_word);
output.outputs[location][component] = .{
.interpolation_type = if (rt.hasResultDecoration(result_word, .Flat) or resultIsInteger(rt, result_word)) .flat else .smooth, // TODO : handle noperspective
.interpolation_type = if (rt.hasResultDecoration(result_word, .Flat) or rt.resultIsInteger(result_word)) .flat else .smooth, // TODO : handle noperspective
.blob = data.allocator.alloc(u8, memory_size + INTERFACE_BLOB_PADDING) catch return VkError.OutOfDeviceMemory,
.size = memory_size,
};
@@ -116,32 +120,7 @@ fn setupBuiltins(rt: *spv.Runtime, vertex_index: usize, instance_index: usize) !
try rt.writeBuiltIn(std.mem.asBytes(&instance_index_u32), .InstanceIndex);
}
fn resultIsInteger(rt: *spv.Runtime, result_word: spv.SpvWord) bool {
const value = rt.results[result_word].getConstValue() catch return false;
return switch (value.*) {
.Int,
.Vector2i32,
.Vector3i32,
.Vector4i32,
.Vector2u32,
.Vector3u32,
.Vector4u32,
=> true,
.Vector => |lanes| lanes.len != 0 and switch (lanes[0]) {
.Int => true,
else => false,
},
else => false,
};
}
fn writeVertexInput(
rt: *spv.Runtime,
allocator: std.mem.Allocator,
raw_input: []const u8,
format: vk.Format,
location: u32,
) !void {
fn writeVertexInput(rt: *spv.Runtime, allocator: std.mem.Allocator, raw_input: []const u8, format: vk.Format, location: u32) !void {
var has_split_components = false;
for (1..4) |component| {
_ = rt.getResultByLocationComponent(location, @intCast(component), .input) catch |err| switch (err) {