2022-08-04 03:00:54 +01:00
|
|
|
#include <Orange/Core/Log.h>
|
|
|
|
|
|
|
|
#include <Orange/Render/Swapchain.h>
|
|
|
|
#include <Orange/Render/VulkanHelpers.h>
|
|
|
|
#include <Orange/Render/Window.h>
|
2023-01-11 14:10:24 +00:00
|
|
|
#include <Orange/Render/Vulkan.h>
|
2022-08-04 03:00:54 +01:00
|
|
|
|
|
|
|
namespace orange
|
|
|
|
{
|
2022-08-04 20:00:53 +01:00
|
|
|
static constexpr uint32_t DefaultFramesInFlight = 2;
|
|
|
|
|
2022-08-04 03:00:54 +01:00
|
|
|
static Result<VkSurfaceFormatKHR> ChooseSwapChainFormat(VkPhysicalDevice physicalDevice, VkSurfaceKHR surface)
|
|
|
|
{
|
2022-08-07 00:15:47 +01:00
|
|
|
SmallVector<VkSurfaceFormatKHR, 32> surfaceFormats;
|
2022-08-04 03:00:54 +01:00
|
|
|
if (!VkEnumerate(vkGetPhysicalDeviceSurfaceFormatsKHR, surfaceFormats, physicalDevice, surface))
|
|
|
|
{
|
|
|
|
log::err("Surface supports no formats");
|
|
|
|
return Result<VkSurfaceFormatKHR>::Error(BasicErrorCode::NotFound);
|
|
|
|
}
|
|
|
|
|
|
|
|
for (auto& surfaceFormat : surfaceFormats)
|
|
|
|
{
|
|
|
|
if (surfaceFormat.format == VK_FORMAT_B8G8R8A8_UNORM && surfaceFormat.colorSpace == VK_COLOR_SPACE_SRGB_NONLINEAR_KHR)
|
|
|
|
return Result<VkSurfaceFormatKHR>::Success(surfaceFormat);
|
|
|
|
}
|
|
|
|
|
|
|
|
return Result<VkSurfaceFormatKHR>::Error(BasicErrorCode::NotFound);
|
|
|
|
}
|
|
|
|
|
|
|
|
static VkPresentModeKHR ChoosePresentMode(VkPhysicalDevice physicalDevice, VkSurfaceKHR surface)
|
|
|
|
{
|
2022-08-07 00:15:47 +01:00
|
|
|
SmallVector<VkPresentModeKHR, 32> presentModes;
|
2022-08-04 03:00:54 +01:00
|
|
|
VkEnumerate(vkGetPhysicalDeviceSurfacePresentModesKHR, presentModes, physicalDevice, surface);
|
|
|
|
|
|
|
|
for (auto& presentMode : presentModes) { if (presentMode == VK_PRESENT_MODE_MAILBOX_KHR) return presentMode; }
|
|
|
|
for (auto& presentMode : presentModes) { if (presentMode == VK_PRESENT_MODE_IMMEDIATE_KHR) return presentMode; }
|
|
|
|
|
|
|
|
return VK_PRESENT_MODE_FIFO_KHR;
|
|
|
|
}
|
|
|
|
|
|
|
|
static VkExtent2D ChooseSwapExtent(const VkSurfaceCapabilitiesKHR& capabilities)
|
|
|
|
{
|
|
|
|
if (capabilities.currentExtent.width != ~0u)
|
|
|
|
return capabilities.currentExtent;
|
|
|
|
|
|
|
|
uint32_t width = 1280;
|
|
|
|
uint32_t height = 720;
|
|
|
|
|
|
|
|
return VkExtent2D
|
|
|
|
{
|
|
|
|
Clamp(width, capabilities.minImageExtent.width, capabilities.maxImageExtent.width),
|
|
|
|
Clamp(height, capabilities.minImageExtent.height, capabilities.maxImageExtent.height)
|
|
|
|
};
|
|
|
|
}
|
|
|
|
|
|
|
|
static uint32_t ChooseImageCount(const VkSurfaceCapabilitiesKHR& capabilities)
|
|
|
|
{
|
|
|
|
uint32_t imageCount = 3;
|
|
|
|
|
|
|
|
if (capabilities.minImageCount != 0)
|
|
|
|
imageCount = Max(imageCount, capabilities.minImageCount);
|
|
|
|
|
|
|
|
if (capabilities.maxImageCount != 0)
|
|
|
|
imageCount = Min(imageCount, capabilities.maxImageCount);
|
|
|
|
|
|
|
|
return imageCount;
|
|
|
|
}
|
|
|
|
|
|
|
|
Result<Swapchain> Swapchain::Create(RenderContext& context, VkSurfaceKHR surface)
|
|
|
|
{
|
|
|
|
VkSurfaceCapabilitiesKHR capabilities;
|
|
|
|
vkGetPhysicalDeviceSurfaceCapabilitiesKHR(context.PhysicalDevice(), surface, &capabilities);
|
|
|
|
|
|
|
|
Result<VkSurfaceFormatKHR> r_format = ChooseSwapChainFormat(context.PhysicalDevice(), surface);
|
|
|
|
if (!r_format)
|
2022-08-04 18:23:08 +01:00
|
|
|
return Result<Swapchain>::PrintForwardError(r_format, "Failed to pick swapchain format");
|
2022-08-04 03:00:54 +01:00
|
|
|
|
|
|
|
const VkExtent2D extent = ChooseSwapExtent(capabilities);
|
|
|
|
|
2022-08-12 20:08:28 +01:00
|
|
|
VkFormat viewFormats[2] =
|
|
|
|
{
|
|
|
|
r_format->format,
|
|
|
|
FormatToSrgbFormat(r_format->format),
|
|
|
|
};
|
|
|
|
|
|
|
|
VkImageFormatListCreateInfo formatListInfo =
|
|
|
|
{
|
|
|
|
.sType = VK_STRUCTURE_TYPE_IMAGE_FORMAT_LIST_CREATE_INFO,
|
|
|
|
.viewFormatCount = Size(viewFormats),
|
|
|
|
.pViewFormats = viewFormats,
|
|
|
|
};
|
|
|
|
|
2022-08-04 03:00:54 +01:00
|
|
|
VkSwapchainCreateInfoKHR swapchainInfo =
|
|
|
|
{
|
|
|
|
.sType = VK_STRUCTURE_TYPE_SWAPCHAIN_CREATE_INFO_KHR,
|
2022-08-12 20:08:28 +01:00
|
|
|
.pNext = &formatListInfo,
|
|
|
|
.flags = VK_SWAPCHAIN_CREATE_MUTABLE_FORMAT_BIT_KHR,
|
2022-08-04 03:00:54 +01:00
|
|
|
.surface = surface,
|
|
|
|
.minImageCount = ChooseImageCount(capabilities),
|
|
|
|
.imageFormat = r_format->format,
|
|
|
|
.imageColorSpace = r_format->colorSpace,
|
|
|
|
.imageExtent = extent,
|
|
|
|
.imageArrayLayers = 1,
|
|
|
|
.imageUsage = VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT,
|
|
|
|
.imageSharingMode = VK_SHARING_MODE_EXCLUSIVE,
|
|
|
|
.preTransform = capabilities.currentTransform,
|
|
|
|
.compositeAlpha = VK_COMPOSITE_ALPHA_OPAQUE_BIT_KHR,
|
|
|
|
.presentMode = ChoosePresentMode(context.PhysicalDevice(), surface),
|
|
|
|
.clipped = VK_TRUE,
|
2022-08-04 18:23:08 +01:00
|
|
|
.oldSwapchain = VK_NULL_HANDLE, // TODO
|
2022-08-04 03:00:54 +01:00
|
|
|
};
|
|
|
|
|
|
|
|
VkSwapchainKHR swapchain = VK_NULL_HANDLE;
|
|
|
|
if (vkCreateSwapchainKHR(context.Device(), &swapchainInfo, nullptr, &swapchain) != VK_SUCCESS)
|
2022-08-04 18:23:08 +01:00
|
|
|
return Result<Swapchain>::PrintError("Failed to create swapchain");
|
|
|
|
|
2022-08-07 00:15:47 +01:00
|
|
|
SmallVector<VkImage, 6> swapchainImages;
|
2022-08-04 18:23:08 +01:00
|
|
|
if (!VkEnumerate(vkGetSwapchainImagesKHR, swapchainImages, context.Device(), swapchain))
|
|
|
|
return Result<Swapchain>::PrintError("Failed to get swapchain images");
|
|
|
|
|
2022-08-25 07:27:11 +01:00
|
|
|
SmallVector<VkImageView, 6> swapchainImageViews{ swapchainImages.size() };
|
|
|
|
for (size_t i = 0; i < swapchainImages.size(); i++)
|
2022-08-04 03:00:54 +01:00
|
|
|
{
|
2022-08-04 18:23:08 +01:00
|
|
|
VkImageViewCreateInfo imageViewInfo =
|
|
|
|
{
|
|
|
|
.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO,
|
|
|
|
.image = swapchainImages[i],
|
|
|
|
.viewType = VK_IMAGE_VIEW_TYPE_2D,
|
2022-08-12 13:34:04 +01:00
|
|
|
.format = FormatToSrgbFormat(r_format->format),
|
2022-08-13 07:18:10 +01:00
|
|
|
.subresourceRange = FirstColorMipSubresourceRange,
|
2022-08-04 18:23:08 +01:00
|
|
|
};
|
|
|
|
|
|
|
|
if (vkCreateImageView(context.Device(), &imageViewInfo, nullptr, &swapchainImageViews[i]) != VK_SUCCESS)
|
|
|
|
return Result<Swapchain>::PrintError("Failed to get swapchain image view");
|
2022-08-04 03:00:54 +01:00
|
|
|
}
|
|
|
|
|
2022-08-07 00:15:47 +01:00
|
|
|
SmallVector<VkCommandBuffer, 6> commandBuffers{ DefaultFramesInFlight };
|
2022-08-04 20:00:53 +01:00
|
|
|
{
|
|
|
|
VkCommandBufferAllocateInfo commandBufferInfo =
|
|
|
|
{
|
|
|
|
.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO,
|
|
|
|
.commandPool = context.CommandPool(),
|
|
|
|
.level = VK_COMMAND_BUFFER_LEVEL_PRIMARY,
|
|
|
|
.commandBufferCount = DefaultFramesInFlight,
|
|
|
|
};
|
|
|
|
|
2022-08-25 07:27:11 +01:00
|
|
|
if (vkAllocateCommandBuffers(context.Device(), &commandBufferInfo, commandBuffers.data()) != VK_SUCCESS)
|
2022-08-04 20:00:53 +01:00
|
|
|
return Result<Swapchain>::PrintError("Failed to get swapchain image view");
|
|
|
|
}
|
|
|
|
|
2022-08-07 00:15:47 +01:00
|
|
|
SmallVector<VkSemaphore, MaxFramesInFlight> imageAvailableSemaphores{ DefaultFramesInFlight };
|
|
|
|
SmallVector<VkSemaphore, MaxFramesInFlight> renderFinishedSemaphores{ DefaultFramesInFlight };
|
|
|
|
SmallVector<VkFence, MaxFramesInFlight> inFlightFences { DefaultFramesInFlight };
|
2022-08-04 20:00:53 +01:00
|
|
|
for (uint32_t i = 0; i < DefaultFramesInFlight; i++)
|
|
|
|
{
|
|
|
|
auto r_fence = context.CreateFence(true);
|
|
|
|
if (!r_fence) return Result<Swapchain>::PrintError("Failed to create fence for swapchain");
|
|
|
|
inFlightFences[i] = *r_fence;
|
|
|
|
|
|
|
|
auto r_imageSem = context.CreateSemaphore();
|
|
|
|
if (!r_imageSem) return Result<Swapchain>::PrintError("Failed to create semaphore for swapchain");
|
|
|
|
imageAvailableSemaphores[i] = *r_imageSem;
|
|
|
|
|
|
|
|
auto r_renderSem = context.CreateSemaphore();
|
|
|
|
if (!r_renderSem) return Result<Swapchain>::PrintError("Failed to create semaphore for swapchain");
|
|
|
|
renderFinishedSemaphores[i] = *r_renderSem;
|
|
|
|
}
|
|
|
|
|
2022-08-12 21:18:11 +01:00
|
|
|
|
|
|
|
|
2022-08-04 20:00:53 +01:00
|
|
|
// TODO: Handle failure
|
|
|
|
uint32_t imageIndex;
|
|
|
|
vkAcquireNextImageKHR(context.Device(), swapchain, ~0u, imageAvailableSemaphores[0], VK_NULL_HANDLE, &imageIndex);
|
2022-08-04 22:18:11 +01:00
|
|
|
|
|
|
|
return Result<Swapchain>::Success(
|
|
|
|
context, surface, r_format->format, extent, swapchain,
|
|
|
|
swapchainImages, swapchainImageViews, commandBuffers,
|
|
|
|
imageAvailableSemaphores, renderFinishedSemaphores, inFlightFences,
|
|
|
|
imageIndex);
|
2022-08-04 03:00:54 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
Swapchain::~Swapchain()
|
|
|
|
{
|
2022-08-04 18:23:08 +01:00
|
|
|
for (const auto& imageView : m_imageViews)
|
2022-08-04 20:00:53 +01:00
|
|
|
vkDestroyImageView(m_ctx.Device(), imageView, nullptr);
|
|
|
|
vkDestroySwapchainKHR(m_ctx.Device(), m_swapchain, nullptr);
|
2022-08-04 03:00:54 +01:00
|
|
|
}
|
2022-08-04 22:18:11 +01:00
|
|
|
|
|
|
|
void Swapchain::Present()
|
|
|
|
{
|
2023-03-28 10:10:51 +01:00
|
|
|
VkResult res = VK_SUCCESS;
|
|
|
|
|
2022-08-12 20:08:28 +01:00
|
|
|
vkResetFences(m_ctx.Device(), 1, &m_inFlightFences[m_currentFrame]);
|
|
|
|
|
2022-08-04 22:18:11 +01:00
|
|
|
const VkPipelineStageFlags waitStage = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT;
|
|
|
|
VkSubmitInfo submitInfo =
|
|
|
|
{
|
|
|
|
.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO,
|
|
|
|
.waitSemaphoreCount = 1,
|
|
|
|
.pWaitSemaphores = &m_imageAvailableSemaphores[m_currentFrame],
|
|
|
|
.pWaitDstStageMask = &waitStage,
|
|
|
|
.commandBufferCount = 1,
|
|
|
|
.pCommandBuffers = &m_commandBuffers[m_currentFrame],
|
|
|
|
.signalSemaphoreCount = 1,
|
|
|
|
.pSignalSemaphores = &m_renderFinishedSemaphores[m_currentFrame],
|
|
|
|
};
|
|
|
|
|
|
|
|
// TODO: Handle failure properly.
|
2023-03-28 10:10:51 +01:00
|
|
|
if ((res = vkQueueSubmit(m_ctx.Queue(), 1, &submitInfo, m_inFlightFences[m_currentFrame])) != VK_SUCCESS) {
|
|
|
|
log::err("Failed to submit work: %d", res);
|
2022-08-04 22:18:11 +01:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
VkPresentInfoKHR presentInfo =
|
|
|
|
{
|
|
|
|
.sType = VK_STRUCTURE_TYPE_PRESENT_INFO_KHR,
|
|
|
|
.waitSemaphoreCount = 1,
|
|
|
|
.pWaitSemaphores = &m_renderFinishedSemaphores[m_currentFrame],
|
|
|
|
.swapchainCount = 1,
|
|
|
|
.pSwapchains = &m_swapchain,
|
|
|
|
.pImageIndices = &m_currentImage,
|
|
|
|
};
|
|
|
|
|
2023-03-28 10:10:51 +01:00
|
|
|
res = vkQueuePresentKHR(m_ctx.Queue(), &presentInfo);
|
|
|
|
if (res != VK_SUCCESS && res != VK_SUBOPTIMAL_KHR) {
|
|
|
|
log::err("Failed to submit present: %d", res);
|
2022-08-04 22:18:11 +01:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Prepare for next frame.
|
|
|
|
|
|
|
|
m_currentFrame = (m_currentFrame + 1) % DefaultFramesInFlight;
|
|
|
|
|
|
|
|
vkWaitForFences(m_ctx.Device(), 1, &m_inFlightFences[m_currentFrame], VK_TRUE, UINT64_MAX);
|
|
|
|
vkAcquireNextImageKHR(m_ctx.Device(), m_swapchain, ~0u, m_imageAvailableSemaphores[m_currentFrame], VK_NULL_HANDLE, &m_currentImage);
|
2022-08-12 20:08:28 +01:00
|
|
|
|
|
|
|
vkResetCommandBuffer(m_commandBuffers[m_currentFrame], 0u);
|
2022-08-04 22:18:11 +01:00
|
|
|
}
|
2022-08-04 03:00:54 +01:00
|
|
|
}
|