From 5809b64f1cb540eb4f24c920edfc96057da100d3 Mon Sep 17 00:00:00 2001 From: David Allemang Date: Wed, 3 Apr 2024 16:12:59 -0400 Subject: [PATCH] remove unused swapchain.zig --- src/swapchain.zig | 336 ---------------------------------------------- 1 file changed, 336 deletions(-) delete mode 100644 src/swapchain.zig diff --git a/src/swapchain.zig b/src/swapchain.zig deleted file mode 100644 index ed85604..0000000 --- a/src/swapchain.zig +++ /dev/null @@ -1,336 +0,0 @@ -const std = @import("std"); -const vk = @import("vk"); -const gfx = @import("gfx.zig"); -const Allocator = std.mem.Allocator; - -pub const Context = struct { - vki: gfx.InstanceDispatch, - vkd: gfx.DeviceDispatch, - - pdev: vk.PhysicalDevice, - dev: vk.Device, - - surface: vk.SurfaceKHR, - - queue: vk.Queue, - family: u32, -}; - -pub const Swapchain = struct { - pub const PresentState = enum { - optimal, - suboptimal, - }; - - gc: *const Context, - allocator: Allocator, - - present_mode: vk.PresentModeKHR, - extent: vk.Extent2D, - handle: vk.SwapchainKHR, - - swap_images: []SwapImage, - image_index: u32, - next_image_acquired: vk.Semaphore, - - pub fn init( - gc: *const Context, - allocator: Allocator, - extent: vk.Extent2D, - format: vk.SurfaceFormatKHR, - ) !Swapchain { - return try initRecycle(gc, allocator, extent, format, .null_handle); - } - - pub fn initRecycle( - gc: *const Context, - allocator: Allocator, - extent: vk.Extent2D, - format: vk.SurfaceFormatKHR, - old_handle: vk.SwapchainKHR, - ) !Swapchain { - const caps = try gc.vki.getPhysicalDeviceSurfaceCapabilitiesKHR(gc.pdev, gc.surface); - const actual_extent = findActualExtent(caps, extent); - if (actual_extent.width == 0 or actual_extent.height == 0) { - return error.InvalidSurfaceDimensions; - } - - const present_mode = try findPresentMode(gc, allocator); - - var image_count = caps.min_image_count + 1; - if (caps.max_image_count > 0) { - image_count = @min(image_count, caps.max_image_count); - } - - const handle = try gc.vkd.createSwapchainKHR(gc.dev, &.{ - .surface = gc.surface, - .min_image_count = image_count, - .image_format = format.format, - .image_color_space = format.color_space, - .image_extent = actual_extent, - .image_array_layers = 1, - .image_usage = .{ .color_attachment_bit = true, .transfer_dst_bit = true }, - .image_sharing_mode = .exclusive, - .pre_transform = caps.current_transform, - .composite_alpha = .{ .opaque_bit_khr = true }, - .present_mode = present_mode, - .clipped = vk.TRUE, - .old_swapchain = old_handle, - }, null); - errdefer gc.vkd.destroySwapchainKHR(gc.dev, handle, null); - - if (old_handle != .null_handle) { - // Apparently, the old swapchain handle still needs to be destroyed after recreating. - gc.vkd.destroySwapchainKHR(gc.dev, old_handle, null); - } - - const swap_images = try initSwapchainImages(gc, handle, format.format, allocator); - errdefer { - for (swap_images) |si| si.deinit(gc); - allocator.free(swap_images); - } - - var next_image_acquired = try gc.vkd.createSemaphore(gc.dev, &.{}, null); - errdefer gc.vkd.destroySemaphore(gc.dev, next_image_acquired, null); - - const result = try gc.vkd.acquireNextImageKHR( - gc.dev, - handle, - std.math.maxInt(u64), - next_image_acquired, - .null_handle, - ); - switch (result.result) { - vk.Result.success, vk.Result.suboptimal_khr => {}, - vk.Result.timeout => return error.Timeout, - vk.Result.not_ready => return error.NotReady, - else => unreachable, - } - - std.mem.swap(vk.Semaphore, &swap_images[result.image_index].image_acquired, &next_image_acquired); - return Swapchain{ - .gc = gc, - .allocator = allocator, - .present_mode = present_mode, - .extent = actual_extent, - .handle = handle, - .swap_images = swap_images, - .image_index = result.image_index, - .next_image_acquired = next_image_acquired, - }; - } - - fn deinitExceptSwapchain(self: Swapchain) void { - for (self.swap_images) |si| si.deinit(self.gc); - self.allocator.free(self.swap_images); - self.gc.vkd.destroySemaphore(self.gc.dev, self.next_image_acquired, null); - } - - pub fn waitForAllFences(self: Swapchain) !void { - for (self.swap_images) |si| si.waitForFence(self.gc) catch {}; - } - - pub fn deinit(self: Swapchain) void { - self.deinitExceptSwapchain(); - self.gc.vkd.destroySwapchainKHR(self.gc.dev, self.handle, null); - } - - pub fn recreate( - self: *Swapchain, - new_extent: vk.Extent2D, - format: vk.SurfaceFormatKHR, - ) !void { - const gc = self.gc; - const allocator = self.allocator; - const old_handle = self.handle; - self.deinitExceptSwapchain(); - self.* = try initRecycle(gc, allocator, new_extent, format, old_handle); - } - - pub fn currentImage(self: Swapchain) vk.Image { - return self.swap_images[self.image_index].image; - } - - pub fn currentSwapImage(self: Swapchain) *const SwapImage { - return &self.swap_images[self.image_index]; - } - - pub fn present(self: *Swapchain, cmdbuf: vk.CommandBuffer) !PresentState { - // Simple method: - // 1) Acquire next image - // 2) Wait for and reset fence of the acquired image - // 3) Submit command buffer with fence of acquired image, - // dependendent on the semaphore signalled by the first step. - // 4) Present current frame, dependent on semaphore signalled by previous step - // Problem: This way we can't reference the current image while rendering. - // Better method: Shuffle the steps around such that acquire next image is the last step, - // leaving the swapchain in a state with the current image. - // 1) Wait for and reset fence of current image - // 2) Submit command buffer, signalling fence of current image and dependent on - // the semaphore signalled by step 4. - // 3) Present current frame, dependent on semaphore signalled by the submit - // 4) Acquire next image, signalling its semaphore - // One problem that arises is that we can't know beforehand which semaphore to signal, - // so we keep an extra auxilery semaphore that is swapped around - - // Step 1: Make sure the current frame has finished rendering - const current = self.currentSwapImage(); - try current.waitForFence(self.gc); - try self.gc.vkd.resetFences(self.gc.dev, 1, @ptrCast(¤t.frame_fence)); - - // Step 2: Submit the command buffer - const wait_stage = [_]vk.PipelineStageFlags{.{ .top_of_pipe_bit = true }}; - try self.gc.vkd.queueSubmit(self.gc.queue, 1, &[_]vk.SubmitInfo{.{ - .wait_semaphore_count = 1, - .p_wait_semaphores = @ptrCast(¤t.image_acquired), - .p_wait_dst_stage_mask = &wait_stage, - .command_buffer_count = 1, - .p_command_buffers = @ptrCast(&cmdbuf), - .signal_semaphore_count = 1, - .p_signal_semaphores = @ptrCast(¤t.render_finished), - }}, current.frame_fence); - - // Step 3: Present the current frame - _ = try self.gc.vkd.queuePresentKHR(self.gc.queue, &.{ - .wait_semaphore_count = 1, - .p_wait_semaphores = @as([*]const vk.Semaphore, @ptrCast(¤t.render_finished)), - .swapchain_count = 1, - .p_swapchains = @as([*]const vk.SwapchainKHR, @ptrCast(&self.handle)), - .p_image_indices = @as([*]const u32, @ptrCast(&self.image_index)), - }); - - // Step 4: Acquire next frame - const result = try self.gc.vkd.acquireNextImageKHR( - self.gc.dev, - self.handle, - std.math.maxInt(u64), - self.next_image_acquired, - .null_handle, - ); - - std.mem.swap(vk.Semaphore, &self.swap_images[result.image_index].image_acquired, &self.next_image_acquired); - self.image_index = result.image_index; - - return switch (result.result) { - .success => .optimal, - .suboptimal_khr => .suboptimal, - else => unreachable, - }; - } -}; - -const SwapImage = struct { - image: vk.Image, - view: vk.ImageView, - image_acquired: vk.Semaphore, - render_finished: vk.Semaphore, - frame_fence: vk.Fence, - - fn init(gc: *const Context, image: vk.Image, format: vk.Format) !SwapImage { - const view = try gc.vkd.createImageView(gc.dev, &.{ - .image = image, - .view_type = .@"2d", - .format = format, - .components = .{ .r = .identity, .g = .identity, .b = .identity, .a = .identity }, - .subresource_range = .{ - .aspect_mask = .{ .color_bit = true }, - .base_mip_level = 0, - .level_count = 1, - .base_array_layer = 0, - .layer_count = 1, - }, - }, null); - errdefer gc.vkd.destroyImageView(gc.dev, view, null); - - const image_acquired = try gc.vkd.createSemaphore(gc.dev, &.{}, null); - errdefer gc.vkd.destroySemaphore(gc.dev, image_acquired, null); - - const render_finished = try gc.vkd.createSemaphore(gc.dev, &.{}, null); - errdefer gc.vkd.destroySemaphore(gc.dev, render_finished, null); - - const frame_fence = try gc.vkd.createFence(gc.dev, &.{ .flags = .{ .signaled_bit = true } }, null); - errdefer gc.vkd.destroyFence(gc.dev, frame_fence, null); - - return SwapImage{ - .image = image, - .view = view, - .image_acquired = image_acquired, - .render_finished = render_finished, - .frame_fence = frame_fence, - }; - } - - fn deinit(self: SwapImage, gc: *const Context) void { - // todo critical: this waitForFence deadlocks when recreating swapchain on nvidia. - // Something about the main "present" loop is fucked. Can't just ignore the fence; validation layers show errors - // that you can't destroy a fence while a queue depends on it (details may be wrong... I don't fully understand) - // I suspect this is more an issue with the vulkan-zig example, so I probably need to revisit vulkan-tutorial or - // try to rebuild the swapchain infrastructure myself. - // - // I do think it's clunky how the swapchain is created and recreated; duplicate logic in creation and recreation - // that could maybe be avoided with .null_handle? Maybe there's some more straightforward way to handle it. - - self.waitForFence(gc) catch return; - gc.vkd.destroyImageView(gc.dev, self.view, null); - gc.vkd.destroySemaphore(gc.dev, self.image_acquired, null); - gc.vkd.destroySemaphore(gc.dev, self.render_finished, null); - gc.vkd.destroyFence(gc.dev, self.frame_fence, null); - } - - fn waitForFence(self: SwapImage, gc: *const Context) !void { - _ = try gc.vkd.waitForFences(gc.dev, 1, @ptrCast(&self.frame_fence), vk.TRUE, std.math.maxInt(u64)); - } -}; - -fn initSwapchainImages(gc: *const Context, swapchain: vk.SwapchainKHR, format: vk.Format, allocator: Allocator) ![]SwapImage { - var count: u32 = undefined; - _ = try gc.vkd.getSwapchainImagesKHR(gc.dev, swapchain, &count, null); - const images = try allocator.alloc(vk.Image, count); - defer allocator.free(images); - _ = try gc.vkd.getSwapchainImagesKHR(gc.dev, swapchain, &count, images.ptr); - - const swap_images = try allocator.alloc(SwapImage, count); - errdefer allocator.free(swap_images); - - var i: usize = 0; - errdefer for (swap_images[0..i]) |si| si.deinit(gc); - - for (images) |image| { - swap_images[i] = try SwapImage.init(gc, image, format); - i += 1; - } - - return swap_images; -} - -fn findPresentMode(gc: *const Context, allocator: Allocator) !vk.PresentModeKHR { - var count: u32 = undefined; - _ = try gc.vki.getPhysicalDeviceSurfacePresentModesKHR(gc.pdev, gc.surface, &count, null); - const present_modes = try allocator.alloc(vk.PresentModeKHR, count); - defer allocator.free(present_modes); - _ = try gc.vki.getPhysicalDeviceSurfacePresentModesKHR(gc.pdev, gc.surface, &count, present_modes.ptr); - - const preferred = [_]vk.PresentModeKHR{ - .mailbox_khr, - .immediate_khr, - }; - - for (preferred) |mode| { - if (std.mem.indexOfScalar(vk.PresentModeKHR, present_modes, mode) != null) { - return mode; - } - } - - return .fifo_khr; -} - -fn findActualExtent(caps: vk.SurfaceCapabilitiesKHR, extent: vk.Extent2D) vk.Extent2D { - if (caps.current_extent.width != 0xFFFF_FFFF) { - return caps.current_extent; - } else { - return .{ - .width = std.math.clamp(extent.width, caps.min_image_extent.width, caps.max_image_extent.width), - .height = std.math.clamp(extent.height, caps.min_image_extent.height, caps.max_image_extent.height), - }; - } -}