Vulkan SDK for Android 1.1.1 Mali Developer Center
wsi.cpp
1 /* Copyright (c) 2016-2017, ARM Limited and Contributors
2  *
3  * SPDX-License-Identifier: MIT
4  *
5  * Permission is hereby granted, free of charge,
6  * to any person obtaining a copy of this software and associated documentation files (the "Software"),
7  * to deal in the Software without restriction, including without limitation the rights to
8  * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software,
9  * and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
10  *
11  * The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
12  *
13  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
14  * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
16  * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
17  * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
18  * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
19  */
20 
21 #include "wsi.hpp"
22 #include <string.h>
23 
24 using namespace std;
25 
26 #ifdef FORCE_NO_VALIDATION
27 #define ENABLE_VALIDATION_LAYERS 0
28 #else
29 #define ENABLE_VALIDATION_LAYERS 1
30 #endif
31 
32 namespace MaliSDK
33 {
34 
35 static VKAPI_ATTR VkBool32 VKAPI_CALL debugCallback(VkDebugReportFlagsEXT flags, VkDebugReportObjectTypeEXT type,
36  uint64_t object, size_t location, int32_t messageCode,
37  const char *pLayerPrefix, const char *pMessage, void *pUserData)
38 {
39  auto *platform = static_cast<WSIPlatform *>(pUserData);
40  auto callback = platform->getExternalDebugCallback();
41 
42  if (callback)
43  {
44  return callback(flags, type, object, location, messageCode, pLayerPrefix, pMessage,
45  platform->getExternalDebugCallbackUserData());
46  }
47  else
48  {
49  if (flags & VK_DEBUG_REPORT_ERROR_BIT_EXT)
50  {
51  LOGE("Validation Layer: Error: %s: %s\n", pLayerPrefix, pMessage);
52  }
53  else if (flags & VK_DEBUG_REPORT_WARNING_BIT_EXT)
54  {
55  LOGE("Validation Layer: Warning: %s: %s\n", pLayerPrefix, pMessage);
56  }
57  else if (flags & VK_DEBUG_REPORT_PERFORMANCE_WARNING_BIT_EXT)
58  {
59  LOGI("Validation Layer: Performance warning: %s: %s\n", pLayerPrefix, pMessage);
60  }
61  else
62  {
63  LOGI("Validation Layer: Information: %s: %s\n", pLayerPrefix, pMessage);
64  }
65  return VK_FALSE;
66  }
67 }
68 
69 WSIPlatform::~WSIPlatform()
70 {
71  terminate();
72 }
73 
74 bool WSIPlatform::validateExtensions(const vector<const char *> &required,
75  const std::vector<VkExtensionProperties> &available)
76 {
77  for (auto extension : required)
78  {
79  bool found = false;
80  for (auto &availableExtension : available)
81  {
82  if (strcmp(availableExtension.extensionName, extension) == 0)
83  {
84  found = true;
85  break;
86  }
87  }
88 
89  if (!found)
90  return false;
91  }
92 
93  return true;
94 }
95 
96 Result WSIPlatform::initialize()
97 {
98  pContext = new Context();
99  if (!pContext)
100  return RESULT_ERROR_OUT_OF_MEMORY;
101 
102  return RESULT_SUCCESS;
103 }
104 
105 #define NELEMS(x) (sizeof(x) / sizeof((x)[0]))
106 static const char *pValidationLayers[] = {
107  "VK_LAYER_GOOGLE_threading", "VK_LAYER_LUNARG_parameter_validation", "VK_LAYER_LUNARG_object_tracker",
108  "VK_LAYER_LUNARG_core_validation", "VK_LAYER_LUNARG_device_limits", "VK_LAYER_LUNARG_image",
109  "VK_LAYER_LUNARG_swapchain", "VK_LAYER_GOOGLE_unique_objects",
110 };
111 
112 static const char *pMetaLayers[] = {
113  "VK_LAYER_LUNARG_standard_validation",
114 };
115 
116 static void addSupportedLayers(vector<const char *> &activeLayers, const vector<VkLayerProperties> &instanceLayers,
117  const char **ppRequested, unsigned numRequested)
118 {
119  for (unsigned i = 0; i < numRequested; i++)
120  {
121  auto *layer = ppRequested[i];
122  for (auto &ext : instanceLayers)
123  {
124  if (strcmp(ext.layerName, layer) == 0)
125  {
126  activeLayers.push_back(layer);
127  break;
128  }
129  }
130  }
131 }
132 
133 Result WSIPlatform::initVulkan(const SwapchainDimensions &swapchain,
134  const vector<const char *> &requiredInstanceExtensions,
135  const vector<const char *> &requiredDeviceExtensions)
136 {
137  if (!vulkanSymbolWrapperInitLoader())
138  {
139  LOGE("Cannot find Vulkan loader.\n");
140  return RESULT_ERROR_GENERIC;
141  }
142 
143  if (!vulkanSymbolWrapperLoadGlobalSymbols())
144  {
145  LOGE("Failed to load global Vulkan symbols.\n");
146  return RESULT_ERROR_GENERIC;
147  }
148 
149  uint32_t instanceExtensionCount;
150  vector<const char *> activeInstanceExtensions;
151 
152  VK_CHECK(vkEnumerateInstanceExtensionProperties(nullptr, &instanceExtensionCount, nullptr));
153  vector<VkExtensionProperties> instanceExtensions(instanceExtensionCount);
154  VK_CHECK(vkEnumerateInstanceExtensionProperties(nullptr, &instanceExtensionCount, instanceExtensions.data()));
155 
156  for (auto &instanceExt : instanceExtensions)
157  LOGI("Instance extension: %s\n", instanceExt.extensionName);
158 
159 #if ENABLE_VALIDATION_LAYERS
160  uint32_t instanceLayerCount;
161  VK_CHECK(vkEnumerateInstanceLayerProperties(&instanceLayerCount, nullptr));
162  vector<VkLayerProperties> instanceLayers(instanceLayerCount);
163  VK_CHECK(vkEnumerateInstanceLayerProperties(&instanceLayerCount, instanceLayers.data()));
164 
165  // A layer could have VK_EXT_debug_report extension.
166  for (auto &layer : instanceLayers)
167  {
168  uint32_t count;
169  VK_CHECK(vkEnumerateInstanceExtensionProperties(layer.layerName, &count, nullptr));
170  vector<VkExtensionProperties> extensions(count);
171  VK_CHECK(vkEnumerateInstanceExtensionProperties(layer.layerName, &count, extensions.data()));
172  for (auto &ext : extensions)
173  instanceExtensions.push_back(ext);
174  }
175 
176  // On desktop, the LunarG loader exposes a meta-layer that combines all
177  // relevant validation layers.
178  vector<const char *> activeLayers;
179  addSupportedLayers(activeLayers, instanceLayers, pMetaLayers, NELEMS(pMetaLayers));
180 
181  // On Android, add all relevant layers one by one.
182  if (activeLayers.empty())
183  {
184  addSupportedLayers(activeLayers, instanceLayers, pValidationLayers, NELEMS(pValidationLayers));
185  }
186 
187  if (activeLayers.empty())
188  LOGI("Did not find validation layers.\n");
189  else
190  LOGI("Found validation layers!\n");
191 
192  addExternalLayers(activeLayers, instanceLayers);
193 #endif
194 
195  bool useInstanceExtensions = true;
196  if (!validateExtensions(requiredInstanceExtensions, instanceExtensions))
197  {
198  LOGI("Required instance extensions are missing, will try without.\n");
199  useInstanceExtensions = false;
200  }
201  else
202  activeInstanceExtensions = requiredInstanceExtensions;
203 
204  bool haveDebugReport = false;
205  for (auto &ext : instanceExtensions)
206  {
207  if (strcmp(ext.extensionName, "VK_EXT_debug_report") == 0)
208  {
209  haveDebugReport = true;
210  useInstanceExtensions = true;
211  activeInstanceExtensions.push_back("VK_EXT_debug_report");
212  break;
213  }
214  }
215 
216  VkApplicationInfo app = { VK_STRUCTURE_TYPE_APPLICATION_INFO };
217  app.pApplicationName = "Mali SDK";
218  app.applicationVersion = 0;
219  app.pEngineName = "Mali SDK";
220  app.engineVersion = 0;
221  app.apiVersion = VK_MAKE_VERSION(1, 0, 24);
222 
223  VkInstanceCreateInfo instanceInfo = { VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO };
224  instanceInfo.pApplicationInfo = &app;
225  if (useInstanceExtensions)
226  {
227  instanceInfo.enabledExtensionCount = activeInstanceExtensions.size();
228  instanceInfo.ppEnabledExtensionNames = activeInstanceExtensions.data();
229  }
230 
231 #if ENABLE_VALIDATION_LAYERS
232  if (!activeLayers.empty())
233  {
234  instanceInfo.enabledLayerCount = activeLayers.size();
235  instanceInfo.ppEnabledLayerNames = activeLayers.data();
236  LOGI("Using Vulkan instance validation layers.\n");
237  }
238 #endif
239 
240  // Create the Vulkan instance
241  {
242  VkResult res = vkCreateInstance(&instanceInfo, nullptr, &instance);
243 
244  // Try to fall back to compatible Vulkan versions if the driver is using
245  // older, but compatible API versions.
246  if (res == VK_ERROR_INCOMPATIBLE_DRIVER)
247  {
248  app.apiVersion = VK_MAKE_VERSION(1, 0, 1);
249  res = vkCreateInstance(&instanceInfo, nullptr, &instance);
250  if (res == VK_SUCCESS)
251  LOGI("Created Vulkan instance with API version 1.0.1.\n");
252  }
253 
254  if (res == VK_ERROR_INCOMPATIBLE_DRIVER)
255  {
256  app.apiVersion = VK_MAKE_VERSION(1, 0, 2);
257  res = vkCreateInstance(&instanceInfo, nullptr, &instance);
258  if (res == VK_SUCCESS)
259  LOGI("Created Vulkan instance with API version 1.0.2.\n");
260  }
261 
262  if (res != VK_SUCCESS)
263  {
264  LOGE("Failed to create Vulkan instance (error: %d).\n", int(res));
265  return RESULT_ERROR_GENERIC;
266  }
267  }
268 
269  if (!vulkanSymbolWrapperLoadCoreInstanceSymbols(instance))
270  {
271  LOGE("Failed to load instance symbols.");
272  return RESULT_ERROR_GENERIC;
273  }
274 
275  if (haveDebugReport)
276  {
277  VULKAN_SYMBOL_WRAPPER_LOAD_INSTANCE_EXTENSION_SYMBOL(instance, vkCreateDebugReportCallbackEXT);
278  VkDebugReportCallbackCreateInfoEXT info = { VK_STRUCTURE_TYPE_DEBUG_REPORT_CREATE_INFO_EXT };
279  info.flags = VK_DEBUG_REPORT_ERROR_BIT_EXT | VK_DEBUG_REPORT_WARNING_BIT_EXT |
280  VK_DEBUG_REPORT_PERFORMANCE_WARNING_BIT_EXT;
281 
282  info.pfnCallback = debugCallback;
283  info.pUserData = this;
284 
285  if (vkCreateDebugReportCallbackEXT)
286  vkCreateDebugReportCallbackEXT(instance, &info, nullptr, &debug_callback);
287  LOGI("Enabling Vulkan debug reporting.\n");
288  }
289 
290  uint32_t gpuCount = 0;
291  VK_CHECK(vkEnumeratePhysicalDevices(instance, &gpuCount, nullptr));
292 
293  if (gpuCount < 1)
294  {
295  LOGE("Failed to enumerate Vulkan physical device.\n");
296  return RESULT_ERROR_GENERIC;
297  }
298 
299  vector<VkPhysicalDevice> gpus(gpuCount);
300  VK_CHECK(vkEnumeratePhysicalDevices(instance, &gpuCount, gpus.data()));
301 
302  gpu = VK_NULL_HANDLE;
303  for (auto device : gpus)
304  {
305  VkPhysicalDeviceProperties properties;
306  vkGetPhysicalDeviceProperties(device, &properties);
307 
308  // If we have multiple GPUs in our system, try to find a Mali device.
309  if (strstr(properties.deviceName, "Mali"))
310  {
311  gpu = device;
312  LOGI("Found ARM Mali physical device: %s.\n", properties.deviceName);
313  break;
314  }
315  }
316 
317  // Fallback to the first GPU we find in the system.
318  if (gpu == VK_NULL_HANDLE)
319  gpu = gpus.front();
320 
321  vkGetPhysicalDeviceProperties(gpu, &gpuProperties);
322  vkGetPhysicalDeviceMemoryProperties(gpu, &memoryProperties);
323 
324  uint32_t queueCount;
325  vkGetPhysicalDeviceQueueFamilyProperties(gpu, &queueCount, nullptr);
326  queueProperties.resize(queueCount);
327  vkGetPhysicalDeviceQueueFamilyProperties(gpu, &queueCount, queueProperties.data());
328  if (queueCount < 1)
329  {
330  LOGE("Failed to query number of queues.");
331  return RESULT_ERROR_GENERIC;
332  }
333 
334  uint32_t deviceExtensionCount;
335  VK_CHECK(vkEnumerateDeviceExtensionProperties(gpu, nullptr, &deviceExtensionCount, nullptr));
336  vector<VkExtensionProperties> deviceExtensions(deviceExtensionCount);
337  VK_CHECK(vkEnumerateDeviceExtensionProperties(gpu, nullptr, &deviceExtensionCount, deviceExtensions.data()));
338 
339 #if ENABLE_VALIDATION_LAYERS
340  uint32_t deviceLayerCount;
341  VK_CHECK(vkEnumerateDeviceLayerProperties(gpu, &deviceLayerCount, nullptr));
342  vector<VkLayerProperties> deviceLayers(deviceLayerCount);
343  VK_CHECK(vkEnumerateDeviceLayerProperties(gpu, &deviceLayerCount, deviceLayers.data()));
344 
345  activeLayers.clear();
346  // On desktop, the LunarG loader exposes a meta-layer that combines all
347  // relevant validation layers.
348  addSupportedLayers(activeLayers, deviceLayers, pMetaLayers, NELEMS(pMetaLayers));
349 
350  // On Android, add all relevant layers one by one.
351  if (activeLayers.empty())
352  {
353  addSupportedLayers(activeLayers, deviceLayers, pValidationLayers, NELEMS(pValidationLayers));
354  }
355  addExternalLayers(activeLayers, deviceLayers);
356 #endif
357 
358  for (auto &deviceExt : deviceExtensions)
359  LOGI("Device extension: %s\n", deviceExt.extensionName);
360 
361  bool useDeviceExtensions = true;
362  if (!validateExtensions(requiredDeviceExtensions, deviceExtensions))
363  {
364  LOGI("Required device extensions are missing, will try without.\n");
365  useDeviceExtensions = false;
366  }
367 
368  if (FAILED(loadInstanceSymbols()))
369  {
370  LOGE("Failed to load instance symbols.");
371  return RESULT_ERROR_GENERIC;
372  }
373 
374  surface = createSurface();
375  if (surface == VK_NULL_HANDLE)
376  {
377  LOGE("Failed to create surface.");
378  return RESULT_ERROR_GENERIC;
379  }
380 
381  bool foundQueue = false;
382  for (unsigned i = 0; i < queueCount; i++)
383  {
384  VkBool32 supportsPresent;
385 
386  // There must exist at least one queue that has graphics and compute
387  // support.
388  const VkQueueFlags required = VK_QUEUE_GRAPHICS_BIT | VK_QUEUE_COMPUTE_BIT;
389 
390  vkGetPhysicalDeviceSurfaceSupportKHR(gpu, i, surface, &supportsPresent);
391 
392  // We want a queue which supports all of graphics, compute and presentation.
393  if (((queueProperties[i].queueFlags & required) == required) && supportsPresent)
394  {
395  graphicsQueueIndex = i;
396  foundQueue = true;
397  break;
398  }
399  }
400 
401  if (!foundQueue)
402  {
403  LOGE("Did not find suitable queue which supports graphics, compute and "
404  "presentation.\n");
405  return RESULT_ERROR_GENERIC;
406  }
407 
408  static const float one = 1.0f;
409  VkDeviceQueueCreateInfo queueInfo = { VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO };
410  queueInfo.queueFamilyIndex = graphicsQueueIndex;
411  queueInfo.queueCount = 1;
412  queueInfo.pQueuePriorities = &one;
413 
414  VkPhysicalDeviceFeatures features = { false };
415  VkDeviceCreateInfo deviceInfo = { VK_STRUCTURE_TYPE_DEVICE_CREATE_INFO };
416  deviceInfo.queueCreateInfoCount = 1;
417  deviceInfo.pQueueCreateInfos = &queueInfo;
418  if (useDeviceExtensions)
419  {
420  deviceInfo.enabledExtensionCount = requiredDeviceExtensions.size();
421  deviceInfo.ppEnabledExtensionNames = requiredDeviceExtensions.data();
422  }
423 
424 #if ENABLE_VALIDATION_LAYERS
425  if (!activeLayers.empty())
426  {
427  deviceInfo.enabledLayerCount = activeLayers.size();
428  deviceInfo.ppEnabledLayerNames = activeLayers.data();
429  LOGI("Using Vulkan device validation layers.\n");
430  }
431 #endif
432 
433  deviceInfo.pEnabledFeatures = &features;
434 
435  VK_CHECK(vkCreateDevice(gpu, &deviceInfo, nullptr, &device));
436 
437  if (!vulkanSymbolWrapperLoadCoreDeviceSymbols(device))
438  {
439  LOGE("Failed to load device symbols.");
440  return RESULT_ERROR_GENERIC;
441  }
442 
443  if (FAILED(loadDeviceSymbols()))
444  {
445  LOGE("Failed to load device symbols.");
446  return RESULT_ERROR_GENERIC;
447  }
448 
449  vkGetDeviceQueue(device, graphicsQueueIndex, 0, &queue);
450 
451  Result res = initSwapchain(swapchain);
452  if (res != RESULT_SUCCESS)
453  {
454  LOGE("Failed to init swapchain.");
455  return res;
456  }
457 
458  res = pContext->onPlatformUpdate(this);
459  if (FAILED(res))
460  return res;
461 
462  semaphoreManager = new SemaphoreManager(device);
463  return RESULT_SUCCESS;
464 }
465 
466 void WSIPlatform::destroySwapchain()
467 {
468  if (device)
469  vkDeviceWaitIdle(device);
470 
471  if (swapchain)
472  {
473  vkDestroySwapchainKHR(device, swapchain, nullptr);
474  swapchain = VK_NULL_HANDLE;
475  }
476 }
477 
478 void WSIPlatform::terminate()
479 {
480  // Don't release anything until the GPU is completely idle.
481  if (device)
482  vkDeviceWaitIdle(device);
483 
484  delete semaphoreManager;
485  semaphoreManager = nullptr;
486 
487  // Make sure we tear down the context before destroying the device since
488  // context
489  // also owns some Vulkan resources.
490  delete pContext;
491  pContext = nullptr;
492 
493  destroySwapchain();
494 
495  if (surface)
496  {
497  vkDestroySurfaceKHR(instance, surface, nullptr);
498  surface = VK_NULL_HANDLE;
499  }
500 
501  if (device)
502  {
503  vkDestroyDevice(device, nullptr);
504  device = VK_NULL_HANDLE;
505  }
506 
507  if (debug_callback)
508  {
509  VULKAN_SYMBOL_WRAPPER_LOAD_INSTANCE_EXTENSION_SYMBOL(instance, vkDestroyDebugReportCallbackEXT);
510  vkDestroyDebugReportCallbackEXT(instance, debug_callback, nullptr);
511  debug_callback = VK_NULL_HANDLE;
512  }
513 
514  if (instance)
515  {
516  vkDestroyInstance(instance, nullptr);
517  instance = VK_NULL_HANDLE;
518  }
519 }
520 
521 Result WSIPlatform::loadDeviceSymbols()
522 {
523  if (!VULKAN_SYMBOL_WRAPPER_LOAD_DEVICE_EXTENSION_SYMBOL(device, vkCreateSwapchainKHR))
524  return RESULT_ERROR_GENERIC;
525  if (!VULKAN_SYMBOL_WRAPPER_LOAD_DEVICE_EXTENSION_SYMBOL(device, vkDestroySwapchainKHR))
526  return RESULT_ERROR_GENERIC;
527  if (!VULKAN_SYMBOL_WRAPPER_LOAD_DEVICE_EXTENSION_SYMBOL(device, vkGetSwapchainImagesKHR))
528  return RESULT_ERROR_GENERIC;
529  if (!VULKAN_SYMBOL_WRAPPER_LOAD_DEVICE_EXTENSION_SYMBOL(device, vkAcquireNextImageKHR))
530  return RESULT_ERROR_GENERIC;
531  if (!VULKAN_SYMBOL_WRAPPER_LOAD_DEVICE_EXTENSION_SYMBOL(device, vkQueuePresentKHR))
532  return RESULT_ERROR_GENERIC;
533  return RESULT_SUCCESS;
534 }
535 
536 Result WSIPlatform::loadInstanceSymbols()
537 {
538  if (!VULKAN_SYMBOL_WRAPPER_LOAD_INSTANCE_EXTENSION_SYMBOL(instance, vkGetPhysicalDeviceSurfaceSupportKHR))
539  return RESULT_ERROR_GENERIC;
540  if (!VULKAN_SYMBOL_WRAPPER_LOAD_INSTANCE_EXTENSION_SYMBOL(instance, vkGetPhysicalDeviceSurfaceCapabilitiesKHR))
541  return RESULT_ERROR_GENERIC;
542  if (!VULKAN_SYMBOL_WRAPPER_LOAD_INSTANCE_EXTENSION_SYMBOL(instance, vkGetPhysicalDeviceSurfaceFormatsKHR))
543  return RESULT_ERROR_GENERIC;
544  if (!VULKAN_SYMBOL_WRAPPER_LOAD_INSTANCE_EXTENSION_SYMBOL(instance, vkGetPhysicalDeviceSurfacePresentModesKHR))
545  return RESULT_ERROR_GENERIC;
546  if (!VULKAN_SYMBOL_WRAPPER_LOAD_INSTANCE_EXTENSION_SYMBOL(instance, vkDestroySurfaceKHR))
547  return RESULT_ERROR_GENERIC;
548  return RESULT_SUCCESS;
549 }
550 
551 Result WSIPlatform::initSwapchain(const SwapchainDimensions &dim)
552 {
553  VkSurfaceCapabilitiesKHR surfaceProperties;
554  VK_CHECK(vkGetPhysicalDeviceSurfaceCapabilitiesKHR(gpu, surface, &surfaceProperties));
555 
556  uint32_t formatCount;
557  vkGetPhysicalDeviceSurfaceFormatsKHR(gpu, surface, &formatCount, nullptr);
558  vector<VkSurfaceFormatKHR> formats(formatCount);
559  vkGetPhysicalDeviceSurfaceFormatsKHR(gpu, surface, &formatCount, formats.data());
560 
561  VkSurfaceFormatKHR format;
562  if (formatCount == 1 && formats[0].format == VK_FORMAT_UNDEFINED)
563  {
564  format = formats[0];
565  format.format = dim.format;
566  }
567  else
568  {
569  if (formatCount == 0)
570  {
571  LOGE("Surface has no formats.\n");
572  return RESULT_ERROR_GENERIC;
573  }
574 
575  format.format = VK_FORMAT_UNDEFINED;
576  for (auto &candidate : formats)
577  {
578  switch (candidate.format)
579  {
580  // Favor UNORM formats as the samples are not written for sRGB currently.
581  case VK_FORMAT_R8G8B8A8_UNORM:
582  case VK_FORMAT_B8G8R8A8_UNORM:
583  case VK_FORMAT_A8B8G8R8_UNORM_PACK32:
584  format = candidate;
585  break;
586 
587  default:
588  break;
589  }
590 
591  if (format.format != VK_FORMAT_UNDEFINED)
592  break;
593  }
594 
595  if (format.format == VK_FORMAT_UNDEFINED)
596  format = formats[0];
597  }
598 
599  VkExtent2D swapchainSize;
600  // -1u is a magic value (in Vulkan specification) which means there's no fixed
601  // size.
602  if (surfaceProperties.currentExtent.width == -1u)
603  {
604  swapchainSize.width = dim.width;
605  swapchainSize.height = dim.height;
606  }
607  else
608  swapchainSize = surfaceProperties.currentExtent;
609 
610  // FIFO must be supported by all implementations.
611  VkPresentModeKHR swapchainPresentMode = VK_PRESENT_MODE_FIFO_KHR;
612 
613  // Determine the number of VkImage's to use in the swapchain.
614  // Ideally, we desire to own 1 image at a time, the rest of the images can
615  // either be rendered to and/or
616  // being queued up for display.
617  uint32_t desiredSwapchainImages = surfaceProperties.minImageCount + 1;
618  if ((surfaceProperties.maxImageCount > 0) && (desiredSwapchainImages > surfaceProperties.maxImageCount))
619  {
620  // Application must settle for fewer images than desired.
621  desiredSwapchainImages = surfaceProperties.maxImageCount;
622  }
623 
624  // Figure out a suitable surface transform.
625  VkSurfaceTransformFlagBitsKHR preTransform;
626  if (surfaceProperties.supportedTransforms & VK_SURFACE_TRANSFORM_IDENTITY_BIT_KHR)
627  preTransform = VK_SURFACE_TRANSFORM_IDENTITY_BIT_KHR;
628  else
629  preTransform = surfaceProperties.currentTransform;
630 
631  VkSwapchainKHR oldSwapchain = swapchain;
632 
633  // Find a supported composite type.
634  VkCompositeAlphaFlagBitsKHR composite = VK_COMPOSITE_ALPHA_OPAQUE_BIT_KHR;
635  if (surfaceProperties.supportedCompositeAlpha & VK_COMPOSITE_ALPHA_OPAQUE_BIT_KHR)
636  composite = VK_COMPOSITE_ALPHA_OPAQUE_BIT_KHR;
637  else if (surfaceProperties.supportedCompositeAlpha & VK_COMPOSITE_ALPHA_INHERIT_BIT_KHR)
638  composite = VK_COMPOSITE_ALPHA_INHERIT_BIT_KHR;
639  else if (surfaceProperties.supportedCompositeAlpha & VK_COMPOSITE_ALPHA_PRE_MULTIPLIED_BIT_KHR)
640  composite = VK_COMPOSITE_ALPHA_PRE_MULTIPLIED_BIT_KHR;
641  else if (surfaceProperties.supportedCompositeAlpha & VK_COMPOSITE_ALPHA_POST_MULTIPLIED_BIT_KHR)
642  composite = VK_COMPOSITE_ALPHA_POST_MULTIPLIED_BIT_KHR;
643 
644  VkSwapchainCreateInfoKHR info = { VK_STRUCTURE_TYPE_SWAPCHAIN_CREATE_INFO_KHR };
645  info.surface = surface;
646  info.minImageCount = desiredSwapchainImages;
647  info.imageFormat = format.format;
648  info.imageColorSpace = format.colorSpace;
649  info.imageExtent.width = swapchainSize.width;
650  info.imageExtent.height = swapchainSize.height;
651  info.imageArrayLayers = 1;
652  info.imageUsage = VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT;
653  info.imageSharingMode = VK_SHARING_MODE_EXCLUSIVE;
654  info.preTransform = preTransform;
655  info.compositeAlpha = composite;
656  info.presentMode = swapchainPresentMode;
657  info.clipped = true;
658  info.oldSwapchain = oldSwapchain;
659 
660  VK_CHECK(vkCreateSwapchainKHR(device, &info, nullptr, &swapchain));
661 
662  if (oldSwapchain != VK_NULL_HANDLE)
663  vkDestroySwapchainKHR(device, oldSwapchain, nullptr);
664 
665  swapchainDimensions.width = swapchainSize.width;
666  swapchainDimensions.height = swapchainSize.height;
667  swapchainDimensions.format = format.format;
668 
669  uint32_t imageCount;
670  VK_CHECK(vkGetSwapchainImagesKHR(device, swapchain, &imageCount, nullptr));
671  swapchainImages.resize(imageCount);
672  VK_CHECK(vkGetSwapchainImagesKHR(device, swapchain, &imageCount, swapchainImages.data()));
673 
674  return RESULT_SUCCESS;
675 }
676 
677 void WSIPlatform::getCurrentSwapchain(vector<VkImage> *images, SwapchainDimensions *swapchain)
678 {
679  *images = swapchainImages;
680  *swapchain = swapchainDimensions;
681 }
682 
683 unsigned WSIPlatform::getNumSwapchainImages() const
684 {
685  return swapchainImages.size();
686 }
687 
688 Result WSIPlatform::acquireNextImage(unsigned *image)
689 {
690  auto acquireSemaphore = semaphoreManager->getClearedSemaphore();
691  VkResult res = vkAcquireNextImageKHR(device, swapchain, UINT64_MAX, acquireSemaphore, VK_NULL_HANDLE, image);
692 
693  if (res == VK_SUBOPTIMAL_KHR || res == VK_ERROR_OUT_OF_DATE_KHR)
694  {
695  vkQueueWaitIdle(queue);
696  semaphoreManager->addClearedSemaphore(acquireSemaphore);
697 
698  // Recreate swapchain.
699  if (SUCCEEDED(initSwapchain(swapchainDimensions)))
700  return RESULT_ERROR_OUTDATED_SWAPCHAIN;
701  else
702  return RESULT_ERROR_GENERIC;
703  }
704  else if (res != VK_SUCCESS)
705  {
706  vkQueueWaitIdle(queue);
707  semaphoreManager->addClearedSemaphore(acquireSemaphore);
708  return RESULT_ERROR_GENERIC;
709  }
710  else
711  {
712  // Signal the underlying context that we're using this backbuffer now.
713  // This will also wait for all fences associated with this swapchain image
714  // to complete first.
715  // When submitting command buffer that writes to swapchain, we need to wait
716  // for this semaphore first.
717  // Also, delete the older semaphore.
718  auto oldSemaphore = pContext->beginFrame(*image, acquireSemaphore);
719 
720  // Recycle the old semaphore back into the semaphore manager.
721  if (oldSemaphore != VK_NULL_HANDLE)
722  semaphoreManager->addClearedSemaphore(oldSemaphore);
723 
724  return RESULT_SUCCESS;
725  }
726 }
727 
728 Result WSIPlatform::presentImage(unsigned index)
729 {
730  VkResult result;
731  VkPresentInfoKHR present = { VK_STRUCTURE_TYPE_PRESENT_INFO_KHR };
732  present.swapchainCount = 1;
733  present.pSwapchains = &swapchain;
734  present.pImageIndices = &index;
735  present.pResults = &result;
736  present.waitSemaphoreCount = 1;
737  present.pWaitSemaphores = &pContext->getSwapchainReleaseSemaphore();
738 
739  VkResult res = vkQueuePresentKHR(queue, &present);
740 
741  if (res == VK_SUBOPTIMAL_KHR || res == VK_ERROR_OUT_OF_DATE_KHR)
742  return RESULT_ERROR_OUTDATED_SWAPCHAIN;
743  else if (res != VK_SUCCESS)
744  return RESULT_ERROR_GENERIC;
745  else
746  return RESULT_SUCCESS;
747 }
748 }
VkFormat format
Pixel format of the swapchain.
Definition: platform.hpp:61
The Context is the primary way for samples to interact with the swapchain and get rendered images to ...
Definition: context.hpp:36
unsigned height
Height of the swapchain.
Definition: platform.hpp:59
The SemaphoreManager keeps track of semaphores.
Describes the size and format of the swapchain.
Definition: platform.hpp:54
unsigned width
Width of the swapchain.
Definition: platform.hpp:57