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
 

+ 736 - 556
src/video/kmsdrm/SDL_kmsdrmvideo.c

@@ -41,6 +41,7 @@
 #include "SDL_kmsdrmopengles.h"
 #include "SDL_kmsdrmmouse.h"
 #include "SDL_kmsdrmdyn.h"
+#include "SDL_kmsdrmvulkan.h"
 #include <sys/stat.h>
 #include <dirent.h>
 #include <errno.h>
@@ -249,19 +250,20 @@ err:
 }
 
 static void
-KMSDRM_DestroyDumbBuffer(_THIS, dumb_buffer *buffer)
+KMSDRM_DestroyDumbBuffer(_THIS, dumb_buffer **buffer)
 {
     SDL_VideoData *viddata = ((SDL_VideoData *)_this->driverdata);
 
     struct drm_mode_destroy_dumb destroy = {
-        .handle = buffer->gem_handles[0],
+        .handle = (*buffer)->gem_handles[0],
     };
 
-    KMSDRM_drmModeRmFB(viddata->drm_fd, buffer->fb_id);
+    KMSDRM_drmModeRmFB(viddata->drm_fd, (*buffer)->fb_id);
 
-    munmap(buffer->dumb.mem, buffer->dumb.size);
+    munmap((*buffer)->dumb.mem, (*buffer)->dumb.size);
     KMSDRM_drmIoctl(viddata->drm_fd, DRM_IOCTL_MODE_DESTROY_DUMB, &destroy);
-    free(buffer);
+    free(*buffer);
+    *buffer = NULL;
 }
 
 /* Using the CPU mapping, fill the dumb buffer with black pixels. */
@@ -304,7 +306,7 @@ static dumb_buffer *KMSDRM_CreateBuffer(_THIS)
     return ret;
 
 err:
-    KMSDRM_DestroyDumbBuffer(_this, ret);
+    KMSDRM_DestroyDumbBuffer(_this, &ret);
     return NULL;
 }
 
@@ -389,17 +391,19 @@ void print_plane_info(_THIS, drmModePlanePtr plane)
     drmModeRes *resources;
     uint32_t type = 0;
     SDL_VideoData *viddata = ((SDL_VideoData *)_this->driverdata);
-    int i;
 
     drmModeObjectPropertiesPtr props = KMSDRM_drmModeObjectGetProperties(viddata->drm_fd,
         plane->plane_id, DRM_MODE_OBJECT_PLANE);
 
     /* Search the plane props for the plane type. */
