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