| 1 | /* |
| 2 | Simple DirectMedia Layer |
| 3 | Copyright (C) 1997-2025 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 | |
| 22 | /* |
| 23 | * @author Manuel Alfayate Corchere <redwindwanderer@gmail.com>. |
| 24 | * Based on Jacob Lifshay's SDL_x11vulkan.c. |
| 25 | */ |
| 26 | |
| 27 | #include "SDL_internal.h" |
| 28 | |
| 29 | #if defined(SDL_VIDEO_VULKAN) && defined(SDL_VIDEO_DRIVER_KMSDRM) |
| 30 | |
| 31 | #include "../SDL_vulkan_internal.h" |
| 32 | |
| 33 | #include "SDL_kmsdrmvideo.h" |
| 34 | #include "SDL_kmsdrmdyn.h" |
| 35 | #include "SDL_kmsdrmvulkan.h" |
| 36 | |
| 37 | #include <sys/ioctl.h> |
| 38 | |
| 39 | #ifdef SDL_PLATFORM_OPENBSD |
| 40 | #define DEFAULT_VULKAN "libvulkan.so" |
| 41 | #else |
| 42 | #define DEFAULT_VULKAN "libvulkan.so.1" |
| 43 | #endif |
| 44 | |
| 45 | bool KMSDRM_Vulkan_LoadLibrary(SDL_VideoDevice *_this, const char *path) |
| 46 | { |
| 47 | VkExtensionProperties *extensions = NULL; |
| 48 | Uint32 i, extensionCount = 0; |
| 49 | bool hasSurfaceExtension = false; |
| 50 | bool hasDisplayExtension = false; |
| 51 | PFN_vkGetInstanceProcAddr vkGetInstanceProcAddr = NULL; |
| 52 | |
| 53 | if (_this->vulkan_config.loader_handle) { |
| 54 | return SDL_SetError("Vulkan already loaded" ); |
| 55 | } |
| 56 | |
| 57 | // Load the Vulkan library |
| 58 | if (!path) { |
| 59 | path = SDL_GetHint(SDL_HINT_VULKAN_LIBRARY); |
| 60 | } |
| 61 | if (!path) { |
| 62 | path = DEFAULT_VULKAN; |
| 63 | } |
| 64 | |
| 65 | _this->vulkan_config.loader_handle = SDL_LoadObject(path); |
| 66 | |
| 67 | if (!_this->vulkan_config.loader_handle) { |
| 68 | return false; |
| 69 | } |
| 70 | |
| 71 | SDL_strlcpy(_this->vulkan_config.loader_path, path, |
| 72 | SDL_arraysize(_this->vulkan_config.loader_path)); |
| 73 | |
| 74 | vkGetInstanceProcAddr = (PFN_vkGetInstanceProcAddr)SDL_LoadFunction( |
| 75 | _this->vulkan_config.loader_handle, "vkGetInstanceProcAddr" ); |
| 76 | |
| 77 | if (!vkGetInstanceProcAddr) { |
| 78 | goto fail; |
| 79 | } |
| 80 | |
| 81 | _this->vulkan_config.vkGetInstanceProcAddr = (void *)vkGetInstanceProcAddr; |
| 82 | _this->vulkan_config.vkEnumerateInstanceExtensionProperties = |
| 83 | (void *)((PFN_vkGetInstanceProcAddr)_this->vulkan_config.vkGetInstanceProcAddr)( |
| 84 | VK_NULL_HANDLE, "vkEnumerateInstanceExtensionProperties" ); |
| 85 | |
| 86 | if (!_this->vulkan_config.vkEnumerateInstanceExtensionProperties) { |
| 87 | goto fail; |
| 88 | } |
| 89 | |
| 90 | extensions = SDL_Vulkan_CreateInstanceExtensionsList( |
| 91 | (PFN_vkEnumerateInstanceExtensionProperties) |
| 92 | _this->vulkan_config.vkEnumerateInstanceExtensionProperties, |
| 93 | &extensionCount); |
| 94 | |
| 95 | if (!extensions) { |
| 96 | goto fail; |
| 97 | } |
| 98 | |
| 99 | for (i = 0; i < extensionCount; i++) { |
| 100 | if (SDL_strcmp(VK_KHR_SURFACE_EXTENSION_NAME, extensions[i].extensionName) == 0) { |
| 101 | hasSurfaceExtension = true; |
| 102 | } else if (SDL_strcmp(VK_KHR_DISPLAY_EXTENSION_NAME, extensions[i].extensionName) == 0) { |
| 103 | hasDisplayExtension = true; |
| 104 | } |
| 105 | } |
| 106 | |
| 107 | SDL_free(extensions); |
| 108 | |
| 109 | if (!hasSurfaceExtension) { |
| 110 | SDL_SetError("Installed Vulkan doesn't implement the " VK_KHR_SURFACE_EXTENSION_NAME " extension" ); |
| 111 | goto fail; |
| 112 | } else if (!hasDisplayExtension) { |
| 113 | SDL_SetError("Installed Vulkan doesn't implement the " VK_KHR_DISPLAY_EXTENSION_NAME "extension" ); |
| 114 | goto fail; |
| 115 | } |
| 116 | |
| 117 | return true; |
| 118 | |
| 119 | fail: |
| 120 | SDL_UnloadObject(_this->vulkan_config.loader_handle); |
| 121 | _this->vulkan_config.loader_handle = NULL; |
| 122 | return false; |
| 123 | } |
| 124 | |
| 125 | void KMSDRM_Vulkan_UnloadLibrary(SDL_VideoDevice *_this) |
| 126 | { |
| 127 | if (_this->vulkan_config.loader_handle) { |
| 128 | SDL_UnloadObject(_this->vulkan_config.loader_handle); |
| 129 | _this->vulkan_config.loader_handle = NULL; |
| 130 | } |
| 131 | } |
| 132 | |
| 133 | /*********************************************************************/ |
| 134 | // Here we can put whatever Vulkan extensions we want to be enabled |
| 135 | // at instance creation, which is done in the programs, not in SDL. |
| 136 | // So: programs call SDL_Vulkan_GetInstanceExtensions() and here |
| 137 | // we put the extensions specific to this backend so the programs |
| 138 | // get a list with the extension we want, so they can include that |
| 139 | // list in the ppEnabledExtensionNames and EnabledExtensionCount |
| 140 | // members of the VkInstanceCreateInfo struct passed to |
| 141 | // vkCreateInstance(). |
| 142 | /*********************************************************************/ |
| 143 | char const* const* KMSDRM_Vulkan_GetInstanceExtensions(SDL_VideoDevice *_this, |
| 144 | Uint32 *count) |
| 145 | { |
| 146 | static const char *const extensionsForKMSDRM[] = { |
| 147 | VK_KHR_SURFACE_EXTENSION_NAME, VK_KHR_DISPLAY_EXTENSION_NAME |
| 148 | }; |
| 149 | if (count) { |
| 150 | *count = SDL_arraysize(extensionsForKMSDRM); |
| 151 | } |
| 152 | return extensionsForKMSDRM; |
| 153 | } |
| 154 | |
| 155 | /***********************************************************************/ |
| 156 | // First thing to know is that we don't call vkCreateInstance() here. |
| 157 | // Instead, programs using SDL and Vulkan create their Vulkan instance |
| 158 | // and we get it here, ready to use. |
| 159 | // Extensions specific for this platform are activated in |
| 160 | // KMSDRM_Vulkan_GetInstanceExtensions(), like we do with |
| 161 | // VK_KHR_DISPLAY_EXTENSION_NAME, which is what we need for x-less VK. |
| 162 | /***********************************************************************/ |
| 163 | bool KMSDRM_Vulkan_CreateSurface(SDL_VideoDevice *_this, |
| 164 | SDL_Window *window, |
| 165 | VkInstance instance, |
| 166 | const struct VkAllocationCallbacks *allocator, |
| 167 | VkSurfaceKHR *surface) |
| 168 | { |
| 169 | VkPhysicalDevice gpu = NULL; |
| 170 | uint32_t gpu_count; |
| 171 | uint32_t display_count; |
| 172 | uint32_t mode_count; |
| 173 | uint32_t plane_count; |
| 174 | uint32_t plane = UINT32_MAX; |
| 175 | |
| 176 | VkPhysicalDevice *physical_devices = NULL; |
| 177 | VkPhysicalDeviceProperties *device_props = NULL; |
| 178 | VkDisplayPropertiesKHR *display_props = NULL; |
| 179 | VkDisplayModePropertiesKHR *mode_props = NULL; |
| 180 | VkDisplayPlanePropertiesKHR *plane_props = NULL; |
| 181 | VkDisplayPlaneCapabilitiesKHR plane_caps; |
| 182 | |
| 183 | VkDisplayModeCreateInfoKHR display_mode_create_info; |
| 184 | VkDisplaySurfaceCreateInfoKHR display_plane_surface_create_info; |
| 185 | |
| 186 | VkExtent2D image_size; |
| 187 | VkDisplayKHR display; |
| 188 | VkDisplayModeKHR display_mode = (VkDisplayModeKHR)0; |
| 189 | VkDisplayModePropertiesKHR display_mode_props = { 0 }; |
| 190 | VkDisplayModeParametersKHR new_mode_parameters = { { 0, 0 }, 0 }; |
| 191 | // Prefer a plane that supports per-pixel alpha. |
| 192 | VkDisplayPlaneAlphaFlagBitsKHR alpha_mode = VK_DISPLAY_PLANE_ALPHA_OPAQUE_BIT_KHR; |
| 193 | |
| 194 | VkResult result; |
| 195 | bool ret = false; |
| 196 | bool valid_gpu = false; |
| 197 | bool mode_found = false; |
| 198 | bool plane_supports_display = false; |
| 199 | |
| 200 | // Get the display index from the display being used by the window. |
| 201 | int display_index = SDL_GetDisplayIndex(SDL_GetDisplayForWindow(window)); |
| 202 | int i, j; |
| 203 | |
| 204 | // Get the function pointers for the functions we will use. |
| 205 | PFN_vkGetInstanceProcAddr vkGetInstanceProcAddr = |
| 206 | (PFN_vkGetInstanceProcAddr)_this->vulkan_config.vkGetInstanceProcAddr; |
| 207 | |
| 208 | PFN_vkCreateDisplayPlaneSurfaceKHR vkCreateDisplayPlaneSurfaceKHR = |
| 209 | (PFN_vkCreateDisplayPlaneSurfaceKHR)vkGetInstanceProcAddr( |
| 210 | instance, "vkCreateDisplayPlaneSurfaceKHR" ); |
| 211 | |
| 212 | PFN_vkEnumeratePhysicalDevices vkEnumeratePhysicalDevices = |
| 213 | (PFN_vkEnumeratePhysicalDevices)vkGetInstanceProcAddr( |
| 214 | instance, "vkEnumeratePhysicalDevices" ); |
| 215 | |
| 216 | PFN_vkGetPhysicalDeviceProperties vkGetPhysicalDeviceProperties = |
| 217 | (PFN_vkGetPhysicalDeviceProperties)vkGetInstanceProcAddr( |
| 218 | instance, "vkGetPhysicalDeviceProperties" ); |
| 219 | |
| 220 | PFN_vkGetPhysicalDeviceDisplayPropertiesKHR vkGetPhysicalDeviceDisplayPropertiesKHR = |
| 221 | (PFN_vkGetPhysicalDeviceDisplayPropertiesKHR)vkGetInstanceProcAddr( |
| 222 | instance, "vkGetPhysicalDeviceDisplayPropertiesKHR" ); |
| 223 | |
| 224 | PFN_vkGetDisplayModePropertiesKHR vkGetDisplayModePropertiesKHR = |
| 225 | (PFN_vkGetDisplayModePropertiesKHR)vkGetInstanceProcAddr( |
| 226 | instance, "vkGetDisplayModePropertiesKHR" ); |
| 227 | |
| 228 | PFN_vkGetPhysicalDeviceDisplayPlanePropertiesKHR vkGetPhysicalDeviceDisplayPlanePropertiesKHR = |
| 229 | (PFN_vkGetPhysicalDeviceDisplayPlanePropertiesKHR)vkGetInstanceProcAddr( |
| 230 | instance, "vkGetPhysicalDeviceDisplayPlanePropertiesKHR" ); |
| 231 | |
| 232 | PFN_vkGetDisplayPlaneSupportedDisplaysKHR vkGetDisplayPlaneSupportedDisplaysKHR = |
| 233 | (PFN_vkGetDisplayPlaneSupportedDisplaysKHR)vkGetInstanceProcAddr( |
| 234 | instance, "vkGetDisplayPlaneSupportedDisplaysKHR" ); |
| 235 | |
| 236 | PFN_vkGetDisplayPlaneCapabilitiesKHR vkGetDisplayPlaneCapabilitiesKHR = |
| 237 | (PFN_vkGetDisplayPlaneCapabilitiesKHR)vkGetInstanceProcAddr( |
| 238 | instance, "vkGetDisplayPlaneCapabilitiesKHR" ); |
| 239 | |
| 240 | PFN_vkCreateDisplayModeKHR vkCreateDisplayModeKHR = |
| 241 | (PFN_vkCreateDisplayModeKHR)vkGetInstanceProcAddr( |
| 242 | instance, "vkCreateDisplayModeKHR" ); |
| 243 | |
| 244 | if (!_this->vulkan_config.loader_handle) { |
| 245 | SDL_SetError("Vulkan is not loaded" ); |
| 246 | goto clean; |
| 247 | } |
| 248 | |
| 249 | /*************************************/ |
| 250 | // Block for vulkan surface creation |
| 251 | /*************************************/ |
| 252 | |
| 253 | /****************************************************************/ |
| 254 | // If we got vkCreateDisplayPlaneSurfaceKHR() pointer, it means |
| 255 | // that the VK_KHR_Display extension is active on the instance. |
| 256 | // That's the central extension we need for x-less VK! |
| 257 | /****************************************************************/ |
| 258 | if (!vkCreateDisplayPlaneSurfaceKHR) { |
| 259 | SDL_SetError(VK_KHR_DISPLAY_EXTENSION_NAME |
| 260 | " extension is not enabled in the Vulkan instance." ); |
| 261 | goto clean; |
| 262 | } |
| 263 | |
| 264 | /* A GPU (or physical_device, in vkcube terms) is a physical GPU. |
| 265 | A machine with more than one video output doesn't need to have more than one GPU, |
| 266 | like the Pi4 which has 1 GPU and 2 video outputs. |
| 267 | Just in case, we test that the GPU we choose is Vulkan-capable. |
| 268 | If there are new reports about VK init failures, hardcode |
| 269 | gpu = physical_devices[0], instead of probing, and go with that. |
| 270 | */ |
| 271 | |
| 272 | // Get the physical device count. |
| 273 | vkEnumeratePhysicalDevices(instance, &gpu_count, NULL); |
| 274 | |
| 275 | if (gpu_count == 0) { |
| 276 | SDL_SetError("Vulkan can't find physical devices (gpus)." ); |
| 277 | goto clean; |
| 278 | } |
| 279 | |
| 280 | // Get the physical devices. |
| 281 | physical_devices = SDL_malloc(sizeof(VkPhysicalDevice) * gpu_count); |
| 282 | device_props = SDL_malloc(sizeof(VkPhysicalDeviceProperties)); |
| 283 | vkEnumeratePhysicalDevices(instance, &gpu_count, physical_devices); |
| 284 | |
| 285 | // Iterate on the physical devices. |
| 286 | for (i = 0; i < gpu_count; i++) { |
| 287 | |
| 288 | // Get the physical device properties. |
| 289 | vkGetPhysicalDeviceProperties( |
| 290 | physical_devices[i], |
| 291 | device_props); |
| 292 | |
| 293 | // Is this device a real GPU that supports API version 1 at least? |
| 294 | if (device_props->apiVersion >= 1 && |
| 295 | (device_props->deviceType == 1 || device_props->deviceType == 2)) { |
| 296 | gpu = physical_devices[i]; |
| 297 | valid_gpu = true; |
| 298 | break; |
| 299 | } |
| 300 | } |
| 301 | |
| 302 | if (!valid_gpu) { |
| 303 | SDL_SetError("Vulkan can't find a valid physical device (gpu)." ); |
| 304 | goto clean; |
| 305 | } |
| 306 | |
| 307 | /* A display is a video output. 1 GPU can have N displays. |
| 308 | Vulkan only counts the connected displays. |
| 309 | Get the display count of the GPU. */ |
| 310 | vkGetPhysicalDeviceDisplayPropertiesKHR(gpu, &display_count, NULL); |
| 311 | if (display_count == 0) { |
| 312 | SDL_SetError("Vulkan can't find any displays." ); |
| 313 | goto clean; |
| 314 | } |
| 315 | |
| 316 | // Get the props of the displays of the physical device. |
| 317 | display_props = (VkDisplayPropertiesKHR *)SDL_malloc(display_count * sizeof(*display_props)); |
| 318 | vkGetPhysicalDeviceDisplayPropertiesKHR(gpu, |
| 319 | &display_count, |
| 320 | display_props); |
| 321 | |
| 322 | // Get the chosen display based on the display index. |
| 323 | display = display_props[display_index].display; |
| 324 | |
| 325 | // Get the list of the display videomodes. |
| 326 | vkGetDisplayModePropertiesKHR(gpu, |
| 327 | display, |
| 328 | &mode_count, NULL); |
| 329 | |
| 330 | if (mode_count == 0) { |
| 331 | SDL_SetError("Vulkan can't find any video modes for display %i (%s)" , 0, |
| 332 | display_props[display_index].displayName); |
| 333 | goto clean; |
| 334 | } |
| 335 | |
| 336 | mode_props = (VkDisplayModePropertiesKHR *)SDL_malloc(mode_count * sizeof(*mode_props)); |
| 337 | vkGetDisplayModePropertiesKHR(gpu, |
| 338 | display, |
| 339 | &mode_count, mode_props); |
| 340 | |
| 341 | /* Get a video mode equal to the window size among the predefined ones, |
| 342 | if possible. |
| 343 | REMEMBER: We have to get a small enough videomode for the window size, |
| 344 | because videomode determines how big the scanout region is and we can't |
| 345 | scanout a region bigger than the window (we would be reading past the |
| 346 | buffer, and Vulkan would give us a confusing VK_ERROR_SURFACE_LOST_KHR). */ |
| 347 | for (i = 0; i < mode_count; i++) { |
| 348 | if (mode_props[i].parameters.visibleRegion.width == window->w && |
| 349 | mode_props[i].parameters.visibleRegion.height == window->h) { |
| 350 | display_mode_props = mode_props[i]; |
| 351 | mode_found = true; |
| 352 | break; |
| 353 | } |
| 354 | } |
| 355 | |
| 356 | if (mode_found && |
| 357 | display_mode_props.parameters.visibleRegion.width > 0 && |
| 358 | display_mode_props.parameters.visibleRegion.height > 0) { |
| 359 | // Found a suitable mode among the predefined ones. Use that. |
| 360 | display_mode = display_mode_props.displayMode; |
| 361 | } else { |
| 362 | |
| 363 | /* Couldn't find a suitable mode among the predefined ones, so try to create our own. |
| 364 | This won't work for some video chips atm (like Pi's VideoCore) so these are limited |
| 365 | to supported resolutions. Don't try to use "closest" resolutions either, because |
| 366 | those are often bigger than the window size, thus causing out-of-bunds scanout. */ |
| 367 | new_mode_parameters.visibleRegion.width = window->w; |
| 368 | new_mode_parameters.visibleRegion.height = window->h; |
| 369 | /* SDL (and DRM, if we look at drmModeModeInfo vrefresh) uses plain integer Hz for |
| 370 | display mode refresh rate, but Vulkan expects higher precision. */ |
| 371 | new_mode_parameters.refreshRate = (uint32_t)(window->current_fullscreen_mode.refresh_rate * 1000); |
| 372 | |
| 373 | SDL_zero(display_mode_create_info); |
| 374 | display_mode_create_info.sType = VK_STRUCTURE_TYPE_DISPLAY_MODE_CREATE_INFO_KHR; |
| 375 | display_mode_create_info.parameters = new_mode_parameters; |
| 376 | result = vkCreateDisplayModeKHR(gpu, |
| 377 | display, |
| 378 | &display_mode_create_info, |
| 379 | NULL, &display_mode); |
| 380 | if (result != VK_SUCCESS) { |
| 381 | SDL_SetError("Vulkan couldn't find a predefined mode for that window size and couldn't create a suitable mode." ); |
| 382 | goto clean; |
| 383 | } |
| 384 | } |
| 385 | |
| 386 | // Just in case we get here without a display_mode. |
| 387 | if (!display_mode) { |
| 388 | SDL_SetError("Vulkan couldn't get a display mode." ); |
| 389 | goto clean; |
| 390 | } |
| 391 | |
| 392 | // Get the list of the physical device planes. |
| 393 | vkGetPhysicalDeviceDisplayPlanePropertiesKHR(gpu, &plane_count, NULL); |
| 394 | if (plane_count == 0) { |
| 395 | SDL_SetError("Vulkan can't find any planes." ); |
| 396 | goto clean; |
| 397 | } |
| 398 | plane_props = SDL_malloc(sizeof(VkDisplayPlanePropertiesKHR) * plane_count); |
| 399 | vkGetPhysicalDeviceDisplayPlanePropertiesKHR(gpu, &plane_count, plane_props); |
| 400 | |
| 401 | /* Iterate on the list of planes of the physical device |
| 402 | to find a plane that matches these criteria: |
| 403 | -It must be compatible with the chosen display + mode. |
| 404 | -It isn't currently bound to another display. |
| 405 | -It supports per-pixel alpha, if possible. */ |
| 406 | for (i = 0; i < plane_count; i++) { |
| 407 | |
| 408 | uint32_t supported_displays_count = 0; |
| 409 | VkDisplayKHR *supported_displays; |
| 410 | |
| 411 | // See if the plane is compatible with the current display. |
| 412 | vkGetDisplayPlaneSupportedDisplaysKHR(gpu, i, &supported_displays_count, NULL); |
| 413 | if (supported_displays_count == 0) { |
| 414 | // This plane doesn't support any displays. Continue to the next plane. |
| 415 | continue; |
| 416 | } |
| 417 | |
| 418 | // Get the list of displays supported by this plane. |
| 419 | supported_displays = (VkDisplayKHR *)SDL_malloc(sizeof(VkDisplayKHR) * supported_displays_count); |
| 420 | vkGetDisplayPlaneSupportedDisplaysKHR(gpu, i, |
| 421 | &supported_displays_count, supported_displays); |
| 422 | |
| 423 | /* The plane must be bound to the chosen display, or not in use. |
| 424 | If none of these is true, iterate to another plane. */ |
| 425 | if (!((plane_props[i].currentDisplay == display) || (plane_props[i].currentDisplay == VK_NULL_HANDLE))) { |
| 426 | continue; |
| 427 | } |
| 428 | |
| 429 | /* Iterate the list of displays supported by this plane |
| 430 | in order to find out if the chosen display is among them. */ |
| 431 | plane_supports_display = false; |
| 432 | for (j = 0; j < supported_displays_count; j++) { |
| 433 | if (supported_displays[j] == display) { |
| 434 | plane_supports_display = true; |
| 435 | break; |
| 436 | } |
| 437 | } |
| 438 | |
| 439 | // Free the list of displays supported by this plane. |
| 440 | if (supported_displays) { |
| 441 | SDL_free(supported_displays); |
| 442 | } |
| 443 | |
| 444 | // If the display is not supported by this plane, iterate to the next plane. |
| 445 | if (!plane_supports_display) { |
| 446 | continue; |
| 447 | } |
| 448 | |
| 449 | // Want a plane that supports the alpha mode we have chosen. |
| 450 | vkGetDisplayPlaneCapabilitiesKHR(gpu, display_mode, i, &plane_caps); |
| 451 | if (plane_caps.supportedAlpha == alpha_mode) { |
| 452 | // Yep, this plane is alright. |
| 453 | plane = i; |
| 454 | break; |
| 455 | } |
| 456 | } |
| 457 | |
| 458 | // If we couldn't find an appropriate plane, error out. |
| 459 | if (plane == UINT32_MAX) { |
| 460 | SDL_SetError("Vulkan couldn't find an appropriate plane." ); |
| 461 | goto clean; |
| 462 | } |
| 463 | |
| 464 | /********************************************/ |
| 465 | // Let's finally create the Vulkan surface! |
| 466 | /********************************************/ |
| 467 | |
| 468 | image_size.width = window->w; |
| 469 | image_size.height = window->h; |
| 470 | |
| 471 | SDL_zero(display_plane_surface_create_info); |
| 472 | display_plane_surface_create_info.sType = VK_STRUCTURE_TYPE_DISPLAY_SURFACE_CREATE_INFO_KHR; |
| 473 | display_plane_surface_create_info.displayMode = display_mode; |
| 474 | display_plane_surface_create_info.planeIndex = plane; |
| 475 | display_plane_surface_create_info.imageExtent = image_size; |
| 476 | display_plane_surface_create_info.transform = VK_SURFACE_TRANSFORM_IDENTITY_BIT_KHR; |
| 477 | display_plane_surface_create_info.alphaMode = alpha_mode; |
| 478 | result = vkCreateDisplayPlaneSurfaceKHR(instance, |
| 479 | &display_plane_surface_create_info, |
| 480 | allocator, |
| 481 | surface); |
| 482 | if (result != VK_SUCCESS) { |
| 483 | SDL_SetError("vkCreateDisplayPlaneSurfaceKHR failed: %s" , |
| 484 | SDL_Vulkan_GetResultString(result)); |
| 485 | goto clean; |
| 486 | } |
| 487 | |
| 488 | ret = true; // success! |
| 489 | |
| 490 | clean: |
| 491 | if (physical_devices) { |
| 492 | SDL_free(physical_devices); |
| 493 | } |
| 494 | if (display_props) { |
| 495 | SDL_free(display_props); |
| 496 | } |
| 497 | if (device_props) { |
| 498 | SDL_free(device_props); |
| 499 | } |
| 500 | if (plane_props) { |
| 501 | SDL_free(plane_props); |
| 502 | } |
| 503 | if (mode_props) { |
| 504 | SDL_free(mode_props); |
| 505 | } |
| 506 | |
| 507 | return ret; |
| 508 | } |
| 509 | |
| 510 | void KMSDRM_Vulkan_DestroySurface(SDL_VideoDevice *_this, |
| 511 | VkInstance instance, |
| 512 | VkSurfaceKHR surface, |
| 513 | const struct VkAllocationCallbacks *allocator) |
| 514 | { |
| 515 | if (_this->vulkan_config.loader_handle) { |
| 516 | SDL_Vulkan_DestroySurface_Internal(_this->vulkan_config.vkGetInstanceProcAddr, instance, surface, allocator); |
| 517 | } |
| 518 | } |
| 519 | |
| 520 | #endif |
| 521 | |