1 /*
2   Simple DirectMedia Layer
3   Copyright (C) 1997-2021 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 
26 #if SDL_VIDEO_VULKAN
27 
SDL_Vulkan_GetResultString(VkResult result)28 const char *SDL_Vulkan_GetResultString(VkResult result)
29 {
30     switch ((int)result) {
31         case VK_SUCCESS:
32             return "VK_SUCCESS";
33         case VK_NOT_READY:
34             return "VK_NOT_READY";
35         case VK_TIMEOUT:
36             return "VK_TIMEOUT";
37         case VK_EVENT_SET:
38             return "VK_EVENT_SET";
39         case VK_EVENT_RESET:
40             return "VK_EVENT_RESET";
41         case VK_INCOMPLETE:
42             return "VK_INCOMPLETE";
43         case VK_ERROR_OUT_OF_HOST_MEMORY:
44             return "VK_ERROR_OUT_OF_HOST_MEMORY";
45         case VK_ERROR_OUT_OF_DEVICE_MEMORY:
46             return "VK_ERROR_OUT_OF_DEVICE_MEMORY";
47         case VK_ERROR_INITIALIZATION_FAILED:
48             return "VK_ERROR_INITIALIZATION_FAILED";
49         case VK_ERROR_DEVICE_LOST:
50             return "VK_ERROR_DEVICE_LOST";
51         case VK_ERROR_MEMORY_MAP_FAILED:
52             return "VK_ERROR_MEMORY_MAP_FAILED";
53         case VK_ERROR_LAYER_NOT_PRESENT:
54             return "VK_ERROR_LAYER_NOT_PRESENT";
55         case VK_ERROR_EXTENSION_NOT_PRESENT:
56             return "VK_ERROR_EXTENSION_NOT_PRESENT";
57         case VK_ERROR_FEATURE_NOT_PRESENT:
58             return "VK_ERROR_FEATURE_NOT_PRESENT";
59         case VK_ERROR_INCOMPATIBLE_DRIVER:
60             return "VK_ERROR_INCOMPATIBLE_DRIVER";
61         case VK_ERROR_TOO_MANY_OBJECTS:
62             return "VK_ERROR_TOO_MANY_OBJECTS";
63         case VK_ERROR_FORMAT_NOT_SUPPORTED:
64             return "VK_ERROR_FORMAT_NOT_SUPPORTED";
65         case VK_ERROR_FRAGMENTED_POOL:
66             return "VK_ERROR_FRAGMENTED_POOL";
67         case VK_ERROR_UNKNOWN:
68             return "VK_ERROR_UNKNOWN";
69         case VK_ERROR_OUT_OF_POOL_MEMORY:
70             return "VK_ERROR_OUT_OF_POOL_MEMORY";
71         case VK_ERROR_INVALID_EXTERNAL_HANDLE:
72             return "VK_ERROR_INVALID_EXTERNAL_HANDLE";
73         case VK_ERROR_FRAGMENTATION:
74             return "VK_ERROR_FRAGMENTATION";
75         case VK_ERROR_INVALID_OPAQUE_CAPTURE_ADDRESS:
76             return "VK_ERROR_INVALID_OPAQUE_CAPTURE_ADDRESS";
77         case VK_ERROR_SURFACE_LOST_KHR:
78             return "VK_ERROR_SURFACE_LOST_KHR";
79         case VK_ERROR_NATIVE_WINDOW_IN_USE_KHR:
80             return "VK_ERROR_NATIVE_WINDOW_IN_USE_KHR";
81         case VK_SUBOPTIMAL_KHR:
82             return "VK_SUBOPTIMAL_KHR";
83         case VK_ERROR_OUT_OF_DATE_KHR:
84             return "VK_ERROR_OUT_OF_DATE_KHR";
85         case VK_ERROR_INCOMPATIBLE_DISPLAY_KHR:
86             return "VK_ERROR_INCOMPATIBLE_DISPLAY_KHR";
87         case VK_ERROR_VALIDATION_FAILED_EXT:
88             return "VK_ERROR_VALIDATION_FAILED_EXT";
89         case VK_ERROR_INVALID_SHADER_NV:
90             return "VK_ERROR_INVALID_SHADER_NV";
91 #if VK_HEADER_VERSION >= 135 && VK_HEADER_VERSION < 162
92         case VK_ERROR_INCOMPATIBLE_VERSION_KHR:
93             return "VK_ERROR_INCOMPATIBLE_VERSION_KHR";
94 #endif
95         case VK_ERROR_INVALID_DRM_FORMAT_MODIFIER_PLANE_LAYOUT_EXT:
96             return "VK_ERROR_INVALID_DRM_FORMAT_MODIFIER_PLANE_LAYOUT_EXT";
97         case VK_ERROR_NOT_PERMITTED_EXT:
98             return "VK_ERROR_NOT_PERMITTED_EXT";
99         case VK_ERROR_FULL_SCREEN_EXCLUSIVE_MODE_LOST_EXT:
100             return "VK_ERROR_FULL_SCREEN_EXCLUSIVE_MODE_LOST_EXT";
101         case VK_THREAD_IDLE_KHR:
102             return "VK_THREAD_IDLE_KHR";
103         case VK_THREAD_DONE_KHR:
104             return "VK_THREAD_DONE_KHR";
105         case VK_OPERATION_DEFERRED_KHR:
106             return "VK_OPERATION_DEFERRED_KHR";
107         case VK_OPERATION_NOT_DEFERRED_KHR:
108             return "VK_OPERATION_NOT_DEFERRED_KHR";
109         case VK_PIPELINE_COMPILE_REQUIRED_EXT:
110             return "VK_PIPELINE_COMPILE_REQUIRED_EXT";
111         default:
112             break;
113     }
114     if (result < 0) {
115         return "VK_ERROR_<Unknown>";
116     }
117     return "VK_<Unknown>";
118 }
119 
SDL_Vulkan_CreateInstanceExtensionsList(PFN_vkEnumerateInstanceExtensionProperties vkEnumerateInstanceExtensionProperties,Uint32 * extensionCount)120 VkExtensionProperties *SDL_Vulkan_CreateInstanceExtensionsList(
121     PFN_vkEnumerateInstanceExtensionProperties vkEnumerateInstanceExtensionProperties,
122     Uint32 *extensionCount)
123 {
124     Uint32 count = 0;
125     VkResult result = vkEnumerateInstanceExtensionProperties(NULL, &count, NULL);
126     VkExtensionProperties *retval;
127 
128     if (result == VK_ERROR_INCOMPATIBLE_DRIVER) {
129         /* Avoid the ERR_MAX_STRLEN limit by passing part of the message as a string argument.  */
130         SDL_SetError(
131             "You probably don't have a working Vulkan driver installed. %s %s %s(%d)",
132             "Getting Vulkan extensions failed:",
133             "vkEnumerateInstanceExtensionProperties returned",
134             SDL_Vulkan_GetResultString(result),
135             (int)result);
136         return NULL;
137     } else if (result != VK_SUCCESS) {
138         SDL_SetError(
139             "Getting Vulkan extensions failed: vkEnumerateInstanceExtensionProperties returned "
140             "%s(%d)",
141             SDL_Vulkan_GetResultString(result),
142             (int)result);
143         return NULL;
144     }
145 
146     if (count == 0) {
147         retval = SDL_calloc(1, sizeof(VkExtensionProperties)); // so we can return non-null
148     } else {
149         retval = SDL_calloc(count, sizeof(VkExtensionProperties));
150     }
151 
152     if (!retval) {
153         SDL_OutOfMemory();
154         return NULL;
155     }
156 
157     result = vkEnumerateInstanceExtensionProperties(NULL, &count, retval);
158     if (result != VK_SUCCESS) {
159         SDL_SetError(
160             "Getting Vulkan extensions failed: vkEnumerateInstanceExtensionProperties returned "
161             "%s(%d)",
162             SDL_Vulkan_GetResultString(result),
163             (int)result);
164         SDL_free(retval);
165         return NULL;
166     }
167     *extensionCount = count;
168     return retval;
169 }
170 
SDL_Vulkan_GetInstanceExtensions_Helper(unsigned * userCount,const char ** userNames,unsigned nameCount,const char * const * names)171 SDL_bool SDL_Vulkan_GetInstanceExtensions_Helper(unsigned *userCount,
172                                                  const char **userNames,
173                                                  unsigned nameCount,
174                                                  const char *const *names)
175 {
176     if (userNames) {
177         unsigned i;
178 
179         if (*userCount < nameCount) {
180             SDL_SetError("Output array for SDL_Vulkan_GetInstanceExtensions needs to be at least %d big", nameCount);
181             return SDL_FALSE;
182         }
183 
184         for (i = 0; i < nameCount; i++) {
185             userNames[i] = names[i];
186         }
187     }
188     *userCount = nameCount;
189     return SDL_TRUE;
190 }
191 
192 /* Alpha modes, in order of preference */
193 static const VkDisplayPlaneAlphaFlagBitsKHR alphaModes[4] = {
194     VK_DISPLAY_PLANE_ALPHA_OPAQUE_BIT_KHR,
195     VK_DISPLAY_PLANE_ALPHA_GLOBAL_BIT_KHR,
196     VK_DISPLAY_PLANE_ALPHA_PER_PIXEL_BIT_KHR,
197     VK_DISPLAY_PLANE_ALPHA_PER_PIXEL_PREMULTIPLIED_BIT_KHR,
198 };
199 
SDL_Vulkan_Display_CreateSurface(void * vkGetInstanceProcAddr_,VkInstance instance,VkSurfaceKHR * surface)200 SDL_bool SDL_Vulkan_Display_CreateSurface(void *vkGetInstanceProcAddr_,
201                                   VkInstance instance,
202                                   VkSurfaceKHR *surface)
203 {
204     PFN_vkGetInstanceProcAddr vkGetInstanceProcAddr =
205         (PFN_vkGetInstanceProcAddr)vkGetInstanceProcAddr_;
206 #define VULKAN_INSTANCE_FUNCTION(name)                                           \
207     PFN_##name name = (PFN_##name)vkGetInstanceProcAddr((VkInstance)instance, #name)
208     VULKAN_INSTANCE_FUNCTION(vkEnumeratePhysicalDevices);
209     VULKAN_INSTANCE_FUNCTION(vkGetPhysicalDeviceDisplayPropertiesKHR);
210     VULKAN_INSTANCE_FUNCTION(vkGetDisplayModePropertiesKHR);
211     VULKAN_INSTANCE_FUNCTION(vkGetPhysicalDeviceDisplayPlanePropertiesKHR);
212     VULKAN_INSTANCE_FUNCTION(vkGetDisplayPlaneCapabilitiesKHR);
213     VULKAN_INSTANCE_FUNCTION(vkGetDisplayPlaneSupportedDisplaysKHR);
214     VULKAN_INSTANCE_FUNCTION(vkCreateDisplayPlaneSurfaceKHR);
215 #undef VULKAN_INSTANCE_FUNCTION
216     VkDisplaySurfaceCreateInfoKHR createInfo;
217     VkResult result;
218     uint32_t physicalDeviceCount = 0;
219     VkPhysicalDevice *physicalDevices = NULL;
220     uint32_t physicalDeviceIndex;
221     const char *chosenDisplayId;
222     int displayId = 0; /* Counting from physical device 0, display 0 */
223 
224     if (!vkEnumeratePhysicalDevices ||
225        !vkGetPhysicalDeviceDisplayPropertiesKHR ||
226        !vkGetDisplayModePropertiesKHR ||
227        !vkGetPhysicalDeviceDisplayPlanePropertiesKHR ||
228        !vkGetDisplayPlaneCapabilitiesKHR ||
229        !vkGetDisplayPlaneSupportedDisplaysKHR ||
230        !vkCreateDisplayPlaneSurfaceKHR) {
231         SDL_SetError(VK_KHR_DISPLAY_EXTENSION_NAME " extension is not enabled in the Vulkan instance.");
232         goto error;
233     }
234 
235     if ((chosenDisplayId = SDL_getenv("SDL_VULKAN_DISPLAY")) != NULL) {
236         displayId = SDL_atoi(chosenDisplayId);
237     }
238 
239     /* Enumerate physical devices */
240     result = vkEnumeratePhysicalDevices(instance, &physicalDeviceCount, NULL);
241     if (result != VK_SUCCESS) {
242         SDL_SetError("Could not enumerate Vulkan physical devices");
243         goto error;
244     }
245 
246     if (physicalDeviceCount == 0) {
247         SDL_SetError("No Vulkan physical devices");
248         goto error;
249     }
250 
251     physicalDevices = SDL_malloc(sizeof(VkPhysicalDevice) * physicalDeviceCount);
252     if (!physicalDevices) {
253         SDL_OutOfMemory();
254         goto error;
255     }
256 
257     result = vkEnumeratePhysicalDevices(instance, &physicalDeviceCount, physicalDevices);
258     if (result != VK_SUCCESS) {
259         SDL_SetError("Error enumerating physical devices");
260         goto error;
261     }
262 
263     for (physicalDeviceIndex = 0; physicalDeviceIndex < physicalDeviceCount; physicalDeviceIndex++) {
264         VkPhysicalDevice physicalDevice = physicalDevices[physicalDeviceIndex];
265         uint32_t displayPropertiesCount = 0;
266         VkDisplayPropertiesKHR *displayProperties = NULL;
267         uint32_t displayModePropertiesCount = 0;
268         VkDisplayModePropertiesKHR *displayModeProperties = NULL;
269         int bestMatchIndex = -1;
270         uint32_t refreshRate = 0;
271         uint32_t i;
272         uint32_t displayPlanePropertiesCount = 0;
273         int planeIndex = -1;
274         VkDisplayKHR display;
275         VkDisplayPlanePropertiesKHR *displayPlaneProperties = NULL;
276         VkExtent2D extent;
277         VkDisplayPlaneCapabilitiesKHR planeCaps;
278 
279         /* Get information about the physical displays */
280         result = vkGetPhysicalDeviceDisplayPropertiesKHR(physicalDevice, &displayPropertiesCount, NULL);
281         if (result != VK_SUCCESS || displayPropertiesCount == 0) {
282             /* This device has no physical device display properties, move on to next. */
283             continue;
284         }
285         SDL_LogDebug(SDL_LOG_CATEGORY_VIDEO, "vulkandisplay: Number of display properties for device %u: %u",
286                 physicalDeviceIndex, displayPropertiesCount);
287 
288         if (displayId < 0 || (uint32_t) displayId >= displayPropertiesCount) {
289             /* Display id specified was higher than number of available displays, move to next physical device. */
290             displayId -= displayPropertiesCount;
291             continue;
292         }
293 
294         displayProperties = SDL_malloc(sizeof(VkDisplayPropertiesKHR) * displayPropertiesCount);
295         if (!displayProperties) {
296             SDL_OutOfMemory();
297             goto error;
298         }
299 
300         result = vkGetPhysicalDeviceDisplayPropertiesKHR(physicalDevice, &displayPropertiesCount, displayProperties);
301         if (result != VK_SUCCESS || displayPropertiesCount == 0) {
302             SDL_free(displayProperties);
303             SDL_SetError("Error enumerating physical device displays");
304             goto error;
305         }
306 
307         display = displayProperties[displayId].display;
308         extent = displayProperties[displayId].physicalResolution;
309         SDL_LogDebug(SDL_LOG_CATEGORY_VIDEO, "vulkandisplay: Display: %s Native resolution: %ux%u",
310                 displayProperties[displayId].displayName, extent.width, extent.height);
311 
312         SDL_free(displayProperties);
313         displayProperties = NULL;
314 
315         /* Get display mode properties for the chosen display */
316         result = vkGetDisplayModePropertiesKHR(physicalDevice, display, &displayModePropertiesCount, NULL);
317         if (result != VK_SUCCESS || displayModePropertiesCount == 0)
318         {
319             SDL_SetError("Error enumerating display modes");
320             goto error;
321         }
322         SDL_LogDebug(SDL_LOG_CATEGORY_VIDEO, "vulkandisplay: Number of display modes: %u", displayModePropertiesCount);
323 
324         displayModeProperties = SDL_malloc(sizeof(VkDisplayModePropertiesKHR) * displayModePropertiesCount);
325         if (!displayModeProperties) {
326             SDL_OutOfMemory();
327             goto error;
328         }
329 
330         result = vkGetDisplayModePropertiesKHR(physicalDevice, display, &displayModePropertiesCount, displayModeProperties);
331         if (result != VK_SUCCESS || displayModePropertiesCount == 0) {
332             SDL_SetError("Error enumerating display modes");
333             SDL_free(displayModeProperties);
334             goto error;
335         }
336 
337         /* Try to find a display mode that matches the native resolution */
338         for (i = 0; i < displayModePropertiesCount; ++i) {
339             if (displayModeProperties[i].parameters.visibleRegion.width == extent.width &&
340                 displayModeProperties[i].parameters.visibleRegion.height == extent.height &&
341                 displayModeProperties[i].parameters.refreshRate > refreshRate) {
342                 bestMatchIndex = i;
343                 refreshRate = displayModeProperties[i].parameters.refreshRate;
344             }
345         }
346 
347         if (bestMatchIndex < 0) {
348             SDL_SetError("Found no matching display mode");
349             SDL_free(displayModeProperties);
350             goto error;
351         }
352 
353         SDL_zero(createInfo);
354         createInfo.displayMode = displayModeProperties[bestMatchIndex].displayMode;
355         SDL_LogDebug(SDL_LOG_CATEGORY_VIDEO, "vulkandisplay: Matching mode %ux%u with refresh rate %u",
356                 displayModeProperties[bestMatchIndex].parameters.visibleRegion.width,
357                 displayModeProperties[bestMatchIndex].parameters.visibleRegion.height,
358                 refreshRate);
359 
360         SDL_free(displayModeProperties);
361         displayModeProperties = NULL;
362 
363         /* Try to find a plane index that supports our display */
364         result = vkGetPhysicalDeviceDisplayPlanePropertiesKHR(physicalDevice, &displayPlanePropertiesCount, NULL);
365         if (result != VK_SUCCESS || displayPlanePropertiesCount == 0) {
366             SDL_SetError("Error enumerating display planes");
367             goto error;
368         }
369         SDL_LogDebug(SDL_LOG_CATEGORY_VIDEO, "vulkandisplay: Number of display planes: %u", displayPlanePropertiesCount);
370 
371         displayPlaneProperties = SDL_malloc(sizeof(VkDisplayPlanePropertiesKHR) * displayPlanePropertiesCount);
372         if (!displayPlaneProperties) {
373             SDL_OutOfMemory();
374             goto error;
375         }
376 
377         result = vkGetPhysicalDeviceDisplayPlanePropertiesKHR(physicalDevice, &displayPlanePropertiesCount, displayPlaneProperties);
378         if (result != VK_SUCCESS || displayPlanePropertiesCount == 0) {
379             SDL_SetError("Error enumerating display plane properties");
380             SDL_free(displayPlaneProperties);
381             goto error;
382         }
383 
384         for (i = 0; i < displayPlanePropertiesCount; ++i) {
385             uint32_t planeSupportedDisplaysCount = 0;
386             VkDisplayKHR *planeSupportedDisplays = NULL;
387             uint32_t j;
388 
389             /* Check if plane is attached to a display, if not, continue. */
390             if (displayPlaneProperties[i].currentDisplay == VK_NULL_HANDLE) {
391                 continue;
392             }
393 
394             /* Check supported displays for this plane. */
395             result = vkGetDisplayPlaneSupportedDisplaysKHR(physicalDevice, i, &planeSupportedDisplaysCount, NULL);
396             if (result != VK_SUCCESS || planeSupportedDisplaysCount == 0) {
397                 continue;  /* No supported displays, on to next plane. */
398             }
399 
400             SDL_LogDebug(SDL_LOG_CATEGORY_VIDEO, "vulkandisplay: Number of supported displays for plane %u: %u", i, planeSupportedDisplaysCount);
401 
402             planeSupportedDisplays = SDL_malloc(sizeof(VkDisplayKHR) * planeSupportedDisplaysCount);
403             if (!planeSupportedDisplays) {
404                 SDL_free(displayPlaneProperties);
405                 SDL_OutOfMemory();
406                 goto error;
407             }
408 
409             result = vkGetDisplayPlaneSupportedDisplaysKHR(physicalDevice, i, &planeSupportedDisplaysCount, planeSupportedDisplays);
410             if (result != VK_SUCCESS || planeSupportedDisplaysCount == 0) {
411                 SDL_SetError("Error enumerating supported displays, or no supported displays");
412                 SDL_free(planeSupportedDisplays);
413                 SDL_free(displayPlaneProperties);
414                 goto error;
415             }
416 
417             for (j = 0; j < planeSupportedDisplaysCount && planeSupportedDisplays[j] != display; ++j)
418             {
419             }
420 
421             SDL_free(planeSupportedDisplays);
422             planeSupportedDisplays = NULL;
423 
424             if (j == planeSupportedDisplaysCount) {
425                 /* This display is not supported for this plane, move on. */
426                 continue;
427             }
428 
429             result = vkGetDisplayPlaneCapabilitiesKHR(physicalDevice, createInfo.displayMode, i, &planeCaps);
430             if (result != VK_SUCCESS) {
431                 SDL_SetError("Error getting display plane capabilities");
432                 SDL_free(displayPlaneProperties);
433                 goto error;
434             }
435 
436             /* Check if plane fulfills extent requirements. */
437             if (extent.width >= planeCaps.minDstExtent.width && extent.height >= planeCaps.minDstExtent.height &&
438                 extent.width <= planeCaps.maxDstExtent.width && extent.height <= planeCaps.maxDstExtent.height) {
439                 /* If it does, choose this plane. */
440                 SDL_LogDebug(SDL_LOG_CATEGORY_VIDEO, "vulkandisplay: Choosing plane %d, minimum extent %dx%d maximum extent %dx%d", i,
441                         planeCaps.minDstExtent.width, planeCaps.minDstExtent.height,
442                         planeCaps.maxDstExtent.width, planeCaps.maxDstExtent.height);
443                 planeIndex = i;
444                 break;
445             }
446         }
447 
448         if (planeIndex < 0) {
449             SDL_SetError("No plane supports the selected resolution");
450             SDL_free(displayPlaneProperties);
451             goto error;
452         }
453 
454         createInfo.planeIndex = planeIndex;
455         createInfo.planeStackIndex = displayPlaneProperties[planeIndex].currentStackIndex;
456         SDL_free(displayPlaneProperties);
457         displayPlaneProperties = NULL;
458 
459         /* Find a supported alpha mode. Not all planes support OPAQUE */
460         createInfo.alphaMode = VK_DISPLAY_PLANE_ALPHA_OPAQUE_BIT_KHR;
461         for (i = 0; i < SDL_arraysize(alphaModes); i++) {
462             if (planeCaps.supportedAlpha & alphaModes[i]) {
463                 createInfo.alphaMode = alphaModes[i];
464                 break;
465             }
466         }
467         SDL_LogDebug(SDL_LOG_CATEGORY_VIDEO, "vulkandisplay: Chose alpha mode 0x%x", createInfo.alphaMode);
468 
469         /* Found a match, finally! Fill in extent, and break from loop */
470         createInfo.imageExtent = extent;
471         break;
472     }
473 
474     SDL_free(physicalDevices);
475     physicalDevices = NULL;
476 
477     if (physicalDeviceIndex == physicalDeviceCount) {
478         SDL_SetError("No usable displays found or requested display out of range");
479         return SDL_FALSE;
480     }
481 
482     createInfo.sType = VK_STRUCTURE_TYPE_DISPLAY_SURFACE_CREATE_INFO_KHR;
483     createInfo.transform = VK_SURFACE_TRANSFORM_IDENTITY_BIT_KHR;
484     createInfo.globalAlpha = 1.0f;
485 
486     result = vkCreateDisplayPlaneSurfaceKHR(instance, &createInfo, NULL, surface);
487     if (result != VK_SUCCESS) {
488         SDL_SetError("vkCreateDisplayPlaneSurfaceKHR failed: %s", SDL_Vulkan_GetResultString(result));
489         return SDL_FALSE;
490     }
491     SDL_LogDebug(SDL_LOG_CATEGORY_VIDEO, "vulkandisplay: Created surface");
492     return SDL_TRUE;
493 
494 error:
495     SDL_free(physicalDevices);
496     return SDL_FALSE;
497 }
498 
499 #endif
500 
501 /* vi: set ts=4 sw=4 expandtab: */
502