sdl

FORK: Simple Directmedia Layer
git clone https://git.neptards.moe/neptards/sdl.git
Log | Files | Refs

SDL_kmsdrmvulkan.c (15488B)


      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 
     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 SDL_VIDEO_VULKAN && SDL_VIDEO_DRIVER_KMSDRM
     30 
     31 #include "SDL_kmsdrmvideo.h"
     32 #include "SDL_kmsdrmdyn.h"
     33 #include "SDL_assert.h"
     34 
     35 #include "SDL_loadso.h"
     36 #include "SDL_kmsdrmvulkan.h"
     37 #include "SDL_syswm.h"
     38 #include "sys/ioctl.h"
     39 
     40 #if defined(__OpenBSD__)
     41 #define DEFAULT_VULKAN  "libvulkan.so"
     42 #else
     43 #define DEFAULT_VULKAN  "libvulkan.so.1"
     44 #endif
     45 
     46 int KMSDRM_Vulkan_LoadLibrary(_THIS, const char *path)
     47 {
     48     VkExtensionProperties *extensions = NULL;
     49     Uint32 i, extensionCount = 0;
     50     SDL_bool hasSurfaceExtension = SDL_FALSE;
     51     SDL_bool hasDisplayExtension = SDL_FALSE;
     52     PFN_vkGetInstanceProcAddr vkGetInstanceProcAddr = NULL;
     53 
     54     if(_this->vulkan_config.loader_handle)
     55         return SDL_SetError("Vulkan already loaded");
     56 
     57     /* Load the Vulkan library */
     58     if(!path)
     59         path = SDL_getenv("SDL_VULKAN_LIBRARY");
     60     if(!path)
     61         path = DEFAULT_VULKAN;
     62 
     63     _this->vulkan_config.loader_handle = SDL_LoadObject(path);
     64 
     65     if(!_this->vulkan_config.loader_handle)
     66         return -1;
     67 
     68     SDL_strlcpy(_this->vulkan_config.loader_path, path,
     69                 SDL_arraysize(_this->vulkan_config.loader_path));
     70 
     71     vkGetInstanceProcAddr = (PFN_vkGetInstanceProcAddr)SDL_LoadFunction(
     72         _this->vulkan_config.loader_handle, "vkGetInstanceProcAddr");
     73 
     74     if(!vkGetInstanceProcAddr)
     75         goto fail;
     76 
     77     _this->vulkan_config.vkGetInstanceProcAddr = (void *)vkGetInstanceProcAddr;
     78     _this->vulkan_config.vkEnumerateInstanceExtensionProperties =
     79         (void *)((PFN_vkGetInstanceProcAddr)_this->vulkan_config.vkGetInstanceProcAddr)(
     80             VK_NULL_HANDLE, "vkEnumerateInstanceExtensionProperties");
     81 
     82     if(!_this->vulkan_config.vkEnumerateInstanceExtensionProperties)
     83         goto fail;
     84 
     85     extensions = SDL_Vulkan_CreateInstanceExtensionsList(
     86         (PFN_vkEnumerateInstanceExtensionProperties)
     87             _this->vulkan_config.vkEnumerateInstanceExtensionProperties,
     88         &extensionCount);
     89 
     90     if(!extensions)
     91         goto fail;
     92 
     93     for(i = 0; i < extensionCount; i++)
     94     {
     95         if(SDL_strcmp(VK_KHR_SURFACE_EXTENSION_NAME, extensions[i].extensionName) == 0)
     96             hasSurfaceExtension = SDL_TRUE;
     97         else if(SDL_strcmp(VK_KHR_DISPLAY_EXTENSION_NAME, extensions[i].extensionName) == 0)
     98             hasDisplayExtension = SDL_TRUE;
     99     }
    100 
    101     SDL_free(extensions);
    102 
    103     if(!hasSurfaceExtension)
    104     {
    105         SDL_SetError("Installed Vulkan doesn't implement the "
    106                      VK_KHR_SURFACE_EXTENSION_NAME " extension");
    107         goto fail;
    108     }
    109     else if(!hasDisplayExtension)
    110     {
    111         SDL_SetError("Installed Vulkan doesn't implement the "
    112                      VK_KHR_DISPLAY_EXTENSION_NAME "extension");
    113         goto fail;
    114     }
    115 
    116     return 0;
    117 
    118 fail:
    119     SDL_UnloadObject(_this->vulkan_config.loader_handle);
    120     _this->vulkan_config.loader_handle = NULL;
    121     return -1;
    122 }
    123 
    124 void KMSDRM_Vulkan_UnloadLibrary(_THIS)
    125 {
    126     if(_this->vulkan_config.loader_handle)
    127     {
    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 SDL_bool KMSDRM_Vulkan_GetInstanceExtensions(_THIS,
    144                                           SDL_Window *window,
    145                                           unsigned *count,
    146                                           const char **names)
    147 {
    148     static const char *const extensionsForKMSDRM[] = {
    149         VK_KHR_SURFACE_EXTENSION_NAME, VK_KHR_DISPLAY_EXTENSION_NAME
    150     };
    151     if(!_this->vulkan_config.loader_handle)
    152     {
    153         SDL_SetError("Vulkan is not loaded");
    154         return SDL_FALSE;
    155     }
    156     return SDL_Vulkan_GetInstanceExtensions_Helper(
    157             count, names, SDL_arraysize(extensionsForKMSDRM),
    158             extensionsForKMSDRM);
    159 }
    160 
    161 void KMSDRM_Vulkan_GetDrawableSize(_THIS, SDL_Window *window, int *w, int *h)
    162 {
    163     if (w) {
    164         *w = window->w;
    165     }
    166 
    167     if (h) {
    168         *h = window->h;
    169     }
    170 }
    171 
    172 /***********************************************************************/
    173 /* First thing to know is that we don't call vkCreateInstance() here.  */
    174 /* Instead, programs using SDL and Vulkan create their Vulkan instance */
    175 /* and we get it here, ready to use.                                   */
    176 /* Extensions specific for this platform are activated in              */
    177 /* KMSDRM_Vulkan_GetInstanceExtensions(), like we do with              */
    178 /* VK_KHR_DISPLAY_EXTENSION_NAME, which is what we need for x-less VK. */                
    179 /***********************************************************************/
    180 SDL_bool KMSDRM_Vulkan_CreateSurface(_THIS,
    181                                   SDL_Window *window,
    182                                   VkInstance instance,
    183                                   VkSurfaceKHR *surface)
    184 {
    185     VkPhysicalDevice gpu;
    186     uint32_t gpu_count;
    187     uint32_t display_count;
    188     uint32_t mode_count;
    189     uint32_t plane_count;
    190 
    191     VkPhysicalDevice *physical_devices = NULL;
    192     VkDisplayPropertiesKHR *displays_props = NULL;
    193     VkDisplayModePropertiesKHR *modes_props = NULL;
    194     VkDisplayPlanePropertiesKHR *planes_props = NULL;
    195 
    196     VkDisplayModeCreateInfoKHR display_mode_create_info;
    197     VkDisplaySurfaceCreateInfoKHR display_plane_surface_create_info;
    198 
    199     VkExtent2D image_size;
    200     VkDisplayModeKHR display_mode;
    201     VkDisplayModePropertiesKHR display_mode_props = {0};
    202 
    203     VkResult result;
    204     SDL_bool ret = SDL_FALSE;
    205 
    206     /* We don't receive a display index in KMSDRM_CreateDevice(), only
    207        a device index, which determines the GPU to use, but not the output.
    208        So we simply use the first connected output (ie, the first connected
    209        video output) for now.
    210        In other words, change this index to select a different output. Easy! */
    211     int display_index = 0;
    212 
    213     int i;
    214 
    215     SDL_VideoData *viddata = ((SDL_VideoData *)_this->driverdata);
    216 
    217     /* Get the function pointers for the functions we will use. */
    218     PFN_vkGetInstanceProcAddr vkGetInstanceProcAddr =
    219         (PFN_vkGetInstanceProcAddr)_this->vulkan_config.vkGetInstanceProcAddr;
    220 
    221     PFN_vkCreateDisplayPlaneSurfaceKHR vkCreateDisplayPlaneSurfaceKHR =
    222         (PFN_vkCreateDisplayPlaneSurfaceKHR)vkGetInstanceProcAddr(
    223             instance, "vkCreateDisplayPlaneSurfaceKHR");
    224 
    225     PFN_vkEnumeratePhysicalDevices vkEnumeratePhysicalDevices =
    226         (PFN_vkEnumeratePhysicalDevices)vkGetInstanceProcAddr(
    227             instance, "vkEnumeratePhysicalDevices");
    228 
    229     PFN_vkGetPhysicalDeviceDisplayPropertiesKHR vkGetPhysicalDeviceDisplayPropertiesKHR =
    230         (PFN_vkGetPhysicalDeviceDisplayPropertiesKHR)vkGetInstanceProcAddr(
    231             instance, "vkGetPhysicalDeviceDisplayPropertiesKHR");
    232 
    233     PFN_vkGetDisplayModePropertiesKHR vkGetDisplayModePropertiesKHR =
    234         (PFN_vkGetDisplayModePropertiesKHR)vkGetInstanceProcAddr(
    235             instance, "vkGetDisplayModePropertiesKHR");
    236 
    237     PFN_vkGetPhysicalDeviceDisplayPlanePropertiesKHR vkGetPhysicalDeviceDisplayPlanePropertiesKHR =
    238         (PFN_vkGetPhysicalDeviceDisplayPlanePropertiesKHR)vkGetInstanceProcAddr(
    239             instance, "vkGetPhysicalDeviceDisplayPlanePropertiesKHR");
    240 
    241     /*PFN_vkGetDisplayPlaneSupportedDisplaysKHR vkGetDisplayPlaneSupportedDisplaysKHR =
    242         (PFN_vkGetDisplayPlaneSupportedDisplaysKHR)vkGetInstanceProcAddr(
    243             instance, "vkGetDisplayPlaneSupportedDisplaysKHR");
    244     
    245     PFN_vkGetDisplayPlaneCapabilitiesKHR vkGetDisplayPlaneCapabilitiesKHR =
    246         (PFN_vkGetDisplayPlaneCapabilitiesKHR)vkGetInstanceProcAddr(
    247             instance, "vkGetDisplayPlaneCapabilitiesKHR");
    248     */
    249 
    250     PFN_vkCreateDisplayModeKHR vkCreateDisplayModeKHR =
    251         (PFN_vkCreateDisplayModeKHR)vkGetInstanceProcAddr(
    252             instance, "vkCreateDisplayModeKHR");
    253 
    254     if(!_this->vulkan_config.loader_handle)
    255     {
    256         SDL_SetError("Vulkan is not loaded");
    257         goto clean;
    258     }
    259 
    260     /*************************************/
    261     /* Block for vulkan surface creation */
    262     /*************************************/
    263 
    264     /****************************************************************/
    265     /* If we got vkCreateDisplayPlaneSurfaceKHR() pointer, it means */
    266     /* that the VK_KHR_Display extension is active on the instance. */
    267     /* That's the central extension we need for x-less VK!          */
    268     /****************************************************************/
    269     if(!vkCreateDisplayPlaneSurfaceKHR)
    270     {
    271         SDL_SetError(VK_KHR_DISPLAY_EXTENSION_NAME
    272                      " extension is not enabled in the Vulkan instance.");
    273         goto clean;
    274     }
    275 
    276     /* Get the physical device count. */
    277     vkEnumeratePhysicalDevices(instance, &gpu_count, NULL);
    278 
    279     if (gpu_count == 0) {
    280         SDL_SetError("Vulkan can't find physical devices (gpus).");
    281         goto clean;
    282     }
    283 
    284     /* Get the physical devices. */
    285     physical_devices = malloc(sizeof(VkPhysicalDevice) * gpu_count);
    286     vkEnumeratePhysicalDevices(instance, &gpu_count, physical_devices);
    287 
    288     /* A GPU (or physical_device, in vkcube terms) is a GPU. A machine with more
    289        than one video output doen't need to have more than one GPU, like the Pi4
    290        which has 1 GPU and 2 video outputs.
    291        We grab the GPU/physical_device with the index we got in KMSDR_CreateDevice(). */
    292     gpu = physical_devices[viddata->devindex]; 
    293 
    294     /* A display is a video output. 1 GPU can have N displays.
    295        Vulkan only counts the connected displays.
    296        Get the display count of the GPU. */
    297     vkGetPhysicalDeviceDisplayPropertiesKHR(gpu, &display_count, NULL);
    298     if (display_count == 0) {
    299         SDL_SetError("Vulkan can't find any displays.");
    300         goto clean;
    301     }
    302 
    303     /* Get the props of the displays of the physical device. */
    304     displays_props = (VkDisplayPropertiesKHR *) malloc(display_count * sizeof(*displays_props));
    305     vkGetPhysicalDeviceDisplayPropertiesKHR(gpu,
    306                                            &display_count,
    307                                            displays_props);
    308 
    309     /* Get the videomode count for the first display. */
    310     vkGetDisplayModePropertiesKHR(gpu,
    311                                  displays_props[display_index].display,
    312                                  &mode_count, NULL);
    313 
    314     if (mode_count == 0) {
    315         SDL_SetError("Vulkan can't find any video modes for display %i (%s)\n", 0,
    316                                displays_props[display_index].displayName);
    317         goto clean;
    318     }
    319 
    320     /* Get the props of the videomodes for the first display. */
    321     modes_props = (VkDisplayModePropertiesKHR *) malloc(mode_count * sizeof(*modes_props));
    322     vkGetDisplayModePropertiesKHR(gpu,
    323                                  displays_props[display_index].display,
    324                                  &mode_count, modes_props);
    325 
    326     /* Get the planes count of the physical device. */
    327     vkGetPhysicalDeviceDisplayPlanePropertiesKHR(gpu, &plane_count, NULL);
    328     if (plane_count == 0) {
    329         SDL_SetError("Vulkan can't find any planes.");
    330         goto clean;
    331     }
    332 
    333     /* Get the props of the planes for the physical device. */
    334     planes_props = malloc(sizeof(VkDisplayPlanePropertiesKHR) * plane_count);
    335     vkGetPhysicalDeviceDisplayPlanePropertiesKHR(gpu, &plane_count, planes_props);
    336 
    337     /* Get a video mode equal or smaller than the window size. REMEMBER:
    338        We have to get a small enough videomode for the window size,
    339        because videomode determines how big the scanout region is and we can't
    340        scanout a region bigger than the window (we would be reading past the
    341        buffer, and Vulkan would give us a confusing VK_ERROR_SURFACE_LOST_KHR). */
    342     for (i = 0; i < mode_count; i++) {
    343         if (modes_props[i].parameters.visibleRegion.width <= window->w &&
    344             modes_props[i].parameters.visibleRegion.height <= window->h)
    345         {
    346             display_mode_props = modes_props[i];
    347             break;
    348         }
    349     }
    350 
    351     if (display_mode_props.parameters.visibleRegion.width == 0
    352         || display_mode_props.parameters.visibleRegion.height == 0)
    353     {
    354         SDL_SetError("Vulkan can't find a proper display mode for the window size.");
    355         goto clean;
    356     }
    357 
    358     /* We have the props of the display mode, but we need an actual display mode. */
    359     display_mode_create_info.sType = VK_STRUCTURE_TYPE_DISPLAY_MODE_CREATE_INFO_KHR;
    360     display_mode_create_info.parameters = display_mode_props.parameters;
    361     result = vkCreateDisplayModeKHR(gpu,
    362 			      displays_props[display_index].display,
    363 			      &display_mode_create_info,
    364 			      NULL, &display_mode);
    365     if (result != VK_SUCCESS) {
    366         SDL_SetError("Vulkan can't create the display mode.");
    367         goto clean;
    368     }
    369 
    370     /* Let's finally create the Vulkan surface! */
    371 
    372     image_size.width = window->w;
    373     image_size.height = window->h;
    374     
    375     display_plane_surface_create_info.sType = VK_STRUCTURE_TYPE_DISPLAY_SURFACE_CREATE_INFO_KHR;
    376     display_plane_surface_create_info.displayMode = display_mode;
    377     /* For now, simply use the first plane. */
    378     display_plane_surface_create_info.planeIndex = 0;
    379     display_plane_surface_create_info.imageExtent = image_size;
    380     result = vkCreateDisplayPlaneSurfaceKHR(instance,
    381                                      &display_plane_surface_create_info,
    382                                      NULL,
    383                                      surface);
    384     if(result != VK_SUCCESS)
    385     {
    386         SDL_SetError("vkCreateKMSDRMSurfaceKHR failed: %s",
    387             SDL_Vulkan_GetResultString(result));
    388         goto clean;
    389     }
    390 
    391     ret = SDL_TRUE;
    392 
    393 clean:
    394     if (physical_devices)
    395         free (physical_devices);
    396     if (displays_props)
    397         free (displays_props);
    398     if (planes_props)
    399         free (planes_props);
    400     if (modes_props)
    401         free (modes_props);
    402 
    403     return ret;
    404 }
    405 
    406 #endif
    407 
    408 /* vim: set ts=4 sw=4 expandtab: */