Commit 9e9227add3e72a3afc5dbb3d4b6252c7f9393085

Manuel Alfayate Corchete 2020-09-12T04:52:56

kmsdrm: reimplement modesetting for fullscreen window scaling and AR-correction.

diff --git a/src/video/kmsdrm/SDL_kmsdrmopengles.c b/src/video/kmsdrm/SDL_kmsdrmopengles.c
index a536b0a..f7c2d85 100644
--- a/src/video/kmsdrm/SDL_kmsdrmopengles.c
+++ b/src/video/kmsdrm/SDL_kmsdrmopengles.c
@@ -179,6 +179,23 @@ KMSDRM_GLES_SwapWindowFenced(_THIS, SDL_Window * window)
         return SDL_SetError("Failed to request prop changes for setting plane buffer and CRTC");
     }
 
+    /* Do we have a pending modesetting? If so, set the necessary 
+       props so it's included in the incoming atomic commit. */
+    if (dispdata->modeset_pending) {
+        SDL_VideoData *viddata = (SDL_VideoData *)_this->driverdata;
+	uint32_t blob_id;
+        dispdata->atomic_flags |= DRM_MODE_ATOMIC_ALLOW_MODESET;
+ 	if (add_connector_property(dispdata->atomic_req, dispdata->connector, "CRTC_ID", dispdata->crtc->crtc->crtc_id) < 0)
+            return -1;
+        if (KMSDRM_drmModeCreatePropertyBlob(viddata->drm_fd, &dispdata->mode, sizeof(dispdata->mode), &blob_id) != 0)
+            return -1;
+        if (add_crtc_property(dispdata->atomic_req, dispdata->crtc, "MODE_ID", blob_id) < 0)
+            return -1;
+        if (add_crtc_property(dispdata->atomic_req, dispdata->crtc, "ACTIVE", 1) < 0)
+            return -1;
+        dispdata->modeset_pending = SDL_FALSE;
+    }
+
     /*****************************************************************/
     /* Tell the display (KMS) that it will have to wait on the fence */
     /* for the GPU-side FENCE.                                       */
diff --git a/src/video/kmsdrm/SDL_kmsdrmvideo.c b/src/video/kmsdrm/SDL_kmsdrmvideo.c
index 0fe093b..00113b6 100644
--- a/src/video/kmsdrm/SDL_kmsdrmvideo.c
+++ b/src/video/kmsdrm/SDL_kmsdrmvideo.c
@@ -978,11 +978,12 @@ KMSDRM_CreateSurfaces(_THIS, SDL_Window * window)
     /* Destroy the surfaces and buffers before creating the new ones. */
     KMSDRM_DestroySurfaces(_this, window);
 
