232 lines
7.3 KiB
Zig
232 lines
7.3 KiB
Zig
//! The point here is to select _a_ physical device and create a logical device around it.
|
|
|
|
const std = @import("std");
|
|
const builtin = @import("builtin");
|
|
|
|
const vk = @import("vk");
|
|
const c = @import("../c.zig");
|
|
|
|
const Instance = @import("Instance.zig");
|
|
const Window = @import("Window.zig");
|
|
|
|
const Self = @This();
|
|
|
|
const required_extensions: []const [*:0]const u8 = &.{
|
|
vk.extension_info.khr_swapchain.name,
|
|
vk.extension_info.khr_dynamic_rendering.name,
|
|
};
|
|
|
|
const preferred_surface_format: vk.SurfaceFormatKHR = .{
|
|
.color_space = .srgb_nonlinear_khr,
|
|
.format = .r8g8b8a8_sint,
|
|
};
|
|
|
|
const preferred_present_mode: vk.PresentModeKHR = .mailbox_khr;
|
|
|
|
dev: vk.Device,
|
|
pdev: vk.PhysicalDevice,
|
|
vkd: Wrapper,
|
|
|
|
inst: *const Instance,
|
|
win: *const Window,
|
|
|
|
format: vk.SurfaceFormatKHR,
|
|
mode: vk.PresentModeKHR,
|
|
family: u32,
|
|
queue: vk.Queue,
|
|
|
|
pool: vk.CommandPool,
|
|
|
|
pub fn init(
|
|
ally: std.mem.Allocator,
|
|
inst: *const Instance,
|
|
win: *const Window,
|
|
) !Self {
|
|
var pdev_count: u32 = undefined;
|
|
_ = try inst.vki.enumeratePhysicalDevices(inst.ref, &pdev_count, null);
|
|
const pdevs = try ally.alloc(vk.PhysicalDevice, pdev_count);
|
|
defer ally.free(pdevs);
|
|
_ = try inst.vki.enumeratePhysicalDevices(inst.ref, &pdev_count, pdevs.ptr);
|
|
|
|
for (pdevs) |pdev| {
|
|
return wrap(ally, inst, win, pdev) catch continue;
|
|
}
|
|
|
|
return error.NoSuitableDevice;
|
|
}
|
|
|
|
pub fn wrap(
|
|
ally: std.mem.Allocator,
|
|
inst: *const Instance,
|
|
win: *const Window,
|
|
pdev: vk.PhysicalDevice,
|
|
) !Self {
|
|
const props = inst.vki.getPhysicalDeviceProperties(pdev);
|
|
if (props.device_type != .discrete_gpu) return error.NotDiscrete;
|
|
|
|
var format_count: u32 = undefined;
|
|
_ = try inst.vki.getPhysicalDeviceSurfaceFormatsKHR(pdev, win.surface, &format_count, null);
|
|
if (format_count == 0) return error.NoSurfaceFormat;
|
|
const formats = try ally.alloc(vk.SurfaceFormatKHR, format_count);
|
|
defer ally.free(formats);
|
|
_ = try inst.vki.getPhysicalDeviceSurfaceFormatsKHR(pdev, win.surface, &format_count, formats.ptr);
|
|
|
|
const format = for (formats) |f| {
|
|
if (std.meta.eql(f, preferred_surface_format))
|
|
break f;
|
|
} else formats[0];
|
|
|
|
var mode_count: u32 = undefined;
|
|
_ = try inst.vki.getPhysicalDeviceSurfacePresentModesKHR(pdev, win.surface, &mode_count, null);
|
|
if (mode_count == 0) return error.NoSurfaceMode;
|
|
const modes = try ally.alloc(vk.PresentModeKHR, mode_count);
|
|
defer ally.free(modes);
|
|
_ = try inst.vki.getPhysicalDeviceSurfacePresentModesKHR(pdev, win.surface, &mode_count, modes.ptr);
|
|
|
|
const mode = for (modes) |m| {
|
|
if (std.meta.eql(m, preferred_present_mode))
|
|
break m;
|
|
} else modes[0];
|
|
|
|
var ext_count: u32 = undefined;
|
|
_ = try inst.vki.enumerateDeviceExtensionProperties(pdev, null, &ext_count, null);
|
|
const exts = try ally.alloc(vk.ExtensionProperties, ext_count);
|
|
defer ally.free(exts);
|
|
_ = try inst.vki.enumerateDeviceExtensionProperties(pdev, null, &ext_count, exts.ptr);
|
|
|
|
for (required_extensions) |name| {
|
|
for (exts) |ext| {
|
|
if (std.mem.eql(
|
|
u8,
|
|
std.mem.span(name),
|
|
std.mem.sliceTo(&ext.extension_name, 0),
|
|
)) {
|
|
break;
|
|
}
|
|
} else {
|
|
return error.MissingRequiredExtension;
|
|
}
|
|
}
|
|
|
|
var family_count: u32 = undefined;
|
|
inst.vki.getPhysicalDeviceQueueFamilyProperties(pdev, &family_count, null);
|
|
const families = try ally.alloc(vk.QueueFamilyProperties, family_count);
|
|
defer ally.free(families);
|
|
inst.vki.getPhysicalDeviceQueueFamilyProperties(pdev, &family_count, families.ptr);
|
|
|
|
// just find one family that does graphics and present, so we can use exclusive sharing
|
|
// on the swapchain. apparently most hardware supports this. logic for queue allocation
|
|
// and swapchain creation is so much simpler this way. swapchain creation needs to know
|
|
// the list of queue family indices which will have access to the images, and there's a
|
|
// performance penalty to allow concurrent access to multiple queue families.
|
|
//
|
|
// multiple _queues_ may have exclusive access, but only if they're in the smae family.
|
|
|
|
const family: u32 = for (families, 0..) |family, idx| {
|
|
const graphics = family.queue_flags.graphics_bit;
|
|
const present = try inst.vki.getPhysicalDeviceSurfaceSupportKHR(pdev, @intCast(idx), win.surface) == vk.TRUE;
|
|
if (graphics and present) break @intCast(idx);
|
|
} else {
|
|
return error.NoSuitableQueueFamily;
|
|
};
|
|
|
|
const qci: []const vk.DeviceQueueCreateInfo = &.{
|
|
vk.DeviceQueueCreateInfo{
|
|
.queue_family_index = family,
|
|
.queue_count = 1,
|
|
.p_queue_priorities = &[_]f32{1.0},
|
|
},
|
|
};
|
|
|
|
const dev = try inst.vki.createDevice(pdev, &.{
|
|
.queue_create_info_count = @intCast(qci.len),
|
|
.p_queue_create_infos = qci.ptr,
|
|
.enabled_extension_count = @intCast(required_extensions.len),
|
|
.pp_enabled_extension_names = required_extensions.ptr,
|
|
.p_next = &vk.PhysicalDeviceDynamicRenderingFeaturesKHR{
|
|
.dynamic_rendering = vk.TRUE,
|
|
},
|
|
}, null);
|
|
const vkd = try Wrapper.load(dev, inst.vki.dispatch.vkGetDeviceProcAddr);
|
|
errdefer vkd.destroyDevice(dev, null);
|
|
|
|
const pool = try vkd.createCommandPool(dev, &.{
|
|
.queue_family_index = family,
|
|
}, null);
|
|
errdefer vkd.destroyCommandPool(dev, pool, null);
|
|
|
|
const queue = vkd.getDeviceQueue(dev, family, 0);
|
|
|
|
return .{
|
|
.dev = dev,
|
|
.pdev = pdev,
|
|
.vkd = vkd,
|
|
.inst = inst,
|
|
.win = win,
|
|
.format = format,
|
|
.mode = mode,
|
|
.pool = pool,
|
|
.family = family,
|
|
.queue = queue,
|
|
};
|
|
}
|
|
|
|
pub fn deinit(self: Self) void {
|
|
self.vkd.destroyCommandPool(self.dev, self.pool, null);
|
|
self.vkd.destroyDevice(self.dev, null);
|
|
}
|
|
|
|
pub const Wrapper = vk.DeviceWrapper(.{
|
|
.destroyDevice = true,
|
|
.getDeviceQueue = true,
|
|
.createSemaphore = true,
|
|
.createFence = true,
|
|
.createImageView = true,
|
|
.destroyImageView = true,
|
|
.destroySemaphore = true,
|
|
.destroyFence = true,
|
|
.getSwapchainImagesKHR = true,
|
|
.createSwapchainKHR = true,
|
|
.destroySwapchainKHR = true,
|
|
.acquireNextImageKHR = true,
|
|
.deviceWaitIdle = true,
|
|
.waitForFences = true,
|
|
.resetFences = true,
|
|
.queueSubmit = true,
|
|
.queuePresentKHR = true,
|
|
.createCommandPool = true,
|
|
.destroyCommandPool = true,
|
|
.allocateCommandBuffers = true,
|
|
.freeCommandBuffers = true,
|
|
.queueWaitIdle = true,
|
|
.createShaderModule = true,
|
|
.destroyShaderModule = true,
|
|
.createPipelineLayout = true,
|
|
.destroyPipelineLayout = true,
|
|
.createGraphicsPipelines = true,
|
|
.destroyPipeline = true,
|
|
.beginCommandBuffer = true,
|
|
.endCommandBuffer = true,
|
|
.allocateMemory = true,
|
|
.freeMemory = true,
|
|
.createBuffer = true,
|
|
.destroyBuffer = true,
|
|
.getBufferMemoryRequirements = true,
|
|
.mapMemory = true,
|
|
.unmapMemory = true,
|
|
.bindBufferMemory = true,
|
|
.cmdBeginRenderPass = true,
|
|
.cmdEndRenderPass = true,
|
|
.cmdBindPipeline = true,
|
|
.cmdDraw = true,
|
|
.cmdDrawIndexed = true,
|
|
.cmdSetViewport = true,
|
|
.cmdSetScissor = true,
|
|
.cmdBindVertexBuffers = true,
|
|
.cmdBindIndexBuffer = true,
|
|
.cmdCopyBuffer = true,
|
|
.cmdBeginRenderingKHR = true,
|
|
.cmdEndRenderingKHR = true,
|
|
.cmdPipelineBarrier = true,
|
|
});
|