LCOV - code coverage report
Current view: top level - source/renderer/backend/vulkan - SwapChain.cpp (source / functions) Hit Total Coverage
Test: 0 A.D. test coverage report Lines: 1 166 0.6 %
Date: 2023-01-19 00:18:29 Functions: 2 11 18.2 %

          Line data    Source code
       1             : /* Copyright (C) 2023 Wildfire Games.
       2             :  * This file is part of 0 A.D.
       3             :  *
       4             :  * 0 A.D. is free software: you can redistribute it and/or modify
       5             :  * it under the terms of the GNU General Public License as published by
       6             :  * the Free Software Foundation, either version 2 of the License, or
       7             :  * (at your option) any later version.
       8             :  *
       9             :  * 0 A.D. is distributed in the hope that it will be useful,
      10             :  * but WITHOUT ANY WARRANTY; without even the implied warranty of
      11             :  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
      12             :  * GNU General Public License for more details.
      13             :  *
      14             :  * You should have received a copy of the GNU General Public License
      15             :  * along with 0 A.D.  If not, see <http://www.gnu.org/licenses/>.
      16             :  */
      17             : 
      18             : #include "precompiled.h"
      19             : 
      20             : #include "SwapChain.h"
      21             : 
      22             : #include "lib/hash.h"
      23             : #include "maths/MathUtil.h"
      24             : #include "ps/ConfigDB.h"
      25             : #include "ps/Profile.h"
      26             : #include "renderer/backend/vulkan/Device.h"
      27             : #include "renderer/backend/vulkan/Framebuffer.h"
      28             : #include "renderer/backend/vulkan/RingCommandContext.h"
      29             : #include "renderer/backend/vulkan/Texture.h"
      30             : #include "renderer/backend/vulkan/Utilities.h"
      31             : 
      32             : #include <algorithm>
      33             : #include <limits>
      34             : 
      35             : namespace Renderer
      36             : {
      37             : 
      38             : namespace Backend
      39             : {
      40             : 
      41             : namespace Vulkan
      42             : {
      43             : 
      44             : // static
      45           0 : std::unique_ptr<CSwapChain> CSwapChain::Create(
      46             :     CDevice* device, VkSurfaceKHR surface, int surfaceDrawableWidth, int surfaceDrawableHeight,
      47             :     std::unique_ptr<CSwapChain> oldSwapChain)
      48             : {
      49           0 :     std::unique_ptr<CSwapChain> swapChain(new CSwapChain());
      50           0 :     swapChain->m_Device = device;
      51             : 
      52           0 :     VkPhysicalDevice physicalDevice = device->GetChoosenPhysicalDevice().device;
      53             :     
      54           0 :     VkSurfaceCapabilitiesKHR surfaceCapabilities{};
      55           0 :     ENSURE_VK_SUCCESS(vkGetPhysicalDeviceSurfaceCapabilitiesKHR(
      56             :         physicalDevice, surface, &surfaceCapabilities));
      57             : 
      58           0 :     std::vector<VkSurfaceFormatKHR> surfaceFormats;
      59           0 :     uint32_t surfaceFormatCount = 0;
      60           0 :     ENSURE_VK_SUCCESS(vkGetPhysicalDeviceSurfaceFormatsKHR(
      61             :         physicalDevice, surface, &surfaceFormatCount, nullptr));
      62           0 :     if (surfaceFormatCount > 0)
      63             :     {
      64           0 :         surfaceFormats.resize(surfaceFormatCount);
      65           0 :         ENSURE_VK_SUCCESS(vkGetPhysicalDeviceSurfaceFormatsKHR(
      66             :             physicalDevice, surface, &surfaceFormatCount, surfaceFormats.data()));
      67             :     }
      68             : 
      69           0 :     std::vector<VkPresentModeKHR> presentModes;
      70           0 :     uint32_t presentModeCount = 0;
      71           0 :     ENSURE_VK_SUCCESS(vkGetPhysicalDeviceSurfacePresentModesKHR(
      72             :         physicalDevice, surface, &presentModeCount, nullptr));
      73           0 :     if (presentModeCount > 0)
      74             :     {
      75           0 :         presentModes.resize(presentModeCount);
      76           0 :         ENSURE_VK_SUCCESS(vkGetPhysicalDeviceSurfacePresentModesKHR(
      77             :             physicalDevice, surface, &presentModeCount, presentModes.data()));
      78             :     }
      79             : 
      80             :     // VK_PRESENT_MODE_FIFO_KHR is guaranteed to be supported.
      81           0 :     VkPresentModeKHR presentMode = VK_PRESENT_MODE_FIFO_KHR;
      82           0 :     auto isPresentModeAvailable = [&presentModes](const VkPresentModeKHR presentMode)
      83           0 :     {
      84           0 :         return std::find(presentModes.begin(), presentModes.end(), presentMode) != presentModes.end();
      85           0 :     };
      86           0 :     bool vsyncEnabled = true;
      87           0 :     CFG_GET_VAL("vsync", vsyncEnabled);
      88           0 :     if (vsyncEnabled)
      89             :     {
      90             :         // TODO: use the adaptive one when possible.
      91             :         // https://gitlab.freedesktop.org/mesa/mesa/-/issues/5516
      92             :         //if (isPresentModeAvailable(VK_PRESENT_MODE_MAILBOX_KHR))
      93             :         //  presentMode = VK_PRESENT_MODE_MAILBOX_KHR;
      94             :     }
      95             :     else
      96             :     {
      97           0 :         if (isPresentModeAvailable(VK_PRESENT_MODE_IMMEDIATE_KHR))
      98           0 :             presentMode = VK_PRESENT_MODE_IMMEDIATE_KHR;
      99             :     }
     100             : 
     101             :     // Spec says:
     102             :     //   The number of format pairs supported must be greater than or equal to 1.
     103             :     //   pSurfaceFormats must not contain an entry whose value for format is
     104             :     //   VK_FORMAT_UNDEFINED.
     105             :     const auto surfaceFormatIt =
     106           0 :         std::find_if(surfaceFormats.begin(), surfaceFormats.end(), IsSurfaceFormatSupported);
     107           0 :     if (surfaceFormatIt == surfaceFormats.end())
     108             :     {
     109           0 :         LOGERROR("Can't find a suitable surface format to render to.");
     110           0 :         return nullptr;
     111             :     }
     112           0 :     const VkSurfaceFormatKHR& surfaceFormat = *surfaceFormatIt;
     113             : 
     114           0 :     const uint32_t swapChainWidth = Clamp<int>(surfaceDrawableWidth,
     115           0 :         surfaceCapabilities.minImageExtent.width,
     116           0 :         surfaceCapabilities.maxImageExtent.width);
     117           0 :     const uint32_t swapChainHeight = Clamp<int>(surfaceDrawableHeight,
     118           0 :         surfaceCapabilities.minImageExtent.height,
     119           0 :         surfaceCapabilities.maxImageExtent.height);
     120             : 
     121           0 :     VkSwapchainCreateInfoKHR swapChainCreateInfo{};
     122           0 :     swapChainCreateInfo.sType = VK_STRUCTURE_TYPE_SWAPCHAIN_CREATE_INFO_KHR;
     123           0 :     swapChainCreateInfo.surface = surface;
     124             :     // minImageCount + 1 is to have a less chance for a presenter to wait.
     125             :     // maxImageCount might be zero, it means it's unlimited.
     126           0 :     swapChainCreateInfo.minImageCount =
     127           0 :         Clamp<uint32_t>(NUMBER_OF_FRAMES_IN_FLIGHT,
     128           0 :             surfaceCapabilities.minImageCount + 1,
     129           0 :             surfaceCapabilities.maxImageCount > 0
     130             :                 ? surfaceCapabilities.maxImageCount
     131             :                 : std::numeric_limits<uint32_t>::max());
     132           0 :     swapChainCreateInfo.imageFormat = surfaceFormat.format;
     133           0 :     swapChainCreateInfo.imageColorSpace = surfaceFormat.colorSpace;
     134           0 :     swapChainCreateInfo.imageExtent.width = swapChainWidth;
     135           0 :     swapChainCreateInfo.imageExtent.height = swapChainHeight;
     136           0 :     swapChainCreateInfo.imageArrayLayers = 1;
     137             :     // VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT is guaranteed to present.
     138             :     // VK_IMAGE_USAGE_TRANSFER_SRC_BIT allows a simpler backbuffer readback.
     139             :     // VK_IMAGE_USAGE_TRANSFER_DST_BIT allows a blit to the backbuffer.
     140           0 :     swapChainCreateInfo.imageUsage =
     141           0 :         (VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT | VK_IMAGE_USAGE_TRANSFER_SRC_BIT | VK_IMAGE_USAGE_TRANSFER_DST_BIT) &
     142           0 :         surfaceCapabilities.supportedUsageFlags;
     143           0 :     swapChainCreateInfo.imageSharingMode = VK_SHARING_MODE_EXCLUSIVE;
     144             :     // We need to set these only if imageSharingMode is VK_SHARING_MODE_CONCURRENT.
     145           0 :     swapChainCreateInfo.queueFamilyIndexCount = 0;
     146           0 :     swapChainCreateInfo.pQueueFamilyIndices = nullptr;
     147             :     // By default VK_SURFACE_TRANSFORM_IDENTITY_BIT_KHR is preferable.
     148           0 :     if (surfaceCapabilities.supportedTransforms & VK_SURFACE_TRANSFORM_IDENTITY_BIT_KHR)
     149           0 :         swapChainCreateInfo.preTransform = VK_SURFACE_TRANSFORM_IDENTITY_BIT_KHR;
     150             :     else
     151           0 :         swapChainCreateInfo.preTransform = surfaceCapabilities.currentTransform;
     152             :     // By default VK_COMPOSITE_ALPHA_OPAQUE_BIT_KHR is preferable, other bits
     153             :     // might require some format or rendering adjustemnts to avoid
     154             :     // semi-transparent areas.
     155           0 :     const VkCompositeAlphaFlagBitsKHR compositeAlphaOrder[] =
     156             :     {
     157             :         VK_COMPOSITE_ALPHA_OPAQUE_BIT_KHR,
     158             :         VK_COMPOSITE_ALPHA_INHERIT_BIT_KHR,
     159             :         VK_COMPOSITE_ALPHA_PRE_MULTIPLIED_BIT_KHR,
     160             :         VK_COMPOSITE_ALPHA_POST_MULTIPLIED_BIT_KHR
     161             :     };
     162           0 :     for (const VkCompositeAlphaFlagBitsKHR compositeAlpha : compositeAlphaOrder)
     163           0 :         if (compositeAlpha & surfaceCapabilities.supportedCompositeAlpha)
     164             :         {
     165           0 :             swapChainCreateInfo.compositeAlpha = compositeAlpha;
     166           0 :             break;
     167             :         }
     168           0 :     swapChainCreateInfo.presentMode = presentMode;
     169           0 :     swapChainCreateInfo.clipped = VK_TRUE;
     170           0 :     if (oldSwapChain)
     171           0 :         swapChainCreateInfo.oldSwapchain = oldSwapChain->GetVkSwapchain();
     172             : 
     173           0 :     ENSURE_VK_SUCCESS(vkCreateSwapchainKHR(
     174             :         device->GetVkDevice(), &swapChainCreateInfo, nullptr, &swapChain->m_SwapChain));
     175             : 
     176             :     char nameBuffer[64];
     177           0 :     snprintf(nameBuffer, std::size(nameBuffer), "SwapChain: %dx%d", surfaceDrawableWidth, surfaceDrawableHeight);
     178           0 :     device->SetObjectName(VK_OBJECT_TYPE_SWAPCHAIN_KHR, swapChain->m_SwapChain, nameBuffer);
     179             : 
     180           0 :     uint32_t imageCount = 0;
     181           0 :     ENSURE_VK_SUCCESS(vkGetSwapchainImagesKHR(
     182             :         device->GetVkDevice(), swapChain->m_SwapChain, &imageCount, nullptr));
     183           0 :     swapChain->m_Images.resize(imageCount);
     184           0 :     ENSURE_VK_SUCCESS(vkGetSwapchainImagesKHR(
     185             :         device->GetVkDevice(), swapChain->m_SwapChain, &imageCount, swapChain->m_Images.data()));
     186             : 
     187           0 :     swapChain->m_DepthTexture = CTexture::Create(
     188             :         device, "SwapChainDepthTexture", ITexture::Type::TEXTURE_2D,
     189             :         ITexture::Usage::DEPTH_STENCIL_ATTACHMENT,
     190             :         device->GetPreferredDepthStencilFormat(
     191             :             Renderer::Backend::ITexture::Usage::DEPTH_STENCIL_ATTACHMENT,
     192             :             true, true),
     193           0 :         swapChainWidth, swapChainHeight, Sampler::MakeDefaultSampler(
     194             :             Sampler::Filter::NEAREST, Sampler::AddressMode::CLAMP_TO_EDGE),
     195           0 :         1, 1);
     196             : 
     197           0 :     swapChain->m_ImageFormat = swapChainCreateInfo.imageFormat;
     198             : 
     199           0 :     swapChain->m_Textures.resize(imageCount);
     200           0 :     swapChain->m_Backbuffers.resize(imageCount);
     201           0 :     for (size_t index = 0; index < imageCount; ++index)
     202             :     {
     203           0 :         snprintf(nameBuffer, std::size(nameBuffer), "SwapChainImage #%zu", index);
     204           0 :         device->SetObjectName(VK_OBJECT_TYPE_IMAGE, swapChain->m_Images[index], nameBuffer);
     205           0 :         snprintf(nameBuffer, std::size(nameBuffer), "SwapChainImageView #%zu", index);
     206           0 :         swapChain->m_Textures[index] = CTexture::WrapBackbufferImage(
     207           0 :             device, nameBuffer, swapChain->m_Images[index], swapChainCreateInfo.imageFormat,
     208             :             swapChainCreateInfo.imageUsage, swapChainWidth, swapChainHeight);
     209             :     }
     210             : 
     211           0 :     swapChain->m_IsValid = true;
     212             : 
     213           0 :     return swapChain;
     214             : }
     215             : 
     216             : CSwapChain::CSwapChain() = default;
     217             : 
     218           0 : CSwapChain::~CSwapChain()
     219             : {
     220           0 :     m_Backbuffers.clear();
     221             : 
     222           0 :     m_Textures.clear();
     223           0 :     m_DepthTexture.reset();
     224             : 
     225           0 :     if (m_SwapChain != VK_NULL_HANDLE)
     226           0 :         vkDestroySwapchainKHR(m_Device->GetVkDevice(), m_SwapChain, nullptr);
     227           0 : }
     228             : 
     229           0 : size_t CSwapChain::SwapChainBackbuffer::BackbufferKeyHash::operator()(const BackbufferKey& key) const
     230             : {
     231           0 :     size_t seed = 0;
     232           0 :     hash_combine(seed, std::get<0>(key));
     233           0 :     hash_combine(seed, std::get<1>(key));
     234           0 :     hash_combine(seed, std::get<2>(key));
     235           0 :     hash_combine(seed, std::get<3>(key));
     236           0 :     return seed;
     237             : }
     238             : 
     239             : CSwapChain::SwapChainBackbuffer::SwapChainBackbuffer() = default;
     240             : 
     241             : CSwapChain::SwapChainBackbuffer::SwapChainBackbuffer(SwapChainBackbuffer&& other) = default;
     242             : 
     243             : CSwapChain::SwapChainBackbuffer& CSwapChain::SwapChainBackbuffer::operator=(SwapChainBackbuffer&& other) = default;
     244             : 
     245           0 : bool CSwapChain::AcquireNextImage(VkSemaphore acquireImageSemaphore)
     246             : {
     247           0 :     ENSURE(m_CurrentImageIndex == std::numeric_limits<uint32_t>::max());
     248             : 
     249           0 :     const VkResult acquireResult = vkAcquireNextImageKHR(
     250           0 :         m_Device->GetVkDevice(), m_SwapChain, std::numeric_limits<uint64_t>::max(),
     251             :         acquireImageSemaphore,
     252           0 :         VK_NULL_HANDLE, &m_CurrentImageIndex);
     253           0 :     if (acquireResult != VK_SUCCESS)
     254             :     {
     255           0 :         if (acquireResult == VK_ERROR_OUT_OF_DATE_KHR)
     256           0 :             m_IsValid = false;
     257           0 :         else if (acquireResult != VK_SUBOPTIMAL_KHR)
     258             :         {
     259           0 :             LOGERROR("Acquire result: %d", static_cast<int>(acquireResult));
     260           0 :             debug_warn("Unknown acquire error.");
     261             :         }
     262             :     }
     263           0 :     return m_IsValid;
     264             : }
     265             : 
     266           0 : void CSwapChain::SubmitCommandsAfterAcquireNextImage(
     267             :     CRingCommandContext& commandContext)
     268             : {
     269           0 :     const bool firstAcquirement = !m_Textures[m_CurrentImageIndex]->IsInitialized();
     270           0 :     Utilities::SubmitImageMemoryBarrier(
     271             :         commandContext.GetCommandBuffer(),
     272           0 :         m_Images[m_CurrentImageIndex], 0, 0,
     273             :         0, VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT,
     274             :         firstAcquirement ? VK_IMAGE_LAYOUT_UNDEFINED : VK_IMAGE_LAYOUT_PRESENT_SRC_KHR,
     275             :         VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL,
     276             :         firstAcquirement ? VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT : VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT, VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT);
     277           0 :     if (!m_DepthTexture->IsInitialized())
     278             :     {
     279           0 :         Utilities::SubmitImageMemoryBarrier(
     280             :             commandContext.GetCommandBuffer(),
     281             :             m_DepthTexture->GetImage(), 0, 0,
     282             :             0, VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_READ_BIT | VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT,
     283             :             VK_IMAGE_LAYOUT_UNDEFINED,
     284             :             VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL,
     285             :             VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT, VK_PIPELINE_STAGE_EARLY_FRAGMENT_TESTS_BIT,
     286             :             VK_IMAGE_ASPECT_DEPTH_BIT | VK_IMAGE_ASPECT_STENCIL_BIT);
     287             :     }
     288           0 : }
     289             : 
     290           0 : void CSwapChain::SubmitCommandsBeforePresent(
     291             :     CRingCommandContext& commandContext)
     292             : {
     293           0 :     ENSURE(m_CurrentImageIndex != std::numeric_limits<uint32_t>::max());
     294             : 
     295           0 :     Utilities::SubmitImageMemoryBarrier(
     296           0 :         commandContext.GetCommandBuffer(), m_Images[m_CurrentImageIndex], 0, 0,
     297             :         VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT, 0,
     298             :         VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL, VK_IMAGE_LAYOUT_PRESENT_SRC_KHR,
     299             :         VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT, VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT);
     300           0 : }
     301             : 
     302           0 : void CSwapChain::Present(VkSemaphore submitDone, VkQueue queue)
     303             : {
     304           0 :     ENSURE(m_CurrentImageIndex != std::numeric_limits<uint32_t>::max());
     305             : 
     306           0 :     VkSwapchainKHR swapChains[] = {m_SwapChain};
     307             : 
     308           0 :     VkPresentInfoKHR presentInfo{};
     309           0 :     presentInfo.sType = VK_STRUCTURE_TYPE_PRESENT_INFO_KHR;
     310           0 :     presentInfo.swapchainCount = 1;
     311           0 :     presentInfo.pSwapchains = swapChains;
     312           0 :     presentInfo.pImageIndices = &m_CurrentImageIndex;
     313           0 :     presentInfo.waitSemaphoreCount = 1;
     314           0 :     presentInfo.pWaitSemaphores = &submitDone;
     315           0 :     const VkResult presentResult = vkQueuePresentKHR(queue, &presentInfo);
     316           0 :     if (presentResult != VK_SUCCESS)
     317             :     {
     318           0 :         if (presentResult == VK_ERROR_OUT_OF_DATE_KHR)
     319           0 :             m_IsValid = false;
     320           0 :         else if (presentResult != VK_SUBOPTIMAL_KHR)
     321             :         {
     322           0 :             LOGERROR("Present result: %d", static_cast<int>(presentResult));
     323           0 :             debug_warn("Unknown present error.");
     324             :         }
     325             :     }
     326             : 
     327           0 :     m_CurrentImageIndex = std::numeric_limits<uint32_t>::max();
     328           0 : }
     329             : 
     330           0 : CFramebuffer* CSwapChain::GetCurrentBackbuffer(
     331             :     const AttachmentLoadOp colorAttachmentLoadOp,
     332             :     const AttachmentStoreOp colorAttachmentStoreOp,
     333             :     const AttachmentLoadOp depthStencilAttachmentLoadOp,
     334             :     const AttachmentStoreOp depthStencilAttachmentStoreOp)
     335             : {
     336           0 :     ENSURE(m_CurrentImageIndex != std::numeric_limits<uint32_t>::max());
     337             :     SwapChainBackbuffer& swapChainBackbuffer =
     338           0 :         m_Backbuffers[m_CurrentImageIndex];
     339             :     const SwapChainBackbuffer::BackbufferKey key{
     340             :         colorAttachmentLoadOp, colorAttachmentStoreOp,
     341           0 :         depthStencilAttachmentLoadOp, depthStencilAttachmentStoreOp};
     342           0 :     auto it = swapChainBackbuffer.backbuffers.find(key);
     343           0 :     if (it == swapChainBackbuffer.backbuffers.end())
     344             :     {
     345             :         char nameBuffer[64];
     346           0 :         snprintf(nameBuffer, std::size(nameBuffer), "Backbuffer #%u", m_CurrentImageIndex);
     347             : 
     348           0 :         SColorAttachment colorAttachment{};
     349           0 :         colorAttachment.texture = m_Textures[m_CurrentImageIndex].get();
     350           0 :         colorAttachment.loadOp = colorAttachmentLoadOp;
     351           0 :         colorAttachment.storeOp = colorAttachmentStoreOp;
     352             : 
     353           0 :         SDepthStencilAttachment depthStencilAttachment{};
     354           0 :         depthStencilAttachment.texture = m_DepthTexture.get();
     355           0 :         depthStencilAttachment.loadOp = depthStencilAttachmentLoadOp;
     356           0 :         depthStencilAttachment.storeOp = depthStencilAttachmentStoreOp;
     357             : 
     358           0 :         it = swapChainBackbuffer.backbuffers.emplace(key, CFramebuffer::Create(
     359           0 :             m_Device, nameBuffer, &colorAttachment, &depthStencilAttachment)).first;
     360             :     }
     361           0 :     return it->second.get();
     362             : }
     363             : 
     364             : } // namespace Vulkan
     365             : 
     366             : } // namespace Backend
     367             : 
     368           3 : } // namespace Renderer

Generated by: LCOV version 1.13