const std = @import("std"); const builtin = @import("builtin"); const vk = @import("vk"); const nu = @import("../../nu.zig"); const Debug = @import("Debug.zig"); const config = nu.config.render; pub const versions: []const vk.ApiInfo = &.{ vk.features.version_1_0, vk.features.version_1_1, vk.features.version_1_2, vk.features.version_1_3, }; pub const instance_exts: []const vk.ApiInfo = if (config.use_debug_messenger) &.{ vk.extensions.ext_debug_utils, vk.extensions.khr_surface, } else &.{}; pub const device_exts: []const vk.ApiInfo = &.{ vk.extensions.khr_swapchain, vk.extensions.khr_dynamic_rendering, vk.extensions.khr_timeline_semaphore, }; pub const apis = versions ++ instance_exts ++ device_exts; pub const layers: []const [*:0]const u8 = if (config.use_debug_messenger) &.{ "VK_LAYER_KHRONOS_validation", } else &.{}; pub const BaseWrapper = vk.BaseWrapper(apis); pub const InstanceWrapper = vk.InstanceWrapper(apis); pub const DeviceWrapper = vk.DeviceWrapper(apis); pub const InstanceProxy = vk.InstanceProxy(apis); pub const DeviceProxy = vk.DeviceProxy(apis); pub const QueueProxy = vk.QueueProxy(apis); pub const CommandBufferProxy = vk.CommandBufferProxy(apis); var _iw: InstanceWrapper = undefined; pub const iw: *const InstanceWrapper = &_iw; var _dw: DeviceWrapper = undefined; pub const dw: *const DeviceWrapper = &_dw; var _B: BaseWrapper = undefined; pub const B: *const BaseWrapper = &_B; var _I: InstanceProxy = undefined; pub const I: *const InstanceProxy = &_I; var _D: DeviceProxy = undefined; pub const D: *const DeviceProxy = &_D; var _Q: QueueProxy = undefined; pub const Q: *const QueueProxy = &_Q; var _instance: vk.Instance = undefined; pub const instance: *const vk.Instance = &_instance; var _messenger: if (config.use_debug_messenger) vk.DebugUtilsMessengerEXT else void = undefined; pub const messenger: *const if (config.use_debug_messenger) vk.DebugUtilsMessengerEXT else void = &_messenger; var _surface: vk.SurfaceKHR = undefined; pub const surface: *const vk.SurfaceKHR = &_surface; var _pdevice: vk.PhysicalDevice = undefined; pub const pdevice: *const vk.PhysicalDevice = &_pdevice; var _device: vk.Device = undefined; pub const device: *const vk.Device = &_device; var _family: u32 = undefined; pub const family: *const u32 = &_family; var _queue: vk.Queue = undefined; pub const queue: *const vk.Queue = &_queue; pub fn init(alloc: std.mem.Allocator) !void { _B = try BaseWrapper.load(glfwGetInstanceProcAddress); _instance = try _create_instance(alloc); _iw = try InstanceWrapper.load(_instance, glfwGetInstanceProcAddress); errdefer _destroy_instance(); _I = InstanceProxy.init(_instance, iw); if (config.use_debug_messenger) _messenger = try _create_messenger(); errdefer if (config.use_debug_messenger) _destroy_messenger(); _surface = try _create_surface(); errdefer _destroy_surface(); _pdevice = try _select_pdevice(alloc); _family = try _select_queue_family_index(alloc); // only one queue supported _device = try _create_device(alloc); _dw = try DeviceWrapper.load(_device, iw.dispatch.vkGetDeviceProcAddr); errdefer _destroy_device(); _D = DeviceProxy.init(_device, dw); _queue = D.getDeviceQueue(_family, 0); // only one queue supported _Q = QueueProxy.init(_queue, dw); } pub fn deinit() void { _destroy_device(); _destroy_surface(); if (config.use_debug_messenger) _destroy_messenger(); _destroy_instance(); } fn _create_instance(alloc: std.mem.Allocator) !vk.Instance { var extnames = std.ArrayList([*:0]const u8).init(alloc); defer extnames.deinit(); for (instance_exts) |ext| try extnames.append(ext.name); var glfw_exts_count: u32 = 0; const glfw_exts: [*]const [*:0]const u8 = glfwGetRequiredInstanceExtensions(&glfw_exts_count); try extnames.appendSlice(glfw_exts[0..glfw_exts_count]); var ci: vk.InstanceCreateInfo = .{ .p_application_info = &vk.ApplicationInfo{ .p_application_name = config.app_name, .application_version = vk.makeApiVersion( config.app_version.variant, config.app_version.major, config.app_version.minor, config.app_version.patch, ), .p_engine_name = config.engine_name, .engine_version = vk.makeApiVersion( config.engine_version.variant, config.engine_version.major, config.engine_version.minor, config.engine_version.patch, ), .api_version = vk.features.version_1_3.version, }, .enabled_extension_count = @intCast(extnames.items.len), .pp_enabled_extension_names = extnames.items.ptr, .enabled_layer_count = @intCast(layers.len), .pp_enabled_layer_names = layers.ptr, }; if (config.use_debug_messenger) ci.p_next = &Debug.ci; return try B.createInstance(&ci, null); } fn _destroy_instance() void { I.destroyInstance(null); } fn _create_messenger() !vk.DebugUtilsMessengerEXT { return try I.createDebugUtilsMessengerEXT(&Debug.ci, null); } fn _destroy_messenger() void { I.destroyDebugUtilsMessengerEXT(_messenger, null); } fn _create_surface() !vk.SurfaceKHR { var res: vk.SurfaceKHR = undefined; if (glfwCreateWindowSurface( _instance, nu.Window.handle, null, &res, ) != .success) { return error.CreateWindowSurfaceFailed; } return res; } fn _destroy_surface() void { I.destroySurfaceKHR(_surface, null); } fn _select_pdevice(alloc: std.mem.Allocator) !vk.PhysicalDevice { var count: u32 = undefined; _ = try I.enumeratePhysicalDevices( &count, null, ); const pdevs = try alloc.alloc(vk.PhysicalDevice, count); defer alloc.free(pdevs); _ = try I.enumeratePhysicalDevices( &count, pdevs.ptr, ); const scores = try alloc.alloc(i32, count); @memset(scores, 0); defer alloc.free(scores); for (pdevs, scores) |pdev, *score| { const props = I.getPhysicalDeviceProperties(pdev); score.* += switch (props.device_type) { .discrete_gpu => 1000, .integrated_gpu => 500, else => 0, }; } const idx = std.mem.indexOfMax(i32, scores); return pdevs[idx]; } fn _select_queue_family_index(alloc: std.mem.Allocator) !u32 { var count: u32 = undefined; I.getPhysicalDeviceQueueFamilyProperties( _pdevice, &count, null, ); const families = try alloc.alloc(vk.QueueFamilyProperties, count); defer alloc.free(families); I.getPhysicalDeviceQueueFamilyProperties( _pdevice, &count, families.ptr, ); for (families, 0..) |prop, idx| { if (!prop.queue_flags.graphics_bit) continue; if (!prop.queue_flags.transfer_bit) continue; if (try I.getPhysicalDeviceSurfaceSupportKHR( _pdevice, @intCast(idx), _surface, ) != vk.TRUE) continue; return @intCast(idx); } return error.NoSuitableQueueFamily; } fn _create_device(alloc: std.mem.Allocator) !vk.Device { const qci: []const vk.DeviceQueueCreateInfo = &.{ vk.DeviceQueueCreateInfo{ .queue_count = 1, .queue_family_index = @intCast(_family), .p_queue_priorities = &[_]f32{1.0}, }, }; var extnames = std.ArrayList([*:0]const u8).init(alloc); defer extnames.deinit(); for (device_exts) |ext| try extnames.append(ext.name); const ci: vk.DeviceCreateInfo = .{ .queue_create_info_count = @intCast(qci.len), .p_queue_create_infos = qci.ptr, .enabled_extension_count = @intCast(extnames.items.len), .pp_enabled_extension_names = extnames.items.ptr, .p_next = &vk.PhysicalDeviceDynamicRenderingFeaturesKHR{ .dynamic_rendering = vk.TRUE, }, }; return try I.createDevice(_pdevice, &ci, null); } fn _destroy_device() void { D.deviceWaitIdle() catch |err| switch (err) { error.OutOfHostMemory, error.OutOfDeviceMemory, error.DeviceLost, => { // In these cases we would destroy the device anyway, so just fall through. Note any child objects must // already be destroyed. This assumes normal cleanup has been done before _destroy_device was called. }, else => unreachable, }; D.destroyDevice(null); } pub extern fn glfwGetInstanceProcAddress( instance: vk.Instance, procname: [*:0]const u8, ) vk.PfnVoidFunction; pub extern fn glfwGetPhysicalDevicePresentationSupport( instance: vk.Instance, pdev: vk.PhysicalDevice, queuefamily: u32, ) c_int; pub extern fn glfwCreateWindowSurface( instance: vk.Instance, window: *nu.Window.c.GLFWwindow, allocation_callbacks: ?*const vk.AllocationCallbacks, surface: *vk.SurfaceKHR, ) vk.Result; pub extern fn glfwGetRequiredInstanceExtensions( count: *u32, ) [*][*:0]const u8;