Implement a basic swapchain
This commit is contained in:
parent
e03d4c59ac
commit
b40005c7c7
2 changed files with 119 additions and 4 deletions
|
@ -12,7 +12,7 @@ pub const QueueFamilyIndices = struct {
|
|||
}
|
||||
};
|
||||
|
||||
pub const SwapChainDetails = struct {
|
||||
pub const SwapchainDetails = struct {
|
||||
surface_capabilities: vk.SurfaceCapabilitiesKHR,
|
||||
formats: []vk.SurfaceFormatKHR,
|
||||
presentation_modes: []vk.PresentModeKHR,
|
||||
|
|
|
@ -3,7 +3,7 @@ const sdl = @import("sdl2");
|
|||
const vk = @import("vulkan");
|
||||
const Utilities = @import("utilities.zig");
|
||||
const QueueFamilyIndices = Utilities.QueueFamilyIndices;
|
||||
const SwapChainDetails = Utilities.SwapChainDetails;
|
||||
const SwapchainDetails = Utilities.SwapchainDetails;
|
||||
|
||||
const enable_validation_layers = true;
|
||||
const validation_layers = [_][*:0]const u8{"VK_LAYER_KHRONOS_validation"};
|
||||
|
@ -44,6 +44,8 @@ pub const VulkanRenderer = struct {
|
|||
|
||||
surface: vk.SurfaceKHR,
|
||||
|
||||
swapchain: vk.SwapchainKHR,
|
||||
|
||||
debug_utils: ?vk.DebugUtilsMessengerEXT,
|
||||
|
||||
pub fn init(window: sdl.Window, allocator: std.mem.Allocator) !Self {
|
||||
|
@ -79,6 +81,8 @@ pub const VulkanRenderer = struct {
|
|||
self.graphics_queue = Queue.init(queues[0], vkd);
|
||||
self.presentation_queue = Queue.init(queues[1], vkd);
|
||||
|
||||
self.swapchain = try self.createSwapChain();
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
|
@ -157,6 +161,63 @@ pub const VulkanRenderer = struct {
|
|||
return try self.instance.createDevice(self.physical_device, &device_create_info, null);
|
||||
}
|
||||
|
||||
fn createSwapChain(self: *Self) !vk.SwapchainKHR {
|
||||
const swapchain_details = try self.getSwapchainDetails(self.physical_device);
|
||||
defer self.allocator.free(swapchain_details.formats);
|
||||
defer self.allocator.free(swapchain_details.presentation_modes);
|
||||
|
||||
// 1. Choose best surface format
|
||||
const surface_format = chooseBestSurfaceFormat(swapchain_details.formats);
|
||||
// 2. Choose best presentation mode
|
||||
const present_mode = chooseBestPresentationMode(swapchain_details.presentation_modes);
|
||||
// 3. Choose swapchain image resolution
|
||||
const extent = chooseSwapExtent(&self.window, swapchain_details.surface_capabilities);
|
||||
|
||||
// How many images are in the swapchain? Get 1 more than the minimum to allow triple buffering
|
||||
var image_count: u32 = swapchain_details.surface_capabilities.min_image_count + 1;
|
||||
const max_image_count = swapchain_details.surface_capabilities.max_image_count;
|
||||
|
||||
// Clamp down if higher
|
||||
// If 0, it means it's limitless
|
||||
if (max_image_count != 0 and image_count > max_image_count) {
|
||||
image_count = max_image_count;
|
||||
}
|
||||
|
||||
var swapchain_create_info: vk.SwapchainCreateInfoKHR = .{
|
||||
.image_format = surface_format.format,
|
||||
.image_color_space = surface_format.color_space,
|
||||
.present_mode = present_mode,
|
||||
.image_extent = extent,
|
||||
.min_image_count = image_count,
|
||||
.image_array_layers = 1, // Number of layers for each image
|
||||
.image_usage = .{ .color_attachment_bit = true }, // What attachment will images be used as
|
||||
.pre_transform = swapchain_details.surface_capabilities.current_transform, // Transform to perform on swapchain images
|
||||
.composite_alpha = .{ .opaque_bit_khr = true }, // How to handle blending images with external graphics (e.g.: other windows)
|
||||
.clipped = vk.TRUE, // Whether to clip parts of images not in view (e.g.: behind another window, off-screen, etc...)
|
||||
.old_swapchain = .null_handle, // Links old one to quickly share responsibilities in case it's been destroyed and replaced
|
||||
.surface = self.surface,
|
||||
.image_sharing_mode = .exclusive,
|
||||
};
|
||||
|
||||
// Get queue family indices
|
||||
const family_indices = try self.getQueueFamilies(self.physical_device);
|
||||
|
||||
// If graphic and presentation families are different, then swapchain must let images be shared between families
|
||||
|
||||
if (family_indices.graphics_family.? != family_indices.presentation_family.?) {
|
||||
const qfi = [_]u32{
|
||||
family_indices.graphics_family.?,
|
||||
family_indices.presentation_family.?,
|
||||
};
|
||||
|
||||
swapchain_create_info.image_sharing_mode = .concurrent;
|
||||
swapchain_create_info.queue_family_index_count = 2; // Number of queues to share images between
|
||||
swapchain_create_info.p_queue_family_indices = &qfi;
|
||||
}
|
||||
|
||||
return try self.device.createSwapchainKHR(&swapchain_create_info, null);
|
||||
}
|
||||
|
||||
fn getPhysicalDevice(self: Self) !vk.PhysicalDevice {
|
||||
var pdev_count: u32 = 0;
|
||||
_ = try self.instance.enumeratePhysicalDevices(&pdev_count, null);
|
||||
|
@ -289,7 +350,7 @@ pub const VulkanRenderer = struct {
|
|||
|
||||
const extension_support = self.checkDeviceExtensions(pdev) catch return false;
|
||||
|
||||
const swapchain_details = self.getSwapChainDetails(pdev) catch return false;
|
||||
const swapchain_details = self.getSwapchainDetails(pdev) catch return false;
|
||||
defer self.allocator.free(swapchain_details.formats);
|
||||
defer self.allocator.free(swapchain_details.presentation_modes);
|
||||
|
||||
|
@ -322,7 +383,7 @@ pub const VulkanRenderer = struct {
|
|||
return false;
|
||||
}
|
||||
|
||||
fn getSwapChainDetails(self: Self, pdev: vk.PhysicalDevice) !SwapChainDetails {
|
||||
fn getSwapchainDetails(self: Self, pdev: vk.PhysicalDevice) !SwapchainDetails {
|
||||
// Capabilities
|
||||
const surface_capabilities = try self.instance.getPhysicalDeviceSurfaceCapabilitiesKHR(pdev, self.surface);
|
||||
|
||||
|
@ -351,6 +412,7 @@ pub const VulkanRenderer = struct {
|
|||
if (enable_validation_layers) {
|
||||
self.instance.destroyDebugUtilsMessengerEXT(self.debug_utils.?, null);
|
||||
}
|
||||
self.device.destroySwapchainKHR(self.swapchain, null);
|
||||
self.device.destroyDevice(null);
|
||||
self.instance.destroySurfaceKHR(self.surface, null);
|
||||
self.instance.destroyInstance(null);
|
||||
|
@ -360,6 +422,59 @@ pub const VulkanRenderer = struct {
|
|||
}
|
||||
};
|
||||
|
||||
// Format: VK_FORMAT_R8G8B8A8_UNORM (VK_FORMAT_B8G8R8A8_UNORM as backup)
|
||||
// Color space: VK_COLOR_SPACE_SRGB_NONLINEAR_KHR
|
||||
fn chooseBestSurfaceFormat(formats: []vk.SurfaceFormatKHR) vk.SurfaceFormatKHR {
|
||||
// If only one format available and is undefined, then this means all formats are available
|
||||
if (formats.len == 1 and formats[0].format == vk.Format.undefined) {
|
||||
return .{
|
||||
.format = vk.Format.r8g8b8a8_unorm,
|
||||
.color_space = vk.ColorSpaceKHR.srgb_nonlinear_khr,
|
||||
};
|
||||
}
|
||||
|
||||
for (formats) |format| {
|
||||
if ((format.format == vk.Format.r8g8b8a8_unorm or format.format == vk.Format.b8g8r8a8_unorm) and format.color_space == vk.ColorSpaceKHR.srgb_nonlinear_khr) {
|
||||
return format;
|
||||
}
|
||||
}
|
||||
|
||||
return formats[0];
|
||||
}
|
||||
|
||||
fn chooseBestPresentationMode(presentation_modes: []vk.PresentModeKHR) vk.PresentModeKHR {
|
||||
for (presentation_modes) |presentation_mode| {
|
||||
if (presentation_mode == vk.PresentModeKHR.mailbox_khr) {
|
||||
return presentation_mode;
|
||||
}
|
||||
}
|
||||
|
||||
// Use FIFO as Vulkan spec says it must be present
|
||||
return vk.PresentModeKHR.fifo_khr;
|
||||
}
|
||||
|
||||
fn chooseSwapExtent(window: *sdl.Window, surface_capabilities: vk.SurfaceCapabilitiesKHR) vk.Extent2D {
|
||||
// If the current extent is at max value, the extent can vary. Otherwise it's the size of the window
|
||||
if (surface_capabilities.current_extent.width != std.math.maxInt(u32)) {
|
||||
return surface_capabilities.current_extent;
|
||||
}
|
||||
|
||||
// If value can very, need to set the extent manually
|
||||
const framebuffer_size = sdl.vulkan.getDrawableSize(window);
|
||||
|
||||
var extent: vk.Extent2D = .{
|
||||
.width = @intCast(framebuffer_size.width),
|
||||
.height = @intCast(framebuffer_size.height),
|
||||
};
|
||||
|
||||
// Surface also defines max and min, so make sure it's within boundaries by clamping values
|
||||
extent.width = @max(surface_capabilities.min_image_extent.width, @min(surface_capabilities.max_image_extent.width, extent.width));
|
||||
extent.height = @max(surface_capabilities.min_image_extent.height, @min(surface_capabilities.max_image_extent.height, extent.height));
|
||||
|
||||
return extent;
|
||||
}
|
||||
|
||||
// Validation layers stuff
|
||||
fn createDebugMessenger(instance: Instance) !vk.DebugUtilsMessengerEXT {
|
||||
const debug_create_info = getDebugUtilsCreateInfo();
|
||||
|
||||
|
|
Loading…
Reference in a new issue