#include "swapchain.hpp" #include #include #include namespace sophia { Swapchain::Swapchain(Device& device, vk::Extent2D extent) : device{device}, extent{extent} { initialize(); } Swapchain::Swapchain(Device& device, vk::Extent2D extent, std::shared_ptr previous) : device{device}, extent{extent}, oldSwapchain{previous} { initialize(); this->oldSwapchain = nullptr; } Swapchain::~Swapchain() { this->images.clear(); this->depthImages.clear(); this->device.get()->destroySwapchainKHR(this->swapchain); // log::trace("destroyed vk::SwapchainKHR"); } vk::Result Swapchain::acquireNextImage(vk::Semaphore signalSemaphore, u32& imageIndex) { return this->device.get()->acquireNextImageKHR( this->swapchain, std::numeric_limits::max(), signalSemaphore, VK_NULL_HANDLE, &imageIndex); } vk::Result Swapchain::present(vk::Queue presentQueue, u32 imageIndex, vk::Semaphore waitSemaphore) { vk::PresentInfoKHR presentInfo = {}; presentInfo.waitSemaphoreCount = 1; presentInfo.pWaitSemaphores = &waitSemaphore; vk::SwapchainKHR swapChains[] = {this->swapchain}; presentInfo.swapchainCount = 1; presentInfo.pSwapchains = swapChains; presentInfo.pImageIndices = &imageIndex; return presentQueue.presentKHR(&presentInfo); } void Swapchain::initialize() { setDefaultCreateInfo(); createSwapchain(); createImages(); createDepthImages(); } void Swapchain::setDefaultCreateInfo() { SwapchainSupportDetails swapchainSupport = this->device.getSwapchainSupport(); vk::SurfaceFormatKHR surfaceFormat = chooseSurfaceFormat(swapchainSupport.formats); vk::PresentModeKHR presentMode = choosePresentMode(swapchainSupport.presentModes); this->extent = chooseExtent(swapchainSupport.capabilities); u32 imageCount = swapchainSupport.capabilities.minImageCount + 1; if (swapchainSupport.capabilities.maxImageCount > 0 && imageCount > swapchainSupport.capabilities.maxImageCount) { imageCount = swapchainSupport.capabilities.maxImageCount; } this->swapchainCreateInfo = {vk::SwapchainCreateFlagsKHR(), this->device.getSurface(), imageCount, surfaceFormat.format, surfaceFormat.colorSpace, this->extent, 1, vk::ImageUsageFlagBits::eColorAttachment}; QueueFamilyIndices indices = this->device.getQueueIndices(); u32 queueFamilyIndices[] = {indices.graphicsFamily.value(), indices.presentFamily.value()}; if (indices.graphicsFamily != indices.presentFamily) { swapchainCreateInfo.imageSharingMode = vk::SharingMode::eConcurrent; swapchainCreateInfo.queueFamilyIndexCount = 2; swapchainCreateInfo.pQueueFamilyIndices = queueFamilyIndices; } else { swapchainCreateInfo.imageSharingMode = vk::SharingMode::eExclusive; } swapchainCreateInfo.preTransform = swapchainSupport.capabilities.currentTransform; swapchainCreateInfo.compositeAlpha = vk::CompositeAlphaFlagBitsKHR::eOpaque; swapchainCreateInfo.presentMode = presentMode; swapchainCreateInfo.clipped = VK_TRUE; swapchainCreateInfo.oldSwapchain = vk::SwapchainKHR(nullptr); swapchainCreateInfo.oldSwapchain = this->oldSwapchain == nullptr ? VK_NULL_HANDLE : this->oldSwapchain->swapchain; this->imageFormat = surfaceFormat.format; } void Swapchain::createSwapchain() { try { this->swapchain = this->device.get()->createSwapchainKHR(this->swapchainCreateInfo); // log::trace("created vk::SwapchainKHR"); } catch (const vk::SystemError& err) { log::fatal("failed to create swapchain. Error: ", err.what()); throw std::runtime_error("failed to create swapchain"); } } void Swapchain::createImages() { std::vector swapchainImages = this->device.get()->getSwapchainImagesKHR(this->swapchain); this->images.clear(); this->images.reserve(swapchainImages.size()); for (vk::Image image : swapchainImages) { this->images.push_back(Image::Builder(this->device) .wrapExisting(image) .setFormat(this->imageFormat) .setExtent(width(), height()) .withView() .build()); } // log::trace("created all swapchain vk::ImageView"); } void Swapchain::createDepthImages() { this->depthFormat = findDepthFormat(); this->depthImages.clear(); this->depthImages.reserve(imageCount()); for (size_t i = 0; i < imageCount(); i++) { this->depthImages.push_back(Image::Builder(this->device) .asDepthAttachment() .setFormat(this->depthFormat) .setExtent(width(), height()) .withView() .build()); } // log::trace("created all depth resources"); } vk::SurfaceFormatKHR Swapchain::chooseSurfaceFormat( const std::vector& availableFormats) { if (availableFormats.size() == 1 && availableFormats[0].format == vk::Format::eUndefined) { return {vk::Format::eB8G8R8A8Unorm, vk::ColorSpaceKHR::eSrgbNonlinear}; } for (const auto& availableFormat : availableFormats) { if (availableFormat.format == vk::Format::eB8G8R8A8Unorm && availableFormat.colorSpace == vk::ColorSpaceKHR::eSrgbNonlinear) { return availableFormat; } } log::info("No available surface formats, choosing default."); return availableFormats[0]; } vk::PresentModeKHR Swapchain::choosePresentMode( const std::vector availablePresentModes) { vk::PresentModeKHR bestMode = vk::PresentModeKHR::eFifo; for (const auto& availablePresentMode : availablePresentModes) { if (availablePresentMode == vk::PresentModeKHR::eMailbox) { log::info("Selected present mode: MAILBOX (triple buffering)"); return availablePresentMode; } else if (availablePresentMode == vk::PresentModeKHR::eImmediate) { bestMode = availablePresentMode; } } log::warning("Present mode MAILBOX not available."); if (bestMode == vk::PresentModeKHR::eImmediate) { log::info("Selected present mode: IMMEDIATE (no vsync)"); } else { log::info("Selected present mode: FIFO (vsync)"); } return bestMode; } vk::Extent2D Swapchain::chooseExtent( const vk::SurfaceCapabilitiesKHR& capabilities) { if (capabilities.currentExtent.width != std::numeric_limits::max()) { return capabilities.currentExtent; } else { vk::Extent2D choosenExtent = {}; choosenExtent.width = std::clamp(this->extent.width, capabilities.minImageExtent.width, capabilities.maxImageExtent.width); choosenExtent.height = std::clamp(this->extent.height, capabilities.minImageExtent.height, capabilities.maxImageExtent.height); return choosenExtent; } } vk::Format Swapchain::findDepthFormat() { return this->device.findSupportedFormat( {vk::Format::eD32Sfloat, vk::Format::eD32SfloatS8Uint, vk::Format::eD24UnormS8Uint}, vk::ImageTiling::eOptimal, vk::FormatFeatureFlagBits::eDepthStencilAttachment); } } // namespace sophia