Replace RenderWindow with swapchains

This commit is contained in:
SirLynix
2023-01-21 12:02:34 +01:00
committed by Jérôme Leclercq
parent 8db1c04568
commit 18851c9185
66 changed files with 1404 additions and 2048 deletions

View File

@@ -9,6 +9,7 @@
#include <Nazara/VulkanRenderer/VulkanRenderPipeline.hpp>
#include <Nazara/VulkanRenderer/VulkanRenderPipelineLayout.hpp>
#include <Nazara/VulkanRenderer/VulkanShaderModule.hpp>
#include <Nazara/VulkanRenderer/VulkanSwapchain.hpp>
#include <Nazara/VulkanRenderer/VulkanTexture.hpp>
#include <Nazara/VulkanRenderer/VulkanTextureFramebuffer.hpp>
#include <Nazara/VulkanRenderer/VulkanTextureSampler.hpp>
@@ -85,6 +86,11 @@ namespace Nz
return stage;
}
std::shared_ptr<Swapchain> VulkanDevice::InstantiateSwapchain(WindowHandle windowHandle, const Vector2ui& windowSize, const SwapchainParameters& parameters)
{
return std::make_shared<VulkanSwapchain>(*this, windowHandle, windowSize, parameters);
}
std::shared_ptr<Texture> VulkanDevice::InstantiateTexture(const TextureInfo& params)
{
return std::make_shared<VulkanTexture>(*this, params);

View File

@@ -5,13 +5,13 @@
#include <Nazara/VulkanRenderer/VulkanRenderImage.hpp>
#include <Nazara/VulkanRenderer/VulkanCommandBuffer.hpp>
#include <Nazara/VulkanRenderer/VulkanCommandBufferBuilder.hpp>
#include <Nazara/VulkanRenderer/VulkanRenderWindow.hpp>
#include <Nazara/VulkanRenderer/VulkanSwapchain.hpp>
#include <stdexcept>
#include <Nazara/VulkanRenderer/Debug.hpp>
namespace Nz
{
VulkanRenderImage::VulkanRenderImage(VulkanRenderWindow& owner) :
VulkanRenderImage::VulkanRenderImage(VulkanSwapchain& owner) :
m_owner(owner),
m_uploadPool(m_owner.GetDevice(), 2 * 1024 * 1024)
{

View File

@@ -6,8 +6,7 @@
#include <Nazara/Core/ErrorFlags.hpp>
#include <Nazara/Renderer/RenderDevice.hpp>
#include <Nazara/VulkanRenderer/VulkanBuffer.hpp>
#include <Nazara/VulkanRenderer/VulkanRenderWindow.hpp>
#include <Nazara/VulkanRenderer/VulkanSurface.hpp>
#include <Nazara/VulkanRenderer/VulkanSwapchain.hpp>
#include <Nazara/VulkanRenderer/Wrapper/Loader.hpp>
#include <cassert>
#include <sstream>
@@ -20,16 +19,6 @@ namespace Nz
Vulkan::Uninitialize();
}
std::unique_ptr<RenderSurface> VulkanRenderer::CreateRenderSurfaceImpl()
{
return std::make_unique<VulkanSurface>();
}
std::unique_ptr<RenderWindowImpl> VulkanRenderer::CreateRenderWindowImpl(RenderWindow& owner)
{
return std::make_unique<VulkanRenderWindow>(owner);
}
std::shared_ptr<RenderDevice> VulkanRenderer::InstanciateRenderDevice(std::size_t deviceIndex, const RenderDeviceFeatures& enabledFeatures)
{
const auto& physDevices = Vulkan::GetPhysicalDevices();

View File

@@ -1,98 +0,0 @@
// Copyright (C) 2022 Jérôme "Lynix" Leclercq (lynix680@gmail.com)
// This file is part of the "Nazara Engine - Vulkan renderer"
// For conditions of distribution and use, see copyright notice in Config.hpp
#include <Nazara/VulkanRenderer/VulkanSurface.hpp>
#include <Nazara/VulkanRenderer/Vulkan.hpp>
#include <vulkan/vk_sdk_platform.h>
#include <Nazara/VulkanRenderer/Debug.hpp>
#ifdef VK_USE_PLATFORM_METAL_EXT
#include <objc/runtime.h>
#include <vulkan/vulkan_metal.h>
#endif
namespace Nz
{
#ifdef VK_USE_PLATFORM_METAL_EXT
id CreateAndAttachMetalLayer(void* window);
#endif
VulkanSurface::VulkanSurface() :
m_surface(Vulkan::GetInstance())
{
}
bool VulkanSurface::Create(WindowHandle handle)
{
bool success = false;
#if defined(NAZARA_PLATFORM_WINDOWS)
{
NazaraAssert(handle.type == WindowBackend::Windows, "expected Windows window");
HWND winHandle = reinterpret_cast<HWND>(handle.windows.window);
HINSTANCE instance = reinterpret_cast<HINSTANCE>(GetWindowLongPtrW(winHandle, GWLP_HINSTANCE));
success = m_surface.Create(instance, winHandle);
}
#elif defined(NAZARA_PLATFORM_LINUX)
{
switch (handle.type)
{
#ifdef VK_USE_PLATFORM_WAYLAND_KHR
case WindowBackend::Wayland:
{
wl_display* display = static_cast<wl_display*>(handle.wayland.display);
wl_surface* surface = static_cast<wl_surface*>(handle.wayland.surface);
success = m_surface.Create(display, surface);
break;
}
#endif
#ifdef VK_USE_PLATFORM_XLIB_KHR
case WindowBackend::X11:
{
Display* display = static_cast<Display*>(handle.x11.display);
::Window window = static_cast<::Window>(handle.x11.window);
success = m_surface.Create(display, window);
break;
}
#endif
default:
{
NazaraError("unhandled window type");
return false;
}
}
}
#elif defined(NAZARA_PLATFORM_MACOS)
{
NazaraAssert(handle.type == WindowBackend::Cocoa, "expected cocoa window");
id layer = CreateAndAttachMetalLayer(handle.cocoa.window);
success = m_surface.Create(layer);
}
#else
#error This OS is not supported by Vulkan
#endif
if (!success)
{
NazaraError("Failed to create Vulkan surface: " + TranslateVulkanError(m_surface.GetLastErrorCode()));
return false;
}
return true;
}
void VulkanSurface::Destroy()
{
m_surface.Destroy();
}
}
#if defined(NAZARA_PLATFORM_WINDOWS)
#include <Nazara/Core/AntiWindows.hpp>
#endif

View File

@@ -4,13 +4,17 @@
namespace Nz
{
id CreateAndAttachMetalLayer(void* window) {
NSWindow* obj = (__bridge NSWindow*)window;
id CreateAndAttachMetalLayer(void* window)
{
NSWindow* obj = (__bridge NSWindow*) window;
NSView* view = [[NSView alloc] initWithFrame:obj.frame];
[view setLayer:[CAMetalLayer layer]];
[view setWantsLayer:YES];
view.autoresizingMask = NSViewWidthSizable | NSViewHeightSizable;
[obj.contentView addSubview:view];
return view.layer;
}
}

View File

@@ -2,34 +2,160 @@
// This file is part of the "Nazara Engine - Vulkan renderer"
// For conditions of distribution and use, see copyright notice in Config.hpp
#include <Nazara/VulkanRenderer/VulkanRenderWindow.hpp>
#include <Nazara/VulkanRenderer/VulkanSwapchain.hpp>
#include <Nazara/Core/Error.hpp>
#include <Nazara/Core/ErrorFlags.hpp>
#include <Nazara/Math/Vector2.hpp>
#include <Nazara/Renderer/RenderWindow.hpp>
#include <Nazara/Renderer/WindowSwapchain.hpp>
#include <Nazara/Utility/PixelFormat.hpp>
#include <Nazara/VulkanRenderer/Vulkan.hpp>
#include <Nazara/VulkanRenderer/VulkanCommandPool.hpp>
#include <Nazara/VulkanRenderer/VulkanDevice.hpp>
#include <Nazara/VulkanRenderer/VulkanSurface.hpp>
#include <Nazara/Utils/StackArray.hpp>
#include <array>
#include <stdexcept>
#ifdef VK_USE_PLATFORM_METAL_EXT
#include <objc/runtime.h>
#include <vulkan/vulkan_metal.h>
#endif
#include <Nazara/VulkanRenderer/Debug.hpp>
namespace Nz
{
VulkanRenderWindow::VulkanRenderWindow(RenderWindow& owner) :
#ifdef VK_USE_PLATFORM_METAL_EXT
id CreateAndAttachMetalLayer(void* window);
#endif
VulkanSwapchain::VulkanSwapchain(VulkanDevice& device, WindowHandle windowHandle, const Vector2ui& windowSize, const SwapchainParameters& parameters) :
m_currentFrame(0),
m_owner(owner),
m_surface(device.GetInstance()),
m_swapchainSize(windowSize),
m_device(device),
m_shouldRecreateSwapchain(false)
{
if (!SetupSurface(windowHandle))
throw std::runtime_error("failed to create surface");
const auto& physDeviceInfo = m_device.GetPhysicalDeviceInfo();
const std::vector<Vk::Device::QueueFamilyInfo>& queueFamilyInfo = m_device.GetEnabledQueues();
UInt32 graphicsFamilyQueueIndex = UINT32_MAX;
UInt32 presentableFamilyQueueIndex = UINT32_MAX;
for (const Vk::Device::QueueFamilyInfo& queueInfo : queueFamilyInfo)
{
bool supported = false;
if (m_surface.GetSupportPresentation(physDeviceInfo.physDevice, queueInfo.familyIndex, &supported) && supported)
{
if (presentableFamilyQueueIndex == UINT32_MAX || queueInfo.flags & VK_QUEUE_GRAPHICS_BIT)
{
presentableFamilyQueueIndex = queueInfo.familyIndex;
if (queueInfo.flags & VK_QUEUE_GRAPHICS_BIT)
{
graphicsFamilyQueueIndex = queueInfo.familyIndex;
break;
}
}
}
}
if (presentableFamilyQueueIndex == UINT32_MAX)
throw std::runtime_error("device doesn't support presenting to this surface");
if (graphicsFamilyQueueIndex == UINT32_MAX)
{
for (const Vk::Device::QueueFamilyInfo& queueInfo : queueFamilyInfo)
{
if (queueInfo.flags & VK_QUEUE_GRAPHICS_BIT)
{
graphicsFamilyQueueIndex = queueInfo.familyIndex;
break;
}
}
}
if (graphicsFamilyQueueIndex == UINT32_MAX)
throw std::runtime_error("device doesn't support graphics operation");
UInt32 transferFamilyQueueIndex = UINT32_MAX;
// Search for a transfer queue (first one being different to the graphics one)
for (const Vk::Device::QueueFamilyInfo& queueInfo : queueFamilyInfo)
{
// Transfer bit is not mandatory if compute and graphics bits are set (as they implicitly support transfer)
if (queueInfo.flags & (VK_QUEUE_COMPUTE_BIT | VK_QUEUE_GRAPHICS_BIT | VK_QUEUE_TRANSFER_BIT))
{
transferFamilyQueueIndex = queueInfo.familyIndex;
if (transferFamilyQueueIndex != graphicsFamilyQueueIndex)
break;
}
}
assert(transferFamilyQueueIndex != UINT32_MAX);
m_graphicsQueue = m_device.GetQueue(graphicsFamilyQueueIndex, 0);
m_presentQueue = m_device.GetQueue(presentableFamilyQueueIndex, 0);
m_transferQueue = m_device.GetQueue(transferFamilyQueueIndex, 0);
std::vector<VkSurfaceFormatKHR> surfaceFormats;
if (!m_surface.GetFormats(physDeviceInfo.physDevice, &surfaceFormats))
throw std::runtime_error("failed to query supported surface formats");
m_surfaceFormat = [&]() -> VkSurfaceFormatKHR
{
if (surfaceFormats.size() == 1 && surfaceFormats.front().format == VK_FORMAT_UNDEFINED)
{
// If the list contains one undefined format, it means any format can be used
return { VK_FORMAT_R8G8B8A8_UNORM, VK_COLOR_SPACE_SRGB_NONLINEAR_KHR };
}
else
{
// Search for RGBA8 and default to first format
for (const VkSurfaceFormatKHR& surfaceFormat : surfaceFormats)
{
if (surfaceFormat.format == VK_FORMAT_R8G8B8A8_UNORM)
return surfaceFormat;
}
return surfaceFormats.front();
}
}();
m_depthStencilFormat = VK_FORMAT_UNDEFINED;
if (!parameters.depthFormats.empty())
{
for (PixelFormat format : parameters.depthFormats)
{
PixelFormatContent formatContent = PixelFormatInfo::GetContent(format);
if (formatContent != PixelFormatContent::DepthStencil && formatContent != PixelFormatContent::Stencil)
NazaraWarning("Invalid format " + PixelFormatInfo::GetName(format) + " for depth-stencil attachment");
m_depthStencilFormat = ToVulkan(format);
if (m_depthStencilFormat == VK_FORMAT_UNDEFINED)
continue;
VkFormatProperties formatProperties = m_device.GetInstance().GetPhysicalDeviceFormatProperties(physDeviceInfo.physDevice, m_depthStencilFormat);
if (formatProperties.optimalTilingFeatures & VK_FORMAT_FEATURE_DEPTH_STENCIL_ATTACHMENT_BIT)
break; //< Found it
m_depthStencilFormat = VK_FORMAT_UNDEFINED;
}
if (m_depthStencilFormat == VK_FORMAT_UNDEFINED)
throw std::runtime_error("failed to find a support depth-stencil format");
}
if (!SetupRenderPass())
throw std::runtime_error("failed to create renderpass");
if (!CreateSwapchain())
throw std::runtime_error("failed to create swapchain");
}
VulkanRenderWindow::~VulkanRenderWindow()
VulkanSwapchain::~VulkanSwapchain()
{
if (m_device)
m_device->WaitForIdle();
m_device.WaitForIdle();
m_concurrentImageData.clear();
m_renderPass.reset();
@@ -37,22 +163,13 @@ namespace Nz
m_swapchain.Destroy();
}
RenderFrame VulkanRenderWindow::Acquire()
RenderFrame VulkanSwapchain::AcquireFrame()
{
bool invalidateFramebuffer = false;
Vector2ui size = m_owner.GetSize();
// Special case: window is minimized
if (size == Nz::Vector2ui::Zero() || m_owner.IsMinimized())
return RenderFrame();
if (m_shouldRecreateSwapchain || size != m_swapchainSize)
if (m_shouldRecreateSwapchain)
{
Vk::Surface& vulkanSurface = static_cast<VulkanSurface*>(m_owner.GetSurface())->GetSurface();
OnRenderTargetSizeChange(this, size);
if (!CreateSwapchain(vulkanSurface, size))
if (!CreateSwapchain())
throw std::runtime_error("failed to recreate swapchain");
m_shouldRecreateSwapchain = false;
@@ -79,7 +196,7 @@ namespace Nz
case VK_ERROR_OUT_OF_DATE_KHR:
m_shouldRecreateSwapchain = true;
return Acquire();
return AcquireFrame();
// Not expected (since timeout is infinite)
case VK_TIMEOUT:
@@ -102,191 +219,16 @@ namespace Nz
currentFrame.Reset(imageIndex);
return RenderFrame(&currentFrame, invalidateFramebuffer, size, imageIndex);
return RenderFrame(&currentFrame, invalidateFramebuffer, m_swapchainSize, imageIndex);
}
bool VulkanRenderWindow::Create(RendererImpl* /*renderer*/, RenderSurface* surface, const RenderWindowParameters& parameters)
{
std::shared_ptr<VulkanDevice> device = std::static_pointer_cast<VulkanDevice>(m_owner.GetRenderDevice());
const auto& physDeviceInfo = device->GetPhysicalDeviceInfo();
Vk::Surface& vulkanSurface = static_cast<VulkanSurface*>(surface)->GetSurface();
const std::vector<Vk::Device::QueueFamilyInfo>& queueFamilyInfo = device->GetEnabledQueues();
UInt32 graphicsFamilyQueueIndex = UINT32_MAX;
UInt32 presentableFamilyQueueIndex = UINT32_MAX;
for (const Vk::Device::QueueFamilyInfo& queueInfo : queueFamilyInfo)
{
bool supported = false;
if (vulkanSurface.GetSupportPresentation(physDeviceInfo.physDevice, queueInfo.familyIndex, &supported) && supported)
{
if (presentableFamilyQueueIndex == UINT32_MAX || queueInfo.flags & VK_QUEUE_GRAPHICS_BIT)
{
presentableFamilyQueueIndex = queueInfo.familyIndex;
if (queueInfo.flags & VK_QUEUE_GRAPHICS_BIT)
{
graphicsFamilyQueueIndex = queueInfo.familyIndex;
break;
}
}
}
}
if (presentableFamilyQueueIndex == UINT32_MAX)
{
NazaraError("device doesn't support presenting to this surface");
return false;
}
if (graphicsFamilyQueueIndex == UINT32_MAX)
{
for (const Vk::Device::QueueFamilyInfo& queueInfo : queueFamilyInfo)
{
if (queueInfo.flags & VK_QUEUE_GRAPHICS_BIT)
{
graphicsFamilyQueueIndex = queueInfo.familyIndex;
break;
}
}
}
if (graphicsFamilyQueueIndex == UINT32_MAX)
{
NazaraError("device doesn't support graphics operations");
return false;
}
UInt32 transferFamilyQueueIndex = UINT32_MAX;
// Search for a transfer queue (first one being different to the graphics one)
for (const Vk::Device::QueueFamilyInfo& queueInfo : queueFamilyInfo)
{
// Transfer bit is not mandatory if compute and graphics bits are set (as they implicitly support transfer)
if (queueInfo.flags & (VK_QUEUE_COMPUTE_BIT | VK_QUEUE_GRAPHICS_BIT | VK_QUEUE_TRANSFER_BIT))
{
transferFamilyQueueIndex = queueInfo.familyIndex;
if (transferFamilyQueueIndex != graphicsFamilyQueueIndex)
break;
}
}
assert(transferFamilyQueueIndex != UINT32_MAX);
m_device = std::move(device);
m_graphicsQueue = m_device->GetQueue(graphicsFamilyQueueIndex, 0);
m_presentQueue = m_device->GetQueue(presentableFamilyQueueIndex, 0);
m_transferQueue = m_device->GetQueue(transferFamilyQueueIndex, 0);
std::vector<VkSurfaceFormatKHR> surfaceFormats;
if (!vulkanSurface.GetFormats(physDeviceInfo.physDevice, &surfaceFormats))
{
NazaraError("Failed to query supported surface formats");
return false;
}
m_surfaceFormat = [&] () -> VkSurfaceFormatKHR
{
if (surfaceFormats.size() == 1 && surfaceFormats.front().format == VK_FORMAT_UNDEFINED)
{
// If the list contains one undefined format, it means any format can be used
return { VK_FORMAT_R8G8B8A8_UNORM, VK_COLOR_SPACE_SRGB_NONLINEAR_KHR };
}
else
{
// Search for RGBA8 and default to first format
for (const VkSurfaceFormatKHR& surfaceFormat : surfaceFormats)
{
if (surfaceFormat.format == VK_FORMAT_R8G8B8A8_UNORM)
return surfaceFormat;
}
return surfaceFormats.front();
}
}();
m_depthStencilFormat = VK_FORMAT_MAX_ENUM;
if (!parameters.depthFormats.empty())
{
for (PixelFormat format : parameters.depthFormats)
{
switch (format)
{
case PixelFormat::Depth16:
m_depthStencilFormat = VK_FORMAT_D16_UNORM;
break;
case PixelFormat::Depth16Stencil8:
m_depthStencilFormat = VK_FORMAT_D16_UNORM_S8_UINT;
break;
case PixelFormat::Depth24:
case PixelFormat::Depth24Stencil8:
m_depthStencilFormat = VK_FORMAT_D24_UNORM_S8_UINT;
break;
case PixelFormat::Depth32F:
m_depthStencilFormat = VK_FORMAT_D32_SFLOAT;
break;
case PixelFormat::Depth32FStencil8:
m_depthStencilFormat = VK_FORMAT_D32_SFLOAT_S8_UINT;
break;
case PixelFormat::Stencil1:
case PixelFormat::Stencil4:
case PixelFormat::Stencil8:
m_depthStencilFormat = VK_FORMAT_S8_UINT;
break;
case PixelFormat::Stencil16:
continue;
default:
{
PixelFormatContent formatContent = PixelFormatInfo::GetContent(format);
if (formatContent != PixelFormatContent::DepthStencil && formatContent != PixelFormatContent::Stencil)
NazaraWarning("Invalid format " + PixelFormatInfo::GetName(format) + " for depth-stencil attachment");
m_depthStencilFormat = VK_FORMAT_MAX_ENUM;
break;
}
}
if (m_depthStencilFormat != VK_FORMAT_MAX_ENUM)
{
VkFormatProperties formatProperties = m_device->GetInstance().GetPhysicalDeviceFormatProperties(physDeviceInfo.physDevice, m_depthStencilFormat);
if (formatProperties.optimalTilingFeatures & VK_FORMAT_FEATURE_DEPTH_STENCIL_ATTACHMENT_BIT)
break; //< Found it
m_depthStencilFormat = VK_FORMAT_MAX_ENUM;
}
}
}
if (!SetupRenderPass())
{
NazaraError("Failed to create render pass");
return false;
}
if (!CreateSwapchain(vulkanSurface, m_owner.GetSize()))
{
NazaraError("failed to create swapchain");
return false;
}
return true;
}
std::shared_ptr<CommandPool> VulkanRenderWindow::CreateCommandPool(QueueType queueType)
std::shared_ptr<CommandPool> VulkanSwapchain::CreateCommandPool(QueueType queueType)
{
UInt32 queueFamilyIndex = [&] {
switch (queueType)
{
case QueueType::Compute:
return m_device->GetDefaultFamilyIndex(QueueType::Compute);
return m_device.GetDefaultFamilyIndex(QueueType::Compute);
case QueueType::Graphics:
return m_graphicsQueue.GetQueueFamilyIndex();
@@ -298,31 +240,62 @@ namespace Nz
throw std::runtime_error("invalid queue type " + std::to_string(UnderlyingCast(queueType)));
}();
return std::make_shared<VulkanCommandPool>(*m_device, queueFamilyIndex);
return std::make_shared<VulkanCommandPool>(m_device, queueFamilyIndex);
}
const VulkanWindowFramebuffer& VulkanRenderWindow::GetFramebuffer(std::size_t i) const
bool VulkanSwapchain::CreateSwapchain()
{
if (!SetupSwapchain(m_device.GetPhysicalDeviceInfo()))
{
NazaraError("Failed to create swapchain");
return false;
}
if (m_depthStencilFormat != VK_FORMAT_MAX_ENUM && !SetupDepthBuffer())
{
NazaraError("Failed to create depth buffer");
return false;
}
if (!SetupFrameBuffers())
{
NazaraError("failed to create framebuffers");
return false;
}
return true;
}
const VulkanWindowFramebuffer& VulkanSwapchain::GetFramebuffer(std::size_t i) const
{
assert(i < m_framebuffers.size());
return m_framebuffers[i];
}
std::size_t VulkanRenderWindow::GetFramebufferCount() const
std::size_t VulkanSwapchain::GetFramebufferCount() const
{
return m_framebuffers.size();
}
const VulkanRenderPass& VulkanRenderWindow::GetRenderPass() const
const VulkanRenderPass& VulkanSwapchain::GetRenderPass() const
{
return *m_renderPass;
}
const Vector2ui& VulkanRenderWindow::GetSize() const
const Vector2ui& VulkanSwapchain::GetSize() const
{
return m_swapchainSize;
}
void VulkanRenderWindow::Present(UInt32 imageIndex, VkSemaphore waitSemaphore)
void VulkanSwapchain::NotifyResize(const Vector2ui& newSize)
{
OnRenderTargetSizeChange(this, newSize);
m_swapchainSize = newSize;
m_shouldRecreateSwapchain = true;
}
void VulkanSwapchain::Present(UInt32 imageIndex, VkSemaphore waitSemaphore)
{
NazaraAssert(imageIndex < m_inflightFences.size(), "Invalid image index");
@@ -354,31 +327,7 @@ namespace Nz
}
}
bool VulkanRenderWindow::CreateSwapchain(Vk::Surface& surface, const Vector2ui& size)
{
assert(m_device);
if (!SetupSwapchain(m_device->GetPhysicalDeviceInfo(), surface, size))
{
NazaraError("Failed to create swapchain");
return false;
}
if (m_depthStencilFormat != VK_FORMAT_MAX_ENUM && !SetupDepthBuffer(size))
{
NazaraError("Failed to create depth buffer");
return false;
}
if (!SetupFrameBuffers(size))
{
NazaraError("failed to create framebuffers");
return false;
}
return true;
}
bool VulkanRenderWindow::SetupDepthBuffer(const Vector2ui& size)
bool VulkanSwapchain::SetupDepthBuffer()
{
VkImageCreateInfo imageCreateInfo = {
VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO, // VkStructureType sType;
@@ -386,7 +335,7 @@ namespace Nz
0U, // VkImageCreateFlags flags;
VK_IMAGE_TYPE_2D, // VkImageType imageType;
m_depthStencilFormat, // VkFormat format;
{size.x, size.y, 1U}, // VkExtent3D extent;
{ m_swapchainSize.x, m_swapchainSize.y, 1U }, // VkExtent3D extent;
1U, // uint32_t mipLevels;
1U, // uint32_t arrayLayers;
VK_SAMPLE_COUNT_1_BIT, // VkSampleCountFlagBits samples;
@@ -398,14 +347,14 @@ namespace Nz
VK_IMAGE_LAYOUT_UNDEFINED, // VkImageLayout initialLayout;
};
if (!m_depthBuffer.Create(*m_device, imageCreateInfo))
if (!m_depthBuffer.Create(m_device, imageCreateInfo))
{
NazaraError("Failed to create depth buffer");
return false;
}
VkMemoryRequirements memoryReq = m_depthBuffer.GetMemoryRequirements();
if (!m_depthBufferMemory.Create(*m_device, memoryReq.size, memoryReq.memoryTypeBits, VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT))
if (!m_depthBufferMemory.Create(m_device, memoryReq.size, memoryReq.memoryTypeBits, VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT))
{
NazaraError("Failed to allocate depth buffer memory");
return false;
@@ -433,21 +382,21 @@ namespace Nz
VK_IMAGE_VIEW_TYPE_2D, // VkImageViewType viewType;
m_depthStencilFormat, // VkFormat format;
{ // VkComponentMapping components;
VK_COMPONENT_SWIZZLE_R, // VkComponentSwizzle .r;
VK_COMPONENT_SWIZZLE_G, // VkComponentSwizzle .g;
VK_COMPONENT_SWIZZLE_B, // VkComponentSwizzle .b;
VK_COMPONENT_SWIZZLE_A // VkComponentSwizzle .a;
VK_COMPONENT_SWIZZLE_R, // VkComponentSwizzle .r;
VK_COMPONENT_SWIZZLE_G, // VkComponentSwizzle .g;
VK_COMPONENT_SWIZZLE_B, // VkComponentSwizzle .b;
VK_COMPONENT_SWIZZLE_A // VkComponentSwizzle .a;
},
{ // VkImageSubresourceRange subresourceRange;
aspectMask, // VkImageAspectFlags .aspectMask;
0, // uint32_t .baseMipLevel;
1, // uint32_t .levelCount;
0, // uint32_t .baseArrayLayer;
1 // uint32_t .layerCount;
aspectMask, // VkImageAspectFlags .aspectMask;
0, // uint32_t .baseMipLevel;
1, // uint32_t .levelCount;
0, // uint32_t .baseArrayLayer;
1 // uint32_t .layerCount;
}
};
if (!m_depthBufferView.Create(*m_device, imageViewCreateInfo))
if (!m_depthBufferView.Create(m_device, imageViewCreateInfo))
{
NazaraError("Failed to create depth buffer view");
return false;
@@ -456,7 +405,7 @@ namespace Nz
return true;
}
bool VulkanRenderWindow::SetupFrameBuffers(const Vector2ui& size)
bool VulkanSwapchain::SetupFrameBuffers()
{
UInt32 imageCount = m_swapchain.GetImageCount();
@@ -473,14 +422,14 @@ namespace Nz
m_renderPass->GetRenderPass(),
(attachments[1] != VK_NULL_HANDLE) ? 2U : 1U,
attachments.data(),
size.x,
size.y,
m_swapchainSize.x,
m_swapchainSize.y,
1U
};
Vk::Framebuffer framebuffer;
if (!framebuffer.Create(*m_device, frameBufferCreate))
if (!framebuffer.Create(*m_swapchain.GetDevice(), frameBufferCreate))
{
NazaraError("Failed to create framebuffer for image #" + NumberToString(i) + ": " + TranslateVulkanError(framebuffer.GetLastErrorCode()));
return false;
@@ -492,7 +441,7 @@ namespace Nz
return true;
}
bool VulkanRenderWindow::SetupRenderPass()
bool VulkanSwapchain::SetupRenderPass()
{
std::optional<PixelFormat> colorFormat = FromVulkan(m_surfaceFormat.format);
if (!colorFormat)
@@ -517,34 +466,98 @@ namespace Nz
std::vector<RenderPass::SubpassDependency> subpassDependencies;
BuildRenderPass(*colorFormat, depthStencilFormat.value_or(PixelFormat::Undefined), attachments, subpassDescriptions, subpassDependencies);
m_renderPass.emplace(*m_device, std::move(attachments), std::move(subpassDescriptions), std::move(subpassDependencies));
m_renderPass.emplace(m_device, std::move(attachments), std::move(subpassDescriptions), std::move(subpassDependencies));
return true;
}
bool VulkanRenderWindow::SetupSwapchain(const Vk::PhysicalDevice& deviceInfo, Vk::Surface& surface, const Vector2ui& size)
bool VulkanSwapchain::SetupSurface(WindowHandle windowHandle)
{
bool success = false;
#if defined(NAZARA_PLATFORM_WINDOWS)
{
NazaraAssert(windowHandle.type == WindowBackend::Windows, "expected Windows window");
HWND winHandle = reinterpret_cast<HWND>(windowHandle.windows.window);
HINSTANCE instance = reinterpret_cast<HINSTANCE>(GetWindowLongPtrW(winHandle, GWLP_HINSTANCE));
success = m_surface.Create(instance, winHandle);
}
#elif defined(NAZARA_PLATFORM_LINUX)
{
switch (windowHandle.type)
{
#ifdef VK_USE_PLATFORM_WAYLAND_KHR
case WindowBackend::Wayland:
{
wl_display* display = static_cast<wl_display*>(windowHandle.wayland.display);
wl_surface* surface = static_cast<wl_surface*>(windowHandle.wayland.surface);
success = m_surface.Create(display, surface);
break;
}
#endif
#ifdef VK_USE_PLATFORM_XLIB_KHR
case WindowBackend::X11:
{
Display* display = static_cast<Display*>(windowHandle.x11.display);
::Window window = static_cast<::Window>(windowHandle.x11.window);
success = m_surface.Create(display, window);
break;
}
#endif
default:
{
NazaraError("unhandled window type");
return false;
}
}
}
#elif defined(NAZARA_PLATFORM_MACOS)
{
NazaraAssert(windowHandle.type == WindowBackend::Cocoa, "expected cocoa window");
id layer = CreateAndAttachMetalLayer(windowHandle.cocoa.window);
success = m_surface.Create(layer);
}
#else
#error This OS is not supported by Vulkan
#endif
if (!success)
{
NazaraError("Failed to create Vulkan surface: " + TranslateVulkanError(m_surface.GetLastErrorCode()));
return false;
}
return true;
}
bool VulkanSwapchain::SetupSwapchain(const Vk::PhysicalDevice& deviceInfo)
{
VkSurfaceCapabilitiesKHR surfaceCapabilities;
if (!surface.GetCapabilities(deviceInfo.physDevice, &surfaceCapabilities))
if (!m_surface.GetCapabilities(deviceInfo.physDevice, &surfaceCapabilities))
{
NazaraError("Failed to query surface capabilities");
return false;
}
Nz::UInt32 imageCount = surfaceCapabilities.minImageCount + 1;
UInt32 imageCount = surfaceCapabilities.minImageCount + 1;
if (surfaceCapabilities.maxImageCount > 0 && imageCount > surfaceCapabilities.maxImageCount)
imageCount = surfaceCapabilities.maxImageCount;
VkExtent2D extent;
if (surfaceCapabilities.currentExtent.width == 0xFFFFFFFF)
{
extent.width = Nz::Clamp<Nz::UInt32>(size.x, surfaceCapabilities.minImageExtent.width, surfaceCapabilities.maxImageExtent.width);
extent.height = Nz::Clamp<Nz::UInt32>(size.y, surfaceCapabilities.minImageExtent.height, surfaceCapabilities.maxImageExtent.height);
extent.width = std::clamp<UInt32>(m_swapchainSize.x, surfaceCapabilities.minImageExtent.width, surfaceCapabilities.maxImageExtent.width);
extent.height = std::clamp<UInt32>(m_swapchainSize.y, surfaceCapabilities.minImageExtent.height, surfaceCapabilities.maxImageExtent.height);
}
else
extent = surfaceCapabilities.currentExtent;
std::vector<VkPresentModeKHR> presentModes;
if (!surface.GetPresentModes(deviceInfo.physDevice, &presentModes))
if (!m_surface.GetPresentModes(deviceInfo.physDevice, &presentModes))
{
NazaraError("Failed to query supported present modes");
return false;
@@ -564,13 +577,13 @@ namespace Nz
}
// Ensure all operations on the device have been finished before recreating the swapchain (this can be avoided but is more complicated)
m_device->WaitForIdle();
m_device.WaitForIdle();
VkSwapchainCreateInfoKHR swapchainInfo = {
VK_STRUCTURE_TYPE_SWAPCHAIN_CREATE_INFO_KHR,
nullptr,
0,
surface,
m_surface,
imageCount,
m_surfaceFormat.format,
m_surfaceFormat.colorSpace,
@@ -587,14 +600,13 @@ namespace Nz
};
Vk::Swapchain newSwapchain;
if (!newSwapchain.Create(*m_device, swapchainInfo))
if (!newSwapchain.Create(m_device, swapchainInfo))
{
NazaraError("failed to create swapchain: " + TranslateVulkanError(newSwapchain.GetLastErrorCode()));
return false;
}
m_swapchain = std::move(newSwapchain);
m_swapchainSize = size;
// Framebuffers
imageCount = m_swapchain.GetImageCount();