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