Wasabi
vulkanswapchain.hpp
1 /*
2 * Class wrapping access to the swap chain
3 *
4 * A swap chain is a collection of framebuffers used for rendering
5 * The swap chain images can then presented to the windowing system
6 *
7 * Copyright (C) 2016 by Sascha Willems - www.saschawillems.de
8 *
9 * This code is licensed under the MIT license (MIT) (http://opensource.org/licenses/MIT)
10 */
11 
12 #pragma once
13 
14 #include <stdlib.h>
15 #include <string>
16 #include <fstream>
17 #include <assert.h>
18 #include <stdio.h>
19 #include <vector>
20 #ifdef _WIN32
21 #include <windows.h>
22 #include <fcntl.h>
23 #include <io.h>
24 #elif (defined __linux__)
25 #include <xcb/xcb.h>
26 #include <X11/Xlib.h>
27 #endif
28 
30 #include <vulkan/vulkan.h>
31 #include "Wasabi/Core/VkTools/vulkantools.hpp"
32 
33 #ifdef __ANDROID__
34 #include "vulkanandroid.hpp"
35 #endif
36 
37 typedef uint32_t uint;
38 
39 // Macro to get a procedure address based on a vulkan instance
40 #define GET_INSTANCE_PROC_ADDR(inst, entrypoint) \
41 { \
42  fp##entrypoint = reinterpret_cast<PFN_vk##entrypoint>(vkGetInstanceProcAddr(inst, "vk"#entrypoint)); \
43  if (fp##entrypoint == NULL) \
44  { \
45  exit(1); \
46  } \
47 }
48 
49 // Macro to get a procedure address based on a vulkan device
50 #define GET_DEVICE_PROC_ADDR(dev, entrypoint) \
51 { \
52  fp##entrypoint = reinterpret_cast<PFN_vk##entrypoint>(vkGetDeviceProcAddr(dev, "vk"#entrypoint)); \
53  if (fp##entrypoint == NULL) \
54  { \
55  exit(1); \
56  } \
57 }
58 
59 typedef struct _SwapChainBuffers {
60  VkImage image;
61  VkImageView view;
63 
65 {
66 private:
67  VkInstance instance;
68  VkDevice device;
69  VkPhysicalDevice physicalDevice;
70  VkSurfaceKHR surface;
71  // Function pointers
72  PFN_vkGetPhysicalDeviceSurfaceSupportKHR fpGetPhysicalDeviceSurfaceSupportKHR;
73  PFN_vkGetPhysicalDeviceSurfaceCapabilitiesKHR fpGetPhysicalDeviceSurfaceCapabilitiesKHR;
74  PFN_vkGetPhysicalDeviceSurfaceFormatsKHR fpGetPhysicalDeviceSurfaceFormatsKHR;
75  PFN_vkGetPhysicalDeviceSurfacePresentModesKHR fpGetPhysicalDeviceSurfacePresentModesKHR;
76  PFN_vkCreateSwapchainKHR fpCreateSwapchainKHR;
77  PFN_vkDestroySwapchainKHR fpDestroySwapchainKHR;
78  PFN_vkGetSwapchainImagesKHR fpGetSwapchainImagesKHR;
79  PFN_vkAcquireNextImageKHR fpAcquireNextImageKHR;
80  PFN_vkQueuePresentKHR fpQueuePresentKHR;
81 public:
82  VkFormat colorFormat;
83  VkColorSpaceKHR colorSpace;
84 
85  VkSwapchainKHR swapChain = VK_NULL_HANDLE;
86 
87  uint32_t imageCount;
88  std::vector<VkImage> images;
89  std::vector<SwapChainBuffer> buffers;
90 
91  // Index of the deteced graphics and presenting device queue
92  uint32_t queueNodeIndex = UINT32_MAX;
93 
94  // Creates an os specific surface
95  // Tries to find a graphics and a present queue
96  bool initSurface(VkSurfaceKHR _surface)
97  {
98  VkResult err;
99 
100  // Create surface depending on OS
101  surface = _surface;
102 
103  // Get available queue family properties
104  uint32_t queueCount;
105  vkGetPhysicalDeviceQueueFamilyProperties(physicalDevice, &queueCount, NULL);
106  assert(queueCount >= 1);
107 
108  std::vector<VkQueueFamilyProperties> queueProps(queueCount);
109  vkGetPhysicalDeviceQueueFamilyProperties(physicalDevice, &queueCount, queueProps.data());
110 
111  // Iterate over each queue to learn whether it supports presenting:
112  // Find a queue with present support
113  // Will be used to present the swap chain images to the windowing system
114  std::vector<VkBool32> supportsPresent(queueCount);
115  for (uint32_t i = 0; i < queueCount; i++)
116  {
117  fpGetPhysicalDeviceSurfaceSupportKHR(physicalDevice, i, surface, &supportsPresent[i]);
118  }
119 
120  // Search for a graphics and a present queue in the array of queue
121  // families, try to find one that supports both
122  uint32_t graphicsQueueNodeIndex = UINT32_MAX;
123  uint32_t presentQueueNodeIndex = UINT32_MAX;
124  for (uint32_t i = 0; i < queueCount; i++)
125  {
126  if ((queueProps[i].queueFlags & VK_QUEUE_GRAPHICS_BIT) != 0)
127  {
128  if (graphicsQueueNodeIndex == UINT32_MAX)
129  {
130  graphicsQueueNodeIndex = i;
131  }
132 
133  if (supportsPresent[i] == VK_TRUE)
134  {
135  graphicsQueueNodeIndex = i;
136  presentQueueNodeIndex = i;
137  break;
138  }
139  }
140  }
141  if (presentQueueNodeIndex == UINT32_MAX)
142  {
143  // If there's no queue that supports both present and graphics
144  // try to find a separate present queue
145  for (uint32_t i = 0; i < queueCount; ++i)
146  {
147  if (supportsPresent[i] == VK_TRUE)
148  {
149  presentQueueNodeIndex = i;
150  break;
151  }
152  }
153  }
154 
155  // Exit if either a graphics or a presenting queue hasn't been found
156  if (graphicsQueueNodeIndex == UINT32_MAX || presentQueueNodeIndex == UINT32_MAX)
157  return false;
158 
159  // todo : Add support for separate graphics and presenting queue
160  if (graphicsQueueNodeIndex != presentQueueNodeIndex)
161  return false;
162 
163  queueNodeIndex = graphicsQueueNodeIndex;
164 
165  // Get list of supported surface formats
166  uint32_t formatCount;
167  err = fpGetPhysicalDeviceSurfaceFormatsKHR(physicalDevice, surface, &formatCount, NULL);
168  assert(!err);
169  assert(formatCount > 0);
170 
171  std::vector<VkSurfaceFormatKHR> surfaceFormats(formatCount);
172  err = fpGetPhysicalDeviceSurfaceFormatsKHR(physicalDevice, surface, &formatCount, surfaceFormats.data());
173  assert(!err);
174 
175  // If the surface format list only includes one entry with VK_FORMAT_UNDEFINED,
176  // there is no preferered format, so we assume VK_FORMAT_B8G8R8A8_UNORM
177  if ((formatCount == 1) && (surfaceFormats[0].format == VK_FORMAT_UNDEFINED))
178  {
179  colorFormat = VK_FORMAT_B8G8R8A8_UNORM;
180  }
181  else
182  {
183  // Always select the first available color format
184  // If you need a specific format (e.g. SRGB) you'd need to
185  // iterate over the list of available surface format and
186  // check for it's presence
187  colorFormat = surfaceFormats[0].format;
188  }
189  colorSpace = surfaceFormats[0].colorSpace;
190 
191  return true;
192  }
193 
194  // Connect to the instance und device and get all required function pointers
195  void connect(VkInstance inst, VkPhysicalDevice physDev, VkDevice dev)
196  {
197  this->instance = inst;
198  this->physicalDevice = physDev;
199  this->device = dev;
200  GET_INSTANCE_PROC_ADDR(instance, GetPhysicalDeviceSurfaceSupportKHR);
201  GET_INSTANCE_PROC_ADDR(instance, GetPhysicalDeviceSurfaceCapabilitiesKHR);
202  GET_INSTANCE_PROC_ADDR(instance, GetPhysicalDeviceSurfaceFormatsKHR);
203  GET_INSTANCE_PROC_ADDR(instance, GetPhysicalDeviceSurfacePresentModesKHR);
204  GET_DEVICE_PROC_ADDR(device, CreateSwapchainKHR);
205  GET_DEVICE_PROC_ADDR(device, DestroySwapchainKHR);
206  GET_DEVICE_PROC_ADDR(device, GetSwapchainImagesKHR);
207  GET_DEVICE_PROC_ADDR(device, AcquireNextImageKHR);
208  GET_DEVICE_PROC_ADDR(device, QueuePresentKHR);
209  }
210 
211  // Create the swap chain and get images with given width and height
212  void create(VkCommandBuffer cmdBuffer, uint32_t *width, uint32_t *height, uint32_t numDesiredSwapchainImages = std::numeric_limits<uint32_t>::max())
213  {
214  VkResult err;
215  VkSwapchainKHR oldSwapchain = swapChain;
216 
217  // Get physical device surface properties and formats
218  VkSurfaceCapabilitiesKHR surfCaps;
219  err = fpGetPhysicalDeviceSurfaceCapabilitiesKHR(physicalDevice, surface, &surfCaps);
220  assert(!err);
221 
222  // Get available present modes
223  uint32_t presentModeCount;
224  err = fpGetPhysicalDeviceSurfacePresentModesKHR(physicalDevice, surface, &presentModeCount, NULL);
225  assert(!err);
226  assert(presentModeCount > 0);
227 
228  std::vector<VkPresentModeKHR> presentModes(presentModeCount);
229 
230  err = fpGetPhysicalDeviceSurfacePresentModesKHR(physicalDevice, surface, &presentModeCount, presentModes.data());
231  assert(!err);
232 
233  VkExtent2D swapchainExtent = {};
234  // width and height are either both -1, or both not -1.
235  if (surfCaps.currentExtent.width == std::numeric_limits<uint32_t>::max())
236  {
237  // If the surface size is undefined, the size is set to
238  // the size of the images requested.
239  swapchainExtent.width = *width;
240  swapchainExtent.height = *height;
241  }
242  else
243  {
244  // If the surface size is defined, the swap chain size must match
245  swapchainExtent = surfCaps.currentExtent;
246  *width = surfCaps.currentExtent.width;
247  *height = surfCaps.currentExtent.height;
248  }
249 
250  // Prefer mailbox mode if present, it's the lowest latency non-tearing present mode
251  VkPresentModeKHR swapchainPresentMode = VK_PRESENT_MODE_FIFO_KHR;
252  for (size_t i = 0; i < presentModeCount; i++)
253  {
254  if (presentModes[i] == VK_PRESENT_MODE_MAILBOX_KHR)
255  {
256  swapchainPresentMode = VK_PRESENT_MODE_MAILBOX_KHR;
257  break;
258  }
259  if ((swapchainPresentMode != VK_PRESENT_MODE_MAILBOX_KHR) && (presentModes[i] == VK_PRESENT_MODE_IMMEDIATE_KHR))
260  {
261  swapchainPresentMode = VK_PRESENT_MODE_IMMEDIATE_KHR;
262  }
263  }
264 
265  // Determine the number of images
266  uint32_t desiredNumberOfSwapchainImages = numDesiredSwapchainImages;
267  if (desiredNumberOfSwapchainImages == std::numeric_limits<uint32_t>::max())
268  desiredNumberOfSwapchainImages = surfCaps.minImageCount + 1;
269  if ((surfCaps.maxImageCount > 0) && (desiredNumberOfSwapchainImages > surfCaps.maxImageCount))
270  {
271  desiredNumberOfSwapchainImages = surfCaps.maxImageCount;
272  }
273 
274  VkSurfaceTransformFlagBitsKHR preTransform;
275  if (surfCaps.supportedTransforms & VK_SURFACE_TRANSFORM_IDENTITY_BIT_KHR)
276  {
277  preTransform = VK_SURFACE_TRANSFORM_IDENTITY_BIT_KHR;
278  }
279  else
280  {
281  preTransform = surfCaps.currentTransform;
282  }
283 
284  VkSwapchainCreateInfoKHR swapchainCI = {};
285  swapchainCI.sType = VK_STRUCTURE_TYPE_SWAPCHAIN_CREATE_INFO_KHR;
286  swapchainCI.pNext = NULL;
287  swapchainCI.surface = surface;
288  swapchainCI.minImageCount = desiredNumberOfSwapchainImages;
289  swapchainCI.imageFormat = colorFormat;
290  swapchainCI.imageColorSpace = colorSpace;
291  swapchainCI.imageExtent = { swapchainExtent.width, swapchainExtent.height };
292  swapchainCI.imageUsage = VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT;
293  swapchainCI.preTransform = preTransform;
294  swapchainCI.imageArrayLayers = 1;
295  swapchainCI.imageSharingMode = VK_SHARING_MODE_EXCLUSIVE;
296  swapchainCI.queueFamilyIndexCount = 0;
297  swapchainCI.pQueueFamilyIndices = NULL;
298  swapchainCI.presentMode = swapchainPresentMode;
299  swapchainCI.oldSwapchain = oldSwapchain;
300  swapchainCI.clipped = true;
301  swapchainCI.compositeAlpha = VK_COMPOSITE_ALPHA_OPAQUE_BIT_KHR;
302 
303  err = fpCreateSwapchainKHR(device, &swapchainCI, nullptr, &swapChain);
304  assert(!err);
305 
306  // If an existing sawp chain is re-created, destroy the old swap chain
307  // This also cleans up all the presentable images
308  if (oldSwapchain != VK_NULL_HANDLE)
309  {
310  for (uint32_t i = 0; i < imageCount; i++)
311  {
312  vkDestroyImageView(device, buffers[i].view, nullptr);
313  }
314  fpDestroySwapchainKHR(device, oldSwapchain, nullptr);
315  }
316 
317  err = fpGetSwapchainImagesKHR(device, swapChain, &imageCount, NULL);
318  assert(!err);
319 
320  // Get the swap chain images
321  images.resize(imageCount);
322  err = fpGetSwapchainImagesKHR(device, swapChain, &imageCount, images.data());
323  assert(!err);
324 
325  // Get the swap chain buffers containing the image and imageview
326  buffers.resize(imageCount);
327  for (uint32_t i = 0; i < imageCount; i++)
328  {
329  VkImageViewCreateInfo colorAttachmentView = {};
330  colorAttachmentView.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO;
331  colorAttachmentView.pNext = NULL;
332  colorAttachmentView.format = colorFormat;
333  colorAttachmentView.components = {
334  VK_COMPONENT_SWIZZLE_R,
335  VK_COMPONENT_SWIZZLE_G,
336  VK_COMPONENT_SWIZZLE_B,
337  VK_COMPONENT_SWIZZLE_A
338  };
339  colorAttachmentView.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
340  colorAttachmentView.subresourceRange.baseMipLevel = 0;
341  colorAttachmentView.subresourceRange.levelCount = 1;
342  colorAttachmentView.subresourceRange.baseArrayLayer = 0;
343  colorAttachmentView.subresourceRange.layerCount = 1;
344  colorAttachmentView.viewType = VK_IMAGE_VIEW_TYPE_2D;
345  colorAttachmentView.flags = 0;
346 
347  buffers[i].image = images[i];
348 
349  // Transform images from initial (undefined) to present layout
350  vkTools::setImageLayout(
351  cmdBuffer,
352  buffers[i].image,
353  VK_IMAGE_ASPECT_COLOR_BIT,
354  VK_IMAGE_LAYOUT_UNDEFINED,
355  VK_IMAGE_LAYOUT_PRESENT_SRC_KHR);
356 
357  colorAttachmentView.image = buffers[i].image;
358 
359  err = vkCreateImageView(device, &colorAttachmentView, nullptr, &buffers[i].view);
360  assert(!err);
361  }
362  }
363 
364  // Acquires the next image in the swap chain
365  VkResult acquireNextImage(VkSemaphore presentCompleteSemaphore, uint32_t *currentBuffer)
366  {
367  return fpAcquireNextImageKHR(device, swapChain, UINT64_MAX, presentCompleteSemaphore, nullptr, currentBuffer);
368  }
369 
370  // Present the current image to the queue
371  VkResult queuePresent(VkQueue queue, uint32_t currentBuffer)
372  {
373  VkPresentInfoKHR presentInfo = {};
374  presentInfo.sType = VK_STRUCTURE_TYPE_PRESENT_INFO_KHR;
375  presentInfo.pNext = NULL;
376  presentInfo.swapchainCount = 1;
377  presentInfo.pSwapchains = &swapChain;
378  presentInfo.pImageIndices = &currentBuffer;
379  return fpQueuePresentKHR(queue, &presentInfo);
380  }
381 
382  // Present the current image to the queue
383  VkResult queuePresent(VkQueue queue, uint32_t currentBuffer, VkSemaphore waitSemaphore)
384  {
385  VkPresentInfoKHR presentInfo = {};
386  presentInfo.sType = VK_STRUCTURE_TYPE_PRESENT_INFO_KHR;
387  presentInfo.pNext = NULL;
388  presentInfo.swapchainCount = 1;
389  presentInfo.pSwapchains = &swapChain;
390  presentInfo.pImageIndices = &currentBuffer;
391  if (waitSemaphore != VK_NULL_HANDLE)
392  {
393  presentInfo.pWaitSemaphores = &waitSemaphore;
394  presentInfo.waitSemaphoreCount = 1;
395  }
396  return fpQueuePresentKHR(queue, &presentInfo);
397  }
398 
399 
400  // Free all Vulkan resources used by the swap chain
401  void cleanup()
402  {
403  for (uint32_t i = 0; i < imageCount; i++)
404  {
405  vkDestroyImageView(device, buffers[i].view, nullptr);
406  }
407  if (swapChain)
408  fpDestroySwapchainKHR(device, swapChain, nullptr);
409  if (surface)
410  vkDestroySurfaceKHR(instance, surface, nullptr);
411  swapChain = VK_NULL_HANDLE;
412  surface = VK_NULL_HANDLE;
413  }
414 
415 };
WCompatibility.hpp
VulkanSwapChain
Definition: vulkanswapchain.hpp:64
_SwapChainBuffers
Definition: vulkanswapchain.hpp:59