192 lines
6.3 KiB
C++
192 lines
6.3 KiB
C++
/// =============================================
|
|
// Aster: swapchain.cpp
|
|
// Copyright (c) 2020-2024 Anish Bhobe
|
|
// ==============================================
|
|
|
|
#include "swapchain.h"
|
|
|
|
#include "device.h"
|
|
#include "physical_device.h"
|
|
#include "surface.h"
|
|
|
|
[[nodiscard]] vk::Extent2D GetExtent(Size size, vk::SurfaceCapabilitiesKHR *surfaceCapabilities);
|
|
|
|
void
|
|
Swapchain::Init(const Surface *surface, const Device *device, Size size, cstr name)
|
|
{
|
|
assert(!m_Swapchain);
|
|
this->Create(surface, device, size, name);
|
|
}
|
|
|
|
void
|
|
Swapchain::Create(const Surface *surface, const Device *device, Size size, cstr name)
|
|
{
|
|
auto surfaceCapabilities = GetSurfaceCapabilities(device->m_PhysicalDevice, surface->m_Surface);
|
|
m_Extent = GetExtent(size, &surfaceCapabilities);
|
|
|
|
while (m_Extent.width == 0 || m_Extent.height == 0)
|
|
{
|
|
glfwWaitEvents();
|
|
surfaceCapabilities = GetSurfaceCapabilities(device->m_PhysicalDevice, surface->m_Surface);
|
|
m_Extent = GetExtent(size, &surfaceCapabilities);
|
|
}
|
|
|
|
auto surfaceFormats = GetSurfaceFormats(device->m_PhysicalDevice, surface->m_Surface);
|
|
auto presentModes = GetSurfacePresentModes(device->m_PhysicalDevice, surface->m_Surface);
|
|
|
|
m_Format = vk::Format::eUndefined;
|
|
vk::ColorSpaceKHR swapchainColorSpace = vk::ColorSpaceKHR::eSrgbNonlinear;
|
|
for (auto [format, colorSpace] : surfaceFormats)
|
|
{
|
|
if (format == vk::Format::eB8G8R8A8Srgb && colorSpace == vk::ColorSpaceKHR::eSrgbNonlinear)
|
|
{
|
|
m_Format = format;
|
|
swapchainColorSpace = colorSpace;
|
|
}
|
|
}
|
|
if (m_Format == vk::Format::eUndefined)
|
|
{
|
|
WARN("Preferred Swapchain format not found. Falling back.");
|
|
auto [format, colorSpace] = surfaceFormats.front();
|
|
m_Format = format;
|
|
swapchainColorSpace = colorSpace;
|
|
}
|
|
|
|
vk::PresentModeKHR swapchainPresentMode = vk::PresentModeKHR::eFifo;
|
|
for (const auto presentMode : presentModes)
|
|
{
|
|
if (presentMode == vk::PresentModeKHR::eMailbox)
|
|
{
|
|
swapchainPresentMode = presentMode;
|
|
break;
|
|
}
|
|
}
|
|
|
|
u32 swapchainImageCount = 3;
|
|
if (surfaceCapabilities.maxImageCount > 0)
|
|
{
|
|
swapchainImageCount =
|
|
glm::clamp(swapchainImageCount, surfaceCapabilities.minImageCount, surfaceCapabilities.maxImageCount);
|
|
}
|
|
|
|
// TODO: Note that different queues might need the images to be shared.
|
|
|
|
const vk::SwapchainCreateInfoKHR swapchainCreateInfo = {
|
|
.surface = surface->m_Surface,
|
|
.minImageCount = swapchainImageCount,
|
|
.imageFormat = m_Format,
|
|
.imageColorSpace = swapchainColorSpace,
|
|
.imageExtent = m_Extent,
|
|
.imageArrayLayers = 1,
|
|
.imageUsage = vk::ImageUsageFlagBits::eColorAttachment | vk::ImageUsageFlagBits::eTransferDst,
|
|
.imageSharingMode = vk::SharingMode::eExclusive,
|
|
.preTransform = surfaceCapabilities.currentTransform,
|
|
.compositeAlpha = vk::CompositeAlphaFlagBitsKHR::eOpaque,
|
|
.presentMode = swapchainPresentMode,
|
|
.clipped = true,
|
|
.oldSwapchain = m_Swapchain,
|
|
};
|
|
|
|
vk::Device vkDevice = device->m_Device;
|
|
|
|
vk::SwapchainKHR swapchain;
|
|
vk::Result result = vkDevice.createSwapchainKHR(&swapchainCreateInfo, nullptr, &swapchain);
|
|
ERROR_IF(Failed(result), "Swapchain {} creation failed. Cause {}", name, result)
|
|
THEN_ABORT(result)
|
|
ELSE_DEBUG("Created Swapchain '{}'", name);
|
|
|
|
// Irrelevant on the first run. Required for re-creation.
|
|
Destroy(device);
|
|
|
|
m_Swapchain = swapchain;
|
|
|
|
device->SetName(m_Swapchain, name);
|
|
|
|
result = vkDevice.getSwapchainImagesKHR(m_Swapchain, &swapchainImageCount, nullptr);
|
|
ERROR_IF(Failed(result), "Failed getting swapchain {}'s images. Cause {}", name, result)
|
|
THEN_ABORT(result);
|
|
|
|
// Managed by the Swapchain.
|
|
m_Images.resize(swapchainImageCount);
|
|
result = vkDevice.getSwapchainImagesKHR(m_Swapchain, &swapchainImageCount, m_Images.data());
|
|
ERROR_IF(Failed(result), "Failed getting swapchain {}'s images. Cause {}", name, result)
|
|
THEN_ABORT(result);
|
|
|
|
vk::ImageViewCreateInfo viewCreateInfo = {
|
|
.viewType = vk::ImageViewType::e2D,
|
|
.format = m_Format,
|
|
.components = {.r = vk::ComponentSwizzle::eIdentity,
|
|
.g = vk::ComponentSwizzle::eIdentity,
|
|
.b = vk::ComponentSwizzle::eIdentity,
|
|
.a = vk::ComponentSwizzle::eIdentity},
|
|
.subresourceRange = {.aspectMask = vk::ImageAspectFlagBits::eColor,
|
|
.baseMipLevel = 0,
|
|
.levelCount = 1,
|
|
.baseArrayLayer = 0,
|
|
.layerCount = 1},
|
|
};
|
|
|
|
u32 index = 0;
|
|
for (auto image : m_Images)
|
|
{
|
|
viewCreateInfo.image = image;
|
|
|
|
vk::ImageView imageView;
|
|
result = vkDevice.createImageView(&viewCreateInfo, nullptr, &imageView);
|
|
ERROR_IF(Failed(result), "Failed creating swapchain {}'s image view [{}]. Cause {}", name, index, result)
|
|
THEN_ABORT(result);
|
|
|
|
m_ImageViews.push_back(imageView);
|
|
|
|
++index;
|
|
}
|
|
|
|
DEBUG("Swapchain {} Image Views created.", name);
|
|
|
|
for (auto &callback : m_ResizeCallbacks)
|
|
{
|
|
callback(m_Extent);
|
|
}
|
|
}
|
|
|
|
void
|
|
Swapchain::RegisterResizeCallback(FnResizeCallback &&callback)
|
|
{
|
|
m_ResizeCallbacks.emplace_back(callback);
|
|
}
|
|
|
|
void
|
|
Swapchain::Destroy(const Device *device)
|
|
{
|
|
if (!m_ImageViews.empty()) // Don't want the condition in the logs.
|
|
DEBUG("Swapchain Image Views destroyed.");
|
|
for (const auto imageView : m_ImageViews)
|
|
{
|
|
device->m_Device.destroy(imageView, nullptr);
|
|
}
|
|
m_ImageViews.clear();
|
|
if (m_Swapchain)
|
|
{
|
|
device->m_Device.destroy(m_Swapchain, nullptr);
|
|
m_Swapchain = nullptr;
|
|
DEBUG("Swapchain destroyed.");
|
|
}
|
|
}
|
|
|
|
vk::Extent2D
|
|
GetExtent(Size size, vk::SurfaceCapabilitiesKHR *surfaceCapabilities)
|
|
{
|
|
if (surfaceCapabilities->currentExtent.width != MaxValue<u32>)
|
|
{
|
|
return surfaceCapabilities->currentExtent;
|
|
}
|
|
|
|
auto [width, height] = size;
|
|
auto [minWidth, minHeight] = surfaceCapabilities->minImageExtent;
|
|
auto [maxWidth, maxHeight] = surfaceCapabilities->maxImageExtent;
|
|
|
|
return {
|
|
.width = glm::clamp(width, minWidth, maxWidth),
|
|
.height = glm::clamp(height, minHeight, maxHeight),
|
|
};
|
|
} |