forked from vv/efemra
1
0
Fork 0
efemra/src/rendering/vulkan/queues.zig

335 lines
11 KiB
Zig

const std = @import("std");
const assert = @import("std").debug.assert;
const vk = @import("vulkan");
const settings = @import("settings.zig");
const Instance = @import("instance.zig").Instance;
const Device = @import("device.zig").Device;
const Window = @import("display.zig").Window;
const Renderer = @import("Renderer.zig");
const Command = @import("Command.zig");
const sync = @import("sync.zig");
// TODO memory
var gpa = std.heap.GeneralPurposeAllocator(.{}){};
const allocator = gpa.allocator();
const present_stages: vk.PipelineStageFlags = .{ .all_commands_bit = true, .top_of_pipe_bit = true, .bottom_of_pipe_bit = true };
const present_access: vk.AccessFlags = .{
.memory_read_bit = true,
.memory_write_bit = true,
};
const graphics_stages: vk.PipelineStageFlags = .{
.all_commands_bit = true,
.all_graphics_bit = true,
.top_of_pipe_bit = true,
.bottom_of_pipe_bit = true,
.draw_indirect_bit = true,
.vertex_input_bit = true,
.vertex_shader_bit = true,
.tessellation_control_shader_bit = true,
.tessellation_evaluation_shader_bit = true,
.geometry_shader_bit = true,
.fragment_shader_bit = true,
.early_fragment_tests_bit = true,
.late_fragment_tests_bit = true,
.color_attachment_output_bit = true,
.conditional_rendering_bit_ext = true,
.task_shader_bit_nv = true,
.mesh_shader_bit_nv = true,
};
const graphics_access: vk.AccessFlags = .{
.memory_read_bit = true,
.memory_write_bit = true,
.indirect_command_read_bit = true,
.index_read_bit = true,
.vertex_attribute_read_bit = true,
.uniform_read_bit = true,
.input_attachment_read_bit = true,
.shader_read_bit = true,
.shader_write_bit = true,
.color_attachment_read_bit = true,
.color_attachment_write_bit = true,
.conditional_rendering_read_bit_ext = true,
.acceleration_structure_read_bit_khr = true,
.acceleration_structure_write_bit_khr = true,
};
const compute_stages: vk.PipelineStageFlags = .{
.all_commands_bit = true,
.top_of_pipe_bit = true,
.bottom_of_pipe_bit = true,
.compute_shader_bit = true,
.acceleration_structure_build_bit_khr = true,
.ray_tracing_shader_bit_khr = true,
};
const compute_access: vk.AccessFlags = .{
.memory_read_bit = true,
.memory_write_bit = true,
.indirect_command_read_bit = true,
.uniform_read_bit = true,
.shader_read_bit = true,
.shader_write_bit = true,
.acceleration_structure_read_bit_khr = true,
.acceleration_structure_write_bit_khr = true,
};
const transfer_stages: vk.PipelineStageFlags = .{
.all_commands_bit = true,
.top_of_pipe_bit = true,
.bottom_of_pipe_bit = true,
.transfer_bit = true,
.host_bit = true,
};
const transfer_access: vk.AccessFlags = .{
.memory_read_bit = true,
.memory_write_bit = true,
.host_read_bit = true,
.host_write_bit = true,
.transfer_read_bit = true,
.transfer_write_bit = true,
};
pub const QueueId = enum(u4) {
graphics,
compute,
transfer,
present,
pub const count = @typeInfo(@This()).Enum.fields.len;
};
pub const QueueFlags = packed struct {
graphics_bit: bool = false,
compute_bit: bool = false,
transfer_bit: bool = false,
present_bit: bool = false,
__reserved_bits_04_31: u28 = 0,
pub usingnamespace vk.FlagsMixin(@This(), vk.Flags);
};
var s_queues: [QueueId.count]Queue = undefined;
pub fn init(instance: *const Instance, device: *const Device, window: *const Window) !void {
const support = try Support.init(instance, device.physical_device, window);
defer support.deinit();
for (s_queues) |*queue, i| {
queue.* = try Queue.init(device, &support, i);
}
}
pub fn deinit(device: *const Device) void {
device.waitIdle() catch {};
for (s_queues) |queue| {
queue.deinit(device);
}
}
pub fn get(id: QueueId) *Queue {
return &s_queues[@enumToInt(id)];
}
pub const Support = struct {
const Self = @This();
families: [QueueId.count]?u32 = undefined,
indices: [QueueId.count]QueueId = undefined,
properties: []vk.QueueFamilyProperties = undefined,
pub fn init(instance: *const Instance, physical_device: vk.PhysicalDevice, window: *const Window) !Self {
var self = Self{};
var count: u32 = undefined;
instance.dispatch.getPhysicalDeviceQueueFamilyProperties(physical_device, &count, null);
self.properties = try allocator.alloc(vk.QueueFamilyProperties, count);
instance.dispatch.getPhysicalDeviceQueueFamilyProperties(physical_device, &count, self.properties.ptr);
self.families[@enumToInt(QueueId.graphics)] = selectGfxFamily(self.properties);
self.families[@enumToInt(QueueId.compute)] = selectCompFamily(self.properties);
self.families[@enumToInt(QueueId.transfer)] = selectXferFamily(self.properties);
self.families[@enumToInt(QueueId.present)] = try selectPresFamily(instance, physical_device, window.surface, self.properties);
// TODO: don't understand the purpose of 'indices' yet...
const choice_counts = try allocator.alloc(u32, count);
defer allocator.free(choice_counts);
for (self.families) |family, i| {
if (family) |f| {
const choice_count = choice_counts[@intCast(usize, f)];
self.indices[i] = @intToEnum(QueueId, choice_count);
// probably wrong
choice_counts[@intCast(usize, f)] = choice_count + 1;
}
}
return self;
}
pub fn deinit(self: *const Self) void {
allocator.free(self.properties);
}
fn selectGfxFamily(families: []vk.QueueFamilyProperties) ?u32 {
var index: ?u32 = null;
var score: i32 = 0;
for (families) |family, i| {
if (family.queue_count == 0) {
continue;
}
if (family.queue_flags.graphics_bit) {
var new_score: i32 = 0;
new_score += if (family.queue_flags.compute_bit) @as(i32, 1) else 0;
new_score += if (family.queue_flags.transfer_bit) @as(i32, 1) else 0;
if (new_score > score) {
score = new_score;
index = @intCast(u32, i);
}
}
}
return index;
}
fn selectCompFamily(families: []vk.QueueFamilyProperties) ?u32 {
var index: ?u32 = null;
var score: i32 = 0;
for (families) |family, i| {
if (family.queue_count == 0) {
continue;
}
if (family.queue_flags.compute_bit) {
var new_score: i32 = 0;
new_score += if (family.queue_flags.graphics_bit) @as(i32, 1) else 0;
new_score += if (family.queue_flags.transfer_bit) @as(i32, 1) else 0;
if (new_score > score) {
score = new_score;
index = @intCast(u32, i);
}
}
}
return index;
}
fn selectXferFamily(families: []vk.QueueFamilyProperties) ?u32 {
var index: ?u32 = null;
var score: i32 = 0;
for (families) |family, i| {
if (family.queue_count == 0) {
continue;
}
if (family.queue_flags.transfer_bit) {
var new_score: i32 = 0;
new_score += if (family.queue_flags.graphics_bit) @as(i32, 1) else 0;
new_score += if (family.queue_flags.compute_bit) @as(i32, 1) else 0;
if (new_score > score) {
score = new_score;
index = @intCast(u32, i);
}
}
}
return index;
}
fn selectPresFamily(instance: *const Instance, phdev: vk.PhysicalDevice, surf: vk.SurfaceKHR, families: []vk.QueueFamilyProperties) !?u32 {
var index: ?u32 = null;
var score: i32 = 0;
for (families) |family, i| {
const result = try instance.dispatch.getPhysicalDeviceSurfaceSupportKHR(phdev, @intCast(u32, i), surf);
const presentable = result != 0;
if (presentable) {
var new_score: i32 = 0;
new_score += if (family.queue_flags.graphics_bit) @as(i32, 1) else 0;
new_score += if (family.queue_flags.compute_bit) @as(i32, 1) else 0;
new_score += if (family.queue_flags.transfer_bit) @as(i32, 1) else 0;
if (new_score > score) {
score = new_score;
index = @intCast(u32, i);
}
}
}
return index;
}
};
pub const Queue = struct {
handle: vk.Queue = undefined,
family: u32 = 0,
index: QueueId = 0,
access_mask: vk.AccessFlags = .{},
stage_mask: vk.PipelineStageFlags = .{},
queueId: u4 = 0,
gfx: bool = false,
comp: bool = false,
xfer: bool = false,
pres: bool = false,
cmd_pool: Command.Pool = undefined,
cmds: [settings.cmds_per_queue]vk.CommandBuffer = undefined,
cmd_fences: [settings.cmds_per_queue]sync.Fence = undefined,
cmd_ids: [settings.cmds_per_queue]u32 = undefined,
head: u32 = 0,
tail: u32 = 0,
const Self = @This();
pub fn init(device: *const Device, support: *const Support, id: usize) !Self {
const family = support.families[id] orelse unreachable; // fix this silliness
const index = support.indices[id];
const handle = device.dispatch.getDeviceQueue(device.handle, family, @enumToInt(index));
var self = Self{
.family = family,
.index = index,
.handle = handle,
};
const pres_family = support.families[@enumToInt(QueueId.present)];
const queue_flags = support.properties[@enumToInt(QueueId.present)].queue_flags;
if (queue_flags.graphics_bit) {
self.gfx = true;
self.stage_mask = self.stage_mask.merge(graphics_stages);
self.access_mask = self.access_mask.merge(graphics_access);
}
if (queue_flags.compute_bit) {
self.comp = true;
self.stage_mask = self.stage_mask.merge(compute_stages);
self.access_mask = self.access_mask.merge(compute_access);
}
if (queue_flags.transfer_bit) {
self.xfer = true;
self.stage_mask = self.stage_mask.merge(transfer_stages);
self.access_mask = self.access_mask.merge(transfer_access);
}
if (family == pres_family) {
self.pres = true;
self.stage_mask = self.stage_mask.merge(present_stages);
self.access_mask = self.access_mask.merge(present_access);
}
assert(self.stage_mask.toInt() != 0);
assert(self.access_mask.toInt() != 0);
try Command.init(device, &self, @intToEnum(QueueId, id));
return self;
}
pub fn deinit(self: *const Self, device: *const Device) void {
Command.deinit(device, self);
}
};