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
+21
View File
@@ -14,6 +14,7 @@ const Event = @import("Event.zig");
const Framebuffer = @import("Framebuffer.zig");
const Image = @import("Image.zig");
const Pipeline = @import("Pipeline.zig");
const QueryPool = @import("QueryPool.zig");
const RenderPass = @import("RenderPass.zig");
const State = enum {
@@ -39,6 +40,7 @@ dispatch_table: *const DispatchTable,
pub const DispatchTable = struct {
begin: *const fn (*Self, *const vk.CommandBufferBeginInfo) VkError!void,
beginQuery: *const fn (*Self, *QueryPool, u32, vk.QueryControlFlags) VkError!void,
beginRenderPass: *const fn (*Self, *RenderPass, *Framebuffer, vk.Rect2D, ?[]const vk.ClearValue) VkError!void,
bindDescriptorSets: *const fn (*Self, vk.PipelineBindPoint, u32, [lib.VULKAN_MAX_DESCRIPTOR_SETS]?*DescriptorSet, []const u32) VkError!void,
bindPipeline: *const fn (*Self, vk.PipelineBindPoint, *Pipeline) VkError!void,
@@ -52,6 +54,7 @@ pub const DispatchTable = struct {
copyBufferToImage: *const fn (*Self, *Buffer, *Image, vk.ImageLayout, []const vk.BufferImageCopy) VkError!void,
copyImage: *const fn (*Self, *Image, vk.ImageLayout, *Image, vk.ImageLayout, []const vk.ImageCopy) VkError!void,
copyImageToBuffer: *const fn (*Self, *Image, vk.ImageLayout, *Buffer, []const vk.BufferImageCopy) VkError!void,
copyQueryPoolResults: *const fn (*Self, *QueryPool, u32, u32, *Buffer, vk.DeviceSize, vk.DeviceSize, vk.QueryResultFlags) VkError!void,
dispatch: *const fn (*Self, u32, u32, u32) VkError!void,
dispatchIndirect: *const fn (*Self, *Buffer, vk.DeviceSize) VkError!void,
draw: *const fn (*Self, usize, usize, usize, usize) VkError!void,
@@ -59,6 +62,7 @@ pub const DispatchTable = struct {
drawIndexedIndirect: *const fn (*Self, *Buffer, usize, usize, usize) VkError!void,
drawIndirect: *const fn (*Self, *Buffer, usize, usize, usize) VkError!void,
end: *const fn (*Self) VkError!void,
endQuery: *const fn (*Self, *QueryPool, u32) VkError!void,
endRenderPass: *const fn (*Self) VkError!void,
executeCommands: *const fn (*Self, *Self) VkError!void,
fillBuffer: *const fn (*Self, *Buffer, vk.DeviceSize, vk.DeviceSize, u32) VkError!void,
@@ -66,6 +70,7 @@ pub const DispatchTable = struct {
pipelineBarrier: *const fn (*Self, vk.PipelineStageFlags, vk.PipelineStageFlags, vk.DependencyFlags, []const vk.MemoryBarrier, []const vk.BufferMemoryBarrier, []const vk.ImageMemoryBarrier) VkError!void,
pushConstants: *const fn (*Self, vk.ShaderStageFlags, u32, []const u8) VkError!void,
reset: *const fn (*Self, vk.CommandBufferResetFlags) VkError!void,
resetQueryPool: *const fn (*Self, *QueryPool, u32, u32) VkError!void,
resetEvent: *const fn (*Self, *Event, vk.PipelineStageFlags) VkError!void,
resolveImage: *const fn (*Self, *Image, vk.ImageLayout, *Image, vk.ImageLayout, vk.ImageResolve) VkError!void,
setEvent: *const fn (*Self, *Event, vk.PipelineStageFlags) VkError!void,
@@ -165,6 +170,10 @@ pub inline fn beginRenderPass(self: *Self, render_pass: *RenderPass, framebuffer
try self.dispatch_table.beginRenderPass(self, render_pass, framebuffer, render_area, clear_values);
}
pub inline fn beginQuery(self: *Self, pool: *QueryPool, query: u32, flags: vk.QueryControlFlags) VkError!void {
try self.dispatch_table.beginQuery(self, pool, query, flags);
}
pub fn bindDescriptorSets(self: *Self, bind_point: vk.PipelineBindPoint, first_set: u32, sets: []const vk.DescriptorSet, dynamic_offsets: []const u32) VkError!void {
if (sets.len > lib.VULKAN_MAX_DESCRIPTOR_SETS or first_set > lib.VULKAN_MAX_DESCRIPTOR_SETS or first_set + sets.len > lib.VULKAN_MAX_DESCRIPTOR_SETS)
return VkError.ValidationFailed;
@@ -224,6 +233,10 @@ pub inline fn copyImageToBuffer(self: *Self, src: *Image, src_layout: vk.ImageLa
try self.dispatch_table.copyImageToBuffer(self, src, src_layout, dst, regions);
}
pub inline fn copyQueryPoolResults(self: *Self, pool: *QueryPool, first: u32, count: u32, dst: *Buffer, offset: vk.DeviceSize, stride: vk.DeviceSize, flags: vk.QueryResultFlags) VkError!void {
try self.dispatch_table.copyQueryPoolResults(self, pool, first, count, dst, offset, stride, flags);
}
pub inline fn dispatch(self: *Self, group_count_x: u32, group_count_y: u32, group_count_z: u32) VkError!void {
try self.dispatch_table.dispatch(self, group_count_x, group_count_y, group_count_z);
}
@@ -252,6 +265,10 @@ pub inline fn endRenderPass(self: *Self) VkError!void {
try self.dispatch_table.endRenderPass(self);
}
pub inline fn endQuery(self: *Self, pool: *QueryPool, query: u32) VkError!void {
try self.dispatch_table.endQuery(self, pool, query);
}
pub inline fn executeCommands(self: *Self, commands: *Self) VkError!void {
try self.dispatch_table.executeCommands(self, commands);
}
@@ -280,6 +297,10 @@ pub inline fn pushConstants(self: *Self, stages: vk.ShaderStageFlags, offset: u3
try self.dispatch_table.pushConstants(self, stages, offset, blob);
}
pub inline fn resetQueryPool(self: *Self, pool: *QueryPool, first: u32, count: u32) VkError!void {
try self.dispatch_table.resetQueryPool(self, pool, first, count);
}
pub inline fn resetEvent(self: *Self, event: *Event, stage: vk.PipelineStageFlags) VkError!void {
try self.dispatch_table.resetEvent(self, event, stage);
}
+112 -2
View File
@@ -11,18 +11,32 @@ const Self = @This();
pub const ObjectType: vk.ObjectType = .query_pool;
owner: *Device,
query_type: vk.QueryType,
queries: []Query,
vtable: *const VTable,
const Query = struct {
value: std.atomic.Value(u64) = std.atomic.Value(u64).init(0),
available: bool = false,
active: bool = false,
};
pub const VTable = struct {
destroy: *const fn (*Self, std.mem.Allocator) void,
};
pub fn init(device: *Device, allocator: std.mem.Allocator, info: *const vk.QueryPoolCreateInfo) VkError!Self {
_ = allocator;
_ = info;
const queries = allocator.alloc(Query, info.query_count) catch return VkError.OutOfHostMemory;
errdefer allocator.free(queries);
for (queries) |*query| {
query.* = .{};
}
return .{
.owner = device,
.query_type = info.query_type,
.queries = queries,
.vtable = undefined,
};
}
@@ -30,3 +44,99 @@ pub fn init(device: *Device, allocator: std.mem.Allocator, info: *const vk.Query
pub inline fn destroy(self: *Self, allocator: std.mem.Allocator) void {
self.vtable.destroy(self, allocator);
}
pub fn reset(self: *Self, first: u32, count: u32) VkError!void {
const range = try self.queryRange(first, count);
for (range) |*query| {
query.value.store(0, .seq_cst);
query.available = false;
query.active = false;
}
}
pub fn begin(self: *Self, query: u32) VkError!void {
if (self.query_type != .occlusion)
return VkError.FeatureNotPresent;
const q = try self.queryAt(query);
q.value.store(0, .seq_cst);
q.available = false;
q.active = true;
}
pub fn end(self: *Self, query: u32) VkError!void {
const q = try self.queryAt(query);
q.active = false;
q.available = true;
}
pub fn addSamples(self: *Self, query: u32, samples: u64) VkError!void {
const q = try self.queryAt(query);
if (q.active)
_ = q.value.fetchAdd(samples, .seq_cst);
}
pub fn writeResults(self: *Self, first: u32, count: u32, bytes: []u8, stride: vk.DeviceSize, flags: vk.QueryResultFlags) VkError!void {
return self.writeResultsImpl(first, count, bytes, stride, flags, true);
}
pub fn copyResults(self: *Self, first: u32, count: u32, bytes: []u8, stride: vk.DeviceSize, flags: vk.QueryResultFlags) VkError!void {
return self.writeResultsImpl(first, count, bytes, stride, flags, false);
}
fn writeResultsImpl(self: *Self, first: u32, count: u32, bytes: []u8, stride: vk.DeviceSize, flags: vk.QueryResultFlags, report_not_ready: bool) VkError!void {
_ = try self.queryRange(first, count);
if (count == 0)
return;
const value_size: usize = if (flags.@"64_bit") 8 else 4;
const item_size = value_size * (1 + @as(usize, @intFromBool(flags.with_availability_bit)));
if (count > 1 and stride < item_size)
return VkError.ValidationFailed;
var not_ready = false;
for (0..count) |i| {
const query = &self.queries[first + i];
if (flags.wait_bit) {
while (!query.available) {
std.Thread.yield() catch std.atomic.spinLoopHint();
}
}
const offset: usize = @intCast(@as(vk.DeviceSize, @intCast(i)) * stride);
if (offset + item_size > bytes.len)
return VkError.Incomplete;
if (query.available or flags.partial_bit) {
writeInt(bytes[offset..][0..value_size], query.value.load(.seq_cst), flags);
} else {
not_ready = true;
}
if (flags.with_availability_bit) {
writeInt(bytes[offset + value_size ..][0..value_size], @intFromBool(query.available), flags);
}
}
if (not_ready and report_not_ready)
return VkError.NotReady;
}
fn writeInt(bytes: []u8, value: u64, flags: vk.QueryResultFlags) void {
if (flags.@"64_bit") {
std.mem.writeInt(u64, bytes[0..8], value, .little);
} else {
std.mem.writeInt(u32, bytes[0..4], @truncate(value), .little);
}
}
fn queryAt(self: *Self, query: u32) VkError!*Query {
if (query >= self.queries.len)
return VkError.ValidationFailed;
return &self.queries[query];
}
fn queryRange(self: *Self, first: u32, count: u32) VkError![]Query {
if (first > self.queries.len or count > self.queries.len - first)
return VkError.ValidationFailed;
return self.queries[first .. first + count];
}
+13 -43
View File
@@ -1451,18 +1451,11 @@ pub export fn apeGetQueryPoolResults(
defer entryPointEndLogTrace();
Dispatchable(Device).checkHandleValidity(p_device) catch |err| return toVkResult(err);
const pool = NonDispatchable(QueryPool).fromHandleObject(p_pool) catch |err| return toVkResult(err);
notImplementedWarning();
_ = p_pool;
_ = first;
_ = count;
_ = size;
_ = data;
_ = stride;
_ = flags;
return .error_unknown;
const bytes = @as([*]u8, @ptrCast(data))[0..size];
pool.writeResults(first, count, bytes, stride, flags) catch |err| return toVkResult(err);
return .success;
}
pub export fn apeGetRenderAreaGranularity(p_device: vk.Device, p_pass: vk.RenderPass, granularity: *vk.Extent2D) callconv(vk.vulkan_call_conv) void {
@@ -1630,13 +1623,8 @@ pub export fn apeCmdBeginQuery(p_cmd: vk.CommandBuffer, p_pool: vk.QueryPool, qu
defer entryPointEndLogTrace();
const cmd = Dispatchable(CommandBuffer).fromHandleObject(p_cmd) catch |err| return errorLogger(err);
notImplementedWarning();
_ = cmd;
_ = p_pool;
_ = query;
_ = flags;
const pool = NonDispatchable(QueryPool).fromHandleObject(p_pool) catch |err| return errorLogger(err);
cmd.beginQuery(pool, query, flags) catch |err| return errorLogger(err);
}
pub export fn apeCmdBeginRenderPass(p_cmd: vk.CommandBuffer, info: *const vk.RenderPassBeginInfo, contents: vk.SubpassContents) callconv(vk.vulkan_call_conv) void {
@@ -1799,18 +1787,9 @@ pub export fn apeCmdCopyQueryPoolResults(p_cmd: vk.CommandBuffer, p_pool: vk.Que
defer entryPointEndLogTrace();
const cmd = Dispatchable(CommandBuffer).fromHandleObject(p_cmd) catch |err| return errorLogger(err);
const dst = NonDispatchable(Image).fromHandleObject(p_dst) catch |err| return errorLogger(err);
notImplementedWarning();
_ = cmd;
_ = p_pool;
_ = first;
_ = count;
_ = dst;
_ = offset;
_ = stride;
_ = flags;
const pool = NonDispatchable(QueryPool).fromHandleObject(p_pool) catch |err| return errorLogger(err);
const dst = NonDispatchable(Buffer).fromHandleObject(p_dst) catch |err| return errorLogger(err);
cmd.copyQueryPoolResults(pool, first, count, dst, offset, stride, flags) catch |err| return errorLogger(err);
}
pub export fn apeCmdDispatch(p_cmd: vk.CommandBuffer, group_count_x: u32, group_count_y: u32, group_count_z: u32) callconv(vk.vulkan_call_conv) void {
@@ -1869,12 +1848,8 @@ pub export fn apeCmdEndQuery(p_cmd: vk.CommandBuffer, p_pool: vk.QueryPool, quer
defer entryPointEndLogTrace();
const cmd = Dispatchable(CommandBuffer).fromHandleObject(p_cmd) catch |err| return errorLogger(err);
notImplementedWarning();
_ = cmd;
_ = p_pool;
_ = query;
const pool = NonDispatchable(QueryPool).fromHandleObject(p_pool) catch |err| return errorLogger(err);
cmd.endQuery(pool, query) catch |err| return errorLogger(err);
}
pub export fn apeCmdEndRenderPass(p_cmd: vk.CommandBuffer) callconv(vk.vulkan_call_conv) void {
@@ -1954,13 +1929,8 @@ pub export fn apeCmdResetQueryPool(p_cmd: vk.CommandBuffer, p_pool: vk.QueryPool
defer entryPointEndLogTrace();
const cmd = Dispatchable(CommandBuffer).fromHandleObject(p_cmd) catch |err| return errorLogger(err);
notImplementedWarning();
_ = cmd;
_ = p_pool;
_ = first;
_ = count;
const pool = NonDispatchable(QueryPool).fromHandleObject(p_pool) catch |err| return errorLogger(err);
cmd.resetQueryPool(pool, first, count) catch |err| return errorLogger(err);
}
pub export fn apeCmdResetEvent(p_cmd: vk.CommandBuffer, p_event: vk.Event, stage_mask: vk.PipelineStageFlags) callconv(vk.vulkan_call_conv) void {