const Vulkan = @This(); const std = @import("std"); const vk = @import("vulkan"); const xcb = @import("xcb"); vulkan_loader: *DynamicLibrary, base: vk.BaseWrapper, instance: vk.InstanceProxy, surface: vk.SurfaceKHR, device: vk.DeviceProxy, queue_family: u32, queue: vk.Queue, command_pool: vk.CommandPool, pub fn init(allocator: std.mem.Allocator, connection: *xcb.Connection, window: xcb.Window) !Vulkan { const vulkan_loader = try DynamicLibrary.open("libvulkan.so.1"); errdefer vulkan_loader.close(); const vkGetInstanceProcAddr = try vulkan_loader.sym(vk.PfnGetInstanceProcAddr, "vkGetInstanceProcAddr"); const base = vk.BaseWrapper.load(vkGetInstanceProcAddr); const instance_handle = blk: { const enabled_layers: []const [*:0]const u8 = &.{}; const enabled_extensions: []const [*:0]const u8 = &.{ vk.extensions.khr_surface.name, vk.extensions.khr_xcb_surface.name, }; break :blk try base.createInstance(&.{ .p_application_info = &.{ .p_application_name = "xcb_sample", .application_version = vk.makeApiVersion(0, 0, 0, 0).toU32(), .p_engine_name = "xcb_sample", .engine_version = vk.makeApiVersion(0, 0, 0, 0).toU32(), .api_version = vk.API_VERSION_1_0.toU32(), }, .enabled_layer_count = @intCast(enabled_layers.len), .pp_enabled_layer_names = enabled_layers.ptr, .enabled_extension_count = @intCast(enabled_extensions.len), .pp_enabled_extension_names = enabled_extensions.ptr, }, null); }; const instance_wrapper_ptr = try allocator.create(vk.InstanceWrapper); errdefer allocator.destroy(instance_wrapper_ptr); instance_wrapper_ptr.* = .load(instance_handle, vkGetInstanceProcAddr); const instance = vk.InstanceProxy.init(instance_handle, instance_wrapper_ptr); errdefer instance.destroyInstance(null); const surface = try instance.createXcbSurfaceKHR(&.{ .connection = connection, .window = window, }, null); errdefer instance.destroySurfaceKHR(surface, null); const physical_device = blk: { const physical_devices = try instance.enumeratePhysicalDevicesAlloc(allocator); defer allocator.free(physical_devices); if (physical_devices.len > 0) { break :blk physical_devices[0]; } return error.NoDevice; }; const queue_family: u32 = blk: { const queue_families_properties = try instance.getPhysicalDeviceQueueFamilyPropertiesAlloc(physical_device, allocator); defer allocator.free(queue_families_properties); // Look for the first family that has the `graphics_bit` set and // supports presentation to a given surface. for (queue_families_properties, 0..) |queue_family_properties, i| { const graphics_bit = queue_family_properties.queue_flags.graphics_bit; const supports_presentation = try instance.getPhysicalDeviceSurfaceSupportKHR(physical_device, @intCast(i), surface) != .false; if (graphics_bit and supports_presentation) { break :blk @intCast(i); } } return error.NoQueue; }; const device_handle = blk: { const queue_create_infos: []const vk.DeviceQueueCreateInfo = &.{ .{ .queue_family_index = queue_family, .queue_count = 1, .p_queue_priorities = &.{0}, }, }; const enabled_extensions: []const [*:0]const u8 = &.{ vk.extensions.khr_swapchain.name, }; break :blk try instance.createDevice(physical_device, &.{ .queue_create_info_count = @intCast(queue_create_infos.len), .p_queue_create_infos = queue_create_infos.ptr, .enabled_extension_count = @intCast(enabled_extensions.len), .pp_enabled_extension_names = enabled_extensions.ptr, }, null); }; const device_wrapper_ptr = try allocator.create(vk.DeviceWrapper); errdefer allocator.destroy(device_wrapper_ptr); device_wrapper_ptr.* = .load(device_handle, instance.wrapper.dispatch.vkGetDeviceProcAddr.?); const device = vk.DeviceProxy.init(device_handle, device_wrapper_ptr); errdefer device.destroyDevice(null); const queue = device.getDeviceQueue(queue_family, 0); const command_pool = try device.createCommandPool(&.{ .flags = .{ .transient_bit = true }, .queue_family_index = queue_family, }, null); errdefer device.destroyCommandPool(command_pool, null); return .{ .vulkan_loader = vulkan_loader, .base = base, .instance = instance, .surface = surface, .device = device, .queue_family = queue_family, .queue = queue, .command_pool = command_pool, }; } pub fn deinit(self: *Vulkan, allocator: std.mem.Allocator) void { self.device.destroyCommandPool(self.command_pool, null); self.device.destroyDevice(null); allocator.destroy(self.device.wrapper); self.instance.destroySurfaceKHR(self.surface, null); self.instance.destroyInstance(null); allocator.destroy(self.instance.wrapper); self.vulkan_loader.close(); self.* = undefined; } const DynamicLibrary = opaque { fn open(path: [*:0]const u8) !*DynamicLibrary { const ptr = std.c.dlopen(path, .{ .NOW = true }) orelse { std.debug.print("{s}\n", .{std.c.dlerror().?}); return error.DlOpen; }; return @ptrCast(ptr); } fn close(self: *DynamicLibrary) void { _ = std.c.dlclose(self); } fn sym(self: *DynamicLibrary, comptime Ptr: type, symbol: [*:0]const u8) !Ptr { const ptr = std.c.dlsym(self, symbol) orelse { std.debug.print("{s}\n", .{std.c.dlerror().?}); return error.DlSym; }; return @ptrCast(@alignCast(ptr)); } };