Browse Source

[Video/KMSDRM]: Add Vulkan support to the KMSDRM backend.

Manuel Alfayate Corchete 4 years ago
parent
commit
f60f8d5d84

+ 123 - 90
src/video/kmsdrm/SDL_kmsdrmmouse.c

@@ -19,6 +19,7 @@
      misrepresented as being the original software.
   3. This notice may not be removed or altered from any source distribution.
 */
+
 #include "../../SDL_internal.h"
 
 #if SDL_VIDEO_DRIVER_KMSDRM
@@ -26,6 +27,7 @@
 #include "SDL_kmsdrmvideo.h"
 #include "SDL_kmsdrmmouse.h"
 #include "SDL_kmsdrmdyn.h"
+#include "SDL_assert.h"
 
 #include "../../events/SDL_mouse_c.h"
 #include "../../events/default_cursor.h"
@@ -54,8 +56,10 @@ drm_atomic_movecursor(KMSDRM_CursorData *curdata, uint16_t x, uint16_t y)
     if (!dispdata->atomic_req)
         dispdata->atomic_req = KMSDRM_drmModeAtomicAlloc();
     
-    add_plane_property(dispdata->atomic_req, dispdata->cursor_plane, "CRTC_X", x - curdata->hot_x);
-    add_plane_property(dispdata->atomic_req, dispdata->cursor_plane, "CRTC_Y", y - curdata->hot_y);
+    add_plane_property(dispdata->atomic_req,
+            dispdata->cursor_plane, "CRTC_X", x - curdata->hot_x);
+    add_plane_property(dispdata->atomic_req,
+            dispdata->cursor_plane, "CRTC_Y", y - curdata->hot_y);
 
     return 0;
 }
@@ -66,8 +70,9 @@ drm_atomic_movecursor(KMSDRM_CursorData *curdata, uint16_t x, uint16_t y)
 
 /* Converts a pixel from straight-alpha [AA, RR, GG, BB], which the SDL cursor surface has,
    to premultiplied-alpha [AA. AA*RR, AA*GG, AA*BB].
-   These multiplications have to be done with floats instead of uint32_t's, and the resulting values have 
-   to be converted to be relative to the 0-255 interval, where 255 is 1.00 and anything between 0 and 255 is 0.xx. */
+   These multiplications have to be done with floats instead of uint32_t's,
+   and the resulting values have to be converted to be relative to the 0-255 interval,
+   where 255 is 1.00 and anything between 0 and 255 is 0.xx. */
 void alpha_premultiply_ARGB8888 (uint32_t *pixel) {
 
     uint32_t A, R, G, B;
@@ -93,35 +98,28 @@ KMSDRM_CreateDefaultCursor(void)
     return SDL_CreateCursor(default_cdata, default_cmask, DEFAULT_CWIDTH, DEFAULT_CHEIGHT, DEFAULT_CHOTX, DEFAULT_CHOTY);
 }
 
