remove unused swapchain.zig
This commit is contained in:
@@ -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),
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
|
Reference in New Issue
Block a user