Browse Source

[KMS/DRM] Add the missing files for Vulkan support to the KMSDRM_LEGACY backend, had forgotted to do -hg add-.

Manuel Alfayate Corchete 4 years ago
parent
commit
335d78ff86

+ 422 - 0
src/video/kmsdrm_legacy/SDL_kmsdrm_legacy_vulkan.c

@@ -0,0 +1,422 @@
+/*
+  Simple DirectMedia Layer
+  Copyright (C) 1997-2021 Sam Lantinga <slouken@libsdl.org>
+
+  This software is provided 'as-is', without any express or implied
+  warranty.  In no event will the authors be held liable for any damages
+  arising from the use of this software.
+
+  Permission is granted to anyone to use this software for any purpose,
+  including commercial applications, and to alter it and redistribute it
+  freely, subject to the following restrictions:
+
+  1. The origin of this software must not be misrepresented; you must not
+     claim that you wrote the original software. If you use this software
+     in a product, an acknowledgment in the product documentation would be
+     appreciated but is not required.
+  2. Altered source versions must be plainly marked as such, and must not be
+     misrepresented as being the original software.
+  3. This notice may not be removed or altered from any source distribution.
+*/
+
+/*
+ * @author Manuel Alfayate Corchere <redwindwanderer@gmail.com>.
+ * Based on Jacob Lifshay's SDL_x11vulkan.c.
+ */
+
+#include "../../SDL_internal.h"
+
+#if SDL_VIDEO_VULKAN && SDL_VIDEO_DRIVER_KMSDRM
+
+#include "SDL_kmsdrm_legacy_video.h"
+#include "SDL_kmsdrm_legacy_dyn.h"
+#include "SDL_assert.h"
+
+#include "SDL_loadso.h"
+#include "SDL_kmsdrm_legacy_vulkan.h"
+#include "SDL_syswm.h"
+#include "sys/ioctl.h"
+
+#if defined(__OpenBSD__)
+#define DEFAULT_VULKAN  "libvulkan.so"
+#else
+#define DEFAULT_VULKAN  "libvulkan.so.1"
+#endif
+
+int KMSDRM_LEGACY_Vulkan_LoadLibrary(_THIS, const char *path)
+{
+    VkExtensionProperties *extensions = NULL;
+    Uint32 i, extensionCount = 0;
+    SDL_bool hasSurfaceExtension = SDL_FALSE;
+    SDL_bool hasDisplayExtension = SDL_FALSE;
+    PFN_vkGetInstanceProcAddr vkGetInstanceProcAddr = NULL;
+
+    if(_this->vulkan_config.loader_handle)
+        return SDL_SetError("Vulkan already loaded");
+
+    /* Load the Vulkan library */
+    if(!path)
+        path = SDL_getenv("SDL_VULKAN_LIBRARY");
+    if(!path)
+        path = DEFAULT_VULKAN;
+
+    _this->vulkan_config.loader_handle = SDL_LoadObject(path);
+
+    if(!_this->vulkan_config.loader_handle)
+        return -1;
+
+    SDL_strlcpy(_this->vulkan_config.loader_path, path,
+                SDL_arraysize(_this->vulkan_config.loader_path));
+
+    vkGetInstanceProcAddr = (PFN_vkGetInstanceProcAddr)SDL_LoadFunction(
+        _this->vulkan_config.loader_handle, "vkGetInstanceProcAddr");
+
+    if(!vkGetInstanceProcAddr)
+        goto fail;
+
+    _this->vulkan_config.vkGetInstanceProcAddr = (void *)vkGetInstanceProcAddr;
+    _this->vulkan_config.vkEnumerateInstanceExtensionProperties =
+        (void *)((PFN_vkGetInstanceProcAddr)_this->vulkan_config.vkGetInstanceProcAddr)(
+            VK_NULL_HANDLE, "vkEnumerateInstanceExtensionProperties");
+
+    if(!_this->vulkan_config.vkEnumerateInstanceExtensionProperties)
+        goto fail;
+
+    extensions = SDL_Vulkan_CreateInstanceExtensionsList(
+        (PFN_vkEnumerateInstanceExtensionProperties)
+            _this->vulkan_config.vkEnumerateInstanceExtensionProperties,
+        &extensionCount);
+
+    if(!extensions)
+        goto fail;
+
+    for(i = 0; i < extensionCount; i++)
+    {
+        if(SDL_strcmp(VK_KHR_SURFACE_EXTENSION_NAME, extensions[i].extensionName) == 0)
+            hasSurfaceExtension = SDL_TRUE;
+        else if(SDL_strcmp(VK_KHR_DISPLAY_EXTENSION_NAME, extensions[i].extensionName) == 0)
+            hasDisplayExtension = SDL_TRUE;
+    }
+
+    SDL_free(extensions);
+
+    if(!hasSurfaceExtension)
+    {
+        SDL_SetError("Installed Vulkan doesn't implement the "
+                     VK_KHR_SURFACE_EXTENSION_NAME " extension");
+        goto fail;
+    }
+    else if(!hasDisplayExtension)
+    {
+        SDL_SetError("Installed Vulkan doesn't implement the "
+                     VK_KHR_DISPLAY_EXTENSION_NAME "extension");
+        goto fail;
+    }
+
+    return 0;
+
+fail:
+    SDL_UnloadObject(_this->vulkan_config.loader_handle);
+    _this->vulkan_config.loader_handle = NULL;
+    return -1;
+}
+
+void KMSDRM_LEGACY_Vulkan_UnloadLibrary(_THIS)
+{
+    if(_this->vulkan_config.loader_handle)
+    {
+        SDL_UnloadObject(_this->vulkan_config.loader_handle);
+        _this->vulkan_config.loader_handle = NULL;
+    }
+}
+
+/*********************************************************************/
+/* Here we can put whatever Vulkan extensions we want to be enabled  */
+/* at instance creation, which is done in the programs, not in SDL.  */
+/* So: programs call SDL_Vulkan_GetInstanceExtensions() and here     */
+/* we put the extensions specific to this backend so the programs    */
+/* get a list with the extension we want, so they can include that   */
+/* list in the ppEnabledExtensionNames and EnabledExtensionCount     */
+/* members of the VkInstanceCreateInfo struct passed to              */
+/* vkCreateInstance().                                               */
+/*********************************************************************/
+SDL_bool KMSDRM_LEGACY_Vulkan_GetInstanceExtensions(_THIS,
+                                          SDL_Window *window,
+                                          unsigned *count,
+                                          const char **names)
+{
+    static const char *const extensionsForKMSDRM[] = {
+        VK_KHR_SURFACE_EXTENSION_NAME, VK_KHR_DISPLAY_EXTENSION_NAME
+    };
+    if(!_this->vulkan_config.loader_handle)
+    {
+        SDL_SetError("Vulkan is not loaded");
+        return SDL_FALSE;
+    }
+    return SDL_Vulkan_GetInstanceExtensions_Helper(
+            count, names, SDL_arraysize(extensionsForKMSDRM),
+            extensionsForKMSDRM);
+}
+
+void KMSDRM_LEGACY_Vulkan_GetDrawableSize(_THIS, SDL_Window *window, int *w, int *h)
+{
+    if (w) {
+        *w = window->w;
+    }
+
+    if (h) {
+        *h = window->h;
+    }
+}
+
+/***********************************************************************/
+/* First thing to know is that we don't call vkCreateInstance() here.  */
+/* Instead, programs using SDL and Vulkan create their Vulkan instance */
+/* and we get it here, ready to use.                                   */
+/* Extensions specific for this platform are activated in              */
+/* KMSDRM_LEGACY_Vulkan_GetInstanceExtensions(), like we do with       */
+/* VK_KHR_DISPLAY_EXTENSION_NAME, which is what we need for x-less VK. */                
+/***********************************************************************/
+SDL_bool KMSDRM_LEGACY_Vulkan_CreateSurface(_THIS,
+                                  SDL_Window *window,
+                                  VkInstance instance,
+                                  VkSurfaceKHR *surface)
+{
+    VkPhysicalDevice gpu;
+    uint32_t gpu_count;
+    uint32_t display_count;
+    uint32_t mode_count;
+    uint32_t plane_count;
+
+    VkPhysicalDevice *physical_devices = NULL;
+    VkDisplayPropertiesKHR *displays_props = NULL;
+    VkDisplayModePropertiesKHR *modes_props = NULL;
+    VkDisplayPlanePropertiesKHR *planes_props = NULL;
+
+    VkDisplayModeCreateInfoKHR display_mode_create_info;
+    VkDisplaySurfaceCreateInfoKHR display_plane_surface_create_info;
+
+    VkExtent2D image_size;
+    VkDisplayModeKHR *display_mode = NULL;
+    VkDisplayModePropertiesKHR display_mode_props = {0};
+    VkDisplayModeParametersKHR new_mode_parameters = { {0, 0}, 0};
+
+    VkResult result;
+    SDL_bool ret = SDL_FALSE;
+    SDL_bool mode_found = SDL_FALSE;
+
+    /* We don't receive a display index in KMSDRM_LEGACY_CreateDevice(), only
+       a device index, which determines the GPU to use, but not the output.
+       So we simply use the first connected output (ie, the first connected
+       video output) for now.
+       In other words, change this index to select a different output. Easy! */
+    int display_index = 0;
+
+    int i;
+
+    SDL_VideoData *viddata = ((SDL_VideoData *)_this->driverdata);
+
+    /* Get the function pointers for the functions we will use. */
+    PFN_vkGetInstanceProcAddr vkGetInstanceProcAddr =
+        (PFN_vkGetInstanceProcAddr)_this->vulkan_config.vkGetInstanceProcAddr;
+
+    PFN_vkCreateDisplayPlaneSurfaceKHR vkCreateDisplayPlaneSurfaceKHR =
+        (PFN_vkCreateDisplayPlaneSurfaceKHR)vkGetInstanceProcAddr(
+            instance, "vkCreateDisplayPlaneSurfaceKHR");
+
+    PFN_vkEnumeratePhysicalDevices vkEnumeratePhysicalDevices =
+        (PFN_vkEnumeratePhysicalDevices)vkGetInstanceProcAddr(
+            instance, "vkEnumeratePhysicalDevices");
+
+    PFN_vkGetPhysicalDeviceDisplayPropertiesKHR vkGetPhysicalDeviceDisplayPropertiesKHR =
+        (PFN_vkGetPhysicalDeviceDisplayPropertiesKHR)vkGetInstanceProcAddr(
+            instance, "vkGetPhysicalDeviceDisplayPropertiesKHR");
+
+    PFN_vkGetDisplayModePropertiesKHR vkGetDisplayModePropertiesKHR =
+        (PFN_vkGetDisplayModePropertiesKHR)vkGetInstanceProcAddr(
+            instance, "vkGetDisplayModePropertiesKHR");
+
+    PFN_vkGetPhysicalDeviceDisplayPlanePropertiesKHR vkGetPhysicalDeviceDisplayPlanePropertiesKHR =
+        (PFN_vkGetPhysicalDeviceDisplayPlanePropertiesKHR)vkGetInstanceProcAddr(
+            instance, "vkGetPhysicalDeviceDisplayPlanePropertiesKHR");
+
+    /*PFN_vkGetDisplayPlaneSupportedDisplaysKHR vkGetDisplayPlaneSupportedDisplaysKHR =
+        (PFN_vkGetDisplayPlaneSupportedDisplaysKHR)vkGetInstanceProcAddr(
+            instance, "vkGetDisplayPlaneSupportedDisplaysKHR");
+    
+    PFN_vkGetDisplayPlaneCapabilitiesKHR vkGetDisplayPlaneCapabilitiesKHR =
+        (PFN_vkGetDisplayPlaneCapabilitiesKHR)vkGetInstanceProcAddr(
+            instance, "vkGetDisplayPlaneCapabilitiesKHR");
+    */
+
+    PFN_vkCreateDisplayModeKHR vkCreateDisplayModeKHR =
+        (PFN_vkCreateDisplayModeKHR)vkGetInstanceProcAddr(
+            instance, "vkCreateDisplayModeKHR");
+
+    if(!_this->vulkan_config.loader_handle)
+    {
+        SDL_SetError("Vulkan is not loaded");
+        goto clean;
+    }
+
+    /*************************************/
+    /* Block for vulkan surface creation */
+    /*************************************/
+
+    /****************************************************************/
+    /* If we got vkCreateDisplayPlaneSurfaceKHR() pointer, it means */
+    /* that the VK_KHR_Display extension is active on the instance. */
+    /* That's the central extension we need for x-less VK!          */
+    /****************************************************************/
+    if(!vkCreateDisplayPlaneSurfaceKHR)
+    {
+        SDL_SetError(VK_KHR_DISPLAY_EXTENSION_NAME
+                     " extension is not enabled in the Vulkan instance.");
+        goto clean;
+    }
+
+    /* Get the physical device count. */
+    vkEnumeratePhysicalDevices(instance, &gpu_count, NULL);
+
+    if (gpu_count == 0) {
+        SDL_SetError("Vulkan can't find physical devices (gpus).");
+        goto clean;
+    }
+
+    /* Get the physical devices. */
+    physical_devices = SDL_malloc(sizeof(VkPhysicalDevice) * gpu_count);
+    vkEnumeratePhysicalDevices(instance, &gpu_count, physical_devices);
+
+    /* A GPU (or physical_device, in vkcube terms) is a GPU. A machine with more
+       than one video output doen't need to have more than one GPU, like the Pi4
+       which has 1 GPU and 2 video outputs.
+       We grab the GPU/physical_device with the index we got in KMSDR_CreateDevice(). */
+    gpu = physical_devices[viddata->devindex]; 
+
+    /* A display is a video output. 1 GPU can have N displays.
+       Vulkan only counts the connected displays.
+       Get the display count of the GPU. */
+    vkGetPhysicalDeviceDisplayPropertiesKHR(gpu, &display_count, NULL);
+    if (display_count == 0) {
+        SDL_SetError("Vulkan can't find any displays.");
+        goto clean;
+    }
+
+    /* Get the props of the displays of the physical device. */
+    displays_props = (VkDisplayPropertiesKHR *) SDL_malloc(display_count * sizeof(*displays_props));
+    vkGetPhysicalDeviceDisplayPropertiesKHR(gpu,
+                                           &display_count,
+                                           displays_props);
+
+    /* Get the videomode count for the first display. */
+    vkGetDisplayModePropertiesKHR(gpu,
+                                 displays_props[display_index].display,
+                                 &mode_count, NULL);
+
+    if (mode_count == 0) {
+        SDL_SetError("Vulkan can't find any video modes for display %i (%s)\n", 0,
+                               displays_props[display_index].displayName);
+        goto clean;
+    }
+
+    /* Get the props of the videomodes for the first display. */
+    modes_props = (VkDisplayModePropertiesKHR *) SDL_malloc(mode_count * sizeof(*modes_props));
+    vkGetDisplayModePropertiesKHR(gpu,
+                                 displays_props[display_index].display,
+                                 &mode_count, modes_props);
+
+    /* Get the planes count of the physical device. */
+    vkGetPhysicalDeviceDisplayPlanePropertiesKHR(gpu, &plane_count, NULL);
+    if (plane_count == 0) {
+        SDL_SetError("Vulkan can't find any planes.");
+        goto clean;
+    }
+
+    /* Get the props of the planes for the physical device. */
+    planes_props = SDL_malloc(sizeof(VkDisplayPlanePropertiesKHR) * plane_count);
+    vkGetPhysicalDeviceDisplayPlanePropertiesKHR(gpu, &plane_count, planes_props);
+
+    /* Get a video mode equal or smaller than the window size. REMEMBER:
+       We have to get a small enough videomode for the window size,
+       because videomode determines how big the scanout region is and we can't
+       scanout a region bigger than the window (we would be reading past the
+       buffer, and Vulkan would give us a confusing VK_ERROR_SURFACE_LOST_KHR). */
+    for (i = 0; i < mode_count; i++) {
+        if (modes_props[i].parameters.visibleRegion.width <= window->w &&
+            modes_props[i].parameters.visibleRegion.height <= window->h)
+        {
+            display_mode_props = modes_props[i];
+            mode_found = SDL_TRUE;
+            break;
+        }
+    }
+
+    if (mode_found &&
+        display_mode_props.parameters.visibleRegion.width > 0 &&
+        display_mode_props.parameters.visibleRegion.height > 0 ) {
+        /* Found a suitable mode among the predefined ones. Use that. */
+        display_mode = &(display_mode_props.displayMode);
+    } else {
+        /* Can't find a suitable mode among the predefined ones, so try to create our own. */
+        new_mode_parameters.visibleRegion.width = window->w;
+        new_mode_parameters.visibleRegion.height = window->h;
+        new_mode_parameters.refreshRate = 60000; /* Always use 60Hz for now. */
+	display_mode_create_info.sType = VK_STRUCTURE_TYPE_DISPLAY_MODE_CREATE_INFO_KHR;
+	display_mode_create_info.parameters = new_mode_parameters;
+	result = vkCreateDisplayModeKHR(gpu,
+				  displays_props[display_index].display,
+				  &display_mode_create_info,
+				  NULL, display_mode);
+	if (result != VK_SUCCESS) {
+	    SDL_SetError("Vulkan couldn't find a predefined mode for that window size and couldn't create a suitable mode.");
+	    goto clean;
+	}
+    }
+
+    /* Just in case we get here without a display_mode. */
+    if (!display_mode) {
+	    SDL_SetError("Vulkan couldn't get a display mode.");
+	    goto clean;
+    }
+
+    /********************************************/
+    /* Let's finally create the Vulkan surface! */
+    /********************************************/
+
+    image_size.width = window->w;
+    image_size.height = window->h;
+    
+    display_plane_surface_create_info.sType = VK_STRUCTURE_TYPE_DISPLAY_SURFACE_CREATE_INFO_KHR;
+    display_plane_surface_create_info.displayMode = *display_mode;
+    /* For now, simply use the first plane. */
+    display_plane_surface_create_info.planeIndex = 0;
+    display_plane_surface_create_info.imageExtent = image_size;
+    result = vkCreateDisplayPlaneSurfaceKHR(instance,
+                                     &display_plane_surface_create_info,
+                                     NULL,
+                                     surface);
+    if(result != VK_SUCCESS)
+    {
+        SDL_SetError("vkCreateDisplayPlaneSurfaceKHR failed: %s",
+            SDL_Vulkan_GetResultString(result));
+        goto clean;
+    }
+
+    ret = SDL_TRUE;
+
+clean:
+    if (physical_devices)
+        SDL_free (physical_devices);
+    if (displays_props)
+        SDL_free (displays_props);
+    if (planes_props)
+        SDL_free (planes_props);
+    if (modes_props)
+        SDL_free (modes_props);
+
+    return ret;
+}
+
+#endif
+
+/* vim: set ts=4 sw=4 expandtab: */