-/* Create a GBM cursor from a surface, which means creating a hardware cursor.
-   Most programs use software cursors, but protracker-clone for example uses
-   an optional hardware cursor. */
+/* This simply copies over the cursor bitmap from the SDLSurface we receive to the
+   cursor GBM BO. Setting up the cursor plane, creating the cursor FB BO, etc.. is
+   done in KMSDRM_InitMouse(): when we get here. everything must be ready. */
 static SDL_Cursor *
 KMSDRM_CreateCursor(SDL_Surface * surface, int hot_x, int hot_y)
 {
-    SDL_VideoDevice *dev = SDL_GetVideoDevice();
-    SDL_VideoData *viddata = ((SDL_VideoData *)dev->driverdata);
+    SDL_DisplayData *dispdata = (SDL_DisplayData *)SDL_GetDisplayDriverData(0);
     KMSDRM_CursorData *curdata;
     SDL_Cursor *cursor;
-    uint64_t usable_cursor_w, usable_cursor_h;
+
     uint32_t bo_stride, pixel;
     uint32_t *buffer = NULL;
     size_t bufsize;
     unsigned int i, j;
 
-    /* All code below assumes ARGB8888 format for the cursor surface, like other backends do.
-       Also, the GBM BO pixels have to be alpha-premultiplied, but the SDL surface we receive has
+    /* All code below assumes ARGB8888 format for the cursor surface,
+       like other backends do. Also, the GBM BO pixels have to be
+       alpha-premultiplied, but the SDL surface we receive has
        straight-alpha pixels, so we always have to convert. */ 
     SDL_assert(surface->format->format == SDL_PIXELFORMAT_ARGB8888);
     SDL_assert(surface->pitch == surface->w * 4);
 
-    if (!KMSDRM_gbm_device_is_format_supported(viddata->gbm_dev, GBM_FORMAT_ARGB8888,
-                                               GBM_BO_USE_CURSOR | GBM_BO_USE_WRITE))
-    {
-        SDL_SetError("Unsupported pixel format for cursor");
-        return NULL;
-    }
-
     cursor = (SDL_Cursor *) SDL_calloc(1, sizeof(*cursor));
     if (!cursor) {
         SDL_OutOfMemory();
@@ -134,34 +132,13 @@ KMSDRM_CreateCursor(SDL_Surface * surface, int hot_x, int hot_y)
         return NULL;
     }
 
-    /* Find out what GBM cursor size is recommended by the driver. */
-    if (KMSDRM_drmGetCap(viddata->drm_fd, DRM_CAP_CURSOR_WIDTH,  &usable_cursor_w) ||
-        KMSDRM_drmGetCap(viddata->drm_fd, DRM_CAP_CURSOR_HEIGHT, &usable_cursor_h))
-    {
-        SDL_SetError("Could not get the recommended GBM cursor size");
-        goto cleanup;
-    }
-
-    if (usable_cursor_w == 0 || usable_cursor_h == 0) {
-        SDL_SetError("Could not get an usable GBM cursor size");
-        goto cleanup;
-    }
-
     /* hox_x and hot_y are the coordinates of the "tip of the cursor" from it's base. */
     curdata->hot_x = hot_x;
     curdata->hot_y = hot_y;
-    curdata->w = usable_cursor_w;
-    curdata->h = usable_cursor_h;
+    curdata->w = dispdata->cursor_w;
+    curdata->h = dispdata->cursor_h;
 
-    curdata->bo = KMSDRM_gbm_bo_create(viddata->gbm_dev, usable_cursor_w, usable_cursor_h,
-                                       GBM_FORMAT_ARGB8888, GBM_BO_USE_CURSOR | GBM_BO_USE_WRITE);
-
-    if (!curdata->bo) {
-        SDL_SetError("Could not create GBM cursor BO");
-        goto cleanup;
-    }
-
-    bo_stride = KMSDRM_gbm_bo_get_stride(curdata->bo);
+    bo_stride = KMSDRM_gbm_bo_get_stride(dispdata->cursor_bo);
     bufsize = bo_stride * curdata->h;
 
     /* Always use a temp buffer: it serves the purpose of storing the
@@ -197,7 +174,7 @@ KMSDRM_CreateCursor(SDL_Surface * surface, int hot_x, int hot_y)
         SDL_UnlockSurface(surface);
     }
 
-    if (KMSDRM_gbm_bo_write(curdata->bo, buffer, bufsize)) {
+    if (KMSDRM_gbm_bo_write(dispdata->cursor_bo, buffer, bufsize)) {
         SDL_SetError("Could not write to GBM cursor BO");
         goto cleanup;
     }
@@ -218,11 +195,9 @@ cleanup:
         SDL_free(cursor);
     }
     if (curdata) {
-        if (curdata->bo) {
-            KMSDRM_gbm_bo_destroy(curdata->bo);
-        }
         SDL_free(curdata);
     }
+
     return NULL;
 }
 
@@ -263,7 +238,8 @@ KMSDRM_ShowCursor(SDL_Cursor * cursor)
            and SDL is stored in mouse->cur_cursor. */
         if (mouse->cur_cursor && mouse->cur_cursor->driverdata) {
             if (dispdata && dispdata->cursor_plane) {
-                info.plane = dispdata->cursor_plane; /* The rest of the members are zeroed. */
+                info.plane = dispdata->cursor_plane;
+                /* The rest of the members are zeroed. */
                 drm_atomic_set_plane_props(&info);
                 if (drm_atomic_commit(display->device, SDL_TRUE))
                     return SDL_SetError("Failed atomic commit in KMSDRM_ShowCursor.");
@@ -288,18 +264,14 @@ KMSDRM_ShowCursor(SDL_Cursor * cursor)
     
     curdata = (KMSDRM_CursorData *) cursor->driverdata;
 
-    if (!curdata || !curdata->bo) {
+    if (!curdata || !dispdata->cursor_bo) {
         return SDL_SetError("Cursor not initialized properly.");
     }
 
-    curdata->crtc_id  = dispdata->crtc->crtc->crtc_id;
-    curdata->plane    = dispdata->cursor_plane;
-    curdata->video    = video_device;
-
-    fb = KMSDRM_FBFromBO(curdata->video, curdata->bo);
+    fb = KMSDRM_FBFromBO(video_device, dispdata->cursor_bo);
 
     info.plane = dispdata->cursor_plane;
-    info.crtc_id = curdata->crtc_id;
+    info.crtc_id = dispdata->crtc->crtc->crtc_id;
     info.fb_id = fb->fb_id; 
     info.src_w = curdata->w;
     info.src_h = curdata->h;
@@ -313,39 +285,17 @@ KMSDRM_ShowCursor(SDL_Cursor * cursor)
     if (drm_atomic_commit(display->device, SDL_TRUE)) {
         return SDL_SetError("Failed atomic commit in KMSDRM_ShowCursor.");
     }
+
     return 0;
 }
 
-/* Unset the cursor from the cursor plane, and ONLY WHEN THAT'S DONE,
-   DONE FOR REAL, and not only requested, destroy it by destroying the curso BO.
-   Destroying the cursor BO is an special an delicate situation,
-   because drm_atomic_set_plane_props() returns immediately, and we DON'T 
-   want to get to gbm_bo_destroy() before the prop changes requested
-   in drm_atomic_set_plane_props() have effectively been done. So we
-   issue a BLOCKING atomic_commit here to avoid that situation.
-   REMEMBER you yan issue an atomic_commit whenever you want, and
-   the changes requested until that moment (for any planes, crtcs, etc.)
-   will be done. */
+/* We have destroyed the cursor by now, in KMSDRM_DestroyCursor.
+   This is only for freeing the SDL_cursor.*/
 static void
 KMSDRM_FreeCursor(SDL_Cursor * cursor)
 {
-    KMSDRM_CursorData *curdata = NULL;
-    SDL_VideoDevice *video_device = SDL_GetVideoDevice();
-    KMSDRM_PlaneInfo info = {0};
+    /* Even if the cursor is not ours, free it. */
     if (cursor) {
-        curdata = (KMSDRM_CursorData *) cursor->driverdata;
-        if (video_device && curdata->bo && curdata->plane) {
-            info.plane = curdata->plane; /* The other members are zeroed. */
-            drm_atomic_set_plane_props(&info);
-            /* Wait until the cursor is unset from the cursor plane before destroying it's BO. */
-            if (drm_atomic_commit(video_device, SDL_TRUE)) {
-                SDL_SetError("Failed atomic commit in KMSDRM_FreeCursor.");
-            }
-            KMSDRM_gbm_bo_destroy(curdata->bo);
-            curdata->bo = NULL;
-        }
-
-        /* Even if the cursor is not ours, free it. */
         SDL_free(cursor->driverdata);
         SDL_free(cursor);
     }
@@ -365,6 +315,7 @@ KMSDRM_WarpMouseGlobal(int x, int y)
 {
     KMSDRM_CursorData *curdata;
     SDL_Mouse *mouse = SDL_GetMouse();
+    SDL_DisplayData *dispdata = (SDL_DisplayData *)SDL_GetDisplayDriverData(0);
 
     if (mouse && mouse->cur_cursor && mouse->cur_cursor->driverdata) {
         /* Update internal mouse position. */
@@ -372,7 +323,7 @@ KMSDRM_WarpMouseGlobal(int x, int y)
 
         /* And now update the cursor graphic position on screen. */
         curdata = (KMSDRM_CursorData *) mouse->cur_cursor->driverdata;
-        if (curdata->bo) {
+        if (dispdata->cursor_bo) {
             if (drm_atomic_movecursor(curdata, x, y)) {
                 return SDL_SetError("drm_atomic_movecursor() failed.");
             }
@@ -382,7 +333,8 @@ KMSDRM_WarpMouseGlobal(int x, int y)
     } else {
         return SDL_SetError("No mouse or current cursor.");
     }
-return 0;
+
+    return 0;
 }
 
 void
@@ -392,8 +344,11 @@ KMSDRM_InitMouse(_THIS)
      * but there's no point in doing so as there's no multimice support...yet!
      */
 
+    SDL_VideoDevice *dev = SDL_GetVideoDevice();
+    SDL_VideoData *viddata = ((SDL_VideoData *)dev->driverdata);
     SDL_DisplayData *dispdata = (SDL_DisplayData *)SDL_GetDisplayDriverData(0);
     SDL_Mouse *mouse = SDL_GetMouse();
+    uint64_t usable_cursor_w, usable_cursor_h;
 
     mouse->CreateCursor = KMSDRM_CreateCursor;
     mouse->ShowCursor = KMSDRM_ShowCursor;
@@ -402,20 +357,96 @@ KMSDRM_InitMouse(_THIS)
     mouse->WarpMouse = KMSDRM_WarpMouse;
     mouse->WarpMouseGlobal = KMSDRM_WarpMouseGlobal;
 
-    /* Init cursor plane, if we haven't yet. */
+    /***************************************************************************/
+    /* REMEMBER TO BE SURE OF UNDOING ALL THESE STEPS PROPERLY BEFORE CALLING  */
+    /* gbm_device_destroy, OR YOU WON'T BE ABLE TO CREATE A NEW ONE (ERROR -13 */
+    /* ON gbm_create_device).                                                  */
+    /***************************************************************************/
+
+    /* 1- Init cursor plane, if we haven't yet. */
     if (!dispdata->cursor_plane) {
         setup_plane(_this, &(dispdata->cursor_plane), DRM_PLANE_TYPE_CURSOR);
     }
 
+    /* 2- Create the cursor GBM BO, if we haven't yet. */
+    if (!dispdata->cursor_bo) {
+
+        if (!KMSDRM_gbm_device_is_format_supported(viddata->gbm_dev, GBM_FORMAT_ARGB8888,
+                                               GBM_BO_USE_CURSOR | GBM_BO_USE_WRITE))
+        {
+            SDL_SetError("Unsupported pixel format for cursor");
+            return;
+        }
+
+	if (KMSDRM_drmGetCap(viddata->drm_fd, DRM_CAP_CURSOR_WIDTH,  &usable_cursor_w) ||
+	    KMSDRM_drmGetCap(viddata->drm_fd, DRM_CAP_CURSOR_HEIGHT, &usable_cursor_h))
+	{
+	    SDL_SetError("Could not get the recommended GBM cursor size");
+	    goto cleanup;
+	}
+
+	if (usable_cursor_w == 0 || usable_cursor_h == 0) {
+	    SDL_SetError("Could not get an usable GBM cursor size");
+	    goto cleanup;
+	}
+
+        dispdata->cursor_w = usable_cursor_w;
+        dispdata->cursor_h = usable_cursor_h;
+
+	dispdata->cursor_bo = KMSDRM_gbm_bo_create(viddata->gbm_dev,
+	    usable_cursor_w, usable_cursor_h,
+	    GBM_FORMAT_ARGB8888, GBM_BO_USE_CURSOR | GBM_BO_USE_WRITE);
+
+	if (!dispdata->cursor_bo) {
+	    SDL_SetError("Could not create GBM cursor BO");
+	    goto cleanup;
+	}
+    }
+
+    /* SDL expects to set the default cursor on screen when we init the mouse. */
     SDL_SetDefaultCursor(KMSDRM_CreateDefaultCursor());
+
+    return;
+
+cleanup:
+    if (dispdata->cursor_bo) {
+	KMSDRM_gbm_bo_destroy(dispdata->cursor_bo);
+	dispdata->cursor_bo = NULL;
+    }
 }
 
 void
-KMSDRM_QuitMouse(_THIS)
+KMSDRM_DeinitMouse(_THIS)
 {
-    /* Free the plane on which the cursor was being shown. */
+    SDL_VideoDevice *video_device = SDL_GetVideoDevice();
     SDL_DisplayData *dispdata = (SDL_DisplayData *)SDL_GetDisplayDriverData(0);
-    free_plane(&dispdata->cursor_plane);
+    KMSDRM_PlaneInfo info = {0};
+ 
+    /*******************************************/
+    /* UNDO WHAT WE DID IN KMSDRM_InitMouse(). */
+    /*******************************************/
+    
+    /* 1- Destroy the curso GBM BO. */
+    if (video_device && dispdata->cursor_bo) {
+	/* Unsethe the cursor BO from the cursor plane.
+	   (The other members of the plane info are zeroed). */
+	info.plane = dispdata->cursor_plane;
+	drm_atomic_set_plane_props(&info);
+	/* Wait until the cursor is unset from the cursor plane
+	   before destroying it's BO. */
+	if (drm_atomic_commit(video_device, SDL_TRUE)) {
+	    SDL_SetError("Failed atomic commit in KMSDRM_DenitMouse.");
+	}
+	/* ..and finally destroy the cursor DRM BO! */
+	KMSDRM_gbm_bo_destroy(dispdata->cursor_bo);
+	dispdata->cursor_bo = NULL;
+    }
+
+    /* 2- Free the cursor plane, on which the cursor was being shown. */
+    if (dispdata->cursor_plane) {
+        free_plane(&dispdata->cursor_plane);
+    }
+
 }
 
 /* This is called when a mouse motion event occurs */
@@ -429,11 +460,13 @@ KMSDRM_MoveCursor(SDL_Cursor * cursor)
        That's why we move the cursor graphic ONLY. */
     if (mouse && mouse->cur_cursor && mouse->cur_cursor->driverdata) {
         curdata = (KMSDRM_CursorData *) mouse->cur_cursor->driverdata;
+
         /* Some programs expect cursor movement even while they don't do SwapWindow() calls,
            and since we ride on the atomic_commit() in SwapWindow() for cursor movement,
-           cursor won't move in these situations. We could do an atomic_commit() for each
-           cursor movement request, but it cripples the movement to 30FPS, so a future solution
-           is needed. SDLPoP "QUIT?" menu is an example of this situation. */
+           cursor won't move in these situations. We could do an atomic_commit() here
+           for each cursor movement request, but it cripples the movement to 30FPS,
+           so a future solution is needed. SDLPoP "QUIT?" menu is an example of this
+           situation. */
 
         if (drm_atomic_movecursor(curdata, mouse->x, mouse->y)) {
             SDL_SetError("drm_atomic_movecursor() failed.");

+ 2 - 8
src/video/kmsdrm/SDL_kmsdrmmouse.h

@@ -33,19 +33,13 @@
 /* Driverdata with driver-side info about the cursor. */
 typedef struct _KMSDRM_CursorData
 {
-    struct gbm_bo *bo;
-    struct plane *plane;
-    uint32_t       crtc_id;
     uint16_t       hot_x, hot_y;
     uint16_t       w, h;
-    /* The video devide implemented on SDL_kmsdrmvideo.c 
-     * to be used as _THIS pointer in SDL_kmsdrmvideo.c 
-     * functions that need it. */
-    SDL_VideoDevice *video;
+
 } KMSDRM_CursorData;
 
 extern void KMSDRM_InitMouse(_THIS);
-extern void KMSDRM_QuitMouse(_THIS);
+extern void KMSDRM_DeinitMouse(_THIS);
 
 #endif /* SDL_KMSDRM_mouse_h_ */
 

+ 15 - 11
src/video/kmsdrm/SDL_kmsdrmopengles.c

@@ -59,11 +59,24 @@ KMSDRM_GLES_DefaultProfileConfig(_THIS, int *mask, int *major, int *minor)
 #endif
 }
 
-
 int
 KMSDRM_GLES_LoadLibrary(_THIS, const char *path) {
+    /* Just pretend you do this here, but don't do it until KMSDRM_CreateWindow(),
+       where we do the same library load we would normally do here.
+       because this gets called by SDL_CreateWindow() before KMSDR_CreateWindow(),
+       so gbm dev isn't yet created when this is called, AND we can't alter the
+       call order in SDL_CreateWindow(). */
+#if 0
     NativeDisplayType display = (NativeDisplayType)((SDL_VideoData *)_this->driverdata)->gbm_dev;
     return SDL_EGL_LoadLibrary(_this, path, display, EGL_PLATFORM_GBM_MESA);
+#endif
+    return 0;
+}
+
+void
+KMSDRM_GLES_UnloadLibrary(_THIS) {
+    /* As with KMSDRM_GLES_LoadLibrary(), we define our own unloading function so
+       we manually unload the library whenever we want. */
 }
 
 SDL_EGL_CreateContext_impl(KMSDRM)
@@ -94,6 +107,7 @@ static EGLSyncKHR create_fence(int fd, _THIS)
         EGL_SYNC_NATIVE_FENCE_FD_ANDROID, fd,
         EGL_NONE,
     };
+
     EGLSyncKHR fence = _this->egl_data->eglCreateSyncKHR
         (_this->egl_data->egl_display, EGL_SYNC_NATIVE_FENCE_ANDROID, attrib_list);
 
@@ -342,16 +356,6 @@ KMSDRM_GLES_SwapWindow(_THIS, SDL_Window * window)
 {
     SDL_WindowData *windata = ((SDL_WindowData *) window->driverdata);
 
-     /* Get the EGL context, now that SDL_CreateRenderer() has already been called,
-       and call eglMakeCurrent() on it and the EGL surface. */
-#if SDL_VIDEO_OPENGL_EGL
-    if (windata->egl_context_pending) {
-        EGLContext egl_context = (EGLContext)SDL_GL_GetCurrentContext();
-        SDL_EGL_MakeCurrent(_this, windata->egl_surface, egl_context);
-        windata->egl_context_pending = SDL_FALSE;
-    }
-#endif
-
     if (windata->swap_window == NULL) {
         /* We want the fenced version by default, but it needs extensions. */
         if ( (SDL_GetHintBoolean(SDL_HINT_VIDEO_DOUBLE_BUFFER, SDL_FALSE)) ||

+ 0 - 1
src/video/kmsdrm/SDL_kmsdrmopengles.h

@@ -32,7 +32,6 @@
 /* OpenGLES functions */
 #define KMSDRM_GLES_GetAttribute SDL_EGL_GetAttribute
 #define KMSDRM_GLES_GetProcAddress SDL_EGL_GetProcAddress
-#define KMSDRM_GLES_UnloadLibrary SDL_EGL_UnloadLibrary
 #define KMSDRM_GLES_DeleteContext SDL_EGL_DeleteContext
 #define KMSDRM_GLES_GetSwapInterval SDL_EGL_GetSwapInterval
 

File diff suppressed because it is too large
+ 691 - 312
src/video/kmsdrm/SDL_kmsdrmvideo.c


+ 12 - 4
src/video/kmsdrm/SDL_kmsdrmvideo.h

@@ -85,6 +85,8 @@ typedef struct SDL_VideoData
 {
     int devindex;               /* device index that was passed on creation */
     int drm_fd;                 /* DRM file desc */
+    char devpath[32];           /* DRM dev path. */
+
     struct gbm_device *gbm_dev;
 
     SDL_Window **windows;
@@ -117,6 +119,7 @@ typedef struct connector {
 typedef struct SDL_DisplayData
 {
     drmModeModeInfo mode;
+    drmModeModeInfo preferred_mode;
     uint32_t atomic_flags;
 
     plane *display_plane;
@@ -141,6 +144,14 @@ typedef struct SDL_DisplayData
     dumb_buffer *dumb_buffer;
 
     SDL_bool modeset_pending;
+    SDL_bool gbm_init;
+
+    /* DRM & GBM cursor stuff lives here, not in an SDL_Cursor's driverdata struct,
+       because setting/unsetting up these is done on window creation/destruction,
+       where we may not have an SDL_Cursor at all (so no SDL_Cursor driverdata).
+       There's only one cursor GBM BO because we only support one cursor. */
+    struct gbm_bo *cursor_bo;
+    uint64_t cursor_w, cursor_h;
 
 } SDL_DisplayData;
 
@@ -167,12 +178,9 @@ typedef struct SDL_WindowData
     int32_t output_h;
     int32_t output_x;
 
-    /* This is for deferred eglMakeCurrent() call: we can't call it until
-       the EGL context is available, but we need the EGL surface sooner. */
-    SDL_bool egl_context_pending;
-
     /* This dictates what approach we'll use for SwapBuffers. */
     int (*swap_window)(_THIS, SDL_Window * window);
+
 } SDL_WindowData;
 
 typedef struct SDL_DisplayModeData

+ 395 - 0
src/video/kmsdrm/SDL_kmsdrmvulkan.c

@@ -0,0 +1,395 @@
+/*
+  Simple DirectMedia Layer
+  Copyright (C) 1997-2020 Sam Lantinga <[email protected]>
+
+  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 <[email protected]>.
+ * Based on Jacob Lifshay's SDL_x11vulkan.c.
+ */
+
+#include "../../SDL_internal.h"
+
+#if SDL_VIDEO_VULKAN && SDL_VIDEO_DRIVER_KMSDRM
+
+#include "SDL_kmsdrmvideo.h"
+#include "SDL_kmsdrmdyn.h"
+#include "SDL_assert.h"
+
+#include "SDL_loadso.h"
+#include "SDL_kmsdrmvulkan.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_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_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_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_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_Vulkan_GetInstanceExtensions(), like we do with              */
+/* VK_KHR_DISPLAY_EXTENSION_NAME, which is what we need for x-less VK. */                
+/***********************************************************************/
+SDL_bool KMSDRM_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;
+    VkDisplayModePropertiesKHR display_mode_props = {0};
+
+    VkResult result;
+    SDL_bool ret = SDL_FALSE;
+
+    /* 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 = malloc(sizeof(VkPhysicalDevice) * gpu_count);
+    vkEnumeratePhysicalDevices(instance, &gpu_count, physical_devices);
+
+    /* For now, just grab the first physical device (gpu = physical_device in vkcube example).*/
+    /* TODO What happens on video systems with multiple display ports like the Rpi4 ? */
+    gpu = physical_devices[0]; 
+
+    /* Get the display count of the phsysical device. */
+    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 *) malloc(display_count * sizeof(*displays_props));
+    vkGetPhysicalDeviceDisplayPropertiesKHR(gpu,
+                                           &display_count,
+                                           displays_props);
+
+    /* Get the videomode count for the first display. */
+    /* TODO What happens on video systems with multiple display ports like the Rpi4 ? */
+    vkGetDisplayModePropertiesKHR(gpu,
+                                 displays_props[0].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[0].displayName);
+        goto clean;
+    }
+
+    /* Get the props of the videomodes for the first display. */
+    modes_props = (VkDisplayModePropertiesKHR *) malloc(mode_count * sizeof(*modes_props));
+    vkGetDisplayModePropertiesKHR(gpu,
+                                 displays_props[0].display,
+                                 &mode_count, modes_props);
+
+    /* Get the planes count of the physical device. */
+    /* TODO: find out if we need other planes. */
+    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 = malloc(sizeof(VkDisplayPlanePropertiesKHR) * plane_count);
+    vkGetPhysicalDeviceDisplayPlanePropertiesKHR(gpu, &plane_count, planes_props);
+
+    /* Get a video mode matching 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 (int 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];
+            break;
+        }
+    } 
+
+    if (display_mode_props.parameters.visibleRegion.width == 0
+        || display_mode_props.parameters.visibleRegion.height == 0)
+    {
+        SDL_SetError("Vulkan can't find a proper display mode for the window size.");
+        goto clean;
+    }
+
+    /* We have the props of the display mode, but we need an actual display mode. */
+    display_mode_create_info.sType = VK_STRUCTURE_TYPE_DISPLAY_MODE_CREATE_INFO_KHR;
+    display_mode_create_info.parameters = display_mode_props.parameters;
+    result = vkCreateDisplayModeKHR(gpu,
+			      displays_props[0].display,
+			      &display_mode_create_info,
+			      NULL, &display_mode);
+    if (result != VK_SUCCESS) {
+        SDL_SetError("Vulkan can't create the 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;
+    display_plane_surface_create_info.planeIndex = 0;  /* For now, simply use the first plane. */
+    display_plane_surface_create_info.imageExtent = image_size;
+
+    result = vkCreateDisplayPlaneSurfaceKHR(instance,
+                                     &display_plane_surface_create_info,
+                                     NULL,
+                                     surface);
+
+    if(result != VK_SUCCESS)
+    {
+        SDL_SetError("vkCreateKMSDRMSurfaceKHR failed: %s", SDL_Vulkan_GetResultString(result));
+        goto clean;
+    }
+
+    ret = SDL_TRUE;
+
+clean:
+    if (physical_devices)
+        free (physical_devices);
+    if (displays_props)
+        free (displays_props);
+    if (planes_props)
+        free (planes_props);
+    if (modes_props)
+        free (modes_props);
+
+    return ret;
+}
+
+#endif
+
+/* vim: set ts=4 sw=4 expandtab: */

+ 53 - 0
src/video/kmsdrm/SDL_kmsdrmvulkan.h

@@ -0,0 +1,53 @@
+/*
+  Simple DirectMedia Layer
+  Copyright (C) 1997-2020 Sam Lantinga <[email protected]>
+
+  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 <[email protected]>.
+ * Based on Jacob Lifshay's SDL_x11vulkan.c.
+ */
+
+#include "../../SDL_internal.h"
+
+#ifndef SDL_kmsdrmvulkan_h_
+#define SDL_kmsdrmvulkan_h_
+
+#include "../SDL_vulkan_internal.h"
+#include "../SDL_sysvideo.h"
+
+#if SDL_VIDEO_VULKAN && SDL_VIDEO_DRIVER_KMSDRM
+
+int KMSDRM_Vulkan_LoadLibrary(_THIS, const char *path);
+void KMSDRM_Vulkan_UnloadLibrary(_THIS);
+SDL_bool KMSDRM_Vulkan_GetInstanceExtensions(_THIS,
+                                          SDL_Window *window,
+                                          unsigned *count,
+                                          const char **names);
+void KMSDRM_Vulkan_GetDrawableSize(_THIS, SDL_Window *window, int *w, int *h);
+SDL_bool KMSDRM_Vulkan_CreateSurface(_THIS,
+                                  SDL_Window *window,
+                                  VkInstance instance,
+                                  VkSurfaceKHR *surface);
+
+#endif
+
+#endif /* SDL_kmsdrmvulkan_h_ */
+
+/* vi: set ts=4 sw=4 expandtab: */

Some files were not shown because too many files changed in this diff