2 Simple DirectMedia Layer
3 Copyright (C) 1997-2020 Sam Lantinga <slouken@libsdl.org>
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.
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:
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.
21 #include "../SDL_internal.h"
23 #include "SDL_vulkan_internal.h"
24 #include "SDL_error.h"
26 /* !!! FIXME: this file doesn't match coding standards for SDL (brace position, etc). */
30 const char *SDL_Vulkan_GetResultString(VkResult result)
37 return "VK_NOT_READY";
41 return "VK_EVENT_SET";
43 return "VK_EVENT_RESET";
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_UNKNOWN:
71 return "VK_ERROR_UNKNOWN";
72 case VK_ERROR_OUT_OF_POOL_MEMORY:
73 return "VK_ERROR_OUT_OF_POOL_MEMORY";
74 case VK_ERROR_INVALID_EXTERNAL_HANDLE:
75 return "VK_ERROR_INVALID_EXTERNAL_HANDLE";
76 case VK_ERROR_FRAGMENTATION:
77 return "VK_ERROR_FRAGMENTATION";
78 case VK_ERROR_INVALID_OPAQUE_CAPTURE_ADDRESS:
79 return "VK_ERROR_INVALID_OPAQUE_CAPTURE_ADDRESS";
80 case VK_ERROR_SURFACE_LOST_KHR:
81 return "VK_ERROR_SURFACE_LOST_KHR";
82 case VK_ERROR_NATIVE_WINDOW_IN_USE_KHR:
83 return "VK_ERROR_NATIVE_WINDOW_IN_USE_KHR";
84 case VK_SUBOPTIMAL_KHR:
85 return "VK_SUBOPTIMAL_KHR";
86 case VK_ERROR_OUT_OF_DATE_KHR:
87 return "VK_ERROR_OUT_OF_DATE_KHR";
88 case VK_ERROR_INCOMPATIBLE_DISPLAY_KHR:
89 return "VK_ERROR_INCOMPATIBLE_DISPLAY_KHR";
90 case VK_ERROR_VALIDATION_FAILED_EXT:
91 return "VK_ERROR_VALIDATION_FAILED_EXT";
92 case VK_ERROR_INVALID_SHADER_NV:
93 return "VK_ERROR_INVALID_SHADER_NV";
94 case VK_ERROR_INCOMPATIBLE_VERSION_KHR:
95 return "VK_ERROR_INCOMPATIBLE_VERSION_KHR";
96 case VK_ERROR_INVALID_DRM_FORMAT_MODIFIER_PLANE_LAYOUT_EXT:
97 return "VK_ERROR_INVALID_DRM_FORMAT_MODIFIER_PLANE_LAYOUT_EXT";
98 case VK_ERROR_NOT_PERMITTED_EXT:
99 return "VK_ERROR_NOT_PERMITTED_EXT";
100 case VK_ERROR_FULL_SCREEN_EXCLUSIVE_MODE_LOST_EXT:
101 return "VK_ERROR_FULL_SCREEN_EXCLUSIVE_MODE_LOST_EXT";
102 case VK_THREAD_IDLE_KHR:
103 return "VK_THREAD_IDLE_KHR";
104 case VK_THREAD_DONE_KHR:
105 return "VK_THREAD_DONE_KHR";
106 case VK_OPERATION_DEFERRED_KHR:
107 return "VK_OPERATION_DEFERRED_KHR";
108 case VK_OPERATION_NOT_DEFERRED_KHR:
109 return "VK_OPERATION_NOT_DEFERRED_KHR";
110 case VK_PIPELINE_COMPILE_REQUIRED_EXT:
111 return "VK_PIPELINE_COMPILE_REQUIRED_EXT";
116 return "VK_ERROR_<Unknown>";
117 return "VK_<Unknown>";
120 VkExtensionProperties *SDL_Vulkan_CreateInstanceExtensionsList(
121 PFN_vkEnumerateInstanceExtensionProperties vkEnumerateInstanceExtensionProperties,
122 Uint32 *extensionCount)
125 VkResult result = vkEnumerateInstanceExtensionProperties(NULL, &count, NULL);
126 VkExtensionProperties *retval;
127 if(result == VK_ERROR_INCOMPATIBLE_DRIVER)
129 /* Avoid the ERR_MAX_STRLEN limit by passing part of the message
130 * as a string argument.
133 "You probably don't have a working Vulkan driver installed. %s %s %s(%d)",
134 "Getting Vulkan extensions failed:",
135 "vkEnumerateInstanceExtensionProperties returned",
136 SDL_Vulkan_GetResultString(result),
140 else if(result != VK_SUCCESS)
143 "Getting Vulkan extensions failed: vkEnumerateInstanceExtensionProperties returned "
145 SDL_Vulkan_GetResultString(result),
151 retval = SDL_calloc(1, sizeof(VkExtensionProperties)); // so we can return non-null
155 retval = SDL_calloc(count, sizeof(VkExtensionProperties));
162 result = vkEnumerateInstanceExtensionProperties(NULL, &count, retval);
163 if(result != VK_SUCCESS)
166 "Getting Vulkan extensions failed: vkEnumerateInstanceExtensionProperties returned "
168 SDL_Vulkan_GetResultString(result),
173 *extensionCount = count;
177 SDL_bool SDL_Vulkan_GetInstanceExtensions_Helper(unsigned *userCount,
178 const char **userNames,
180 const char *const *names)
185 if (*userCount < nameCount) {
186 SDL_SetError("Output array for SDL_Vulkan_GetInstanceExtensions needs to be at least %d big", nameCount);
189 for (i = 0; i < nameCount; i++) {
190 userNames[i] = names[i];
193 *userCount = nameCount;
197 /* Alpha modes, in order of preference */
198 static const VkDisplayPlaneAlphaFlagBitsKHR alphaModes[4] = {
199 VK_DISPLAY_PLANE_ALPHA_OPAQUE_BIT_KHR,
200 VK_DISPLAY_PLANE_ALPHA_GLOBAL_BIT_KHR,
201 VK_DISPLAY_PLANE_ALPHA_PER_PIXEL_BIT_KHR,
202 VK_DISPLAY_PLANE_ALPHA_PER_PIXEL_PREMULTIPLIED_BIT_KHR,
205 SDL_bool SDL_Vulkan_Display_CreateSurface(void *vkGetInstanceProcAddr_,
207 VkSurfaceKHR *surface)
209 PFN_vkGetInstanceProcAddr vkGetInstanceProcAddr =
210 (PFN_vkGetInstanceProcAddr)vkGetInstanceProcAddr_;
211 #define VULKAN_INSTANCE_FUNCTION(name) \
212 PFN_##name name = (PFN_##name)vkGetInstanceProcAddr((VkInstance)instance, #name)
213 VULKAN_INSTANCE_FUNCTION(vkEnumeratePhysicalDevices);
214 VULKAN_INSTANCE_FUNCTION(vkGetPhysicalDeviceDisplayPropertiesKHR);
215 VULKAN_INSTANCE_FUNCTION(vkGetDisplayModePropertiesKHR);
216 VULKAN_INSTANCE_FUNCTION(vkGetPhysicalDeviceDisplayPlanePropertiesKHR);
217 VULKAN_INSTANCE_FUNCTION(vkGetDisplayPlaneCapabilitiesKHR);
218 VULKAN_INSTANCE_FUNCTION(vkGetDisplayPlaneSupportedDisplaysKHR);
219 VULKAN_INSTANCE_FUNCTION(vkCreateDisplayPlaneSurfaceKHR);
220 #undef VULKAN_INSTANCE_FUNCTION
221 VkDisplaySurfaceCreateInfoKHR createInfo;
223 uint32_t physicalDeviceCount = 0;
224 VkPhysicalDevice *physicalDevices = NULL;
225 uint32_t physicalDeviceIndex;
226 const char *chosenDisplayId;
227 int displayId = 0; /* Counting from physical device 0, display 0 */
229 if(!vkEnumeratePhysicalDevices ||
230 !vkGetPhysicalDeviceDisplayPropertiesKHR ||
231 !vkGetDisplayModePropertiesKHR ||
232 !vkGetPhysicalDeviceDisplayPlanePropertiesKHR ||
233 !vkGetDisplayPlaneCapabilitiesKHR ||
234 !vkGetDisplayPlaneSupportedDisplaysKHR ||
235 !vkCreateDisplayPlaneSurfaceKHR)
237 SDL_SetError(VK_KHR_DISPLAY_EXTENSION_NAME
238 " extension is not enabled in the Vulkan instance.");
242 if ((chosenDisplayId = SDL_getenv("SDL_VULKAN_DISPLAY")) != NULL)
244 displayId = SDL_atoi(chosenDisplayId);
247 /* Enumerate physical devices */
249 vkEnumeratePhysicalDevices(instance, &physicalDeviceCount, NULL);
250 if(result != VK_SUCCESS)
252 SDL_SetError("Could not enumerate Vulkan physical devices");
255 if(physicalDeviceCount == 0)
257 SDL_SetError("No Vulkan physical devices");
260 physicalDevices = SDL_malloc(sizeof(VkPhysicalDevice) * physicalDeviceCount);
267 vkEnumeratePhysicalDevices(instance, &physicalDeviceCount, physicalDevices);
268 if(result != VK_SUCCESS)
270 SDL_SetError("Error enumerating physical devices");
274 for(physicalDeviceIndex = 0; physicalDeviceIndex < physicalDeviceCount;
275 physicalDeviceIndex++)
277 VkPhysicalDevice physicalDevice = physicalDevices[physicalDeviceIndex];
278 uint32_t displayPropertiesCount = 0;
279 VkDisplayPropertiesKHR *displayProperties = NULL;
280 uint32_t displayModePropertiesCount = 0;
281 VkDisplayModePropertiesKHR *displayModeProperties = NULL;
282 int bestMatchIndex = -1;
283 uint32_t refreshRate = 0;
285 uint32_t displayPlanePropertiesCount = 0;
287 VkDisplayKHR display;
288 VkDisplayPlanePropertiesKHR *displayPlaneProperties = NULL;
290 VkDisplayPlaneCapabilitiesKHR planeCaps;
292 /* Get information about the physical displays */
294 vkGetPhysicalDeviceDisplayPropertiesKHR(physicalDevice, &displayPropertiesCount, NULL);
295 if (result != VK_SUCCESS || displayPropertiesCount == 0)
297 /* This device has no physical device display properties, move on to next. */
300 SDL_LogDebug(SDL_LOG_CATEGORY_VIDEO, "vulkandisplay: Number of display properties for device %u: %u",
301 physicalDeviceIndex, displayPropertiesCount);
303 if ( (displayId < 0) || (((uint32_t) displayId) >= displayPropertiesCount) )
305 /* Display id specified was higher than number of available displays, move to next physical device. */
306 displayId -= displayPropertiesCount;
310 displayProperties = SDL_malloc(sizeof(VkDisplayPropertiesKHR) * displayPropertiesCount);
311 if(!displayProperties)
318 vkGetPhysicalDeviceDisplayPropertiesKHR(physicalDevice, &displayPropertiesCount, displayProperties);
319 if (result != VK_SUCCESS || displayPropertiesCount == 0) {
320 SDL_free(displayProperties);
321 SDL_SetError("Error enumerating physical device displays");
325 display = displayProperties[displayId].display;
326 extent = displayProperties[displayId].physicalResolution;
327 SDL_LogDebug(SDL_LOG_CATEGORY_VIDEO, "vulkandisplay: Display: %s Native resolution: %ux%u",
328 displayProperties[displayId].displayName, extent.width, extent.height);
330 SDL_free(displayProperties);
331 displayProperties = NULL;
333 /* Get display mode properties for the chosen display */
335 vkGetDisplayModePropertiesKHR(physicalDevice, display, &displayModePropertiesCount, NULL);
336 if (result != VK_SUCCESS || displayModePropertiesCount == 0)
338 SDL_SetError("Error enumerating display modes");
341 SDL_LogDebug(SDL_LOG_CATEGORY_VIDEO, "vulkandisplay: Number of display modes: %u", displayModePropertiesCount);
343 displayModeProperties = SDL_malloc(sizeof(VkDisplayModePropertiesKHR) * displayModePropertiesCount);
344 if(!displayModeProperties)
351 vkGetDisplayModePropertiesKHR(physicalDevice, display, &displayModePropertiesCount, displayModeProperties);
352 if (result != VK_SUCCESS || displayModePropertiesCount == 0) {
353 SDL_SetError("Error enumerating display modes");
354 SDL_free(displayModeProperties);
358 /* Try to find a display mode that matches the native resolution */
359 for (i = 0; i < displayModePropertiesCount; ++i)
361 if (displayModeProperties[i].parameters.visibleRegion.width == extent.width &&
362 displayModeProperties[i].parameters.visibleRegion.height == extent.height &&
363 displayModeProperties[i].parameters.refreshRate > refreshRate)
366 refreshRate = displayModeProperties[i].parameters.refreshRate;
369 if (bestMatchIndex < 0)
371 SDL_SetError("Found no matching display mode");
372 SDL_free(displayModeProperties);
376 SDL_zero(createInfo);
377 createInfo.displayMode = displayModeProperties[bestMatchIndex].displayMode;
378 SDL_LogDebug(SDL_LOG_CATEGORY_VIDEO, "vulkandisplay: Matching mode %ux%u with refresh rate %u",
379 displayModeProperties[bestMatchIndex].parameters.visibleRegion.width,
380 displayModeProperties[bestMatchIndex].parameters.visibleRegion.height,
383 SDL_free(displayModeProperties);
384 displayModeProperties = NULL;
386 /* Try to find a plane index that supports our display */
388 vkGetPhysicalDeviceDisplayPlanePropertiesKHR(physicalDevice, &displayPlanePropertiesCount, NULL);
389 if (result != VK_SUCCESS || displayPlanePropertiesCount == 0)
391 SDL_SetError("Error enumerating display planes");
394 SDL_LogDebug(SDL_LOG_CATEGORY_VIDEO, "vulkandisplay: Number of display planes: %u", displayPlanePropertiesCount);
396 displayPlaneProperties = SDL_malloc(sizeof(VkDisplayPlanePropertiesKHR) * displayPlanePropertiesCount);
397 if(!displayPlaneProperties)
404 vkGetPhysicalDeviceDisplayPlanePropertiesKHR(physicalDevice, &displayPlanePropertiesCount, displayPlaneProperties);
405 if (result != VK_SUCCESS || displayPlanePropertiesCount == 0)
407 SDL_SetError("Error enumerating display plane properties");
408 SDL_free(displayPlaneProperties);
412 for (i = 0; i < displayPlanePropertiesCount; ++i)
414 uint32_t planeSupportedDisplaysCount = 0;
415 VkDisplayKHR *planeSupportedDisplays = NULL;
418 /* Check if plane is attached to a display, if not, continue. */
419 if (displayPlaneProperties[i].currentDisplay == VK_NULL_HANDLE)
422 /* Check supported displays for this plane. */
424 vkGetDisplayPlaneSupportedDisplaysKHR(physicalDevice, i, &planeSupportedDisplaysCount, NULL);
425 if (result != VK_SUCCESS || planeSupportedDisplaysCount == 0)
427 continue; /* No supported displays, on to next plane. */
429 SDL_LogDebug(SDL_LOG_CATEGORY_VIDEO, "vulkandisplay: Number of supported displays for plane %u: %u", i, planeSupportedDisplaysCount);
431 planeSupportedDisplays = SDL_malloc(sizeof(VkDisplayKHR) * planeSupportedDisplaysCount);
432 if(!planeSupportedDisplays)
434 SDL_free(displayPlaneProperties);
440 vkGetDisplayPlaneSupportedDisplaysKHR(physicalDevice, i, &planeSupportedDisplaysCount, planeSupportedDisplays);
441 if (result != VK_SUCCESS || planeSupportedDisplaysCount == 0)
443 SDL_SetError("Error enumerating supported displays, or no supported displays");
444 SDL_free(planeSupportedDisplays);
445 SDL_free(displayPlaneProperties);
449 for (j = 0; j < planeSupportedDisplaysCount && planeSupportedDisplays[j] != display; ++j)
452 SDL_free(planeSupportedDisplays);
453 planeSupportedDisplays = NULL;
455 if (j == planeSupportedDisplaysCount)
457 /* This display is not supported for this plane, move on. */
461 result = vkGetDisplayPlaneCapabilitiesKHR(physicalDevice, createInfo.displayMode, i, &planeCaps);
462 if (result != VK_SUCCESS)
464 SDL_SetError("Error getting display plane capabilities");
465 SDL_free(displayPlaneProperties);
469 /* Check if plane fulfills extent requirements. */
470 if (extent.width >= planeCaps.minDstExtent.width && extent.height >= planeCaps.minDstExtent.height &&
471 extent.width <= planeCaps.maxDstExtent.width && extent.height <= planeCaps.maxDstExtent.height)
473 /* If it does, choose this plane. */
474 SDL_LogDebug(SDL_LOG_CATEGORY_VIDEO, "vulkandisplay: Choosing plane %d, minimum extent %dx%d maximum extent %dx%d", i,
475 planeCaps.minDstExtent.width, planeCaps.minDstExtent.height,
476 planeCaps.maxDstExtent.width, planeCaps.maxDstExtent.height);
484 SDL_SetError("No plane supports the selected resolution");
485 SDL_free(displayPlaneProperties);
489 createInfo.planeIndex = planeIndex;
490 createInfo.planeStackIndex = displayPlaneProperties[planeIndex].currentStackIndex;
491 SDL_free(displayPlaneProperties);
492 displayPlaneProperties = NULL;
494 /* Find a supported alpha mode. Not all planes support OPAQUE */
495 createInfo.alphaMode = VK_DISPLAY_PLANE_ALPHA_OPAQUE_BIT_KHR;
496 for (i = 0; i < SDL_arraysize(alphaModes); i++) {
497 if (planeCaps.supportedAlpha & alphaModes[i]) {
498 createInfo.alphaMode = alphaModes[i];
502 SDL_LogDebug(SDL_LOG_CATEGORY_VIDEO, "vulkandisplay: Chose alpha mode 0x%x", createInfo.alphaMode);
504 /* Found a match, finally! Fill in extent, and break from loop */
505 createInfo.imageExtent = extent;
509 SDL_free(physicalDevices);
510 physicalDevices = NULL;
512 if (physicalDeviceIndex == physicalDeviceCount)
514 SDL_SetError("No usable displays found or requested display out of range");
518 createInfo.sType = VK_STRUCTURE_TYPE_DISPLAY_SURFACE_CREATE_INFO_KHR;
519 createInfo.transform = VK_SURFACE_TRANSFORM_IDENTITY_BIT_KHR;
520 createInfo.globalAlpha = 1.0f;
522 result = vkCreateDisplayPlaneSurfaceKHR(instance, &createInfo,
524 if(result != VK_SUCCESS)
526 SDL_SetError("vkCreateDisplayPlaneSurfaceKHR failed: %s",
527 SDL_Vulkan_GetResultString(result));
530 SDL_LogDebug(SDL_LOG_CATEGORY_VIDEO, "vulkandisplay: Created surface");
533 SDL_free(physicalDevices);
539 /* vi: set ts=4 sw=4 expandtab: */