+ 53 - 0
src/video/kmsdrm_legacy/SDL_kmsdrm_legacy_vulkan.h

@@ -0,0 +1,53 @@
+/*
+  Simple DirectMedia Layer
+  Copyright (C) 1997-2021 Sam Lantinga <slouken@libsdl.org>
+
+  This software is provided 'as-is', without any express or implied
+  warranty.  In no event will the authors be held liable for any damages
+  arising from the use of this software.
+
+  Permission is granted to anyone to use this software for any purpose,
+  including commercial applications, and to alter it and redistribute it
+  freely, subject to the following restrictions:
+
+  1. The origin of this software must not be misrepresented; you must not
+     claim that you wrote the original software. If you use this software
+     in a product, an acknowledgment in the product documentation would be
+     appreciated but is not required.
+  2. Altered source versions must be plainly marked as such, and must not be
+     misrepresented as being the original software.
+  3. This notice may not be removed or altered from any source distribution.
+*/
+
+/*
+ * @author Manuel Alfayate Corchere <redwindwanderer@gmail.com>.
+ * Based on Jacob Lifshay's SDL_x11vulkan.c.
+ */
+
+#include "../../SDL_internal.h"
+
+#ifndef SDL_kmsdrm_legacy_vulkan_h_
+#define SDL_kmsdrm_legacy_vulkan_h_
+
+#include "../SDL_vulkan_internal.h"
+#include "../SDL_sysvideo.h"
+
+#if SDL_VIDEO_VULKAN && SDL_VIDEO_DRIVER_KMSDRM
+
+int KMSDRM_LEGACY_Vulkan_LoadLibrary(_THIS, const char *path);
+void KMSDRM_LEGACY_Vulkan_UnloadLibrary(_THIS);
+SDL_bool KMSDRM_LEGACY_Vulkan_GetInstanceExtensions(_THIS,
+                                          SDL_Window *window,
+                                          unsigned *count,
+                                          const char **names);
+void KMSDRM_LEGACY_Vulkan_GetDrawableSize(_THIS, SDL_Window *window, int *w, int *h);
+SDL_bool KMSDRM_LEGACY_Vulkan_CreateSurface(_THIS,
+                                  SDL_Window *window,
+                                  VkInstance instance,
+                                  VkSurfaceKHR *surface);
+
+#endif
+
+#endif /* SDL_kmsdrm_legacy_vulkan_h_ */
+
+/* vi: set ts=4 sw=4 expandtab: */