-    for (i = 0; i < props->count_props; i++) {
-        drmModePropertyPtr p = KMSDRM_drmModeGetProperty(viddata->drm_fd, props->props[i]);
+    for (int j = 0; j < props->count_props; j++) {
+
+        drmModePropertyPtr p = KMSDRM_drmModeGetProperty(viddata->drm_fd, props->props[j]);
+
         if ((strcmp(p->name, "type") == 0)) {
-            type = props->prop_values[i];
+            type = props->prop_values[j];
         }
+
         KMSDRM_drmModeFreeProperty(p);
     }
 
@@ -428,7 +432,7 @@ void print_plane_info(_THIS, drmModePlanePtr plane)
         return;
 
     printf("--PLANE ID: %d\nPLANE TYPE: %s\nCRTC READING THIS PLANE: %d\nCRTCS SUPPORTED BY THIS PLANE: ",  plane->plane_id, plane_type, plane->crtc_id);
-    for (i = 0; i < resources->count_crtcs; i++) {
+    for (int i = 0; i < resources->count_crtcs; i++) {
         if (plane->possible_crtcs & (1 << i)) {
             uint32_t crtc_id = resources->crtcs[i];
             printf ("%d", crtc_id);
@@ -557,16 +561,19 @@ setup_plane(_THIS, struct plane **plane, uint32_t plane_type)
 {
     uint32_t plane_id;
     SDL_VideoData *viddata = ((SDL_VideoData *)_this->driverdata);
+    int ret = 0;
 
     *plane = SDL_calloc(1, sizeof(**plane));
     if (!(*plane)) {
-        return SDL_OutOfMemory();
+        ret = SDL_OutOfMemory();
+        goto cleanup;
     }
 
     /* Get plane ID. */
     plane_id = get_plane_id(_this, plane_type);
 
     if (!plane_id) {
+        ret = SDL_SetError("Invalid Plane ID");
         goto cleanup;
     }
 
@@ -582,7 +589,7 @@ setup_plane(_THIS, struct plane **plane, uint32_t plane_type)
             sizeof(*(*plane)->props_info));
 
         if ( !((*plane)->props_info) ) {
-            SDL_OutOfMemory();
+            ret = SDL_OutOfMemory();
             goto cleanup;
         }
 
@@ -592,12 +599,15 @@ setup_plane(_THIS, struct plane **plane, uint32_t plane_type)
         }
     }   
 
-    return 0;
-
 cleanup:
-    SDL_free(*plane);
-    *plane = NULL;
-    return -1;
+
+    if (ret != 0) {
+        if (*plane) {
+            SDL_free(*plane);
+            *plane = NULL;
+        }
+    }
+    return ret;
 }
 
 /* Free a plane and it's props. */
@@ -673,7 +683,7 @@ int drm_atomic_commit(_THIS, SDL_bool blocking)
     if (ret) {
         SDL_SetError("Atomic commit failed, returned %d.", ret);
         /* Uncomment this for fast-debugging */
-        // printf("ATOMIC COMMIT FAILED: %d.\n", ret);
+        printf("ATOMIC COMMIT FAILED: %s.\n", strerror(errno));
         goto out;
     }
 
@@ -769,8 +779,6 @@ KMSDRM_GetDisplayDPI(_THIS, SDL_VideoDisplay * display, float * ddpi, float * hd
     return 0;
 }
 
-
-
 static SDL_VideoDevice *
 KMSDRM_CreateDevice(int devindex)
 {
@@ -846,7 +854,13 @@ KMSDRM_CreateDevice(int devindex)
 #endif
     device->PumpEvents = KMSDRM_PumpEvents;
     device->free = KMSDRM_DeleteDevice;
-
+#if SDL_VIDEO_VULKAN
+    device->Vulkan_LoadLibrary = KMSDRM_Vulkan_LoadLibrary;
+    device->Vulkan_UnloadLibrary = KMSDRM_Vulkan_UnloadLibrary;
+    device->Vulkan_GetInstanceExtensions = KMSDRM_Vulkan_GetInstanceExtensions;
+    device->Vulkan_CreateSurface = KMSDRM_Vulkan_CreateSurface;
+    device->Vulkan_GetDrawableSize = KMSDRM_Vulkan_GetDrawableSize;
+#endif
     return device;
 
 cleanup:
@@ -915,8 +929,8 @@ KMSDRM_FBFromBO(_THIS, struct gbm_bo *bo)
     }
 
     /* Create framebuffer object for the buffer.
-       It's VERY important to note that fb_id is what we ise to set the FB_ID prop of a plane
-       when using the ATOMIC interface, and we get fb_id it here. */
+       It's VERY important to note that fb_id is what we use to set the FB_ID prop
+       of a plane when using the ATOMIC interface, and we get the fb_id here. */
     ret = KMSDRM_drmModeAddFB2(viddata->drm_fd, width, height, format,
             handles, strides, offsets, &fb_info->fb_id, 0);
 
@@ -938,409 +952,774 @@ KMSDRM_FBFromBO(_THIS, struct gbm_bo *bo)
 /* _this is a SDL_VideoDevice *                                              */
 /*****************************************************************************/
 
-/* Destroy the window surfaces and buffers. Have the PRIMARY PLANE
-   disconnected from these buffers before doing so, or have the PRIMARY PLANE
-   reading the original FB where the TTY lives, before doing this, or CRTC will
-   be disconnected by the kernel. */
-void
-KMSDRM_DestroySurfaces(_THIS, SDL_Window *window)
-{
-    SDL_WindowData *windata = (SDL_WindowData *) window->driverdata;
-    SDL_DisplayData *dispdata = (SDL_DisplayData *)SDL_GetDisplayDriverData(0);
-    KMSDRM_PlaneInfo plane_info = {0};
+/* Deinitializes the dispdata members needed for KMSDRM operation that are
+   inoffeensive for VK compatibility. */
+void KMSDRM_DisplayDataDeinit (_THIS, SDL_DisplayData *dispdata) {
+    /* Free connector */
+    if (dispdata && dispdata->connector) {
+        if (dispdata->connector->connector) {
+            KMSDRM_drmModeFreeConnector(dispdata->connector->connector);
+            dispdata->connector->connector = NULL;
+        }
+        if (dispdata->connector->props_info) {
+            SDL_free(dispdata->connector->props_info); 
+            dispdata->connector->props_info = NULL;
+        }
+        SDL_free(dispdata->connector); 
+        dispdata->connector = NULL;
+    }
 
-#if SDL_VIDEO_OPENGL_EGL
-    EGLContext egl_context;
-#endif
+    /* Free CRTC */
+    if (dispdata && dispdata->crtc) {
+        if (dispdata->crtc->crtc) {
+            KMSDRM_drmModeFreeCrtc(dispdata->crtc->crtc);
+            dispdata->crtc->crtc = NULL;
+        }
+        if (dispdata->crtc->props_info) {
+            SDL_free(dispdata->crtc->props_info); 
+            dispdata->crtc->props_info = NULL;
+        }
+        SDL_free(dispdata->crtc); 
+        dispdata->crtc = NULL;
+    }
+}
 
-    /********************************************************************/
-    /* BLOCK 1: protect the PRIMARY PLANE before destroying the buffers */
-    /* it's using.                                                      */
-    /********************************************************************/
+/* Initializes the dispdata members needed for KMSDRM operation that are
+   inoffeensive for VK compatibility, except we must leave the drm_fd
+   closed when we get to the end of this function.
+   This is to be called early, in VideoInit(), because it gets us
+   the videomode information, which SDL needs immediately after VideoInit(). */
+int KMSDRM_DisplayDataInit (_THIS, SDL_DisplayData *dispdata) {
+    SDL_VideoData *viddata = ((SDL_VideoData *)_this->driverdata);
 
-    plane_info.plane = dispdata->display_plane;
-    plane_info.crtc_id = dispdata->crtc->crtc->crtc_id;
-    plane_info.fb_id = dispdata->dumb_buffer->fb_id;
-    plane_info.src_w = dispdata->mode.hdisplay;
-    plane_info.src_h = dispdata->mode.vdisplay;
-    plane_info.crtc_w = dispdata->mode.hdisplay;
-    plane_info.crtc_h = dispdata->mode.vdisplay;
+    drmModeRes *resources = NULL;
+    drmModeEncoder *encoder = NULL;
+    drmModeConnector *connector = NULL;
+    drmModeCrtc *crtc = NULL;
 
-    drm_atomic_set_plane_props(&plane_info);
+    char devname[32];
+    int ret = 0;
+    unsigned i,j;
 
-    /* Issue blocking atomic commit. */    
-    if (drm_atomic_commit(_this, SDL_TRUE)) {
-        SDL_SetError("Failed to issue atomic commit on window destruction.");
+    dispdata->atomic_flags = 0;
+    dispdata->atomic_req = NULL;
+    dispdata->kms_fence = NULL;
+    dispdata->gpu_fence = NULL;
+    dispdata->kms_out_fence_fd = -1;
+    dispdata->dumb_buffer = NULL;
+    dispdata->modeset_pending = SDL_FALSE;
+    dispdata->gbm_init = SDL_FALSE;
+
+    dispdata->display_plane = NULL;
+    dispdata->cursor_plane = NULL;
+
+    dispdata->cursor_bo = NULL;
+
+    /* Open /dev/dri/cardNN */
+    SDL_snprintf(viddata->devpath, sizeof(viddata->devpath), "/dev/dri/card%d", viddata->devindex);
+
+    SDL_LogDebug(SDL_LOG_CATEGORY_VIDEO, "Opening device %s", devname);
+    viddata->drm_fd = open(viddata->devpath, O_RDWR | O_CLOEXEC);
+
+    if (viddata->drm_fd < 0) {
+        ret = SDL_SetError("Could not open %s", devname);
+        goto cleanup;
     }
 
-    /****************************************************************************/
-    /* BLOCK 2: We can finally destroy the window GBM and EGL surfaces, and     */
-    /* GBM buffers now that the buffers are not being used by the PRIMARY PLANE */
-    /* anymore.                                                                 */
-    /****************************************************************************/
+    SDL_LogDebug(SDL_LOG_CATEGORY_VIDEO, "Opened DRM FD (%d)", viddata->drm_fd);
 
-    /* Destroy the GBM surface and buffers. */
-    if (windata->bo) {
-        KMSDRM_gbm_surface_release_buffer(windata->gs, windata->bo);
-        windata->bo = NULL;
+    /* Try ATOMIC compatibility */
+    ret = check_atomic_modesetting(viddata->drm_fd);
+    if (ret) {
+        ret = SDL_SetError("not compatible with atomic modesetting");
+        goto cleanup;
     }
 
-    if (windata->next_bo) {
-        KMSDRM_gbm_surface_release_buffer(windata->gs, windata->next_bo);
-        windata->next_bo = NULL;
+    /********************************************/
+    /* Block for enabling ATOMIC compatibility. */
+    /********************************************/
+
+    /* Set ATOMIC compatibility */
+    /* TODO: We have just tried ATOMIC compatibility, haven't we? */
+    ret = KMSDRM_drmSetClientCap(viddata->drm_fd, DRM_CLIENT_CAP_ATOMIC, 1);
+    if (ret) {
+        ret = SDL_SetError("no atomic modesetting support.");
+        goto cleanup;
     }
 
-    /* Destroy the EGL surface. */
-#if SDL_VIDEO_OPENGL_EGL
-    /***************************************************************************/
-    /* In this eglMakeCurrent() call, we disable the current EGL surface       */
-    /* because we're going to destroy it, but DON'T disable the EGL context,   */
-    /* because it won't be enabled again until the programs ask for a pageflip */
-    /* so we get to SwapWindow().                                              */
-    /* If we disable the context until then and a program tries to retrieve    */
-    /* the context version info before calling for a pageflip, the program     */
-    /* will get wrong info and we will be in trouble.                          */
-    /***************************************************************************/
-    egl_context = (EGLContext)SDL_GL_GetCurrentContext();
-    SDL_EGL_MakeCurrent(_this, EGL_NO_SURFACE, egl_context);
+    /* Set UNIVERSAL PLANES compatibility */
+    ret = KMSDRM_drmSetClientCap(viddata->drm_fd, DRM_CLIENT_CAP_UNIVERSAL_PLANES, 1);
+    if (ret) {
+        ret = SDL_SetError("no universal planes support.");
+        goto cleanup;
+    }
 
-    if (windata->egl_surface != EGL_NO_SURFACE) {
-        SDL_EGL_DestroySurface(_this, windata->egl_surface);
-        windata->egl_surface = EGL_NO_SURFACE;
-    }   
-#endif
+    /*******************************************/
+    /* Block for getting the ATOMIC resources. */
+    /*******************************************/
 
-    if (windata->gs) {
-        KMSDRM_gbm_surface_destroy(windata->gs);
-        windata->gs = NULL;
+    /* Get all of the available connectors / devices / crtcs */
+    resources = KMSDRM_drmModeGetResources(viddata->drm_fd);
+    if (!resources) {
+        ret = SDL_SetError("drmModeGetResources(%d) failed", viddata->drm_fd);
+        goto cleanup;
     }
-}
 
-int
-KMSDRM_CreateSurfaces(_THIS, SDL_Window * window)
-{
-    SDL_VideoData *viddata = ((SDL_VideoData *)_this->driverdata);
-    SDL_WindowData *windata = (SDL_WindowData *)window->driverdata;
-    SDL_DisplayData *dispdata = (SDL_DisplayData *) SDL_GetDisplayForWindow(window)->driverdata;
-    uint32_t surface_fmt = GBM_FORMAT_ARGB8888;
-    uint32_t surface_flags = GBM_BO_USE_SCANOUT | GBM_BO_USE_RENDERING;
-    uint32_t width, height;
+    /* Iterate on the available connectors to find a connected connector. */
+    for (i = 0; i < resources->count_connectors; i++) {
+        drmModeConnector *conn = KMSDRM_drmModeGetConnector(viddata->drm_fd,
+            resources->connectors[i]);
 
-    /* Destroy the surfaces and buffers before creating the new ones. */
-    KMSDRM_DestroySurfaces(_this, window);
+        if (!conn) {
+            continue;
+        }
 
-    if (((window->flags & SDL_WINDOW_FULLSCREEN_DESKTOP) == SDL_WINDOW_FULLSCREEN_DESKTOP) ||
-       ((window->flags & SDL_WINDOW_FULLSCREEN) == SDL_WINDOW_FULLSCREEN)) {
+        if (conn->connection == DRM_MODE_CONNECTED && conn->count_modes) {
+            SDL_LogDebug(SDL_LOG_CATEGORY_VIDEO, "Found connector %d with %d modes.",
+                         conn->connector_id, conn->count_modes);
+            connector = conn;
 
-        width = dispdata->mode.hdisplay;
-        height = dispdata->mode.vdisplay;
-    } else {
-        width = window->w;
-        height = window->h;
+            break;
+        }
+
+        KMSDRM_drmModeFreeConnector(conn);
     }
 
-    if (!KMSDRM_gbm_device_is_format_supported(viddata->gbm_dev, surface_fmt, surface_flags)) {
-        SDL_LogWarn(SDL_LOG_CATEGORY_VIDEO, "GBM surface format not supported. Trying anyway.");
+    if (!connector) {
+        ret = SDL_SetError("No currently active connector found.");
+        goto cleanup;
     }
 
-    windata->gs = KMSDRM_gbm_surface_create(viddata->gbm_dev, width, height, surface_fmt, surface_flags);
+    /* Try to find the connector's current encoder */
+    for (i = 0; i < resources->count_encoders; i++) {
+        encoder = KMSDRM_drmModeGetEncoder(viddata->drm_fd, resources->encoders[i]);
 
-    if (!windata->gs) {
-        return SDL_SetError("Could not create GBM surface");
-    }
+        if (!encoder) {
+          continue;
+        }
 
-#if SDL_VIDEO_OPENGL_EGL
-    /* We can't get the EGL context yet because SDL_CreateRenderer has not been called,
-       but we need an EGL surface NOW, or GL won't be able to render into any surface
-       and we won't see the first frame. */ 
-    SDL_EGL_SetRequiredVisualId(_this, surface_fmt);
-    windata->egl_surface = SDL_EGL_CreateSurface(_this, (NativeWindowType)windata->gs);
+        if (encoder->encoder_id == connector->encoder_id) {
+            SDL_LogDebug(SDL_LOG_CATEGORY_VIDEO, "Found encoder %d.", encoder->encoder_id);
+            break;
+        }
 
-    if (windata->egl_surface == EGL_NO_SURFACE) {
-        return SDL_SetError("Could not create EGL window surface");
+        KMSDRM_drmModeFreeEncoder(encoder);
+        encoder = NULL;
     }
 
-    /* Take note that we're still missing the EGL contex,
-       so we can get it in SwapWindow, when SDL_CreateRenderer()
-       has already been called. */
-    windata->egl_context_pending = SDL_TRUE;
-#endif
+    if (!encoder) {
+        /* No encoder was connected, find the first supported one */
+        for (i = 0; i < resources->count_encoders; i++) {
+            encoder = KMSDRM_drmModeGetEncoder(viddata->drm_fd, resources->encoders[i]);
 
-    return 0;
-}
+            if (!encoder) {
+              continue;
+            }
 
-void
-KMSDRM_DestroyWindow(_THIS, SDL_Window *window)
-{
-    SDL_WindowData *windata = (SDL_WindowData *) window->driverdata;
-    SDL_VideoData *viddata;
-    unsigned int i, j;
+            for (j = 0; j < connector->count_encoders; j++) {
+                if (connector->encoders[j] == encoder->encoder_id) {
+                    break;
+                }
+            }
 
-    if (!windata) {
-        return;
+            if (j != connector->count_encoders) {
+              break;
+            }
+
+            KMSDRM_drmModeFreeEncoder(encoder);
+            encoder = NULL;
+        }
     }
 
-    KMSDRM_DestroySurfaces(_this, window);
+    if (!encoder) {
+        ret = SDL_SetError("No connected encoder found.");
+        goto cleanup;
+    }
 
-    /********************************************/
-    /* Remove from the internal SDL window list */
-    /********************************************/
-    viddata = windata->viddata;
+    SDL_LogDebug(SDL_LOG_CATEGORY_VIDEO, "Found encoder %d.", encoder->encoder_id);
 
-    for (i = 0; i < viddata->num_windows; i++) {
-        if (viddata->windows[i] == window) {
-            viddata->num_windows--;
+    /* Try to find a CRTC connected to this encoder */
+    crtc = KMSDRM_drmModeGetCrtc(viddata->drm_fd, encoder->crtc_id);
 
-            for (j = i; j < viddata->num_windows; j++) {
-                viddata->windows[j] = viddata->windows[j + 1];
+    /* If no CRTC was connected to the encoder, find the first CRTC
+       that is supported by the encoder, and use that. */
+    if (!crtc) {
+        for (i = 0; i < resources->count_crtcs; i++) {
+            if (encoder->possible_crtcs & (1 << i)) {
+                encoder->crtc_id = resources->crtcs[i];
+                crtc = KMSDRM_drmModeGetCrtc(viddata->drm_fd, encoder->crtc_id);
+                break;
             }
-
-            break;
         }
     }
 
-    /*********************************************************************/
-    /* Free the window driverdata. Bye bye, surface and buffer pointers! */
-    /*********************************************************************/
-    window->driverdata = NULL;
-    SDL_free(windata);
-}
+    if (!crtc) {
+        ret = SDL_SetError("No CRTC found.");
+        goto cleanup;
+    }
 
-/*****************************************************************************/
-/* Reconfigure the window scaling parameters and re-construct it's surfaces, */
-/* without destroying the window itself.                                     */
-/* To be used by SetWindowSize() and SetWindowFullscreen().                  */
-/*****************************************************************************/
-static int
-KMSDRM_ReconfigureWindow( _THIS, SDL_Window * window) {
-    SDL_WindowData *windata = window->driverdata;
-    SDL_DisplayData *dispdata = (SDL_DisplayData *) SDL_GetDisplayForWindow(window)->driverdata;
-    float ratio;  
+    /* Figure out the default mode to be set. */
+    dispdata->mode = crtc->mode;
 
-    if (((window->flags & SDL_WINDOW_FULLSCREEN_DESKTOP) == SDL_WINDOW_FULLSCREEN_DESKTOP) ||
-       ((window->flags & SDL_WINDOW_FULLSCREEN) == SDL_WINDOW_FULLSCREEN)) {
+    /* Find the connector's preferred mode, to be used in case the current mode
+       is not valid, or if restoring the current mode fails.
+       We can always count on the preferred mode! */
+    for (i = 0; i < connector->count_modes; i++) {
+	if (connector->modes[i].type & DRM_MODE_TYPE_PREFERRED) {
+	    dispdata->preferred_mode = connector->modes[i];
+	}
+    }
 
-        windata->src_w = dispdata->mode.hdisplay;
-        windata->src_h = dispdata->mode.vdisplay;
-        windata->output_w = dispdata->mode.hdisplay;
-        windata->output_h = dispdata->mode.vdisplay;
-        windata->output_x = 0;
+    /* If the current CRTC's mode isn't valid, select the preferred
+       mode of the connector. */
+    if (crtc->mode_valid == 0) {
+        dispdata->mode = dispdata->preferred_mode;
+    }
+
+    if (dispdata->mode.hdisplay == 0 || dispdata->mode.vdisplay == 0 ) {
+        ret = SDL_SetError("Couldn't get a valid connector videomode.");
+        goto cleanup;
+    }
+
+    /* Get CRTC properties */
+    dispdata->crtc->props = KMSDRM_drmModeObjectGetProperties(viddata->drm_fd,
+        crtc->crtc_id, DRM_MODE_OBJECT_CRTC);	
+
+    dispdata->crtc->props_info = SDL_calloc(dispdata->crtc->props->count_props,
+        sizeof(*dispdata->crtc->props_info));
     
-    } else {
+    if (!dispdata->crtc->props_info) {
+        ret = SDL_OutOfMemory();
+        goto cleanup;
+    }
 
-        /* Normal non-fullscreen windows are scaled using the CRTC,
-           so get output (CRTC) size and position, for AR correction. */
-        ratio = (float)window->w / (float)window->h;
-        windata->src_w = window->w;
-        windata->src_h = window->h;
-        windata->output_w = dispdata->mode.vdisplay * ratio;
-        windata->output_h = dispdata->mode.vdisplay;
-        windata->output_x = (dispdata->mode.hdisplay - windata->output_w) / 2;
+    for (i = 0; i < dispdata->crtc->props->count_props; i++) {
+        dispdata->crtc->props_info[i] = KMSDRM_drmModeGetProperty(viddata->drm_fd,
+        dispdata->crtc->props->props[i]);
+    }
 
+    /* Get connector properties */
+    dispdata->connector->props = KMSDRM_drmModeObjectGetProperties(viddata->drm_fd,
+        connector->connector_id, DRM_MODE_OBJECT_CONNECTOR);	
+
+    dispdata->connector->props_info = SDL_calloc(dispdata->connector->props->count_props,
+        sizeof(*dispdata->connector->props_info));
+
+    if (!dispdata->connector->props_info) {
+        ret = SDL_OutOfMemory();
+        goto cleanup;
     }
 
-    if (KMSDRM_CreateSurfaces(_this, window)) {
-        return -1; 
-    }  
+    for (i = 0; i < dispdata->connector->props->count_props; i++) {
+        dispdata->connector->props_info[i] = KMSDRM_drmModeGetProperty(viddata->drm_fd,
+        dispdata->connector->props->props[i]);
+    }
+    
+    /* Store the connector and crtc for future use. This is all we keep from this function,
+       and these are just structs, inoffensive to VK. */
+    dispdata->connector->connector = connector;
+    dispdata->crtc->crtc = crtc;
+
+    /***********************************/
+    /* Block fpr Vulkan compatibility. */
+    /***********************************/
+
+    /* THIS IS FOR VULKAN! Leave the FD closed, so VK can work. 
+       Will reopen this in CreateWindow, but only if requested a non-VK window. */
+    KMSDRM_drmSetClientCap(viddata->drm_fd, DRM_CLIENT_CAP_ATOMIC, 0);
+    KMSDRM_drmSetClientCap(viddata->drm_fd, DRM_CLIENT_CAP_UNIVERSAL_PLANES, 0);
+    close (viddata->drm_fd);
+    viddata->drm_fd = -1;
 
-    return 0;
+cleanup:
+    // TODO Use it as a list to see if everything we use here is freed.
+    if (encoder)
+        KMSDRM_drmModeFreeEncoder(encoder);
+    if (resources)
+        KMSDRM_drmModeFreeResources(resources);
+    if (ret != 0) {
+        /* Error (complete) cleanup */
+        if (dispdata->connector->connector) {
+            KMSDRM_drmModeFreeConnector(dispdata->connector->connector);
+            dispdata->connector->connector = NULL;
+        }
+        if (dispdata->crtc->crtc) {
+            KMSDRM_drmModeFreeCrtc(dispdata->crtc->crtc);
+            dispdata->crtc->crtc = NULL;
+        }
+        if (viddata->drm_fd >= 0) {
+            close(viddata->drm_fd);
+            viddata->drm_fd = -1;
+        }
+        SDL_free(dispdata);
+    }
+
+    return ret;
 }
 
+/* Init the Vulkan-INCOMPATIBLE stuff:
+   Reopen FD, create gbm dev, create dumb buffer and setup display plane.
+   This is to be called late, in WindowCreate(), and ONLY if this is not
+   a Vulkan window.
+   We are doing this so late to allow Vulkan to work if we build a VK window.
+   These things are incompatible with Vulkan, which accesses the same resources
+   internally so they must be free when trying to build a Vulkan surface.
+*/
 int
-KMSDRM_VideoInit(_THIS)
+KMSDRM_GBMInit (_THIS, SDL_DisplayData *dispdata)
 {
-    unsigned int i, j;
+    SDL_VideoData *viddata = (SDL_VideoData *)_this->driverdata;
     int ret = 0;
+
+    /* Reopen the FD! */
+    viddata->drm_fd = open(viddata->devpath, O_RDWR | O_CLOEXEC);
+    KMSDRM_drmSetClientCap(viddata->drm_fd, DRM_CLIENT_CAP_ATOMIC, 1);
+    KMSDRM_drmSetClientCap(viddata->drm_fd, DRM_CLIENT_CAP_UNIVERSAL_PLANES, 1);
+
+    /* Create aux dumb buffer. It's only useful to keep the PRIMARY PLANE occupied
+       when we destroy the GBM surface and it's KMS buffers, so not being able to
+       create it is not fatal. */
+    dispdata->dumb_buffer = KMSDRM_CreateBuffer(_this);
+    if (!dispdata->dumb_buffer) {
+        ret = SDL_SetError("can't create dumb buffer.");
+    } else {
+        /* Fill the dumb buffer with black pixels. */
+        KMSDRM_FillDumbBuffer(dispdata->dumb_buffer);
+    }
+
+    /* Create the GBM device. */
+    viddata->gbm_dev = KMSDRM_gbm_create_device(viddata->drm_fd);
+    if (!viddata->gbm_dev) {
+        ret = SDL_SetError("Couldn't create gbm device.");
+    }
+
+    /* Setup the display plane. ONLY do this after dispdata has the right
+       crtc and connector, because these are used in this function. */
+    ret = setup_plane(_this, &(dispdata->display_plane), DRM_PLANE_TYPE_PRIMARY);
+    if (ret) {
+        ret = SDL_SetError("can't find suitable display plane.");
+    }
+
+    dispdata->gbm_init = SDL_TRUE;
+
+    return ret;
+}
+
+/* Deinit the Vulkan-incompatible KMSDRM stuff. */
+void
+KMSDRM_GBMDeinit (_THIS, SDL_DisplayData *dispdata)
+{
+    KMSDRM_PlaneInfo plane_info = {0};
     SDL_VideoData *viddata = ((SDL_VideoData *)_this->driverdata);
-    SDL_DisplayData *dispdata = NULL;
-    drmModeRes *resources = NULL;
-    drmModeEncoder *encoder = NULL;
-    char devname[32];
-    SDL_VideoDisplay display = {0};
+    drmModeModeInfo mode;
 
-    viddata->video_init = SDL_FALSE;
+    uint32_t blob_id;
 
-    dispdata = (SDL_DisplayData *) SDL_calloc(1, sizeof(SDL_DisplayData));
-    if (!dispdata) {
-        return SDL_OutOfMemory();
+    /*****************************************************************/
+    /*                                                               */
+    /* BLOCK to safely destroy the DUMB BUFFER.                      */
+    /*                                                               */
+    /* We come from DestroyWindow(), where both DestroySurfaces()    */
+    /* and this GBMDeinit() are called, one after another.           */
+    /* So the GBM/EGL surfaces and buffers of the windows have       */
+    /* already been destroyed already and, because of that, the      */
+    /* PRIMARY PLANE is using the DUMB BUFFER. BUT the DUMB BUFFER   */
+    /* we use to keep the PRIMARY PLANE occupied when we do          */
+    /* DestroySurfaces calls is going to be destroyed one way or     */
+    /* another when the program quits so, to prevent the the kernel  */
+    /* from disabling the CRTC when it detects the deletion of a     */
+    /* buffer that IS IN USE BY THE PRIMARY PLANE, we do one of      */
+    /* these:                                                        */
+    /*                                                               */
+    /* -In AMDGPU, where manually disabling the CRTC and             */
+    /*  disconnecting the CONNECTOR from the CRTC is an              */
+    /*  unrecoverable situation, we point the PRIMARY PLANE to       */ 
+    /*  the original TTY buffer (not guaranteed to be there for us!) */
+    /*  and then destroy the DUMB BUFFER).                           */
+    /*                                                               */
+    /* -In other drivers, we disconnect the CONNECTOR from the CRTC  */
+    /*  (remember: several connectors can read a CRTC), deactivate   */
+    /*  the CRTC, and set the PRIMARY PLANE props CRTC_ID and FB_ID  */
+    /*  to 0. Then we destroy the DUMB BUFFER.                       */
+    /*  We can leave all like this if we are exiting the program:    */
+    /*  FBCON or whatever will reconfigure things as it needs.       */
+    /*                                                               */
+    /*****************************************************************/
+
+    /* dispdata->crtc->crtc->mode is the original video mode that was
+       configured on the connector when we lauched the program,
+       dispdata->mode is the current video mode, which could be different,
+       and dispdata->preferred_mode is the native display mode. */
+    mode = dispdata->crtc->crtc->mode;
+
+#if AMDGPU_COMPAT
+    /* This won't work if the console wasn't correctly restored when a prevous
+       program exited, because in that case the TTY buffer won't be in
+       crtc->crtc->buffer_id, so the following atomic commit will fail. */
+    plane_info.plane = dispdata->display_plane;
+    plane_info.crtc_id = dispdata->crtc->crtc->crtc_id;
+    plane_info.fb_id = dispdata->crtc->crtc->buffer_id;
+    plane_info.src_w = mode.hdisplay;
+    plane_info.src_h = mode.vdisplay;
+    plane_info.crtc_w = mode.hdisplay;
+    plane_info.crtc_h = mode.vdisplay;
+
+    drm_atomic_set_plane_props(&plane_info);
+#else
+    add_connector_property(dispdata->atomic_req, dispdata->connector , "CRTC_ID", 0);
+    add_crtc_property(dispdata->atomic_req, dispdata->crtc , "ACTIVE", 0);
+
+    /* Since we initialize plane_info to all zeros,
+       ALL PRIMARY PLANE props are set to 0 with this,
+       including FB_ID and CRTC_ID.
+       Not all drivers like FB_ID and CRTC_ID to 0 yet. */
+    plane_info.plane = dispdata->display_plane;
+    drm_atomic_set_plane_props(&plane_info);
+#endif
+
+    /* Set the props that restore the original video mode. */
+    dispdata->atomic_flags |= DRM_MODE_ATOMIC_ALLOW_MODESET;
+    add_connector_property(dispdata->atomic_req, dispdata->connector, "CRTC_ID", dispdata->crtc->crtc->crtc_id);
+    KMSDRM_drmModeCreatePropertyBlob(viddata->drm_fd, &mode, sizeof(mode), &blob_id);
+    add_crtc_property(dispdata->atomic_req, dispdata->crtc, "MODE_ID", blob_id);
+    add_crtc_property(dispdata->atomic_req, dispdata->crtc, "ACTIVE", 1);
+
+    /* Issue blocking atomic commit. */    
+    if (drm_atomic_commit(_this, SDL_TRUE)) {
+        SDL_SetError("Failed to issue atomic commit on video quitting.");
     }
 
-    dispdata->display_plane = SDL_calloc(1, sizeof(*dispdata->display_plane));
-    dispdata->crtc = SDL_calloc(1, sizeof(*dispdata->crtc));
-    dispdata->connector = SDL_calloc(1, sizeof(*dispdata->connector));
-    if (!(dispdata->display_plane) || !(dispdata->crtc) || !(dispdata->connector)) {
-        return SDL_OutOfMemory();
+    /* Destroy the DUMB buffer if it exists, now that it's not being
+       used anymore by the PRIMARY PLANE. */
+    if (dispdata->dumb_buffer) {
+        KMSDRM_DestroyDumbBuffer(_this, &dispdata->dumb_buffer);
     }
 
-    dispdata->atomic_flags = 0;
-    dispdata->atomic_req = NULL;
-    dispdata->kms_fence = NULL;
-    dispdata->gpu_fence = NULL;
-    dispdata->kms_out_fence_fd = -1;
-    dispdata->dumb_buffer = NULL;
-    dispdata->modeset_pending = SDL_FALSE;
+    /*************************************************/
+    /* BLOCK to safely destroy the dumb buffer ENDS. */
+    /*************************************************/
+  
+    /* Free display plane */
+    free_plane(&dispdata->display_plane);
 
-    SDL_LogDebug(SDL_LOG_CATEGORY_VIDEO, "KMSDRM_VideoInit()");
+    /* Free cursor plane (if still not freed) */
+    free_plane(&dispdata->cursor_plane);
 
-    /* Open /dev/dri/cardNN */
-    SDL_snprintf(devname, sizeof(devname), "/dev/dri/card%d", viddata->devindex);
+    /* Destroy GBM device. GBM surface is destroyed by DestroySurfaces(),
+       already called when we get here. */
+    if (viddata->gbm_dev) {
+        KMSDRM_gbm_device_destroy(viddata->gbm_dev);
+        viddata->gbm_dev = NULL;
+    }
 
-    SDL_LogDebug(SDL_LOG_CATEGORY_VIDEO, "Opening device %s", devname);
-    viddata->drm_fd = open(devname, O_RDWR | O_CLOEXEC);
+    /* Finally close DRM FD. May be reopen on next non-vulkan window creation. */
+    if (viddata->drm_fd >= 0) {
+        close(viddata->drm_fd);
+        viddata->drm_fd = -1;
+    }
 
-    if (viddata->drm_fd < 0) {
-        ret = SDL_SetError("Could not open %s", devname);
-        goto cleanup;
+    dispdata->gbm_init = SDL_FALSE;
+}
+
+/* Destroy the window surfaces and buffers. Have the PRIMARY PLANE
+   disconnected from these buffers before doing so, or have the PRIMARY PLANE
+   reading the original FB where the TTY lives, before doing this, or CRTC will
+   be disconnected by the kernel. */
+void
+KMSDRM_DestroySurfaces(_THIS, SDL_Window *window)
+{
+    SDL_WindowData *windata = (SDL_WindowData *) window->driverdata;
+    SDL_DisplayData *dispdata = (SDL_DisplayData *)SDL_GetDisplayDriverData(0);
+    KMSDRM_PlaneInfo plane_info = {0};
+
+#if SDL_VIDEO_OPENGL_EGL
+    EGLContext egl_context;
+#endif
+
+    /********************************************************************/
+    /* BLOCK 1: protect the PRIMARY PLANE before destroying the buffers */
+    /* it's using, by making it point to the dumb buffer.               */
+    /********************************************************************/
+
+    plane_info.plane = dispdata->display_plane;
+    plane_info.crtc_id = dispdata->crtc->crtc->crtc_id;
+    plane_info.fb_id = dispdata->dumb_buffer->fb_id;
+    plane_info.src_w = dispdata->mode.hdisplay;
+    plane_info.src_h = dispdata->mode.vdisplay;
+    plane_info.crtc_w = dispdata->mode.hdisplay;
+    plane_info.crtc_h = dispdata->mode.vdisplay;
+
+    drm_atomic_set_plane_props(&plane_info);
+
+    /* Issue blocking atomic commit. */    
+    if (drm_atomic_commit(_this, SDL_TRUE)) {
+        SDL_SetError("Failed to issue atomic commit on surfaces destruction.");
     }
 
-    SDL_LogDebug(SDL_LOG_CATEGORY_VIDEO, "Opened DRM FD (%d)", viddata->drm_fd);
+    /****************************************************************************/
+    /* BLOCK 2: We can finally destroy the window GBM and EGL surfaces, and     */
+    /* GBM buffers now that the buffers are not being used by the PRIMARY PLANE */
+    /* anymore.                                                                 */
+    /****************************************************************************/
 
-    /* Try ATOMIC compatibility */
+    /* Destroy the GBM surface and buffers. */
+    if (windata->bo) {
+        KMSDRM_gbm_surface_release_buffer(windata->gs, windata->bo);
+        windata->bo = NULL;
+    }
 
-    ret = check_atomic_modesetting(viddata->drm_fd);
-    if (ret) {
-        goto cleanup;
+    if (windata->next_bo) {
+        KMSDRM_gbm_surface_release_buffer(windata->gs, windata->next_bo);
+        windata->next_bo = NULL;
     }
 
-    ret = KMSDRM_drmSetClientCap(viddata->drm_fd, DRM_CLIENT_CAP_UNIVERSAL_PLANES, 1);
-    if (ret) {
-        ret = SDL_SetError("no universal planes support.");
-        goto cleanup;
+
+    /***************************************************************************/
+    /* Destroy the EGL surface.                                                */
+    /* In this eglMakeCurrent() call, we disable the current EGL surface       */
+    /* because we're going to destroy it, but DON'T disable the EGL context,   */
+    /* because it won't be enabled again until the programs ask for a pageflip */
+    /* so we get to SwapWindow().                                              */
+    /* If we disable the context until then and a program tries to retrieve    */
+    /* the context version info before calling for a pageflip, the program     */
+    /* will get wrong info and we will be in trouble.                          */
+    /***************************************************************************/
+
+#if SDL_VIDEO_OPENGL_EGL
+    egl_context = (EGLContext)SDL_GL_GetCurrentContext();
+    SDL_EGL_MakeCurrent(_this, EGL_NO_SURFACE, egl_context);
+
+    if (windata->egl_surface != EGL_NO_SURFACE) {
+        SDL_EGL_DestroySurface(_this, windata->egl_surface);
+        windata->egl_surface = EGL_NO_SURFACE;
+    }  
+#endif
+
+    if (windata->gs) {
+        KMSDRM_gbm_surface_destroy(windata->gs);
+        windata->gs = NULL;
     }
 
-    /* Create the GBM device */
+}
 
-    viddata->gbm_dev = KMSDRM_gbm_create_device(viddata->drm_fd);
-    if (!viddata->gbm_dev) {
-        ret = SDL_SetError("Couldn't create gbm device.");
-        goto cleanup;
+int
+KMSDRM_CreateSurfaces(_THIS, SDL_Window * window)
+{
+    SDL_VideoData *viddata = ((SDL_VideoData *)_this->driverdata);
+    SDL_WindowData *windata = (SDL_WindowData *)window->driverdata;
+    SDL_DisplayData *dispdata = (SDL_DisplayData *) SDL_GetDisplayForWindow(window)->driverdata;
+    uint32_t surface_fmt = GBM_FORMAT_ARGB8888;
+    uint32_t surface_flags = GBM_BO_USE_SCANOUT | GBM_BO_USE_RENDERING;
+    uint32_t width, height;
+
+    EGLContext egl_context; 
+
+    /* Destroy the surfaces and buffers before creating the new ones. */
+    // TODO REENABLE THIS IF CGENIUS FAILS BECAUSE IT CREATES A NEW WINDOW
+    // W/O DESTRYING THE PREVIOUS ONE.
+    //KMSDRM_DestroySurfaces(_this, window);
+
+    if (((window->flags & SDL_WINDOW_FULLSCREEN_DESKTOP) == SDL_WINDOW_FULLSCREEN_DESKTOP) ||
+       ((window->flags & SDL_WINDOW_FULLSCREEN) == SDL_WINDOW_FULLSCREEN)) {
+
+        width = dispdata->mode.hdisplay;
+        height = dispdata->mode.vdisplay;
+    } else {
+        width = window->w;
+        height = window->h;
     }
 
-    /* Get all of the available connectors / devices / crtcs */
-    resources = KMSDRM_drmModeGetResources(viddata->drm_fd);
-    if (!resources) {
-        ret = SDL_SetError("drmModeGetResources(%d) failed", viddata->drm_fd);
-        goto cleanup;
+    if (!KMSDRM_gbm_device_is_format_supported(viddata->gbm_dev, surface_fmt, surface_flags)) {
+        SDL_LogWarn(SDL_LOG_CATEGORY_VIDEO, "GBM surface format not supported. Trying anyway.");
     }
 
-    /* Iterate on the available connectors to find a connected connector. */
-    for (i = 0; i < resources->count_connectors; i++) {
-        drmModeConnector *conn = KMSDRM_drmModeGetConnector(viddata->drm_fd, resources->connectors[i]);
+    windata->gs = KMSDRM_gbm_surface_create(viddata->gbm_dev, width, height, surface_fmt, surface_flags);
 
-        if (!conn) {
-            continue;
-        }
+    if (!windata->gs) {
+        return SDL_SetError("Could not create GBM surface");
+    }
 
-        if (conn->connection == DRM_MODE_CONNECTED && conn->count_modes) {
-            SDL_LogDebug(SDL_LOG_CATEGORY_VIDEO, "Found connector %d with %d modes.",
-                         conn->connector_id, conn->count_modes);
-            dispdata->connector->connector = conn;
-            break;
-        }
+#if SDL_VIDEO_OPENGL_EGL //TODO Restore this lo LibraryLoad and Unload calls.
+    /* We can't get the EGL context yet because SDL_CreateRenderer has not been called,
+       but we need an EGL surface NOW, or GL won't be able to render into any surface
+       and we won't see the first frame. */ 
+    SDL_EGL_SetRequiredVisualId(_this, surface_fmt);
+    windata->egl_surface = SDL_EGL_CreateSurface(_this, (NativeWindowType)windata->gs);
 
-        KMSDRM_drmModeFreeConnector(conn);
+    if (windata->egl_surface == EGL_NO_SURFACE) {
+        return SDL_SetError("Could not create EGL window surface");
     }
 
-    if (!dispdata->connector->connector) {
-        ret = SDL_SetError("No currently active connector found.");
-        goto cleanup;
+    /* Current context passing to EGL is now done here. If something fails,
+       go back to delayed SDL_EGL_MakeCurrent() call in SwapWindow. */
+    /* TODO Errorcheck on this (may lead to delayed call again...) */
+    egl_context = (EGLContext)SDL_GL_GetCurrentContext();
+    SDL_EGL_MakeCurrent(_this, windata->egl_surface, egl_context);
+
+#endif
+
+    return 0;
+}
+
+void
+KMSDRM_DestroyWindow(_THIS, SDL_Window *window)
+{
+    SDL_WindowData *windata = (SDL_WindowData *) window->driverdata;
+    SDL_DisplayData *dispdata = (SDL_DisplayData *) SDL_GetDisplayForWindow(window)->driverdata;
+    SDL_VideoData *viddata = windata->viddata;
+    SDL_bool is_vulkan = window->flags & SDL_WINDOW_VULKAN; /* Is this a VK window? */
+    unsigned int i, j;
+
+    if (!windata) {
+        return;
     }
 
-    /* Try to find the connector's current encoder */
-    for (i = 0; i < resources->count_encoders; i++) {
-        encoder = KMSDRM_drmModeGetEncoder(viddata->drm_fd, resources->encoders[i]);
+    if (!is_vulkan) {
 
-        if (!encoder) {
-          continue;
-        }
+        KMSDRM_DestroySurfaces(_this, window);
 
-        if (encoder->encoder_id == dispdata->connector->connector->encoder_id) {
-            SDL_LogDebug(SDL_LOG_CATEGORY_VIDEO, "Found encoder %d.", encoder->encoder_id);
-            break;
+        KMSDRM_DeinitMouse(_this);
+ 
+        if (_this->egl_data) {
+	    SDL_EGL_UnloadLibrary(_this);
         }
 
-        KMSDRM_drmModeFreeEncoder(encoder);
-        encoder = NULL;
+        if (dispdata->gbm_init) {
+            KMSDRM_GBMDeinit(_this, dispdata);
+        } 
     }
 
-    if (!encoder) {
-        /* No encoder was connected, find the first supported one */
-        for (i = 0; i < resources->count_encoders; i++) {
-            encoder = KMSDRM_drmModeGetEncoder(viddata->drm_fd, resources->encoders[i]);
+#if AMDGPU_COMPAT
+    /* Since vkDestroySurfaceKHR does not destroy the native surface (only the Vulkan one),
+       runnin Vulkan programs leave the last buffer connected to the primary plane,
+       at least on AMDGPU, and the TTY buffer isn't restored.
+       That's not a big problem, but running a GLES program after that will cause the
+       atomic commit we do for restoring the TTY buffer to fail, because the TTY buffer
+       isn't there when we launch the GLES program.
+       So what we do here is "hack" Vulkan and restore the TTY buffer here, as if
+       we were running a GLES program. We get here after the program's vkDestroySurfaceKHR
+       has been called, which allows us to think as if we were in the beginig
+       of a GLES program.
+       THIS IS DONE IN A SEPARATE BLOCK FOR POSSIBLE EASY FUTURE REMOVAL. DON'T OPTIMIZE. */
+
+    if (is_vulkan) {
+         KMSDRM_GBMInit(_this, dispdata);
+         KMSDRM_GBMDeinit(_this, dispdata);
+    }
+#endif
 
-            if (!encoder) {
-              continue;
-            }
+    /********************************************/
+    /* Remove from the internal SDL window list */
+    /********************************************/
 
-            for (j = 0; j < dispdata->connector->connector->count_encoders; j++) {
-                if (dispdata->connector->connector->encoders[j] == encoder->encoder_id) {
-                    break;
-                }
-            }
+    for (i = 0; i < viddata->num_windows; i++) {
+        if (viddata->windows[i] == window) {
+            viddata->num_windows--;
 
-            if (j != dispdata->connector->connector->count_encoders) {
-              break;
+            for (j = i; j < viddata->num_windows; j++) {
+                viddata->windows[j] = viddata->windows[j + 1];
             }
 
-            KMSDRM_drmModeFreeEncoder(encoder);
-            encoder = NULL;
-        }
+            break;
+        }
+    }
+
+    /*********************************************************************/
+    /* Free the window driverdata. Bye bye, surface and buffer pointers! */
+    /*********************************************************************/
+    window->driverdata = NULL;
+    SDL_free(windata);
+}
+
+/*****************************************************************************/
+/* Reconfigure the window scaling parameters and re-construct it's surfaces, */
+/* without destroying the window itself.                                     */
+/* To be used by SetWindowSize() and SetWindowFullscreen().                  */
+/*****************************************************************************/
+static int
+KMSDRM_ReconfigureWindow( _THIS, SDL_Window * window) {
+    SDL_WindowData *windata = window->driverdata;
+    SDL_DisplayData *dispdata = (SDL_DisplayData *) SDL_GetDisplayForWindow(window)->driverdata;
+    SDL_bool is_vulkan = window->flags & SDL_WINDOW_VULKAN; /* Is this a VK window? */
+    float ratio;  
+
+    if (((window->flags & SDL_WINDOW_FULLSCREEN_DESKTOP) == SDL_WINDOW_FULLSCREEN_DESKTOP) ||
+       ((window->flags & SDL_WINDOW_FULLSCREEN) == SDL_WINDOW_FULLSCREEN)) {
+
+        windata->src_w = dispdata->mode.hdisplay;
+        windata->src_h = dispdata->mode.vdisplay;
+        windata->output_w = dispdata->mode.hdisplay;
+        windata->output_h = dispdata->mode.vdisplay;
+        windata->output_x = 0;
+    
+    } else {
+
+        /* Normal non-fullscreen windows are scaled using the CRTC,
+           so get output (CRTC) size and position, for AR correction. */
+        ratio = (float)window->w / (float)window->h;
+        windata->src_w = window->w;
+        windata->src_h = window->h;
+        windata->output_w = dispdata->mode.vdisplay * ratio;
+        windata->output_h = dispdata->mode.vdisplay;
+        windata->output_x = (dispdata->mode.hdisplay - windata->output_w) / 2;
+
     }
 
-    if (!encoder) {
-        ret = SDL_SetError("No connected encoder found.");
-        goto cleanup;
+    if (!is_vulkan) {
+        if (KMSDRM_CreateSurfaces(_this, window)) {
+            return -1; 
+        }  
     }
+    return 0;
+}
 
-    SDL_LogDebug(SDL_LOG_CATEGORY_VIDEO, "Found encoder %d.", encoder->encoder_id);
+int
+KMSDRM_VideoInit(_THIS)
+{
+    int ret = 0;
 
-    /* Try to find a CRTC connected to this encoder */
-    dispdata->crtc->crtc = KMSDRM_drmModeGetCrtc(viddata->drm_fd, encoder->crtc_id);
+    SDL_VideoData *viddata = ((SDL_VideoData *)_this->driverdata);
+    SDL_DisplayData *dispdata = NULL;
+    SDL_VideoDisplay display = {0};
 
-    /* If no CRTC was connected to the encoder, find the first CRTC that is supported by the encoder, and use that. */
-    if (!dispdata->crtc->crtc) {
-        for (i = 0; i < resources->count_crtcs; i++) {
-            if (encoder->possible_crtcs & (1 << i)) {
-                encoder->crtc_id = resources->crtcs[i];
-                dispdata->crtc->crtc = KMSDRM_drmModeGetCrtc(viddata->drm_fd, encoder->crtc_id);
-                break;
-            }
-        }
+    SDL_LogDebug(SDL_LOG_CATEGORY_VIDEO, "KMSDRM_VideoInit()");
+
+    viddata->video_init = SDL_FALSE;
+
+    dispdata = (SDL_DisplayData *) SDL_calloc(1, sizeof(SDL_DisplayData));
+    if (!dispdata) {
+        return SDL_OutOfMemory();
     }
 
-    if (!dispdata->crtc->crtc) {
-        ret = SDL_SetError("No CRTC found.");
+    /* Alloc memory for these. */
+    dispdata->display_plane = SDL_calloc(1, sizeof(*dispdata->display_plane));
+    dispdata->crtc = SDL_calloc(1, sizeof(*dispdata->crtc));
+    dispdata->connector = SDL_calloc(1, sizeof(*dispdata->connector));
+    if (!(dispdata->display_plane) || !(dispdata->crtc) || !(dispdata->connector)) {
+        ret = SDL_OutOfMemory();
         goto cleanup;
     }
 
-    /* Figure out the default mode to be set. If the current CRTC's mode isn't
-       valid, select the first mode supported by the connector
-
-       FIXME find first mode that specifies DRM_MODE_TYPE_PREFERRED */
-    dispdata->mode = dispdata->crtc->crtc->mode;
+    /* Setup the single display that's available.
+       There's no problem with it being still incomplete. */
+    display.driverdata = dispdata;
 
-    if (dispdata->crtc->crtc->mode_valid == 0) {
-        SDL_LogDebug(SDL_LOG_CATEGORY_VIDEO,
-            "Current mode is invalid, selecting connector's mode #0.");
-        dispdata->mode = dispdata->connector->connector->modes[0];
+    /* Get KMSDRM resources info and store what we need. Getting and storing
+       this info isn't a problem for VK compatibility.
+       For VK-incompatible initializations we have KMSDRM_GBMInit(), which is
+       called on window creation, and only when we know it's not a VK window. */
+    if (KMSDRM_DisplayDataInit(_this, dispdata)) {
+        ret = SDL_SetError("error getting KMS/DRM information");
+        goto cleanup;
     }
 
-    /* Setup the single display that's available */
-
     display.desktop_mode.w = dispdata->mode.hdisplay;
     display.desktop_mode.h = dispdata->mode.vdisplay;
     display.desktop_mode.refresh_rate = dispdata->mode.vrefresh;
-#if 1
     display.desktop_mode.format = SDL_PIXELFORMAT_ARGB8888;
-#else
-    /* FIXME */
-    drmModeFB *fb = drmModeGetFB(viddata->drm_fd, dispdata->crtc->buffer_id);
-    display.desktop_mode.format = drmToSDLPixelFormat(fb->bpp, fb->depth);
-    drmModeFreeFB(fb);
-#endif
-
     display.current_mode = display.desktop_mode;
-    display.driverdata = dispdata;
+
+    /* Add the display only when it's ready, */
     SDL_AddVideoDisplay(&display, SDL_FALSE);
 
     /* Use this if you ever need to see info on all available planes. */
@@ -1348,263 +1727,48 @@ KMSDRM_VideoInit(_THIS)
     get_planes_info(_this);
 #endif
 
-    /* Setup display plane */
-    ret = setup_plane(_this, &(dispdata->display_plane), DRM_PLANE_TYPE_PRIMARY);
-    if (ret) {
-        ret = SDL_SetError("can't find suitable display plane.");
-        goto cleanup;
-    }
-
-    /* Get CRTC properties */
-    dispdata->crtc->props = KMSDRM_drmModeObjectGetProperties(viddata->drm_fd,
-        dispdata->crtc->crtc->crtc_id, DRM_MODE_OBJECT_CRTC);	
-
-    dispdata->crtc->props_info = SDL_calloc(dispdata->crtc->props->count_props,
-        sizeof(*dispdata->crtc->props_info));
-    
-    if (!dispdata->crtc->props_info) {
-        ret = SDL_OutOfMemory();
-        goto cleanup;
-    }
-
-    for (i = 0; i < dispdata->crtc->props->count_props; i++) {
-        dispdata->crtc->props_info[i] = KMSDRM_drmModeGetProperty(viddata->drm_fd,
-        dispdata->crtc->props->props[i]);
-    }
-
-    /* Get connector properties */
-    dispdata->connector->props = KMSDRM_drmModeObjectGetProperties(viddata->drm_fd,
-        dispdata->connector->connector->connector_id, DRM_MODE_OBJECT_CONNECTOR);	
-
-    dispdata->connector->props_info = SDL_calloc(dispdata->connector->props->count_props,
-        sizeof(*dispdata->connector->props_info));
-
-    if (!dispdata->connector->props_info) {
-        ret = SDL_OutOfMemory();
-        goto cleanup;
-    }
-
-    for (i = 0; i < dispdata->connector->props->count_props; i++) {
-        dispdata->connector->props_info[i] = KMSDRM_drmModeGetProperty(viddata->drm_fd,
-        dispdata->connector->props->props[i]);
-    }
-
-    /* Create aux dumb buffer. It's only useful to keep the PRIMARY PLANE occupied
-       when we destroy the GBM surface and it's KMS buffers, so not being able to
-       create it is not fatal. */
-    dispdata->dumb_buffer = KMSDRM_CreateBuffer(_this);
-    if (!dispdata->dumb_buffer) {
-        ret = SDL_SetError("can't create dumb buffer.");
-    } else {
-        /* Fill the dumb buffer with black pixels. */
-        KMSDRM_FillDumbBuffer(dispdata->dumb_buffer);
-    }
-
-    /*********************/
-    /* Atomic block ends */
-    /*********************/
-
 #ifdef SDL_INPUT_LINUXEV
     SDL_EVDEV_Init();
 #endif
 
-    KMSDRM_InitMouse(_this);
-
     viddata->video_init = SDL_TRUE;
 
 cleanup:
 
-    if (encoder)
-        KMSDRM_drmModeFreeEncoder(encoder);
-    if (resources)
-        KMSDRM_drmModeFreeResources(resources);
-    if (ret != 0) {
-        /* Error (complete) cleanup */
-        if (dispdata->connector->connector) {
-            KMSDRM_drmModeFreeConnector(dispdata->connector->connector);
-            dispdata->connector = NULL;
-        }
-        if (dispdata->crtc->crtc) {
-            KMSDRM_drmModeFreeCrtc(dispdata->crtc->crtc);
-            dispdata->crtc = NULL;
-        }
-        if (viddata->gbm_dev) {
-            KMSDRM_gbm_device_destroy(viddata->gbm_dev);
-            viddata->gbm_dev = NULL;
-        }
-        if (viddata->drm_fd >= 0) {
-            close(viddata->drm_fd);
-            viddata->drm_fd = -1;
-        }
-        SDL_free(dispdata);
+    if (ret) {
+	if (dispdata->display_plane) {
+	    SDL_free(dispdata->display_plane);
+	}
+	if (dispdata->crtc) {
+	    SDL_free(dispdata->crtc);
+	}
+	if (dispdata->connector) {
+	    SDL_free(dispdata->connector);
+	}
     }
 
     return ret;
 }
 
-/* The driverdata pointers, like dispdata, viddata, etc...
+/* The driverdata pointers, like dispdata, viddata, windata, etc...
    are freed by SDL internals, so not our job. */
 void
 KMSDRM_VideoQuit(_THIS)
 {
     SDL_VideoData *viddata = ((SDL_VideoData *)_this->driverdata);
-    SDL_DisplayData *dispdata;
-
-    KMSDRM_PlaneInfo plane_info = {0};
-    drmModeModeInfo mode;
-
-    uint32_t blob_id;
-
-    /* Video was not initialized properly, hence SDL internals
-       called VideoQuit(). We will segault somewhere if we go further. */
-    if (!viddata->video_init) {
-        return;
-    }
-
-    /* Don't call any SDL_GetDisplay* function until we get sure that
-       VideoInit() succeeded, because the SDL_GetDisplay* call would
-       throw it's own SDL_SetError(), overwritting the error set in
-       the failed VideoInit() call. */
-    dispdata = (SDL_DisplayData *)SDL_GetDisplayDriverData(0);
-
-    /*****************************************************************/
-    /*                                                               */
-    /* BLOCK to safely destroy the DUMB BUFFER.                      */
-    /*                                                               */
-    /* Since the program should already have called DestroyWindow()  */
-    /* on all the windows by now, there's no need to destroy the     */
-    /* GBM/EGL surfaces and buffers of the windows here: they have   */
-    /* already been destroyed, and the PRIMARY PLANE is using the    */
-    /* DUMB BUFFER. BUT the DUMB BUFFER we use to keep the PRIMARY   */
-    /* PLANE occupied when we do DestroySurfaces calls is going to   */
-    /* be destroyed one way or another when the program quits, so    */
-    /* to avoid the kernel disabling the CRTC when it detects the    */
-    /* deletion of a buffer that IS IN USE BY THE PRIMARY PLANE,     */
-    /* we do one of these:                                           */
-    /*                                                               */
-    /* -In AMDGPU, where manually disabling the CRTC and             */
-    /*  disconnecting the CONNECTOR from the CRTC is an              */
-    /*  unrecoverable situation, so we point the PRIMARY PLANE to    */ 
-    /*  the original TTY buffer (not guaranteed to be there for us!) */
-    /*  and then destroy the DUMB BUFFER).                           */
-    /*                                                               */
-    /* -In other drivers, we disconnect the CONNECTOR from the CRTC  */
-    /*  (remember: several connectors can read a CRTC), deactivate   */
-    /*  the CRTC, and set the PRIMARY PLANE props CRTC_ID and FB_ID  */
-    /*  to 0. Then we destroy the DUMB BUFFER.                       */
-    /*  We can leave all like this if we are exiting the program:    */
-    /*  FBCON or whatever will reconfigure things as it needs.       */
-    /*                                                               */
-    /*****************************************************************/
-
-    mode = dispdata->crtc->crtc->mode;
-
-#if AMDGPU_COMPAT
-    plane_info.plane = dispdata->display_plane;
-    plane_info.crtc_id = dispdata->crtc->crtc->crtc_id;
-    plane_info.fb_id = dispdata->crtc->crtc->buffer_id;
-    plane_info.src_w = mode.hdisplay;
-    plane_info.src_h = mode.vdisplay;
-    plane_info.crtc_w = mode.hdisplay;
-    plane_info.crtc_h = mode.vdisplay;
-
-    drm_atomic_set_plane_props(&plane_info);
-
-#else
-
-    add_connector_property(dispdata->atomic_req, dispdata->connector , "CRTC_ID", 0);
-    add_crtc_property(dispdata->atomic_req, dispdata->crtc , "ACTIVE", 0);
-
-    /* Since we initialize plane_info to all zeros,  ALL PRIMARY PLANE props are set to 0 with this,
-       including FB_ID and CRTC_ID. Not all drivers like FB_ID and CRTC_ID to 0 yet. */
-    plane_info.plane = dispdata->display_plane;
+    SDL_DisplayData *dispdata = (SDL_DisplayData *)SDL_GetDisplayDriverData(0);
 
-    drm_atomic_set_plane_props(&plane_info);
+    KMSDRM_DisplayDataDeinit(_this, dispdata);
 
+#ifdef SDL_INPUT_LINUXEV
+    SDL_EVDEV_Quit();
 #endif
 
-    /* Set props that restore the original video mode. */
-    dispdata->atomic_flags |= DRM_MODE_ATOMIC_ALLOW_MODESET;
-    add_connector_property(dispdata->atomic_req, dispdata->connector, "CRTC_ID", dispdata->crtc->crtc->crtc_id);
-    KMSDRM_drmModeCreatePropertyBlob(viddata->drm_fd, &mode, sizeof(mode), &blob_id);
-    add_crtc_property(dispdata->atomic_req, dispdata->crtc, "MODE_ID", blob_id);
-    add_crtc_property(dispdata->atomic_req, dispdata->crtc, "ACTIVE", 1);
-
-    /* Issue blocking atomic commit. */    
-    if (drm_atomic_commit(_this, SDL_TRUE)) {
-        SDL_SetError("Failed to issue atomic commit on video quitting.");
-    }
-
-    /* Destroy the DUMB buffer if it exists, now that it's not being
-       used anymore by the PRIMARY PLANE. */
-    if (dispdata->dumb_buffer) {
-        KMSDRM_DestroyDumbBuffer(_this, dispdata->dumb_buffer);
-    }
-
-    /***************/
-    /* BLOCK ENDS. */
-    /***************/
-
     /* Clear out the window list */
     SDL_free(viddata->windows);
     viddata->windows = NULL;
     viddata->max_windows = 0;
     viddata->num_windows = 0;
-
-#if SDL_VIDEO_OPENGL_EGL
-    if (_this->gl_config.driver_loaded) {
-        SDL_GL_UnloadLibrary();
-    }
-#endif
-
-    /* Free connector */
-    if (dispdata && dispdata->connector) {
-        if (dispdata->connector->connector) {
-            KMSDRM_drmModeFreeConnector(dispdata->connector->connector);
-            dispdata->connector->connector = NULL;
-        }
-        if (dispdata->connector->props_info) {
-            SDL_free(dispdata->connector->props_info); 
-            dispdata->connector->props_info = NULL;
-        }
-        SDL_free(dispdata->connector); 
-        dispdata->connector = NULL;
-    }
-
-    /* Free CRTC */
-    if (dispdata && dispdata->crtc) {
-        if (dispdata->crtc->crtc) {
-            KMSDRM_drmModeFreeCrtc(dispdata->crtc->crtc);
-            dispdata->crtc->crtc = NULL;
-        }
-        if (dispdata->crtc->props_info) {
-            SDL_free(dispdata->crtc->props_info); 
-            dispdata->crtc->props_info = NULL;
-        }
-        SDL_free(dispdata->crtc); 
-        dispdata->crtc = NULL;
-    }
-
-    /* Free display plane */
-    free_plane(&dispdata->display_plane);
-
-    /* Free cursor plane (if still not freed) */
-    free_plane(&dispdata->cursor_plane);
-
-    /* Destroy GBM device. GBM surface is destroyed by DestroySurfaces(),
-       already called by DestroyWindow() when we get here. */
-    if (viddata->gbm_dev) {
-        KMSDRM_gbm_device_destroy(viddata->gbm_dev);
-        viddata->gbm_dev = NULL;
-    }
-    if (viddata->drm_fd >= 0) {
-        close(viddata->drm_fd);
-        SDL_LogDebug(SDL_LOG_CATEGORY_VIDEO, "Closed DRM FD %d", viddata->drm_fd);
-        viddata->drm_fd = -1;
-    }
-#ifdef SDL_INPUT_LINUXEV
-    SDL_EVDEV_Quit();
-#endif
     viddata->video_init = SDL_FALSE;
 }
 
@@ -1690,19 +1854,36 @@ KMSDRM_SetDisplayMode(_THIS, SDL_VideoDisplay * display, SDL_DisplayMode * mode)
 int
 KMSDRM_CreateWindow(_THIS, SDL_Window * window)
 {
-    SDL_VideoData *viddata = (SDL_VideoData *)_this->driverdata;
-    SDL_VideoDisplay *display = NULL;
-    SDL_DisplayData *dispdata = NULL;
     SDL_WindowData *windata = NULL;
+    SDL_VideoData *viddata = (SDL_VideoData *)_this->driverdata;
+    SDL_VideoDisplay *display = SDL_GetDisplayForWindow(window);
+    SDL_DisplayData *dispdata = display->driverdata;
+    SDL_bool is_vulkan = window->flags & SDL_WINDOW_VULKAN; /* Is this a VK window? */
+    NativeDisplayType egl_display;
+
     float ratio;
 
-#if SDL_VIDEO_OPENGL_EGL
-    if (!_this->egl_data) {
-        if (SDL_GL_LoadLibrary(NULL) < 0) {
-            goto error;
-        }
+    if ( !(dispdata->gbm_init) && (!is_vulkan)) {
+         /* Reopen FD, create gbm dev, setup display plane, etc,.
+            but only when we come here for the first time,
+            and only if it's not a VK window. */
+         KMSDRM_GBMInit(_this, dispdata);
+
+         /* Manually load the EGL library. KMSDRM_EGL_LoadLibrary() has already
+            been called by SDL_CreateWindow() but we don't do anything there,
+            precisely to be able to load it here.
+            If we let SDL_CreateWindow() load the lib, it will be loaded
+            before we call KMSDRM_GBMInit(), causing GLES programs to fail. */
+         // TODO errorcheck the return value of this.
+         if (!_this->egl_data) {
+             egl_display = (NativeDisplayType)((SDL_VideoData *)_this->driverdata)->gbm_dev;
+             SDL_EGL_LoadLibrary(_this, NULL, egl_display, EGL_PLATFORM_GBM_MESA);
+         }
+
+         /* Can't init mouse sooner because planes are not ready. */
+         // TODO Add a mouse_init bool and use it to avoid double intializations.
+         KMSDRM_InitMouse(_this);
     }
-#endif
 
     /* Allocate window internal data */
     windata = (SDL_WindowData *)SDL_calloc(1, sizeof(SDL_WindowData));
@@ -1711,9 +1892,6 @@ KMSDRM_CreateWindow(_THIS, SDL_Window * window)
         goto error;
     }
 
-    display = SDL_GetDisplayForWindow(window);
-    dispdata = display->driverdata;
-
     if (((window->flags & SDL_WINDOW_FULLSCREEN_DESKTOP) == SDL_WINDOW_FULLSCREEN_DESKTOP) ||
        ((window->flags & SDL_WINDOW_FULLSCREEN) == SDL_WINDOW_FULLSCREEN))
     {
@@ -1744,8 +1922,10 @@ KMSDRM_CreateWindow(_THIS, SDL_Window * window)
     windata->viddata = viddata;
     window->driverdata = windata;
 
-    if (KMSDRM_CreateSurfaces(_this, window)) {
-        goto error;
+    if (!is_vulkan) {
+        if (KMSDRM_CreateSurfaces(_this, window)) {
+            goto error;
+        }
     }
 
     /* Add window to the internal list of tracked windows. Note, while it may

+ 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 <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_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 <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_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: */