Add create/resize/destroy_swapchain functions to RenderDriver
This commit is contained in:
@@ -37,5 +37,9 @@ namespace Nova {
|
|||||||
// TODO: set_surface_size
|
// TODO: set_surface_size
|
||||||
// TODO: set_surface_mode
|
// TODO: set_surface_mode
|
||||||
// TODO: set_surface_state
|
// TODO: set_surface_state
|
||||||
|
|
||||||
|
[[nodiscard]] virtual SwapchainID create_swapchain(SurfaceID surface) = 0;
|
||||||
|
virtual void resize_swapchain(SwapchainID swapchain) = 0;
|
||||||
|
virtual void destroy_swapchain(SwapchainID swapchain) = 0;
|
||||||
};
|
};
|
||||||
} // namespace Nova
|
} // namespace Nova
|
||||||
|
|||||||
@@ -13,5 +13,8 @@ namespace Nova {
|
|||||||
struct RenderDevice;
|
struct RenderDevice;
|
||||||
|
|
||||||
struct Surface;
|
struct Surface;
|
||||||
|
struct Swapchain;
|
||||||
|
|
||||||
using SurfaceID = Surface*;
|
using SurfaceID = Surface*;
|
||||||
|
using SwapchainID = Swapchain*;
|
||||||
} // namespace Nova
|
} // namespace Nova
|
||||||
|
|||||||
@@ -14,7 +14,9 @@
|
|||||||
#include <nova/version.h>
|
#include <nova/version.h>
|
||||||
#include <vulkan/vulkan.h>
|
#include <vulkan/vulkan.h>
|
||||||
|
|
||||||
|
#include <algorithm>
|
||||||
#include <format>
|
#include <format>
|
||||||
|
#include <limits>
|
||||||
#include <unordered_map>
|
#include <unordered_map>
|
||||||
|
|
||||||
#define VALIDATION_LAYER "VK_LAYER_KHRONOS_validation"
|
#define VALIDATION_LAYER "VK_LAYER_KHRONOS_validation"
|
||||||
@@ -119,10 +121,176 @@ SurfaceID VulkanRenderDriver::create_surface(WindowID p_window) {
|
|||||||
|
|
||||||
void VulkanRenderDriver::destroy_surface(SurfaceID p_surface) {
|
void VulkanRenderDriver::destroy_surface(SurfaceID p_surface) {
|
||||||
NOVA_AUTO_TRACE();
|
NOVA_AUTO_TRACE();
|
||||||
|
NOVA_ASSERT(p_surface);
|
||||||
vkDestroySurfaceKHR(m_instance, p_surface->handle, get_allocator(VK_OBJECT_TYPE_SURFACE_KHR));
|
vkDestroySurfaceKHR(m_instance, p_surface->handle, get_allocator(VK_OBJECT_TYPE_SURFACE_KHR));
|
||||||
delete p_surface;
|
delete p_surface;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
SwapchainID VulkanRenderDriver::create_swapchain(SurfaceID p_surface) {
|
||||||
|
NOVA_AUTO_TRACE();
|
||||||
|
NOVA_ASSERT(p_surface);
|
||||||
|
|
||||||
|
Swapchain* swapchain = new Swapchain();
|
||||||
|
swapchain->surface = p_surface;
|
||||||
|
|
||||||
|
u32 count;
|
||||||
|
vkGetPhysicalDeviceSurfaceFormatsKHR(m_physical_device, p_surface->handle, &count, nullptr); // TODO: Check result
|
||||||
|
std::vector<VkSurfaceFormatKHR> formats(count);
|
||||||
|
vkGetPhysicalDeviceSurfaceFormatsKHR(m_physical_device, p_surface->handle, &count, formats.data()); // TODO: Check result
|
||||||
|
|
||||||
|
const VkFormat preferred_format = VK_FORMAT_B8G8R8A8_UNORM; // TODO: Get from config?
|
||||||
|
const VkFormat fallback_format = VK_FORMAT_R8G8B8A8_UNORM; // TODO: Get from config?
|
||||||
|
|
||||||
|
if (count == 1 && formats[0].format == VK_FORMAT_UNDEFINED) {
|
||||||
|
swapchain->format = preferred_format;
|
||||||
|
swapchain->color_space = formats[0].colorSpace;
|
||||||
|
} else {
|
||||||
|
for (const auto& format : formats) {
|
||||||
|
if (format.format == preferred_format || format.format == fallback_format) {
|
||||||
|
swapchain->format = format.format;
|
||||||
|
swapchain->color_space = format.colorSpace;
|
||||||
|
if (swapchain->format == preferred_format) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (swapchain->format == VK_FORMAT_UNDEFINED) {
|
||||||
|
throw std::runtime_error("Failed to find a supported swapchain format");
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: Create render pass
|
||||||
|
|
||||||
|
return swapchain;
|
||||||
|
}
|
||||||
|
|
||||||
|
void VulkanRenderDriver::resize_swapchain(SwapchainID p_swapchain) {
|
||||||
|
NOVA_AUTO_TRACE();
|
||||||
|
NOVA_ASSERT(p_swapchain);
|
||||||
|
|
||||||
|
Surface* surface = p_swapchain->surface;
|
||||||
|
|
||||||
|
// TODO: Release old swapchain resources
|
||||||
|
|
||||||
|
VkSurfaceCapabilitiesKHR capabilities;
|
||||||
|
if (vkGetPhysicalDeviceSurfaceCapabilitiesKHR(m_physical_device, surface->handle, &capabilities) != VK_SUCCESS) {
|
||||||
|
throw std::runtime_error("Failed to get surface capabilities");
|
||||||
|
}
|
||||||
|
|
||||||
|
VkExtent2D extent;
|
||||||
|
if (capabilities.currentExtent.width == std::numeric_limits<u32>::max()) {
|
||||||
|
extent.width = std::clamp(surface->width, capabilities.minImageExtent.width, capabilities.maxImageExtent.width);
|
||||||
|
extent.height =
|
||||||
|
std::clamp(surface->height, capabilities.minImageExtent.height, capabilities.maxImageExtent.height);
|
||||||
|
} else {
|
||||||
|
extent = capabilities.currentExtent;
|
||||||
|
surface->width = extent.width;
|
||||||
|
surface->height = extent.height;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (extent.width == 0 || extent.height == 0) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
u32 image_count = capabilities.minImageCount + 1;
|
||||||
|
if (capabilities.maxImageCount > 0 && image_count > capabilities.maxImageCount) {
|
||||||
|
image_count = capabilities.maxImageCount;
|
||||||
|
}
|
||||||
|
|
||||||
|
u32 present_mode_count;
|
||||||
|
vkGetPhysicalDeviceSurfacePresentModesKHR(
|
||||||
|
m_physical_device,
|
||||||
|
surface->handle,
|
||||||
|
&present_mode_count,
|
||||||
|
nullptr
|
||||||
|
); // TODO: Check result
|
||||||
|
std::vector<VkPresentModeKHR> present_modes(present_mode_count);
|
||||||
|
vkGetPhysicalDeviceSurfacePresentModesKHR(
|
||||||
|
m_physical_device,
|
||||||
|
surface->handle,
|
||||||
|
&present_mode_count,
|
||||||
|
present_modes.data()
|
||||||
|
); // TODO: Check result
|
||||||
|
|
||||||
|
VkPresentModeKHR present_mode = VK_PRESENT_MODE_MAILBOX_KHR; // TODO: Get from config
|
||||||
|
if (std::find(present_modes.begin(), present_modes.end(), present_mode) == present_modes.end()) {
|
||||||
|
NOVA_WARN("Preferred present mode not supported, falling back to FIFO");
|
||||||
|
present_mode = VK_PRESENT_MODE_FIFO_KHR;
|
||||||
|
// TODO: Update config
|
||||||
|
}
|
||||||
|
|
||||||
|
VkSwapchainCreateInfoKHR swap_create {};
|
||||||
|
swap_create.sType = VK_STRUCTURE_TYPE_SWAPCHAIN_CREATE_INFO_KHR;
|
||||||
|
swap_create.surface = surface->handle;
|
||||||
|
swap_create.minImageCount = image_count;
|
||||||
|
swap_create.imageFormat = p_swapchain->format;
|
||||||
|
swap_create.imageColorSpace = p_swapchain->color_space;
|
||||||
|
swap_create.imageExtent = extent;
|
||||||
|
swap_create.imageArrayLayers = 1; // TODO: Support VR
|
||||||
|
swap_create.imageUsage = VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT; // TRANSFER_DST_BIT ???
|
||||||
|
swap_create.imageSharingMode = VK_SHARING_MODE_EXCLUSIVE;
|
||||||
|
swap_create.preTransform = capabilities.currentTransform;
|
||||||
|
swap_create.compositeAlpha = VK_COMPOSITE_ALPHA_OPAQUE_BIT_KHR; // TODO: Support transparent windows
|
||||||
|
swap_create.presentMode = present_mode;
|
||||||
|
swap_create.clipped = VK_TRUE;
|
||||||
|
swap_create.oldSwapchain = VK_NULL_HANDLE; // TODO: Handle old swapchain
|
||||||
|
|
||||||
|
if (vkCreateSwapchainKHR(m_device, &swap_create, get_allocator(VK_OBJECT_TYPE_SWAPCHAIN_KHR), &p_swapchain->handle)
|
||||||
|
!= VK_SUCCESS) {
|
||||||
|
throw std::runtime_error("Failed to create swapchain");
|
||||||
|
}
|
||||||
|
|
||||||
|
NOVA_LOG("VkSwapchainKHR created");
|
||||||
|
|
||||||
|
vkGetSwapchainImagesKHR(m_device, p_swapchain->handle, &image_count, nullptr); // TODO: Check result
|
||||||
|
p_swapchain->images.resize(image_count);
|
||||||
|
vkGetSwapchainImagesKHR(m_device, p_swapchain->handle, &image_count, p_swapchain->images.data()); // TODO: Check result
|
||||||
|
|
||||||
|
VkImageViewCreateInfo view_create {};
|
||||||
|
view_create.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO;
|
||||||
|
view_create.viewType = VK_IMAGE_VIEW_TYPE_2D;
|
||||||
|
view_create.format = p_swapchain->format;
|
||||||
|
view_create.components.r = VK_COMPONENT_SWIZZLE_IDENTITY;
|
||||||
|
view_create.components.g = VK_COMPONENT_SWIZZLE_IDENTITY;
|
||||||
|
view_create.components.b = VK_COMPONENT_SWIZZLE_IDENTITY;
|
||||||
|
view_create.components.a = VK_COMPONENT_SWIZZLE_IDENTITY;
|
||||||
|
view_create.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
|
||||||
|
view_create.subresourceRange.baseMipLevel = 0;
|
||||||
|
view_create.subresourceRange.levelCount = 1;
|
||||||
|
view_create.subresourceRange.baseArrayLayer = 0; // TODO: Support VR
|
||||||
|
view_create.subresourceRange.layerCount = 1; // TODO: Support VR
|
||||||
|
|
||||||
|
p_swapchain->image_views.resize(image_count);
|
||||||
|
for (u32 i = 0; i < image_count; i++) {
|
||||||
|
view_create.image = p_swapchain->images[i];
|
||||||
|
if (vkCreateImageView(m_device, &view_create, get_allocator(VK_OBJECT_TYPE_IMAGE_VIEW), &p_swapchain->image_views[i])
|
||||||
|
!= VK_SUCCESS) {
|
||||||
|
throw std::runtime_error("Failed to create image view");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: Create framebuffers
|
||||||
|
}
|
||||||
|
|
||||||
|
void VulkanRenderDriver::destroy_swapchain(SwapchainID p_swapchain) {
|
||||||
|
NOVA_AUTO_TRACE();
|
||||||
|
NOVA_ASSERT(p_swapchain);
|
||||||
|
|
||||||
|
for (const auto& framebuffer : p_swapchain->framebuffers) {
|
||||||
|
vkDestroyFramebuffer(m_device, framebuffer, get_allocator(VK_OBJECT_TYPE_FRAMEBUFFER));
|
||||||
|
}
|
||||||
|
for (const auto& image_view : p_swapchain->image_views) {
|
||||||
|
vkDestroyImageView(m_device, image_view, get_allocator(VK_OBJECT_TYPE_IMAGE_VIEW));
|
||||||
|
}
|
||||||
|
if (p_swapchain->handle) {
|
||||||
|
vkDestroySwapchainKHR(m_device, p_swapchain->handle, get_allocator(VK_OBJECT_TYPE_SWAPCHAIN_KHR));
|
||||||
|
}
|
||||||
|
// TODO: Destroy render pass
|
||||||
|
|
||||||
|
delete p_swapchain;
|
||||||
|
}
|
||||||
|
|
||||||
VkInstance VulkanRenderDriver::get_instance() const {
|
VkInstance VulkanRenderDriver::get_instance() const {
|
||||||
return m_instance;
|
return m_instance;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -16,6 +16,19 @@
|
|||||||
namespace Nova {
|
namespace Nova {
|
||||||
struct Surface {
|
struct Surface {
|
||||||
VkSurfaceKHR handle = VK_NULL_HANDLE;
|
VkSurfaceKHR handle = VK_NULL_HANDLE;
|
||||||
|
u32 width = 0;
|
||||||
|
u32 height = 0;
|
||||||
|
bool dirty = false; // TODO: Use state enum
|
||||||
|
};
|
||||||
|
|
||||||
|
struct Swapchain {
|
||||||
|
VkSwapchainKHR handle = VK_NULL_HANDLE;
|
||||||
|
VkFormat format = VK_FORMAT_UNDEFINED;
|
||||||
|
VkColorSpaceKHR color_space = VK_COLOR_SPACE_SRGB_NONLINEAR_KHR;
|
||||||
|
std::vector<VkImage> images;
|
||||||
|
std::vector<VkImageView> image_views;
|
||||||
|
std::vector<VkFramebuffer> framebuffers;
|
||||||
|
SurfaceID surface = nullptr;
|
||||||
};
|
};
|
||||||
|
|
||||||
class VulkanRenderDriver final : public RenderDriver {
|
class VulkanRenderDriver final : public RenderDriver {
|
||||||
@@ -36,6 +49,10 @@ namespace Nova {
|
|||||||
[[nodiscard]] SurfaceID create_surface(WindowID window) override;
|
[[nodiscard]] SurfaceID create_surface(WindowID window) override;
|
||||||
void destroy_surface(SurfaceID surface) override;
|
void destroy_surface(SurfaceID surface) override;
|
||||||
|
|
||||||
|
[[nodiscard]] SwapchainID create_swapchain(SurfaceID surface) override;
|
||||||
|
void resize_swapchain(SwapchainID swapchain) override;
|
||||||
|
void destroy_swapchain(SwapchainID swapchain) override;
|
||||||
|
|
||||||
[[nodiscard]] VkInstance get_instance() const;
|
[[nodiscard]] VkInstance get_instance() const;
|
||||||
[[nodiscard]] VkAllocationCallbacks* get_allocator(VkObjectType type) const;
|
[[nodiscard]] VkAllocationCallbacks* get_allocator(VkObjectType type) const;
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user