Files
VulkanDriver/src/vulkan/QueryPool.zig
T
kbz_8 b7629ef4d7
Mirror Gitea refs to GitHub / mirror (push) Successful in 11s
Test / build_and_test (push) Successful in 5m47s
Build / build (push) Successful in 7m13s
[Soft] fixing 32bits support because I have free will
2026-07-30 02:21:36 +02:00

195 lines
5.8 KiB
Zig

const std = @import("std");
const vk = @import("vulkan");
const VkError = @import("error_set.zig").VkError;
const Device = @import("Device.zig");
const Self = @This();
pub const ObjectType: vk.ObjectType = .query_pool;
owner: *Device,
query_type: vk.QueryType,
queries: []Query,
vtable: *const VTable,
/// 32 bits platforms only supportss atomics up to 32 bits,
/// so serialize 64-bit query access with a 32-bit lock.
const LockedU64 = struct {
mutex: std.atomic.Value(u32) = std.atomic.Value(u32).init(0),
raw: u64,
fn init(value: u64) LockedU64 {
return .{ .raw = value };
}
fn lock(self: *LockedU64) void {
while (self.mutex.cmpxchgWeak(0, 1, .acquire, .monotonic) != null) {
std.atomic.spinLoopHint();
}
}
fn unlock(self: *LockedU64) void {
self.mutex.store(0, .release);
}
fn load(self: *LockedU64, comptime _: std.builtin.AtomicOrder) u64 {
self.lock();
defer self.unlock();
return self.raw;
}
fn store(self: *LockedU64, value: u64, comptime _: std.builtin.AtomicOrder) void {
self.lock();
defer self.unlock();
self.raw = value;
}
fn fetchAdd(self: *LockedU64, value: u64, comptime _: std.builtin.AtomicOrder) u64 {
self.lock();
defer self.unlock();
const previous = self.raw;
self.raw +%= value;
return previous;
}
};
const QueryValue = if (@bitSizeOf(usize) >= 64) std.atomic.Value(u64) else LockedU64;
const Query = struct {
value: QueryValue = QueryValue.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 {
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,
// SAFETY: the backend assigns the vtable before returning the query pool.
.vtable = undefined,
};
}
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 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)
_ = 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];
}