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
|