//! 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, });