diff --git a/src/main.zig b/src/main.zig index e88c69f..93527c8 100644 --- a/src/main.zig +++ b/src/main.zig @@ -13,8 +13,8 @@ pub fn main() !void { defer _ = gpa.deinit(); const allocator = gpa.allocator(); - const vulkan_renderer = try VulkanRenderer.init(window, allocator); - _ = vulkan_renderer; + var vulkan_renderer = try VulkanRenderer.init(window, allocator); + defer vulkan_renderer.deinit(); mainLoop: while (true) { while (sdl.pollEvent()) |ev| { @@ -36,8 +36,6 @@ fn initWindow() !sdl.Window { std.log.err("Failed to initialize SDL2", .{}); }; - try sdl.vulkan.loadLibrary(null); - return try sdl.createWindow( "Vulkan Test", .{ .centered = {} }, diff --git a/src/utilities.zig b/src/utilities.zig new file mode 100644 index 0000000..c73d2db --- /dev/null +++ b/src/utilities.zig @@ -0,0 +1,10 @@ +const std = @import("std"); +const vk = @import("vulkan"); + +pub const QueueFamilyIndices = struct { + graphics_family: ?u32, + + pub fn isValid(self: QueueFamilyIndices) bool { + return self.graphics_family != null; + } +}; diff --git a/src/vulkan_renderer.zig b/src/vulkan_renderer.zig index 3f28203..83065a6 100644 --- a/src/vulkan_renderer.zig +++ b/src/vulkan_renderer.zig @@ -1,27 +1,33 @@ const std = @import("std"); const sdl = @import("sdl2"); const vk = @import("vulkan"); +const QueueFamilyIndices = @import("utilities.zig").QueueFamilyIndices; const apis: []const vk.ApiInfo = &.{ - // You can either add invidiual functions by manually creating an 'api' .{ .base_commands = .{ .createInstance = true, }, .instance_commands = .{ .createDevice = true, + // .getDeviceProcAddr = true, }, }, - // Or you can add entire feature sets or extensions vk.features.version_1_0, + vk.features.version_1_1, + vk.features.version_1_2, + vk.features.version_1_3, vk.extensions.khr_surface, vk.extensions.khr_swapchain, }; const BaseDispatch = vk.BaseWrapper(apis); const InstanceDispatch = vk.InstanceWrapper(apis); +const DeviceDispatch = vk.DeviceWrapper(apis); const Instance = vk.InstanceProxy(apis); +const Device = vk.DeviceProxy(apis); +const Queue = vk.QueueProxy(apis); pub const VulkanRenderer = struct { const Self = @This(); @@ -33,20 +39,53 @@ pub const VulkanRenderer = struct { window: sdl.Window, instance: Instance, + physical_device: vk.PhysicalDevice, + device: Device, + graphics_queue: Queue, surface: vk.SurfaceKHR, pub fn init(window: sdl.Window, allocator: std.mem.Allocator) !Self { - const vkb = try BaseDispatch.load(try sdl.vulkan.getVkGetInstanceProcAddr()); + var self: Self = undefined; + self.window = window; + self.allocator = allocator; - const extensions = try sdl.vulkan.getInstanceExtensionsAlloc(window, allocator); - defer allocator.free(extensions); + self.vkb = try BaseDispatch.load(try sdl.vulkan.getVkGetInstanceProcAddr()); + + const instance = try self.createInstance(); + + const vki = try allocator.create(InstanceDispatch); + errdefer allocator.destroy(vki); + vki.* = try InstanceDispatch.load(instance, self.vkb.dispatch.vkGetInstanceProcAddr); + + self.instance = Instance.init(instance, vki); + // self.surface = try sdl.vulkan.createSurface(self.window, self.instance.handle); + self.physical_device = try self.getPhysicalDevice(); + + const device = try self.createLogicalDevice(); + + const vkd = try allocator.create(DeviceDispatch); + errdefer allocator.destroy(vkd); + // FIXME Why does it only work with no fail? + vkd.* = DeviceDispatch.loadNoFail(device, self.instance.wrapper.dispatch.vkGetDeviceProcAddr); + + self.device = Device.init(device, vkd); + const queue = try self.getDeviceQueues(); + + self.graphics_queue = Queue.init(queue, vkd); + + return self; + } + + fn createInstance(self: Self) !vk.Instance { + const extensions = try sdl.vulkan.getInstanceExtensionsAlloc(self.window, self.allocator); + defer self.allocator.free(extensions); std.debug.print("[Required instance extensions]\n", .{}); for (extensions) |ext| { std.debug.print("\t- {s}\n", .{ext}); } - if (!try checkExtensions(vkb, &extensions, allocator)) { + if (!try self.checkExtensions(&extensions)) { return error.ExtensionNotPresent; } @@ -58,63 +97,128 @@ pub const VulkanRenderer = struct { .api_version = vk.API_VERSION_1_3, }; - const inst = try vkb.createInstance(&.{ + return try self.vkb.createInstance(&.{ .p_application_info = &app_info, .enabled_extension_count = @intCast(extensions.len), .pp_enabled_extension_names = @ptrCast(extensions), }, null); - - const vki = try allocator.create(InstanceDispatch); - defer allocator.destroy(vki); - vki.* = try InstanceDispatch.load(inst, vkb.dispatch.vkGetInstanceProcAddr); - var instance = Instance.init(inst, vki); - errdefer instance.destroyInstance(null); - - const surface = try sdl.vulkan.createSurface(window, instance.handle); - - var physical_device_count: u32 = 0; - _ = try instance.enumeratePhysicalDevices(&physical_device_count, null); - - std.debug.print("Physical devices: {d}\n", .{physical_device_count}); - - return .{ - .allocator = allocator, - .window = window, - .vkb = vkb, - .instance = instance, - .surface = surface, - }; } - pub fn deinit(self: *Self) void { - self.instance.destroySurfaceKHR(self.surface, null); - self.instance.destroyInstance(null); + fn checkExtensions(self: Self, required_extensions: *const [][*:0]const u8) !bool { + var prop_count: u32 = 0; + _ = try self.vkb.enumerateInstanceExtensionProperties(null, &prop_count, null); - self.allocator.destroy(self.instance.wrapper); - } -}; + const props = try self.allocator.alloc(vk.ExtensionProperties, prop_count); + defer self.allocator.free(props); -fn checkExtensions(vkb: BaseDispatch, required_extensions: *const [][*:0]const u8, allocator: std.mem.Allocator) !bool { - var prop_count: u32 = 0; - _ = try vkb.enumerateInstanceExtensionProperties(null, &prop_count, null); + _ = try self.vkb.enumerateInstanceExtensionProperties(null, &prop_count, props.ptr); - const props = try allocator.alloc(vk.ExtensionProperties, prop_count); - defer allocator.free(props); + for (required_extensions.*) |required_extension| { + var found = false; - _ = try vkb.enumerateInstanceExtensionProperties(null, &prop_count, props.ptr); + for (props) |prop| { + if (std.mem.eql(u8, std.mem.sliceTo(&prop.extension_name, 0), std.mem.span(required_extension))) { + found = true; + } + } - for (required_extensions.*) |required_extension| { - var found = false; - for (props) |prop| { - if (std.mem.eql(u8, std.mem.sliceTo(&prop.extension_name, 0), std.mem.span(required_extension))) { - found = true; + if (!found) { + return false; } } - if (!found) { - return false; - } + return true; } - return true; -} + fn getPhysicalDevice(self: Self) !vk.PhysicalDevice { + var pdev_count: u32 = 0; + _ = try self.instance.enumeratePhysicalDevices(&pdev_count, null); + + const pdevs = try self.allocator.alloc(vk.PhysicalDevice, pdev_count); + defer self.allocator.free(pdevs); + + _ = try self.instance.enumeratePhysicalDevices(&pdev_count, pdevs.ptr); + + for (pdevs) |pdev| { + if (self.checkDeviceSuitable(pdev)) { + return pdev; + } + } + + // TODO Obviously needs to be something else + unreachable; + } + + fn checkDeviceSuitable(self: Self, pdev: vk.PhysicalDevice) bool { + const pdev_properties = self.instance.getPhysicalDeviceProperties(pdev); + + if (pdev_properties.device_type == .cpu) { + return false; + } + + const queue_family_indices = self.getQueueFamilies(pdev) catch { + return false; + }; + + return queue_family_indices.isValid(); + } + + fn getQueueFamilies(self: Self, pdev: vk.PhysicalDevice) !QueueFamilyIndices { + var indices: QueueFamilyIndices = .{ .graphics_family = null }; + + var queue_family_count: u32 = 0; + self.instance.getPhysicalDeviceQueueFamilyProperties(pdev, &queue_family_count, null); + + const queue_family_list = try self.allocator.alloc(vk.QueueFamilyProperties, queue_family_count); + defer self.allocator.free(queue_family_list); + + self.instance.getPhysicalDeviceQueueFamilyProperties(pdev, &queue_family_count, queue_family_list.ptr); + + for (queue_family_list, 0..) |queue_family, i| { + if (queue_family.queue_count > 0 and queue_family.queue_flags.graphics_bit) { + indices.graphics_family = @intCast(i); + } + + if (indices.isValid()) { + return indices; + } + } + + unreachable; + } + + fn createLogicalDevice(self: Self) !vk.Device { + const indices = try self.getQueueFamilies(self.physical_device); + // 1 is the highest priority + const priority = [_]f32{1}; + + const queue_create_info = [_]vk.DeviceQueueCreateInfo{.{ + .queue_family_index = indices.graphics_family.?, + .queue_count = 1, + .p_queue_priorities = &priority, + }}; + + const device_create_info: vk.DeviceCreateInfo = .{ + .queue_create_info_count = 1, + .p_queue_create_infos = &queue_create_info, + .enabled_extension_count = 0, + }; + + return try self.instance.createDevice(self.physical_device, &device_create_info, null); + } + + fn getDeviceQueues(self: Self) !vk.Queue { + const indices = try self.getQueueFamilies(self.physical_device); + + return self.device.getDeviceQueue(indices.graphics_family.?, 0); + } + + pub fn deinit(self: *Self) void { + self.device.destroyDevice(null); + // self.instance.destroySurfaceKHR(self.surface, null); + self.instance.destroyInstance(null); + + self.allocator.destroy(self.device.wrapper); + self.allocator.destroy(self.instance.wrapper); + } +};