-    if ((window->flags & SDL_WINDOW_FULLSCREEN_DESKTOP) == SDL_WINDOW_FULLSCREEN_DESKTOP) {
+    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 {
+    } else {
         width = window->w;
         height = window->h;
     }
@@ -1064,25 +1065,31 @@ KMSDRM_ReconfigureWindow( _THIS, SDL_Window * window) {
     SDL_DisplayData *dispdata = (SDL_DisplayData *) SDL_GetDisplayForWindow(window)->driverdata;
     float ratio;  
 
-    if ((window->flags & SDL_WINDOW_FULLSCREEN_DESKTOP) == SDL_WINDOW_FULLSCREEN_DESKTOP) {
+    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 {
-        /* Get output (CRTC) size and position, for AR correction. */
+
+        /* 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 (KMSDRM_CreateSurfaces(_this, window)) {
 	return -1; 
-    }   
+    }  
 
     return 0;
 }
@@ -1109,6 +1116,7 @@ KMSDRM_VideoInit(_THIS)
     dispdata->gpu_fence = NULL;
     dispdata->kms_out_fence_fd = -1;
     dispdata->dumb_buffer = NULL;
+    dispdata->modeset_pending = SDL_FALSE;
 
     if (!dispdata) {
         return SDL_OutOfMemory();
@@ -1373,6 +1381,8 @@ KMSDRM_VideoQuit(_THIS)
     SDL_VideoData *viddata = ((SDL_VideoData *)_this->driverdata);
     SDL_DisplayData *dispdata = (SDL_DisplayData *)SDL_GetDisplayDriverData(0);
     KMSDRM_PlaneInfo plane_info = {0};
+    drmModeModeInfo mode = dispdata->crtc->crtc->mode;
+    uint32_t blob_id;
 
     /*****************************************************************/
     /*                                                               */
@@ -1409,10 +1419,10 @@ KMSDRM_VideoQuit(_THIS)
     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 = dispdata->mode.hdisplay;
-    plane_info.src_h = dispdata->mode.vdisplay;
-    plane_info.crtc_w = dispdata->mode.hdisplay;
-    plane_info.crtc_h = dispdata->mode.vdisplay;
+    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);
 
@@ -1432,6 +1442,13 @@ KMSDRM_VideoQuit(_THIS)
 
 #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 DestroyWindow().");
@@ -1518,12 +1535,9 @@ KMSDRM_GetDisplayModes(_THIS, SDL_VideoDisplay * display)
 }
 #endif
 
-/* We are NOT really changing the physical display mode, but using
-the PRIMARY PLANE and CRTC to scale as we please. But we need that SDL
-has knowledge of the video modes we are going to use for fullscreen
-window sizes, even if we are faking their use. If not, SDL only considers
-the in-use video mode as available, and sets every window to that size
-before we get to CreateWindow or ReconfigureWindow. */
+/* We only change the video mode for FULLSCREEN windows
+   that are not FULLSCREEN_DESKTOP.
+   Normal non-fullscreen windows are scaled using the CRTC. */
 void
 KMSDRM_GetDisplayModes(_THIS, SDL_VideoDisplay * display)
 {
@@ -1553,12 +1567,35 @@ KMSDRM_GetDisplayModes(_THIS, SDL_VideoDisplay * display)
 int
 KMSDRM_SetDisplayMode(_THIS, SDL_VideoDisplay * display, SDL_DisplayMode * mode)
 {
-    /************************************************************************/
-    /* DO NOT add dynamic videomode changes. It makes NO SENSE, since the   */
-    /* PRIMARY PLANE and the CRTC can be used to scale image, so any window */
-    /* will appear fullscren with AR correction with NO extra video memory  */
-    /* bandwidth usage.                                                     */
-    /************************************************************************/    
+    /* Set the dispdata->mode to the new mode and leave actual modesetting
+       pending to be done on SwapWindow(), to be included on next atomic
+       commit changeset. */
+
+    SDL_VideoData *viddata = (SDL_VideoData *)_this->driverdata;
+    SDL_DisplayData *dispdata = (SDL_DisplayData *)display->driverdata;
+    SDL_DisplayModeData *modedata = (SDL_DisplayModeData *)mode->driverdata;
+    drmModeConnector *conn = dispdata->connector->connector;
+
+    if (!modedata) {
+        return SDL_SetError("Mode doesn't have an associated index");
+    }
+
+    /* Take note of the new mode. It will be used in SwapWindow to
+       set the props needed for mode setting. */
+    dispdata->mode = conn->modes[modedata->mode_index];
+
+    dispdata->modeset_pending = SDL_TRUE;
+
+    for (int i = 0; i < viddata->num_windows; i++) {
+        SDL_Window *window = viddata->windows[i];
+
+	if (KMSDRM_CreateSurfaces(_this, window)) {
+	    return -1; 
+	}   
+
+        /* Tell app about the window resize */
+        SDL_SendWindowEvent(window, SDL_WINDOWEVENT_RESIZED, mode->w, mode->h);
+    }
 
     return 0;
 }
@@ -1586,20 +1623,26 @@ KMSDRM_CreateWindow(_THIS, SDL_Window * window)
     display = SDL_GetDisplayForWindow(window);
     dispdata = display->driverdata;
 
-    if ((window->flags & SDL_WINDOW_FULLSCREEN_DESKTOP) == SDL_WINDOW_FULLSCREEN_DESKTOP) {
+    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 {
-        /* Get output (CRTC) size and position, for AR correction. */
+
+        /* 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;
+
     }
 
     /* Don't force fullscreen on all windows: it confuses programs that try
diff --git a/src/video/kmsdrm/SDL_kmsdrmvideo.h b/src/video/kmsdrm/SDL_kmsdrmvideo.h
index d152792..bb197fd 100644
--- a/src/video/kmsdrm/SDL_kmsdrmvideo.h
+++ b/src/video/kmsdrm/SDL_kmsdrmvideo.h
@@ -116,29 +116,28 @@ typedef struct SDL_DisplayData
     drmModeModeInfo mode;
     uint32_t atomic_flags;
 
-    /* All changes will be requested via this one and only atomic request,
-       that will be sent to the kernel in the one and only atomic_commit()
-       call that takes place in SwapWindow(). */
-    drmModeAtomicReq *atomic_req;
     plane *display_plane;
     plane *cursor_plane;
     crtc *crtc;
     connector *connector;
 
+    /* Central atomic request list, used for the prop
+       changeset related to pageflip in SwapWindow. */ 
+    drmModeAtomicReq *atomic_req;
+
     int kms_in_fence_fd;
     int kms_out_fence_fd;
 
-    EGLSyncKHR kms_fence; /* Signaled when kms completes changes        *
-                           * requested in atomic iotcl (pageflip, etc). */
-
-    EGLSyncKHR gpu_fence; /* Signaled when GPU rendering is done. */
+    EGLSyncKHR kms_fence;
+    EGLSyncKHR gpu_fence;
 
 #if SDL_VIDEO_OPENGL_EGL
     EGLSurface old_egl_surface;
 #endif
 
-    dumb_buffer *dumb_buffer; /* Aux dumb buffer to keep the PRIMARY PLANE
-                                 entertained with when we destroy GBM surface. */
+    dumb_buffer *dumb_buffer;
+
+    SDL_bool modeset_pending;
 
 } SDL_DisplayData;