Commit 31b179453468ae7913c193fcf8e3876b221fcc5c

Manuel Alfayate Corchete 2020-08-28T22:38:26

kmsdrm: use PLANE and CRTC to do hardware-driven window scaling and AR-correction.

diff --git a/src/video/kmsdrm/SDL_kmsdrmopengles.c b/src/video/kmsdrm/SDL_kmsdrmopengles.c
index 53c6fcd..01d5a8b 100644
--- a/src/video/kmsdrm/SDL_kmsdrmopengles.c
+++ b/src/video/kmsdrm/SDL_kmsdrmopengles.c
@@ -142,10 +142,12 @@ KMSDRM_GLES_SwapWindow(_THIS, SDL_Window * window)
     info.plane = dispdata->display_plane;
     info.crtc_id = dispdata->crtc->crtc->crtc_id;
     info.fb_id = fb->fb_id;
-    info.src_w = dispdata->mode.hdisplay;
-    info.src_h = dispdata->mode.vdisplay;
-    info.crtc_w = dispdata->mode.hdisplay;
-    info.crtc_h = dispdata->mode.vdisplay;
+
+    info.src_w = window->w;
+    info.src_h = window->h;
+    info.crtc_w = windata->output_w;
+    info.crtc_h = windata->output_h;
+    info.crtc_x = windata->output_x;
 
     ret = drm_atomic_set_plane_props(&info);
      if (ret) {
@@ -237,10 +239,12 @@ KMSDRM_GLES_SwapWindowDB(_THIS, SDL_Window * window)
     info.plane = dispdata->display_plane;
     info.crtc_id = dispdata->crtc->crtc->crtc_id;
     info.fb_id = fb->fb_id;
-    info.src_w = dispdata->mode.hdisplay;
-    info.src_h = dispdata->mode.vdisplay;
-    info.crtc_w = dispdata->mode.hdisplay;
-    info.crtc_h = dispdata->mode.vdisplay;
+
+    info.src_w = window->w;
+    info.src_h = window->h;
+    info.crtc_w = windata->output_w;
+    info.crtc_h = windata->output_h;
+    info.crtc_x = windata->output_x;
 
     ret = drm_atomic_set_plane_props(&info);
      if (ret) {
diff --git a/src/video/kmsdrm/SDL_kmsdrmvideo.c b/src/video/kmsdrm/SDL_kmsdrmvideo.c
index 7678812..4756a84 100644
--- a/src/video/kmsdrm/SDL_kmsdrmvideo.c
+++ b/src/video/kmsdrm/SDL_kmsdrmvideo.c
@@ -629,6 +629,7 @@ KMSDRM_CreateDevice(int devindex)
     device->SetWindowIcon = KMSDRM_SetWindowIcon;
     device->SetWindowPosition = KMSDRM_SetWindowPosition;
     device->SetWindowSize = KMSDRM_SetWindowSize;
+    device->SetWindowFullscreen = KMSDRM_SetWindowFullscreen;
     device->ShowWindow = KMSDRM_ShowWindow;
     device->HideWindow = KMSDRM_HideWindow;
     device->RaiseWindow = KMSDRM_RaiseWindow;
@@ -814,9 +815,8 @@ 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 width = dispdata->mode.hdisplay;
-    Uint32 height = dispdata->mode.vdisplay;
+    Uint32 width = window->w;
+    Uint32 height = window->h;
     Uint32 surface_fmt = GBM_FORMAT_ARGB8888;
     Uint32 surface_flags = GBM_BO_USE_SCANOUT | GBM_BO_USE_RENDERING;
 #if SDL_VIDEO_OPENGL_EGL
@@ -1273,12 +1273,12 @@ int
 KMSDRM_SetDisplayMode(_THIS, SDL_VideoDisplay * display, SDL_DisplayMode * mode)
 {
     /************************************************************************/
-    /* DO NOT add dynamic videomode changes unless you can REALLY test      */
-    /* on all available KMS drivers and fix them in-kernel, and also test   */
-    /* all SDL2 software: things will fail one way or another, and it       */
-    /* greatly increases backend complexiity thus compromising it's         */
-    /* maintenance. It's NOT as easy as reconstructing GBM and EGL surfaces.*/
-    /************************************************************************/
+    /* DO NOT add dynamic videomode changes. It makes NO SENSE since the    */
+    /* PRIMARY PLANE and the CRTC reading it can be used to scale image,    */
+    /* so any window will appear fullscren with AR correction with NO extra */
+    /* video memory bandwidth usage.                                        */
+    /************************************************************************/    
+
     return 0;
 }
 
@@ -1287,7 +1287,9 @@ 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;
+    float ratio;
 
 #if SDL_VIDEO_OPENGL_EGL
     if (!_this->egl_data) {
@@ -1301,10 +1303,22 @@ KMSDRM_CreateWindow(_THIS, SDL_Window * window)
     windata = (SDL_WindowData *)SDL_calloc(1, sizeof(SDL_WindowData));
 
     display = SDL_GetDisplayForWindow(window);
-
-    /* Windows have one size for now */
-    window->w = display->desktop_mode.w;
-    window->h = display->desktop_mode.h;
+    dispdata = display->driverdata;
+
+    if (window->flags & SDL_WINDOW_FULLSCREEN) {
+        /* Windows only have one possible size in fullscreen mode. */
+        window->w = dispdata->mode.hdisplay;
+        window->h = dispdata->mode.vdisplay;
+        windata->output_w = dispdata->mode.hdisplay;
+        windata->output_h = dispdata->mode.vdisplay;
+        windata->output_x = 0;
+    } else {
+        /* Get output (CRTC) size and position, for AR correction. */
+        ratio = (float)window->w / (float)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;
+    }
 
     /* Don't force fullscreen on all windows: it confuses programs that try
        to set a window fullscreen after creating it as non-fullscreen (sm64ex) */
@@ -1401,6 +1415,36 @@ KMSDRM_SetWindowSize(_THIS, SDL_Window * window)
 {
 }
 void
+KMSDRM_SetWindowFullscreen(_THIS, SDL_Window * window, SDL_VideoDisplay * display, SDL_bool fullscreen)
+{
+
+    SDL_WindowData *windata = window->driverdata;
+    SDL_DisplayData *dispdata = display->driverdata;
+    float ratio;  
+
+    KMSDRM_SetPendingSurfacesDestruction(_this, window);
+
+    if (fullscreen) {
+        /* Windows only have one possible size in fullscreen mode. */
+        window->w = dispdata->mode.hdisplay;
+        window->h = dispdata->mode.vdisplay;
+        windata->output_w = dispdata->mode.hdisplay;
+        windata->output_h = dispdata->mode.vdisplay;
+        windata->output_x = 0;
+
+    } else {
+        /* Get output (CRTC) size and position, for AR correction. */
+        ratio = (float)window->w / (float)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 (KMSDRM_CreateSurfaces(_this, window)) {
+        SDL_SetError("Can't recreate window surfaces on SetWindowFullscreen.");
+    }
+}
+void
 KMSDRM_ShowWindow(_THIS, SDL_Window * window)
 {
 }
diff --git a/src/video/kmsdrm/SDL_kmsdrmvideo.h b/src/video/kmsdrm/SDL_kmsdrmvideo.h
index 0b6543d..72acb10 100644
--- a/src/video/kmsdrm/SDL_kmsdrmvideo.h
+++ b/src/video/kmsdrm/SDL_kmsdrmvideo.h
@@ -125,6 +125,12 @@ typedef struct SDL_WindowData
 #if SDL_VIDEO_OPENGL_EGL
     EGLSurface egl_surface;
 #endif
+
+    /* For scaling and AR correction. */
+    int32_t output_w;
+    int32_t output_h;
+    int32_t output_x;
+
 } SDL_WindowData;
 
 typedef struct KMSDRM_FBInfo
@@ -133,21 +139,19 @@ typedef struct KMSDRM_FBInfo
     uint32_t fb_id;     /* DRM framebuffer ID */
 } KMSDRM_FBInfo;
 
-/* Info passed to set_plane_props calls. hdisplay and vdisplay in a drm mode are uint16_t,
-   so that's what we use for sizes and positions here. IDs are uint32_t as always. */
 typedef struct KMSDRM_PlaneInfo
 {
     struct plane *plane;
     uint32_t fb_id;
     uint32_t crtc_id;
-    uint16_t src_x;
-    uint16_t src_y;
-    uint16_t src_w;
-    uint16_t src_h;
-    uint16_t crtc_x;
-    uint16_t crtc_y;
-    uint16_t crtc_w;
-    uint16_t crtc_h;
+    int32_t src_x;
+    int32_t src_y;
+    int32_t src_w;
+    int32_t src_h;
+    int32_t crtc_x;
+    int32_t crtc_y;
+    int32_t crtc_w;
+    int32_t crtc_h;
 } KMSDRM_PlaneInfo;
 
 /* Helper functions */
@@ -182,6 +186,7 @@ void KMSDRM_SetWindowTitle(_THIS, SDL_Window * window);
 void KMSDRM_SetWindowIcon(_THIS, SDL_Window * window, SDL_Surface * icon);
 void KMSDRM_SetWindowPosition(_THIS, SDL_Window * window);
 void KMSDRM_SetWindowSize(_THIS, SDL_Window * window);
+void KMSDRM_SetWindowFullscreen(_THIS, SDL_Window * window, SDL_VideoDisplay * _display, SDL_bool fullscreen);
 void KMSDRM_ShowWindow(_THIS, SDL_Window * window);
 void KMSDRM_HideWindow(_THIS, SDL_Window * window);
 void KMSDRM_RaiseWindow(_THIS, SDL_Window * window);