SDL  2.0
SDL_vulkan_utils.c
Go to the documentation of this file.
1 /*
2  Simple DirectMedia Layer
3  Copyright (C) 1997-2019 Sam Lantinga <slouken@libsdl.org>
4 
5  This software is provided 'as-is', without any express or implied
6  warranty. In no event will the authors be held liable for any damages
7  arising from the use of this software.
8 
9  Permission is granted to anyone to use this software for any purpose,
10  including commercial applications, and to alter it and redistribute it
11  freely, subject to the following restrictions:
12 
13  1. The origin of this software must not be misrepresented; you must not
14  claim that you wrote the original software. If you use this software
15  in a product, an acknowledgment in the product documentation would be
16  appreciated but is not required.
17  2. Altered source versions must be plainly marked as such, and must not be
18  misrepresented as being the original software.
19  3. This notice may not be removed or altered from any source distribution.
20 */
21 #include "../SDL_internal.h"
22 
23 #include "SDL_vulkan_internal.h"
24 #include "SDL_error.h"
25 #include "SDL_log.h"
26 
27 /* !!! FIXME: this file doesn't match coding standards for SDL (brace position, etc). */
28 
29 #if SDL_VIDEO_VULKAN
30 
31 const char *SDL_Vulkan_GetResultString(VkResult result)
32 {
33  switch((int)result)
34  {
35  case VK_SUCCESS:
36  return "VK_SUCCESS";
37  case VK_NOT_READY:
38  return "VK_NOT_READY";
39  case VK_TIMEOUT:
40  return "VK_TIMEOUT";
41  case VK_EVENT_SET:
42  return "VK_EVENT_SET";
43  case VK_EVENT_RESET:
44  return "VK_EVENT_RESET";
45  case VK_INCOMPLETE:
46  return "VK_INCOMPLETE";
48  return "VK_ERROR_OUT_OF_HOST_MEMORY";
50  return "VK_ERROR_OUT_OF_DEVICE_MEMORY";
52  return "VK_ERROR_INITIALIZATION_FAILED";
54  return "VK_ERROR_DEVICE_LOST";
56  return "VK_ERROR_MEMORY_MAP_FAILED";
58  return "VK_ERROR_LAYER_NOT_PRESENT";
60  return "VK_ERROR_EXTENSION_NOT_PRESENT";
62  return "VK_ERROR_FEATURE_NOT_PRESENT";
64  return "VK_ERROR_INCOMPATIBLE_DRIVER";
66  return "VK_ERROR_TOO_MANY_OBJECTS";
68  return "VK_ERROR_FORMAT_NOT_SUPPORTED";
70  return "VK_ERROR_FRAGMENTED_POOL";
72  return "VK_ERROR_SURFACE_LOST_KHR";
74  return "VK_ERROR_NATIVE_WINDOW_IN_USE_KHR";
75  case VK_SUBOPTIMAL_KHR:
76  return "VK_SUBOPTIMAL_KHR";
78  return "VK_ERROR_OUT_OF_DATE_KHR";
80  return "VK_ERROR_INCOMPATIBLE_DISPLAY_KHR";
82  return "VK_ERROR_VALIDATION_FAILED_EXT";
84  return "VK_ERROR_OUT_OF_POOL_MEMORY_KHR";
86  return "VK_ERROR_INVALID_SHADER_NV";
87  case VK_RESULT_MAX_ENUM:
89  break;
90  }
91  if(result < 0)
92  return "VK_ERROR_<Unknown>";
93  return "VK_<Unknown>";
94 }
95 
96 VkExtensionProperties *SDL_Vulkan_CreateInstanceExtensionsList(
98  Uint32 *extensionCount)
99 {
100  Uint32 count = 0;
103  if(result == VK_ERROR_INCOMPATIBLE_DRIVER)
104  {
105  /* Avoid the ERR_MAX_STRLEN limit by passing part of the message
106  * as a string argument.
107  */
108  SDL_SetError(
109  "You probably don't have a working Vulkan driver installed. %s %s %s(%d)",
110  "Getting Vulkan extensions failed:",
111  "vkEnumerateInstanceExtensionProperties returned",
112  SDL_Vulkan_GetResultString(result),
113  (int)result);
114  return NULL;
115  }
116  else if(result != VK_SUCCESS)
117  {
118  SDL_SetError(
119  "Getting Vulkan extensions failed: vkEnumerateInstanceExtensionProperties returned "
120  "%s(%d)",
121  SDL_Vulkan_GetResultString(result),
122  (int)result);
123  return NULL;
124  }
125  if(count == 0)
126  {
127  retval = SDL_calloc(1, sizeof(VkExtensionProperties)); // so we can return non-null
128  }
129  else
130  {
131  retval = SDL_calloc(count, sizeof(VkExtensionProperties));
132  }
133  if(!retval)
134  {
135  SDL_OutOfMemory();
136  return NULL;
137  }
138  result = vkEnumerateInstanceExtensionProperties(NULL, &count, retval);
139  if(result != VK_SUCCESS)
140  {
141  SDL_SetError(
142  "Getting Vulkan extensions failed: vkEnumerateInstanceExtensionProperties returned "
143  "%s(%d)",
144  SDL_Vulkan_GetResultString(result),
145  (int)result);
146  SDL_free(retval);
147  return NULL;
148  }
149  *extensionCount = count;
150  return retval;
151 }
152 
153 SDL_bool SDL_Vulkan_GetInstanceExtensions_Helper(unsigned *userCount,
154  const char **userNames,
155  unsigned nameCount,
156  const char *const *names)
157 {
158  if (userNames) {
159  unsigned i;
160 
161  if (*userCount < nameCount) {
162  SDL_SetError("Output array for SDL_Vulkan_GetInstanceExtensions needs to be at least %d big", nameCount);
163  return SDL_FALSE;
164  }
165  for (i = 0; i < nameCount; i++) {
166  userNames[i] = names[i];
167  }
168  }
169  *userCount = nameCount;
170  return SDL_TRUE;
171 }
172 
173 /* Alpha modes, in order of preference */
174 static const VkDisplayPlaneAlphaFlagBitsKHR alphaModes[4] = {
179 };
180 
181 SDL_bool SDL_Vulkan_Display_CreateSurface(void *vkGetInstanceProcAddr_,
182  VkInstance instance,
183  VkSurfaceKHR *surface)
184 {
186  (PFN_vkGetInstanceProcAddr)vkGetInstanceProcAddr_;
187 #define VULKAN_INSTANCE_FUNCTION(name) \
188  PFN_##name name = (PFN_##name)vkGetInstanceProcAddr((VkInstance)instance, #name)
196 #undef VULKAN_INSTANCE_FUNCTION
199  uint32_t physicalDeviceCount = 0;
200  VkPhysicalDevice *physicalDevices = NULL;
201  uint32_t physicalDeviceIndex;
202  const char *chosenDisplayId;
203  int displayId = 0; /* Counting from physical device 0, display 0 */
204 
212  {
214  " extension is not enabled in the Vulkan instance.");
215  goto error;
216  }
217 
218  if ((chosenDisplayId = SDL_getenv("SDL_VULKAN_DISPLAY")) != NULL)
219  {
220  displayId = SDL_atoi(chosenDisplayId);
221  }
222 
223  /* Enumerate physical devices */
224  result =
225  vkEnumeratePhysicalDevices(instance, &physicalDeviceCount, NULL);
226  if(result != VK_SUCCESS)
227  {
228  SDL_SetError("Could not enumerate Vulkan physical devices");
229  goto error;
230  }
231  if(physicalDeviceCount == 0)
232  {
233  SDL_SetError("No Vulkan physical devices");
234  goto error;
235  }
236  physicalDevices = SDL_malloc(sizeof(VkPhysicalDevice) * physicalDeviceCount);
237  if(!physicalDevices)
238  {
239  SDL_OutOfMemory();
240  goto error;
241  }
242  result =
243  vkEnumeratePhysicalDevices(instance, &physicalDeviceCount, physicalDevices);
244  if(result != VK_SUCCESS)
245  {
246  SDL_SetError("Error enumerating physical devices");
247  goto error;
248  }
249 
250  for(physicalDeviceIndex = 0; physicalDeviceIndex < physicalDeviceCount;
251  physicalDeviceIndex++)
252  {
253  VkPhysicalDevice physicalDevice = physicalDevices[physicalDeviceIndex];
254  uint32_t displayPropertiesCount = 0;
255  VkDisplayPropertiesKHR *displayProperties = NULL;
256  uint32_t displayModePropertiesCount = 0;
257  VkDisplayModePropertiesKHR *displayModeProperties = NULL;
258  int bestMatchIndex = -1;
259  uint32_t refreshRate = 0;
260  uint32_t i;
261  uint32_t displayPlanePropertiesCount = 0;
262  int planeIndex = -1;
263  VkDisplayKHR display;
264  VkDisplayPlanePropertiesKHR *displayPlaneProperties = NULL;
265  VkExtent2D extent;
267 
268  /* Get information about the physical displays */
269  result =
270  vkGetPhysicalDeviceDisplayPropertiesKHR(physicalDevice, &displayPropertiesCount, NULL);
271  if (result != VK_SUCCESS || displayPropertiesCount == 0)
272  {
273  /* This device has no physical device display properties, move on to next. */
274  continue;
275  }
276  SDL_LogDebug(SDL_LOG_CATEGORY_VIDEO, "vulkandisplay: Number of display properties for device %u: %u",
277  physicalDeviceIndex, displayPropertiesCount);
278 
279  if ( (displayId < 0) || (((uint32_t) displayId) >= displayPropertiesCount) )
280  {
281  /* Display id specified was higher than number of available displays, move to next physical device. */
282  displayId -= displayPropertiesCount;
283  continue;
284  }
285 
286  displayProperties = SDL_malloc(sizeof(VkDisplayPropertiesKHR) * displayPropertiesCount);
287  if(!displayProperties)
288  {
289  SDL_OutOfMemory();
290  goto error;
291  }
292 
293  result =
294  vkGetPhysicalDeviceDisplayPropertiesKHR(physicalDevice, &displayPropertiesCount, displayProperties);
295  if (result != VK_SUCCESS || displayPropertiesCount == 0) {
296  SDL_free(displayProperties);
297  SDL_SetError("Error enumerating physical device displays");
298  goto error;
299  }
300 
301  display = displayProperties[displayId].display;
302  extent = displayProperties[displayId].physicalResolution;
303  SDL_LogDebug(SDL_LOG_CATEGORY_VIDEO, "vulkandisplay: Display: %s Native resolution: %ux%u",
304  displayProperties[displayId].displayName, extent.width, extent.height);
305 
306  SDL_free(displayProperties);
307  displayProperties = NULL;
308 
309  /* Get display mode properties for the chosen display */
310  result =
311  vkGetDisplayModePropertiesKHR(physicalDevice, display, &displayModePropertiesCount, NULL);
312  if (result != VK_SUCCESS || displayModePropertiesCount == 0)
313  {
314  SDL_SetError("Error enumerating display modes");
315  goto error;
316  }
317  SDL_LogDebug(SDL_LOG_CATEGORY_VIDEO, "vulkandisplay: Number of display modes: %u", displayModePropertiesCount);
318 
319  displayModeProperties = SDL_malloc(sizeof(VkDisplayModePropertiesKHR) * displayModePropertiesCount);
320  if(!displayModeProperties)
321  {
322  SDL_OutOfMemory();
323  goto error;
324  }
325 
326  result =
327  vkGetDisplayModePropertiesKHR(physicalDevice, display, &displayModePropertiesCount, displayModeProperties);
328  if (result != VK_SUCCESS || displayModePropertiesCount == 0) {
329  SDL_SetError("Error enumerating display modes");
330  SDL_free(displayModeProperties);
331  goto error;
332  }
333 
334  /* Try to find a display mode that matches the native resolution */
335  for (i = 0; i < displayModePropertiesCount; ++i)
336  {
337  if (displayModeProperties[i].parameters.visibleRegion.width == extent.width &&
338  displayModeProperties[i].parameters.visibleRegion.height == extent.height &&
339  displayModeProperties[i].parameters.refreshRate > refreshRate)
340  {
341  bestMatchIndex = i;
342  refreshRate = displayModeProperties[i].parameters.refreshRate;
343  }
344  }
345  if (bestMatchIndex < 0)
346  {
347  SDL_SetError("Found no matching display mode");
348  SDL_free(displayModeProperties);
349  goto error;
350  }
351 
352  SDL_zero(createInfo);
353  createInfo.displayMode = displayModeProperties[bestMatchIndex].displayMode;
354  SDL_LogDebug(SDL_LOG_CATEGORY_VIDEO, "vulkandisplay: Matching mode %ux%u with refresh rate %u",
355  displayModeProperties[bestMatchIndex].parameters.visibleRegion.width,
356  displayModeProperties[bestMatchIndex].parameters.visibleRegion.height,
357  refreshRate);
358 
359  SDL_free(displayModeProperties);
360  displayModeProperties = NULL;
361 
362  /* Try to find a plane index that supports our display */
363  result =
364  vkGetPhysicalDeviceDisplayPlanePropertiesKHR(physicalDevice, &displayPlanePropertiesCount, NULL);
365  if (result != VK_SUCCESS || displayPlanePropertiesCount == 0)
366  {
367  SDL_SetError("Error enumerating display planes");
368  goto error;
369  }
370  SDL_LogDebug(SDL_LOG_CATEGORY_VIDEO, "vulkandisplay: Number of display planes: %u", displayPlanePropertiesCount);
371 
372  displayPlaneProperties = SDL_malloc(sizeof(VkDisplayPlanePropertiesKHR) * displayPlanePropertiesCount);
373  if(!displayPlaneProperties)
374  {
375  SDL_OutOfMemory();
376  goto error;
377  }
378 
379  result =
380  vkGetPhysicalDeviceDisplayPlanePropertiesKHR(physicalDevice, &displayPlanePropertiesCount, displayPlaneProperties);
381  if (result != VK_SUCCESS || displayPlanePropertiesCount == 0)
382  {
383  SDL_SetError("Error enumerating display plane properties");
384  SDL_free(displayPlaneProperties);
385  goto error;
386  }
387 
388  for (i = 0; i < displayPlanePropertiesCount; ++i)
389  {
390  uint32_t planeSupportedDisplaysCount = 0;
391  VkDisplayKHR *planeSupportedDisplays = NULL;
392  uint32_t j;
393 
394  /* Check if plane is attached to a display, if not, continue. */
395  if (displayPlaneProperties[i].currentDisplay == VK_NULL_HANDLE)
396  continue;
397 
398  /* Check supported displays for this plane. */
399  result =
400  vkGetDisplayPlaneSupportedDisplaysKHR(physicalDevice, i, &planeSupportedDisplaysCount, NULL);
401  if (result != VK_SUCCESS || planeSupportedDisplaysCount == 0)
402  {
403  continue; /* No supported displays, on to next plane. */
404  }
405  SDL_LogDebug(SDL_LOG_CATEGORY_VIDEO, "vulkandisplay: Number of supported displays for plane %u: %u", i, planeSupportedDisplaysCount);
406 
407  planeSupportedDisplays = SDL_malloc(sizeof(VkDisplayKHR) * planeSupportedDisplaysCount);
408  if(!planeSupportedDisplays)
409  {
410  SDL_free(displayPlaneProperties);
411  SDL_OutOfMemory();
412  goto error;
413  }
414 
415  result =
416  vkGetDisplayPlaneSupportedDisplaysKHR(physicalDevice, i, &planeSupportedDisplaysCount, planeSupportedDisplays);
417  if (result != VK_SUCCESS || planeSupportedDisplaysCount == 0)
418  {
419  SDL_SetError("Error enumerating supported displays, or no supported displays");
420  SDL_free(planeSupportedDisplays);
421  SDL_free(displayPlaneProperties);
422  goto error;
423  }
424 
425  for (j = 0; j < planeSupportedDisplaysCount && planeSupportedDisplays[j] != display; ++j)
426  ;
427 
428  SDL_free(planeSupportedDisplays);
429  planeSupportedDisplays = NULL;
430 
431  if (j == planeSupportedDisplaysCount)
432  {
433  /* This display is not supported for this plane, move on. */
434  continue;
435  }
436 
437  result = vkGetDisplayPlaneCapabilitiesKHR(physicalDevice, createInfo.displayMode, i, &planeCaps);
438  if (result != VK_SUCCESS)
439  {
440  SDL_SetError("Error getting display plane capabilities");
441  SDL_free(displayPlaneProperties);
442  goto error;
443  }
444 
445  /* Check if plane fulfills extent requirements. */
446  if (extent.width >= planeCaps.minDstExtent.width && extent.height >= planeCaps.minDstExtent.height &&
447  extent.width <= planeCaps.maxDstExtent.width && extent.height <= planeCaps.maxDstExtent.height)
448  {
449  /* If it does, choose this plane. */
450  SDL_LogDebug(SDL_LOG_CATEGORY_VIDEO, "vulkandisplay: Choosing plane %d, minimum extent %dx%d maximum extent %dx%d", i,
451  planeCaps.minDstExtent.width, planeCaps.minDstExtent.height,
452  planeCaps.maxDstExtent.width, planeCaps.maxDstExtent.height);
453  planeIndex = i;
454  break;
455  }
456  }
457 
458  if (planeIndex < 0)
459  {
460  SDL_SetError("No plane supports the selected resolution");
461  SDL_free(displayPlaneProperties);
462  goto error;
463  }
464 
465  createInfo.planeIndex = planeIndex;
466  createInfo.planeStackIndex = displayPlaneProperties[planeIndex].currentStackIndex;
467  SDL_free(displayPlaneProperties);
468  displayPlaneProperties = NULL;
469 
470  /* Find a supported alpha mode. Not all planes support OPAQUE */
472  for (i = 0; i < SDL_arraysize(alphaModes); i++) {
473  if (planeCaps.supportedAlpha & alphaModes[i]) {
474  createInfo.alphaMode = alphaModes[i];
475  break;
476  }
477  }
478  SDL_LogDebug(SDL_LOG_CATEGORY_VIDEO, "vulkandisplay: Chose alpha mode 0x%x", createInfo.alphaMode);
479 
480  /* Found a match, finally! Fill in extent, and break from loop */
481  createInfo.imageExtent = extent;
482  break;
483  }
484 
485  SDL_free(physicalDevices);
486  physicalDevices = NULL;
487 
488  if (physicalDeviceIndex == physicalDeviceCount)
489  {
490  SDL_SetError("No usable displays found or requested display out of range");
491  return SDL_FALSE;
492  }
493 
496  createInfo.globalAlpha = 1.0f;
497 
498  result = vkCreateDisplayPlaneSurfaceKHR(instance, &createInfo,
499  NULL, surface);
500  if(result != VK_SUCCESS)
501  {
502  SDL_SetError("vkCreateDisplayPlaneSurfaceKHR failed: %s",
503  SDL_Vulkan_GetResultString(result));
504  return SDL_FALSE;
505  }
506  SDL_LogDebug(SDL_LOG_CATEGORY_VIDEO, "vulkandisplay: Created surface");
507  return SDL_TRUE;
508 error:
509  SDL_free(physicalDevices);
510  return SDL_FALSE;
511 }
512 
513 #endif
514 
515 /* vi: set ts=4 sw=4 expandtab: */
VKAPI_ATTR VkResult VKAPI_CALL vkGetDisplayModePropertiesKHR(VkPhysicalDevice physicalDevice, VkDisplayKHR display, uint32_t *pPropertyCount, VkDisplayModePropertiesKHR *pProperties)
VkExtent2D physicalResolution
Definition: vulkan_core.h:4971
#define VK_NULL_HANDLE
Definition: vulkan_core.h:49
VKAPI_ATTR VkResult VKAPI_CALL vkGetPhysicalDeviceDisplayPropertiesKHR(VkPhysicalDevice physicalDevice, uint32_t *pPropertyCount, VkDisplayPropertiesKHR *pProperties)
VkResult
Definition: vulkan_core.h:120
GLuint64EXT * result
VkDisplayPlaneAlphaFlagBitsKHR alphaMode
Definition: vulkan_core.h:5020
GLuint GLuint * names
GLuint GLuint GLsizei count
Definition: SDL_opengl.h:1571
VkSurfaceTransformFlagBitsKHR transform
Definition: vulkan_core.h:5018
EGLSurface surface
Definition: eglext.h:248
VkDisplayModeKHR displayMode
Definition: vulkan_core.h:4983
VKAPI_ATTR VkResult VKAPI_CALL vkCreateDisplayPlaneSurfaceKHR(VkInstance instance, const VkDisplaySurfaceCreateInfoKHR *pCreateInfo, const VkAllocationCallbacks *pAllocator, VkSurfaceKHR *pSurface)
#define VULKAN_INSTANCE_FUNCTION(name)
Definition: testvulkan.c:89
VKAPI_ATTR VkResult VKAPI_CALL vkEnumerateInstanceExtensionProperties(const char *pLayerName, uint32_t *pPropertyCount, VkExtensionProperties *pProperties)
SDL_bool retval
#define SDL_LogDebug
VkDisplayModeKHR displayMode
Definition: vulkan_core.h:5015
VKAPI_ATTR VkResult VKAPI_CALL vkEnumeratePhysicalDevices(VkInstance instance, uint32_t *pPhysicalDeviceCount, VkPhysicalDevice *pPhysicalDevices)
VKAPI_ATTR VkResult VKAPI_CALL vkGetDisplayPlaneCapabilitiesKHR(VkPhysicalDevice physicalDevice, VkDisplayModeKHR mode, uint32_t planeIndex, VkDisplayPlaneCapabilitiesKHR *pCapabilities)
#define SDL_free
return Display return Display Bool Bool int int int return Display XEvent Bool(*) XPointer return Display return Display Drawable _Xconst char unsigned int unsigned int return Display Pixmap Pixmap XColor XColor unsigned int unsigned int return Display _Xconst char char int char return Display Visual unsigned int int int char unsigned int unsigned int int in j)
Definition: SDL_x11sym.h:50
uint32_t width
Definition: vulkan_core.h:2360
VKAPI_ATTR PFN_vkVoidFunction VKAPI_CALL vkGetInstanceProcAddr(VkInstance instance, const char *pName)
#define VK_KHR_DISPLAY_EXTENSION_NAME
Definition: vulkan_core.h:4953
VkDisplayModeParametersKHR parameters
Definition: vulkan_core.h:4984
#define SDL_zero(x)
Definition: SDL_stdinc.h:416
#define SDL_atoi
#define SDL_getenv
return Display return Display Bool Bool int int int return Display XEvent Bool(*) XPointer return Display return Display Drawable _Xconst char unsigned int unsigned int return Display Pixmap Pixmap XColor XColor unsigned int unsigned int return Display _Xconst char char int char return Display Visual unsigned int int int char unsigned int unsigned int in i)
Definition: SDL_x11sym.h:50
#define NULL
Definition: begin_code.h:167
#define SDL_OutOfMemory()
Definition: SDL_error.h:52
SDL_bool
Definition: SDL_stdinc.h:161
unsigned int uint32_t
#define SDL_SetError
VkDisplayPlaneAlphaFlagBitsKHR
Definition: vulkan_core.h:4956
VkResult(VKAPI_PTR * PFN_vkEnumerateInstanceExtensionProperties)(const char *pLayerName, uint32_t *pPropertyCount, VkExtensionProperties *pProperties)
Definition: vulkan_core.h:2863
PFN_vkVoidFunction(VKAPI_PTR * PFN_vkGetInstanceProcAddr)(VkInstance instance, const char *pName)
Definition: vulkan_core.h:2859
#define SDL_calloc
VKAPI_ATTR VkResult VKAPI_CALL vkGetPhysicalDeviceDisplayPlanePropertiesKHR(VkPhysicalDevice physicalDevice, uint32_t *pPropertyCount, VkDisplayPlanePropertiesKHR *pProperties)
uint32_t Uint32
Definition: SDL_stdinc.h:203
#define SDL_arraysize(array)
Definition: SDL_stdinc.h:115
#define SDL_malloc
VkDisplayPlaneAlphaFlagsKHR supportedAlpha
Definition: vulkan_core.h:4995
VKAPI_ATTR VkResult VKAPI_CALL vkGetDisplayPlaneSupportedDisplaysKHR(VkPhysicalDevice physicalDevice, uint32_t planeIndex, uint32_t *pDisplayCount, VkDisplayKHR *pDisplays)
uint32_t height
Definition: vulkan_core.h:2361