PeriDyno 1.0.0
Loading...
Searching...
No Matches
VulkanSwapChain.cpp
Go to the documentation of this file.
1/*
2* Class wrapping access to the swap chain
3*
4* A swap chain is a collection of framebuffers used for rendering and presentation to the windowing system
5*
6* Copyright (C) 2016-2017 by Sascha Willems - www.saschawillems.de
7*
8* This code is licensed under the MIT license (MIT) (http://opensource.org/licenses/MIT)
9*/
10
11#include "VulkanSwapChain.h"
12
14#if defined(VK_USE_PLATFORM_WIN32_KHR)
15void VulkanSwapChain::initSurface(void* platformHandle, void* platformWindow)
16#elif defined(VK_USE_PLATFORM_ANDROID_KHR)
17void VulkanSwapChain::initSurface(ANativeWindow* window)
18#elif defined(VK_USE_PLATFORM_XCB_KHR)
19void VulkanSwapChain::initSurface(xcb_window_t xcbWindow)
20#endif
21{
22 VkResult err = VK_SUCCESS;
23
24 // Create the os-specific surface
25#if defined(VK_USE_PLATFORM_WIN32_KHR)
26 VkWin32SurfaceCreateInfoKHR surfaceCreateInfo = {};
27 surfaceCreateInfo.sType = VK_STRUCTURE_TYPE_WIN32_SURFACE_CREATE_INFO_KHR;
28 surfaceCreateInfo.hinstance = (HINSTANCE)platformHandle;
29 surfaceCreateInfo.hwnd = (HWND)platformWindow;
30 err = vkCreateWin32SurfaceKHR(instance, &surfaceCreateInfo, nullptr, &surface);
31#elif defined(VK_USE_PLATFORM_ANDROID_KHR)
32 VkAndroidSurfaceCreateInfoKHR surfaceCreateInfo = {};
33 surfaceCreateInfo.sType = VK_STRUCTURE_TYPE_ANDROID_SURFACE_CREATE_INFO_KHR;
34 surfaceCreateInfo.window = window;
35 err = vkCreateAndroidSurfaceKHR(instance, &surfaceCreateInfo, NULL, &surface);
36#elif defined(VK_USE_PLATFORM_XCB_KHR)
37 VkXcbSurfaceCreateInfoKHR surfaceCreateInfo = {};
38 surfaceCreateInfo.sType = VK_STRUCTURE_TYPE_XCB_SURFACE_CREATE_INFO_KHR;
39 surfaceCreateInfo.window = xcbWindow;
40 vkCreateXcbSurfaceKHR(instance, &surfaceCreateInfo, nullptr, &surface);
41#endif
42
43 if (err != VK_SUCCESS) {
44 vks::tools::exitFatal("Could not create surface!", err);
45 }
46
47 // Get available queue family properties
48 uint32_t queueCount;
51
52 std::vector<VkQueueFamilyProperties> queueProps(queueCount);
54
55 // Iterate over each queue to learn whether it supports presenting:
56 // Find a queue with present support
57 // Will be used to present the swap chain images to the windowing system
58 std::vector<VkBool32> supportsPresent(queueCount);
59 for (uint32_t i = 0; i < queueCount; i++)
60 {
61 fpGetPhysicalDeviceSurfaceSupportKHR(physicalDevice, i, surface, &supportsPresent[i]);
62 }
63
64 // Search for a graphics and a present queue in the array of queue
65 // families, try to find one that supports both
66 uint32_t graphicsQueueNodeIndex = UINT32_MAX;
67 uint32_t presentQueueNodeIndex = UINT32_MAX;
68 for (uint32_t i = 0; i < queueCount; i++)
69 {
70 if ((queueProps[i].queueFlags & VK_QUEUE_GRAPHICS_BIT) != 0)
71 {
72 if (graphicsQueueNodeIndex == UINT32_MAX)
73 {
75 }
76
77 if (supportsPresent[i] == VK_TRUE)
78 {
81 break;
82 }
83 }
84 }
85 if (presentQueueNodeIndex == UINT32_MAX)
86 {
87 // If there's no queue that supports both present and graphics
88 // try to find a separate present queue
89 for (uint32_t i = 0; i < queueCount; ++i)
90 {
91 if (supportsPresent[i] == VK_TRUE)
92 {
94 break;
95 }
96 }
97 }
98
99 // Exit if either a graphics or a presenting queue hasn't been found
100 if (graphicsQueueNodeIndex == UINT32_MAX || presentQueueNodeIndex == UINT32_MAX)
101 {
102 vks::tools::exitFatal("Could not find a graphics and/or presenting queue!", -1);
103 }
104
105 // todo : Add support for separate graphics and presenting queue
107 {
108 vks::tools::exitFatal("Separate graphics and presenting queues are not supported yet!", -1);
109 }
110
112
113 // Get list of supported surface formats
114 uint32_t formatCount;
115 VK_CHECK_RESULT(fpGetPhysicalDeviceSurfaceFormatsKHR(physicalDevice, surface, &formatCount, NULL));
117
118 std::vector<VkSurfaceFormatKHR> surfaceFormats(formatCount);
119 VK_CHECK_RESULT(fpGetPhysicalDeviceSurfaceFormatsKHR(physicalDevice, surface, &formatCount, surfaceFormats.data()));
120
121 // If the surface format list only includes one entry with VK_FORMAT_UNDEFINED,
122 // there is no preferred format, so we assume VK_FORMAT_B8G8R8A8_UNORM
123 if ((formatCount == 1) && (surfaceFormats[0].format == VK_FORMAT_UNDEFINED))
124 {
125 colorFormat = VK_FORMAT_B8G8R8A8_UNORM;
126 colorSpace = surfaceFormats[0].colorSpace;
127 }
128 else
129 {
130 // iterate over the list of available surface format and
131 // check for the presence of VK_FORMAT_B8G8R8A8_UNORM
132 bool found_B8G8R8A8_UNORM = false;
133 for (auto&& surfaceFormat : surfaceFormats)
134 {
135 if (surfaceFormat.format == VK_FORMAT_B8G8R8A8_UNORM)
136 {
137 colorFormat = surfaceFormat.format;
138 colorSpace = surfaceFormat.colorSpace;
139 found_B8G8R8A8_UNORM = true;
140 break;
141 }
142 }
143
144 // in case VK_FORMAT_B8G8R8A8_UNORM is not available
145 // select the first available color format
146 if (!found_B8G8R8A8_UNORM)
147 {
148 colorFormat = surfaceFormats[0].format;
149 colorSpace = surfaceFormats[0].colorSpace;
150 }
151 }
152
153}
154
163void VulkanSwapChain::connect(VkInstance instance, VkPhysicalDevice physicalDevice, VkDevice device)
164{
165 this->instance = instance;
166 this->physicalDevice = physicalDevice;
167 this->device = device;
168 fpGetPhysicalDeviceSurfaceSupportKHR = reinterpret_cast<PFN_vkGetPhysicalDeviceSurfaceSupportKHR>(vkGetInstanceProcAddr(instance, "vkGetPhysicalDeviceSurfaceSupportKHR"));
169 fpGetPhysicalDeviceSurfaceCapabilitiesKHR = reinterpret_cast<PFN_vkGetPhysicalDeviceSurfaceCapabilitiesKHR>(vkGetInstanceProcAddr(instance, "vkGetPhysicalDeviceSurfaceCapabilitiesKHR"));
170 fpGetPhysicalDeviceSurfaceFormatsKHR = reinterpret_cast<PFN_vkGetPhysicalDeviceSurfaceFormatsKHR>(vkGetInstanceProcAddr(instance, "vkGetPhysicalDeviceSurfaceFormatsKHR"));
171 fpGetPhysicalDeviceSurfacePresentModesKHR = reinterpret_cast<PFN_vkGetPhysicalDeviceSurfacePresentModesKHR>(vkGetInstanceProcAddr(instance, "vkGetPhysicalDeviceSurfacePresentModesKHR"));
172
173 fpCreateSwapchainKHR = reinterpret_cast<PFN_vkCreateSwapchainKHR>(vkGetDeviceProcAddr(device, "vkCreateSwapchainKHR"));
174 fpDestroySwapchainKHR = reinterpret_cast<PFN_vkDestroySwapchainKHR>(vkGetDeviceProcAddr(device, "vkDestroySwapchainKHR"));
175 fpGetSwapchainImagesKHR = reinterpret_cast<PFN_vkGetSwapchainImagesKHR>(vkGetDeviceProcAddr(device, "vkGetSwapchainImagesKHR"));
176 fpAcquireNextImageKHR = reinterpret_cast<PFN_vkAcquireNextImageKHR>(vkGetDeviceProcAddr(device, "vkAcquireNextImageKHR"));
177 fpQueuePresentKHR = reinterpret_cast<PFN_vkQueuePresentKHR>(vkGetDeviceProcAddr(device, "vkQueuePresentKHR"));
178}
179
187void VulkanSwapChain::create(uint32_t *width, uint32_t *height, bool vsync)
188{
189 VkSwapchainKHR oldSwapchain = swapChain;
190
191 // Get physical device surface properties and formats
192 VkSurfaceCapabilitiesKHR surfCaps;
194
195 // Get available present modes
196 uint32_t presentModeCount;
198 assert(presentModeCount > 0);
199
200 std::vector<VkPresentModeKHR> presentModes(presentModeCount);
201 VK_CHECK_RESULT(fpGetPhysicalDeviceSurfacePresentModesKHR(physicalDevice, surface, &presentModeCount, presentModes.data()));
202
203 VkExtent2D swapchainExtent = {};
204 // If width (and height) equals the special value 0xFFFFFFFF, the size of the surface will be set by the swapchain
205 if (surfCaps.currentExtent.width == (uint32_t)-1)
206 {
207 // If the surface size is undefined, the size is set to
208 // the size of the images requested.
209 swapchainExtent.width = *width;
210 swapchainExtent.height = *height;
211 }
212 else
213 {
214 // If the surface size is defined, the swap chain size must match
215 swapchainExtent = surfCaps.currentExtent;
216 *width = surfCaps.currentExtent.width;
217 *height = surfCaps.currentExtent.height;
218 }
219
220
221 // Select a present mode for the swapchain
222
223 // The VK_PRESENT_MODE_FIFO_KHR mode must always be present as per spec
224 // This mode waits for the vertical blank ("v-sync")
225 VkPresentModeKHR swapchainPresentMode = VK_PRESENT_MODE_FIFO_KHR;
226
227 // If v-sync is not requested, try to find a mailbox mode
228 // It's the lowest latency non-tearing present mode available
229 if (!vsync)
230 {
231 for (size_t i = 0; i < presentModeCount; i++)
232 {
233 if (presentModes[i] == VK_PRESENT_MODE_MAILBOX_KHR)
234 {
235 swapchainPresentMode = VK_PRESENT_MODE_MAILBOX_KHR;
236 break;
237 }
238 if ((swapchainPresentMode != VK_PRESENT_MODE_MAILBOX_KHR) && (presentModes[i] == VK_PRESENT_MODE_IMMEDIATE_KHR))
239 {
240 swapchainPresentMode = VK_PRESENT_MODE_IMMEDIATE_KHR;
241 }
242 }
243 }
244
245 // Determine the number of images
246 uint32_t desiredNumberOfSwapchainImages = surfCaps.minImageCount + 1;
247 if ((surfCaps.maxImageCount > 0) && (desiredNumberOfSwapchainImages > surfCaps.maxImageCount))
248 {
249 desiredNumberOfSwapchainImages = surfCaps.maxImageCount;
250 }
251
252 // Find the transformation of the surface
253 VkSurfaceTransformFlagsKHR preTransform;
254 if (surfCaps.supportedTransforms & VK_SURFACE_TRANSFORM_IDENTITY_BIT_KHR)
255 {
256 // We prefer a non-rotated transform
257 preTransform = VK_SURFACE_TRANSFORM_IDENTITY_BIT_KHR;
258 }
259 else
260 {
261 preTransform = surfCaps.currentTransform;
262 }
263
264 // Find a supported composite alpha format (not all devices support alpha opaque)
265 VkCompositeAlphaFlagBitsKHR compositeAlpha = VK_COMPOSITE_ALPHA_OPAQUE_BIT_KHR;
266 // Simply select the first composite alpha format available
267 std::vector<VkCompositeAlphaFlagBitsKHR> compositeAlphaFlags = {
268 VK_COMPOSITE_ALPHA_OPAQUE_BIT_KHR,
269 VK_COMPOSITE_ALPHA_PRE_MULTIPLIED_BIT_KHR,
270 VK_COMPOSITE_ALPHA_POST_MULTIPLIED_BIT_KHR,
271 VK_COMPOSITE_ALPHA_INHERIT_BIT_KHR,
272 };
273 for (auto& compositeAlphaFlag : compositeAlphaFlags) {
274 if (surfCaps.supportedCompositeAlpha & compositeAlphaFlag) {
275 compositeAlpha = compositeAlphaFlag;
276 break;
277 };
278 }
279
280 VkSwapchainCreateInfoKHR swapchainCI = {};
281 swapchainCI.sType = VK_STRUCTURE_TYPE_SWAPCHAIN_CREATE_INFO_KHR;
282 swapchainCI.pNext = NULL;
283 swapchainCI.surface = surface;
284 swapchainCI.minImageCount = desiredNumberOfSwapchainImages;
285 swapchainCI.imageFormat = colorFormat;
286 swapchainCI.imageColorSpace = colorSpace;
287 swapchainCI.imageExtent = { swapchainExtent.width, swapchainExtent.height };
288 swapchainCI.imageUsage = VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT;
289 swapchainCI.preTransform = (VkSurfaceTransformFlagBitsKHR)preTransform;
290 swapchainCI.imageArrayLayers = 1;
291 swapchainCI.imageSharingMode = VK_SHARING_MODE_EXCLUSIVE;
292 swapchainCI.queueFamilyIndexCount = 0;
293 swapchainCI.pQueueFamilyIndices = NULL;
294 swapchainCI.presentMode = swapchainPresentMode;
295 swapchainCI.oldSwapchain = oldSwapchain;
296 // Setting clipped to VK_TRUE allows the implementation to discard rendering outside of the surface area
297 swapchainCI.clipped = VK_TRUE;
298 swapchainCI.compositeAlpha = compositeAlpha;
299
300 // Enable transfer source on swap chain images if supported
301 if (surfCaps.supportedUsageFlags & VK_IMAGE_USAGE_TRANSFER_SRC_BIT) {
302 swapchainCI.imageUsage |= VK_IMAGE_USAGE_TRANSFER_SRC_BIT;
303 }
304
305 // Enable transfer destination on swap chain images if supported
306 if (surfCaps.supportedUsageFlags & VK_IMAGE_USAGE_TRANSFER_DST_BIT) {
307 swapchainCI.imageUsage |= VK_IMAGE_USAGE_TRANSFER_DST_BIT;
308 }
309
310 VK_CHECK_RESULT(fpCreateSwapchainKHR(device, &swapchainCI, nullptr, &swapChain));
311
312 // If an existing swap chain is re-created, destroy the old swap chain
313 // This also cleans up all the presentable images
314 if (oldSwapchain != VK_NULL_HANDLE)
315 {
316 for (uint32_t i = 0; i < imageCount; i++)
317 {
318 vkDestroyImageView(device, buffers[i].view, nullptr);
319 }
320 fpDestroySwapchainKHR(device, oldSwapchain, nullptr);
321 }
323
324 // Get the swap chain images
325 images.resize(imageCount);
327
328 // Get the swap chain buffers containing the image and imageview
329 buffers.resize(imageCount);
330 for (uint32_t i = 0; i < imageCount; i++)
331 {
332 VkImageViewCreateInfo colorAttachmentView = {};
333 colorAttachmentView.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO;
334 colorAttachmentView.pNext = NULL;
335 colorAttachmentView.format = colorFormat;
336 colorAttachmentView.components = {
337 VK_COMPONENT_SWIZZLE_R,
338 VK_COMPONENT_SWIZZLE_G,
339 VK_COMPONENT_SWIZZLE_B,
340 VK_COMPONENT_SWIZZLE_A
341 };
342 colorAttachmentView.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
343 colorAttachmentView.subresourceRange.baseMipLevel = 0;
344 colorAttachmentView.subresourceRange.levelCount = 1;
345 colorAttachmentView.subresourceRange.baseArrayLayer = 0;
346 colorAttachmentView.subresourceRange.layerCount = 1;
347 colorAttachmentView.viewType = VK_IMAGE_VIEW_TYPE_2D;
348 colorAttachmentView.flags = 0;
349
350 buffers[i].image = images[i];
351
352 colorAttachmentView.image = buffers[i].image;
353
354 VK_CHECK_RESULT(vkCreateImageView(device, &colorAttachmentView, nullptr, &buffers[i].view));
355 }
356}
357
368VkResult VulkanSwapChain::acquireNextImage(VkSemaphore presentCompleteSemaphore, uint32_t *imageIndex)
369{
370 // By setting timeout to UINT64_MAX we will always wait until the next image has been acquired or an actual error is thrown
371 // With that we don't have to handle VK_NOT_READY
372 return fpAcquireNextImageKHR(device, swapChain, UINT64_MAX, presentCompleteSemaphore, (VkFence)nullptr, imageIndex);
373}
374
384VkResult VulkanSwapChain::queuePresent(VkQueue queue, uint32_t imageIndex, VkSemaphore waitSemaphore)
385{
386 VkPresentInfoKHR presentInfo = {};
387 presentInfo.sType = VK_STRUCTURE_TYPE_PRESENT_INFO_KHR;
388 presentInfo.pNext = NULL;
389 presentInfo.swapchainCount = 1;
390 presentInfo.pSwapchains = &swapChain;
391 presentInfo.pImageIndices = &imageIndex;
392 // Check if a wait semaphore has been specified to wait for before presenting the image
393 if (waitSemaphore != VK_NULL_HANDLE)
394 {
395 presentInfo.pWaitSemaphores = &waitSemaphore;
396 presentInfo.waitSemaphoreCount = 1;
397 }
398 return fpQueuePresentKHR(queue, &presentInfo);
399}
400
401
406{
407 if (swapChain != VK_NULL_HANDLE)
408 {
409 for (uint32_t i = 0; i < imageCount; i++)
410 {
411 vkDestroyImageView(device, buffers[i].view, nullptr);
412 }
413 }
414 if (surface != VK_NULL_HANDLE)
415 {
417 vkDestroySurfaceKHR(instance, surface, nullptr);
418 }
419 surface = VK_NULL_HANDLE;
420 swapChain = VK_NULL_HANDLE;
421}
uint32_t presentQueueNodeIndex
assert(queueCount >=1)
vkGetPhysicalDeviceQueueFamilyProperties(physicalDevice, &queueCount, NULL)
std::vector< VkBool32 > supportsPresent(queueCount)
uint32_t formatCount
uint32_t graphicsQueueNodeIndex
std::vector< VkQueueFamilyProperties > queueProps(queueCount)
queueNodeIndex
std::vector< VkSurfaceFormatKHR > surfaceFormats(formatCount)
uint32_t queueCount
#define VK_CHECK_RESULT(f)
Definition VulkanTools.h:55
PFN_vkGetPhysicalDeviceSurfaceCapabilitiesKHR fpGetPhysicalDeviceSurfaceCapabilitiesKHR
PFN_vkGetSwapchainImagesKHR fpGetSwapchainImagesKHR
std::vector< SwapChainBuffer > buffers
PFN_vkAcquireNextImageKHR fpAcquireNextImageKHR
std::vector< VkImage > images
PFN_vkQueuePresentKHR fpQueuePresentKHR
void create(uint32_t *width, uint32_t *height, bool vsync=false)
VkPhysicalDevice physicalDevice
VkResult queuePresent(VkQueue queue, uint32_t imageIndex, VkSemaphore waitSemaphore=VK_NULL_HANDLE)
VkSurfaceKHR surface
void connect(VkInstance instance, VkPhysicalDevice physicalDevice, VkDevice device)
PFN_vkGetPhysicalDeviceSurfaceSupportKHR fpGetPhysicalDeviceSurfaceSupportKHR
VkSwapchainKHR swapChain
PFN_vkCreateSwapchainKHR fpCreateSwapchainKHR
VkInstance instance
VkResult acquireNextImage(VkSemaphore presentCompleteSemaphore, uint32_t *imageIndex)
PFN_vkDestroySwapchainKHR fpDestroySwapchainKHR
VkColorSpaceKHR colorSpace
PFN_vkGetPhysicalDeviceSurfacePresentModesKHR fpGetPhysicalDeviceSurfacePresentModesKHR
PFN_vkGetPhysicalDeviceSurfaceFormatsKHR fpGetPhysicalDeviceSurfaceFormatsKHR
void exitFatal(const std::string &message, int32_t exitCode)