summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorGravatar ReinUsesLisp2019-03-28 23:58:43 -0300
committerGravatar ReinUsesLisp2019-03-29 00:00:51 -0300
commit746dab407eb2e2bf08d38c68c0058fa0476e78c7 (patch)
treea4d0b47971abc2bb01e9f0de9dace4e6640dcb55
parentMerge pull request #2266 from FernandoS27/arbitration (diff)
downloadyuzu-746dab407eb2e2bf08d38c68c0058fa0476e78c7.tar.gz
yuzu-746dab407eb2e2bf08d38c68c0058fa0476e78c7.tar.xz
yuzu-746dab407eb2e2bf08d38c68c0058fa0476e78c7.zip
vk_swapchain: Implement a swapchain manager
Diffstat (limited to '')
-rw-r--r--src/video_core/CMakeLists.txt4
-rw-r--r--src/video_core/renderer_vulkan/vk_swapchain.cpp210
-rw-r--r--src/video_core/renderer_vulkan/vk_swapchain.h92
3 files changed, 305 insertions, 1 deletions
diff --git a/src/video_core/CMakeLists.txt b/src/video_core/CMakeLists.txt
index 14b76680f..44c761d3e 100644
--- a/src/video_core/CMakeLists.txt
+++ b/src/video_core/CMakeLists.txt
@@ -128,7 +128,9 @@ if (ENABLE_VULKAN)
128 renderer_vulkan/vk_scheduler.cpp 128 renderer_vulkan/vk_scheduler.cpp
129 renderer_vulkan/vk_scheduler.h 129 renderer_vulkan/vk_scheduler.h
130 renderer_vulkan/vk_stream_buffer.cpp 130 renderer_vulkan/vk_stream_buffer.cpp
131 renderer_vulkan/vk_stream_buffer.h) 131 renderer_vulkan/vk_stream_buffer.h
132 renderer_vulkan/vk_swapchain.cpp
133 renderer_vulkan/vk_swapchain.h)
132 134
133 target_include_directories(video_core PRIVATE ../../externals/Vulkan-Headers/include) 135 target_include_directories(video_core PRIVATE ../../externals/Vulkan-Headers/include)
134 target_compile_definitions(video_core PRIVATE HAS_VULKAN) 136 target_compile_definitions(video_core PRIVATE HAS_VULKAN)
diff --git a/src/video_core/renderer_vulkan/vk_swapchain.cpp b/src/video_core/renderer_vulkan/vk_swapchain.cpp
new file mode 100644
index 000000000..08279e562
--- /dev/null
+++ b/src/video_core/renderer_vulkan/vk_swapchain.cpp
@@ -0,0 +1,210 @@
1// Copyright 2019 yuzu Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#include <algorithm>
6#include <array>
7#include <limits>
8#include <vector>
9
10#include "common/assert.h"
11#include "common/logging/log.h"
12#include "core/core.h"
13#include "core/frontend/framebuffer_layout.h"
14#include "video_core/renderer_vulkan/declarations.h"
15#include "video_core/renderer_vulkan/vk_device.h"
16#include "video_core/renderer_vulkan/vk_resource_manager.h"
17#include "video_core/renderer_vulkan/vk_swapchain.h"
18
19namespace Vulkan {
20
21namespace {
22vk::SurfaceFormatKHR ChooseSwapSurfaceFormat(const std::vector<vk::SurfaceFormatKHR>& formats) {
23 if (formats.size() == 1 && formats[0].format == vk::Format::eUndefined) {
24 return {vk::Format::eB8G8R8A8Unorm, vk::ColorSpaceKHR::eSrgbNonlinear};
25 }
26 const auto& found = std::find_if(formats.begin(), formats.end(), [](const auto& format) {
27 return format.format == vk::Format::eB8G8R8A8Unorm &&
28 format.colorSpace == vk::ColorSpaceKHR::eSrgbNonlinear;
29 });
30 return found != formats.end() ? *found : formats[0];
31}
32
33vk::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
41vk::Extent2D ChooseSwapExtent(const vk::SurfaceCapabilitiesKHR& capabilities, u32 width,
42 u32 height) {
43 constexpr auto undefined_size{std::numeric_limits<u32>::max()};
44 if (capabilities.currentExtent.width != undefined_size) {
45 return capabilities.currentExtent;
46 }
47 vk::Extent2D extent = {width, height};
48 extent.width = std::max(capabilities.minImageExtent.width,
49 std::min(capabilities.maxImageExtent.width, extent.width));
50 extent.height = std::max(capabilities.minImageExtent.height,
51 std::min(capabilities.maxImageExtent.height, extent.height));
52 return extent;
53}
54} // namespace
55
56VKSwapchain::VKSwapchain(vk::SurfaceKHR surface, const VKDevice& device)
57 : surface{surface}, device{device} {}
58
59VKSwapchain::~VKSwapchain() = default;
60
61void VKSwapchain::Create(u32 width, u32 height) {
62 const auto dev = device.GetLogical();
63 const auto& dld = device.GetDispatchLoader();
64 const auto physical_device = device.GetPhysical();
65
66 const vk::SurfaceCapabilitiesKHR capabilities{
67 physical_device.getSurfaceCapabilitiesKHR(surface, dld)};
68 if (capabilities.maxImageExtent.width == 0 || capabilities.maxImageExtent.height == 0) {
69 return;
70 }
71
72 dev.waitIdle(dld);
73 Destroy();
74
75 CreateSwapchain(capabilities, width, height);
76 CreateSemaphores();
77 CreateImageViews();
78
79 fences.resize(image_count, nullptr);
80}
81
82void VKSwapchain::AcquireNextImage() {
83 const auto dev{device.GetLogical()};
84 const auto& dld{device.GetDispatchLoader()};
85 dev.acquireNextImageKHR(*swapchain, std::numeric_limits<u64>::max(),
86 *present_semaphores[frame_index], {}, &image_index, dld);
87
88 if (auto& fence = fences[image_index]; fence) {
89 fence->Wait();
90 fence->Release();
91 fence = nullptr;
92 }
93}
94
95bool VKSwapchain::Present(vk::Semaphore render_semaphore, VKFence& fence) {
96 const vk::Semaphore present_semaphore{*present_semaphores[frame_index]};
97 const std::array<vk::Semaphore, 2> semaphores{present_semaphore, render_semaphore};
98 const u32 wait_semaphore_count{render_semaphore ? 2U : 1U};
99 const auto& dld{device.GetDispatchLoader()};
100 const auto present_queue{device.GetPresentQueue()};
101 bool recreated = false;
102
103 const vk::PresentInfoKHR present_info(wait_semaphore_count, semaphores.data(), 1,
104 &swapchain.get(), &image_index, {});
105 switch (const auto result = present_queue.presentKHR(&present_info, dld); result) {
106 case vk::Result::eSuccess:
107 break;
108 case vk::Result::eErrorOutOfDateKHR:
109 if (current_width > 0 && current_height > 0) {
110 Create(current_width, current_height);
111 recreated = true;
112 }
113 break;
114 default:
115 LOG_CRITICAL(Render_Vulkan, "Vulkan failed to present swapchain due to {}!",
116 vk::to_string(result));
117 UNREACHABLE();
118 }
119
120 ASSERT(fences[image_index] == nullptr);
121 fences[image_index] = &fence;
122 frame_index = (frame_index + 1) % image_count;
123 return recreated;
124}
125
126bool VKSwapchain::HasFramebufferChanged(const Layout::FramebufferLayout& framebuffer) const {
127 // TODO(Rodrigo): Handle framebuffer pixel format changes
128 return framebuffer.width != current_width || framebuffer.height != current_height;
129}
130
131void VKSwapchain::CreateSwapchain(const vk::SurfaceCapabilitiesKHR& capabilities, u32 width,
132 u32 height) {
133 const auto dev{device.GetLogical()};
134 const auto& dld{device.GetDispatchLoader()};
135 const auto physical_device{device.GetPhysical()};
136
137 const std::vector<vk::SurfaceFormatKHR> formats{
138 physical_device.getSurfaceFormatsKHR(surface, dld)};
139
140 const std::vector<vk::PresentModeKHR> present_modes{
141 physical_device.getSurfacePresentModesKHR(surface, dld)};
142
143 const vk::SurfaceFormatKHR surface_format{ChooseSwapSurfaceFormat(formats)};
144 const vk::PresentModeKHR present_mode{ChooseSwapPresentMode(present_modes)};
145 extent = ChooseSwapExtent(capabilities, width, height);
146
147 current_width = extent.width;
148 current_height = extent.height;
149
150 u32 requested_image_count{capabilities.minImageCount + 1};
151 if (capabilities.maxImageCount > 0 && requested_image_count > capabilities.maxImageCount) {
152 requested_image_count = capabilities.maxImageCount;
153 }
154
155 vk::SwapchainCreateInfoKHR swapchain_ci(
156 {}, surface, requested_image_count, surface_format.format, surface_format.colorSpace,
157 extent, 1, vk::ImageUsageFlagBits::eColorAttachment, {}, {}, {},
158 capabilities.currentTransform, vk::CompositeAlphaFlagBitsKHR::eOpaque, present_mode, false,
159 {});
160
161 const u32 graphics_family{device.GetGraphicsFamily()};
162 const u32 present_family{device.GetPresentFamily()};
163 const std::array<u32, 2> queue_indices{graphics_family, present_family};
164 if (graphics_family != present_family) {
165 swapchain_ci.imageSharingMode = vk::SharingMode::eConcurrent;
166 swapchain_ci.queueFamilyIndexCount = static_cast<u32>(queue_indices.size());
167 swapchain_ci.pQueueFamilyIndices = queue_indices.data();
168 } else {
169 swapchain_ci.imageSharingMode = vk::SharingMode::eExclusive;
170 }
171
172 swapchain = dev.createSwapchainKHRUnique(swapchain_ci, nullptr, dld);
173
174 images = dev.getSwapchainImagesKHR(*swapchain, dld);
175 image_count = static_cast<u32>(images.size());
176 image_format = surface_format.format;
177}
178
179void VKSwapchain::CreateSemaphores() {
180 const auto dev{device.GetLogical()};
181 const auto& dld{device.GetDispatchLoader()};
182
183 present_semaphores.resize(image_count);
184 for (std::size_t i = 0; i < image_count; i++) {
185 present_semaphores[i] = dev.createSemaphoreUnique({}, nullptr, dld);
186 }
187}
188
189void VKSwapchain::CreateImageViews() {
190 const auto dev{device.GetLogical()};
191 const auto& dld{device.GetDispatchLoader()};
192
193 image_views.resize(image_count);
194 for (std::size_t i = 0; i < image_count; i++) {
195 const vk::ImageViewCreateInfo image_view_ci({}, images[i], vk::ImageViewType::e2D,
196 image_format, {},
197 {vk::ImageAspectFlagBits::eColor, 0, 1, 0, 1});
198 image_views[i] = dev.createImageViewUnique(image_view_ci, nullptr, dld);
199 }
200}
201
202void VKSwapchain::Destroy() {
203 frame_index = 0;
204 present_semaphores.clear();
205 framebuffers.clear();
206 image_views.clear();
207 swapchain.reset();
208}
209
210} // namespace Vulkan
diff --git a/src/video_core/renderer_vulkan/vk_swapchain.h b/src/video_core/renderer_vulkan/vk_swapchain.h
new file mode 100644
index 000000000..2ad84f185
--- /dev/null
+++ b/src/video_core/renderer_vulkan/vk_swapchain.h
@@ -0,0 +1,92 @@
1// Copyright 2019 yuzu Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#pragma once
6
7#include <vector>
8
9#include "common/common_types.h"
10#include "video_core/renderer_vulkan/declarations.h"
11
12namespace Layout {
13struct FramebufferLayout;
14}
15
16namespace Vulkan {
17
18class VKDevice;
19class VKFence;
20
21class VKSwapchain {
22public:
23 explicit VKSwapchain(vk::SurfaceKHR surface, const VKDevice& device);
24 ~VKSwapchain();
25
26 /// Creates (or recreates) the swapchain with a given size.
27 void Create(u32 width, u32 height);
28
29 /// Acquires the next image in the swapchain, waits as needed.
30 void AcquireNextImage();
31
32 /// Presents the rendered image to the swapchain. Returns true when the swapchains had to be
33 /// recreated. Takes responsability for the ownership of fence.
34 bool Present(vk::Semaphore render_semaphore, VKFence& fence);
35
36 /// Returns true when the framebuffer layout has changed.
37 bool HasFramebufferChanged(const Layout::FramebufferLayout& framebuffer) const;
38
39 const vk::Extent2D& GetSize() const {
40 return extent;
41 }
42
43 u32 GetImageCount() const {
44 return image_count;
45 }
46
47 u32 GetImageIndex() const {
48 return image_index;
49 }
50
51 vk::Image GetImageIndex(u32 index) const {
52 return images[index];
53 }
54
55 vk::ImageView GetImageViewIndex(u32 index) const {
56 return *image_views[index];
57 }
58
59 vk::Format GetImageFormat() const {
60 return image_format;
61 }
62
63private:
64 void CreateSwapchain(const vk::SurfaceCapabilitiesKHR& capabilities, u32 width, u32 height);
65 void CreateSemaphores();
66 void CreateImageViews();
67
68 void Destroy();
69
70 const vk::SurfaceKHR surface;
71 const VKDevice& device;
72
73 UniqueSwapchainKHR swapchain;
74
75 u32 image_count{};
76 std::vector<vk::Image> images;
77 std::vector<UniqueImageView> image_views;
78 std::vector<UniqueFramebuffer> framebuffers;
79 std::vector<VKFence*> fences;
80 std::vector<UniqueSemaphore> present_semaphores;
81
82 u32 image_index{};
83 u32 frame_index{};
84
85 vk::Format image_format{};
86 vk::Extent2D extent{};
87
88 u32 current_width{};
89 u32 current_height{};
90};
91
92} // namespace Vulkan