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,
|
surface_capabilities: vk.SurfaceCapabilitiesKHR,
|
||||||
formats: []vk.SurfaceFormatKHR,
|
formats: []vk.SurfaceFormatKHR,
|
||||||
presentation_modes: []vk.PresentModeKHR,
|
presentation_modes: []vk.PresentModeKHR,
|
||||||
|
|
|
@ -3,7 +3,7 @@ const sdl = @import("sdl2");
|
||||||
const vk = @import("vulkan");
|
const vk = @import("vulkan");
|
||||||
const Utilities = @import("utilities.zig");
|
const Utilities = @import("utilities.zig");
|
||||||
const QueueFamilyIndices = Utilities.QueueFamilyIndices;
|
const QueueFamilyIndices = Utilities.QueueFamilyIndices;
|
||||||
const SwapChainDetails = Utilities.SwapChainDetails;
|
const SwapchainDetails = Utilities.SwapchainDetails;
|
||||||
|
|
||||||
const enable_validation_layers = true;
|
const enable_validation_layers = true;
|
||||||
const validation_layers = [_][*:0]const u8{"VK_LAYER_KHRONOS_validation"};
|
const validation_layers = [_][*:0]const u8{"VK_LAYER_KHRONOS_validation"};
|
||||||
|
@ -44,6 +44,8 @@ pub const VulkanRenderer = struct {
|
||||||
|
|
||||||
surface: vk.SurfaceKHR,
|
surface: vk.SurfaceKHR,
|
||||||
|
|
||||||
|
swapchain: vk.SwapchainKHR,
|
||||||
|
|
||||||
debug_utils: ?vk.DebugUtilsMessengerEXT,
|
debug_utils: ?vk.DebugUtilsMessengerEXT,
|
||||||
|
|
||||||
pub fn init(window: sdl.Window, allocator: std.mem.Allocator) !Self {
|
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.graphics_queue = Queue.init(queues[0], vkd);
|
||||||
self.presentation_queue = Queue.init(queues[1], vkd);
|
self.presentation_queue = Queue.init(queues[1], vkd);
|
||||||
|
|
||||||
|
self.swapchain = try self.createSwapChain();
|
||||||
|
|
||||||
return self;
|
return self;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -157,6 +161,63 @@ pub const VulkanRenderer = struct {
|
||||||
return try self.instance.createDevice(self.physical_device, &device_create_info, null);
|
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 {
|
fn getPhysicalDevice(self: Self) !vk.PhysicalDevice {
|
||||||
var pdev_count: u32 = 0;
|
var pdev_count: u32 = 0;
|
||||||
_ = try self.instance.enumeratePhysicalDevices(&pdev_count, null);
|
_ = 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 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.formats);
|
||||||
defer self.allocator.free(swapchain_details.presentation_modes);
|
defer self.allocator.free(swapchain_details.presentation_modes);
|
||||||
|
|
||||||
|
@ -322,7 +383,7 @@ pub const VulkanRenderer = struct {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
fn getSwapChainDetails(self: Self, pdev: vk.PhysicalDevice) !SwapChainDetails {
|
fn getSwapchainDetails(self: Self, pdev: vk.PhysicalDevice) !SwapchainDetails {
|
||||||
// Capabilities
|
// Capabilities
|
||||||
const surface_capabilities = try self.instance.getPhysicalDeviceSurfaceCapabilitiesKHR(pdev, self.surface);
|
const surface_capabilities = try self.instance.getPhysicalDeviceSurfaceCapabilitiesKHR(pdev, self.surface);
|
||||||
|
|
||||||
|
@ -351,6 +412,7 @@ pub const VulkanRenderer = struct {
|
||||||
if (enable_validation_layers) {
|
if (enable_validation_layers) {
|
||||||
self.instance.destroyDebugUtilsMessengerEXT(self.debug_utils.?, null);
|
self.instance.destroyDebugUtilsMessengerEXT(self.debug_utils.?, null);
|
||||||
}
|
}
|
||||||
|
self.device.destroySwapchainKHR(self.swapchain, null);
|
||||||
self.device.destroyDevice(null);
|
self.device.destroyDevice(null);
|
||||||
self.instance.destroySurfaceKHR(self.surface, null);
|
self.instance.destroySurfaceKHR(self.surface, null);
|
||||||
self.instance.destroyInstance(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 {
|
fn createDebugMessenger(instance: Instance) !vk.DebugUtilsMessengerEXT {
|
||||||
const debug_create_info = getDebugUtilsCreateInfo();
|
const debug_create_info = getDebugUtilsCreateInfo();
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue