vk_swapchain.cpp 8.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211
  1. // Copyright 2019 yuzu Emulator Project
  2. // Licensed under GPLv2 or any later version
  3. // Refer to the license.txt file included.
  4. #include <algorithm>
  5. #include <array>
  6. #include <limits>
  7. #include <vector>
  8. #include "common/assert.h"
  9. #include "common/logging/log.h"
  10. #include "core/core.h"
  11. #include "core/frontend/framebuffer_layout.h"
  12. #include "video_core/renderer_vulkan/declarations.h"
  13. #include "video_core/renderer_vulkan/vk_device.h"
  14. #include "video_core/renderer_vulkan/vk_resource_manager.h"
  15. #include "video_core/renderer_vulkan/vk_swapchain.h"
  16. namespace Vulkan {
  17. namespace {
  18. vk::SurfaceFormatKHR ChooseSwapSurfaceFormat(const std::vector<vk::SurfaceFormatKHR>& formats,
  19. bool srgb) {
  20. if (formats.size() == 1 && formats[0].format == vk::Format::eUndefined) {
  21. vk::SurfaceFormatKHR format;
  22. format.format = vk::Format::eB8G8R8A8Unorm;
  23. format.colorSpace = vk::ColorSpaceKHR::eSrgbNonlinear;
  24. return format;
  25. }
  26. const auto& found = std::find_if(formats.begin(), formats.end(), [srgb](const auto& format) {
  27. const auto request_format = srgb ? vk::Format::eB8G8R8A8Srgb : vk::Format::eB8G8R8A8Unorm;
  28. return format.format == request_format &&
  29. format.colorSpace == vk::ColorSpaceKHR::eSrgbNonlinear;
  30. });
  31. return found != formats.end() ? *found : formats[0];
  32. }
  33. vk::PresentModeKHR ChooseSwapPresentMode(const std::vector<vk::PresentModeKHR>& modes) {
  34. // Mailbox doesn't lock the application like fifo (vsync), prefer it
  35. const auto& found = std::find_if(modes.begin(), modes.end(), [](const auto& mode) {
  36. return mode == vk::PresentModeKHR::eMailbox;
  37. });
  38. return found != modes.end() ? *found : vk::PresentModeKHR::eFifo;
  39. }
  40. vk::Extent2D ChooseSwapExtent(const vk::SurfaceCapabilitiesKHR& capabilities, u32 width,
  41. u32 height) {
  42. constexpr auto undefined_size{std::numeric_limits<u32>::max()};
  43. if (capabilities.currentExtent.width != undefined_size) {
  44. return capabilities.currentExtent;
  45. }
  46. vk::Extent2D extent = {width, height};
  47. extent.width = std::max(capabilities.minImageExtent.width,
  48. std::min(capabilities.maxImageExtent.width, extent.width));
  49. extent.height = std::max(capabilities.minImageExtent.height,
  50. std::min(capabilities.maxImageExtent.height, extent.height));
  51. return extent;
  52. }
  53. } // Anonymous namespace
  54. VKSwapchain::VKSwapchain(vk::SurfaceKHR surface, const VKDevice& device)
  55. : surface{surface}, device{device} {}
  56. VKSwapchain::~VKSwapchain() = default;
  57. void VKSwapchain::Create(u32 width, u32 height, bool srgb) {
  58. const auto& dld = device.GetDispatchLoader();
  59. const auto physical_device = device.GetPhysical();
  60. const auto capabilities{physical_device.getSurfaceCapabilitiesKHR(surface, dld)};
  61. if (capabilities.maxImageExtent.width == 0 || capabilities.maxImageExtent.height == 0) {
  62. return;
  63. }
  64. device.GetLogical().waitIdle(dld);
  65. Destroy();
  66. CreateSwapchain(capabilities, width, height, srgb);
  67. CreateSemaphores();
  68. CreateImageViews();
  69. fences.resize(image_count, nullptr);
  70. }
  71. void VKSwapchain::AcquireNextImage() {
  72. const auto dev{device.GetLogical()};
  73. const auto& dld{device.GetDispatchLoader()};
  74. dev.acquireNextImageKHR(*swapchain, std::numeric_limits<u64>::max(),
  75. *present_semaphores[frame_index], {}, &image_index, dld);
  76. if (auto& fence = fences[image_index]; fence) {
  77. fence->Wait();
  78. fence->Release();
  79. fence = nullptr;
  80. }
  81. }
  82. bool VKSwapchain::Present(vk::Semaphore render_semaphore, VKFence& fence) {
  83. const vk::Semaphore present_semaphore{*present_semaphores[frame_index]};
  84. const std::array<vk::Semaphore, 2> semaphores{present_semaphore, render_semaphore};
  85. const u32 wait_semaphore_count{render_semaphore ? 2U : 1U};
  86. const auto& dld{device.GetDispatchLoader()};
  87. const auto present_queue{device.GetPresentQueue()};
  88. bool recreated = false;
  89. const vk::PresentInfoKHR present_info(wait_semaphore_count, semaphores.data(), 1,
  90. &swapchain.get(), &image_index, {});
  91. switch (const auto result = present_queue.presentKHR(&present_info, dld); result) {
  92. case vk::Result::eSuccess:
  93. break;
  94. case vk::Result::eErrorOutOfDateKHR:
  95. if (current_width > 0 && current_height > 0) {
  96. Create(current_width, current_height, current_srgb);
  97. recreated = true;
  98. }
  99. break;
  100. default:
  101. LOG_CRITICAL(Render_Vulkan, "Vulkan failed to present swapchain due to {}!",
  102. vk::to_string(result));
  103. UNREACHABLE();
  104. }
  105. ASSERT(fences[image_index] == nullptr);
  106. fences[image_index] = &fence;
  107. frame_index = (frame_index + 1) % image_count;
  108. return recreated;
  109. }
  110. bool VKSwapchain::HasFramebufferChanged(const Layout::FramebufferLayout& framebuffer) const {
  111. // TODO(Rodrigo): Handle framebuffer pixel format changes
  112. return framebuffer.width != current_width || framebuffer.height != current_height;
  113. }
  114. void VKSwapchain::CreateSwapchain(const vk::SurfaceCapabilitiesKHR& capabilities, u32 width,
  115. u32 height, bool srgb) {
  116. const auto& dld{device.GetDispatchLoader()};
  117. const auto physical_device{device.GetPhysical()};
  118. const auto formats{physical_device.getSurfaceFormatsKHR(surface, dld)};
  119. const auto present_modes{physical_device.getSurfacePresentModesKHR(surface, dld)};
  120. const vk::SurfaceFormatKHR surface_format{ChooseSwapSurfaceFormat(formats, srgb)};
  121. const vk::PresentModeKHR present_mode{ChooseSwapPresentMode(present_modes)};
  122. extent = ChooseSwapExtent(capabilities, width, height);
  123. current_width = extent.width;
  124. current_height = extent.height;
  125. current_srgb = srgb;
  126. u32 requested_image_count{capabilities.minImageCount + 1};
  127. if (capabilities.maxImageCount > 0 && requested_image_count > capabilities.maxImageCount) {
  128. requested_image_count = capabilities.maxImageCount;
  129. }
  130. vk::SwapchainCreateInfoKHR swapchain_ci(
  131. {}, surface, requested_image_count, surface_format.format, surface_format.colorSpace,
  132. extent, 1, vk::ImageUsageFlagBits::eColorAttachment, {}, {}, {},
  133. capabilities.currentTransform, vk::CompositeAlphaFlagBitsKHR::eOpaque, present_mode, false,
  134. {});
  135. const u32 graphics_family{device.GetGraphicsFamily()};
  136. const u32 present_family{device.GetPresentFamily()};
  137. const std::array<u32, 2> queue_indices{graphics_family, present_family};
  138. if (graphics_family != present_family) {
  139. swapchain_ci.imageSharingMode = vk::SharingMode::eConcurrent;
  140. swapchain_ci.queueFamilyIndexCount = static_cast<u32>(queue_indices.size());
  141. swapchain_ci.pQueueFamilyIndices = queue_indices.data();
  142. } else {
  143. swapchain_ci.imageSharingMode = vk::SharingMode::eExclusive;
  144. }
  145. const auto dev{device.GetLogical()};
  146. swapchain = dev.createSwapchainKHRUnique(swapchain_ci, nullptr, dld);
  147. images = dev.getSwapchainImagesKHR(*swapchain, dld);
  148. image_count = static_cast<u32>(images.size());
  149. image_format = surface_format.format;
  150. }
  151. void VKSwapchain::CreateSemaphores() {
  152. const auto dev{device.GetLogical()};
  153. const auto& dld{device.GetDispatchLoader()};
  154. present_semaphores.resize(image_count);
  155. for (std::size_t i = 0; i < image_count; i++) {
  156. present_semaphores[i] = dev.createSemaphoreUnique({}, nullptr, dld);
  157. }
  158. }
  159. void VKSwapchain::CreateImageViews() {
  160. const auto dev{device.GetLogical()};
  161. const auto& dld{device.GetDispatchLoader()};
  162. image_views.resize(image_count);
  163. for (std::size_t i = 0; i < image_count; i++) {
  164. const vk::ImageViewCreateInfo image_view_ci({}, images[i], vk::ImageViewType::e2D,
  165. image_format, {},
  166. {vk::ImageAspectFlagBits::eColor, 0, 1, 0, 1});
  167. image_views[i] = dev.createImageViewUnique(image_view_ci, nullptr, dld);
  168. }
  169. }
  170. void VKSwapchain::Destroy() {
  171. frame_index = 0;
  172. present_semaphores.clear();
  173. framebuffers.clear();
  174. image_views.clear();
  175. swapchain.reset();
  176. }
  177. } // namespace Vulkan