Commit aac74db6859d8471645688531f948520faf54f10

Manuel Alfayate Corchete 2021-01-13T15:54:26

[KMS/DRM] Enable async pageflips.

diff --git a/src/video/kmsdrm/SDL_kmsdrmopengles.c b/src/video/kmsdrm/SDL_kmsdrmopengles.c
index a20a494..8881907 100644
--- a/src/video/kmsdrm/SDL_kmsdrmopengles.c
+++ b/src/video/kmsdrm/SDL_kmsdrmopengles.c
@@ -72,12 +72,6 @@ SDL_EGL_CreateContext_impl(KMSDRM)
 
 int KMSDRM_GLES_SetSwapInterval(_THIS, int interval) {
 
-    /* Issuing a new pageflip before the previous has completed
-       causes drmModePageFlip() to return EBUSY errors.
-       So just set egl_swapinterval to 1 to prevent that. */
-
-#if 0
-    
     if (!_this->egl_data) {
         return SDL_SetError("EGL not initialized");
     }
@@ -87,9 +81,6 @@ int KMSDRM_GLES_SetSwapInterval(_THIS, int interval) {
     } else {
         return SDL_SetError("Only swap intervals of 0 or 1 are supported");
     }
-#endif
-
-    _this->egl_data->egl_swapinterval = 1;
 
     return 0;
 }
@@ -100,15 +91,15 @@ KMSDRM_GLES_SwapWindow(_THIS, SDL_Window * window) {
     SDL_DisplayData *dispdata = (SDL_DisplayData *) SDL_GetDisplayForWindow(window)->driverdata;
     SDL_VideoData *viddata = ((SDL_VideoData *)_this->driverdata);
     KMSDRM_FBInfo *fb_info;
-    int ret, timeout;
+    int ret = 0;
+
+    /* Always wait for the previous issued flip before issing a new one,
+       even if you do async flips. */
+    uint32_t flip_flags = DRM_MODE_PAGE_FLIP_EVENT;
 
     /* Wait for confirmation that the next front buffer has been flipped, at which
        point the previous front buffer can be released */
-    timeout = 0;
-    if (_this->egl_data->egl_swapinterval == 1) {
-        timeout = -1;
-    }
-    if (!KMSDRM_WaitPageFlip(_this, windata, timeout)) {
+    if (!KMSDRM_WaitPageFlip(_this, windata)) {
         return 0;
     }
 
@@ -162,16 +153,27 @@ KMSDRM_GLES_SwapWindow(_THIS, SDL_Window * window) {
     }
 
     /* Issue pageflip on the next front buffer.
-       The pageflip will be done during the next vblank. */
+       Remember: drmModePageFlip() never blocks, it just issues the flip,
+       which will be done during the next vblank.
+       Since it will return EBUSY if we call it again without having
+       completed the last issued flip, we must pass the
+       DRM_MODE_PAGE_FLIP_ASYNC if we don't block on EGL (egl_swapinterval = 0).
+       That makes it flip immediately, without waiting for the next vblank,
+       so even if we don't block on EGL, it will have flipped when we
+       get back here. */
+
+    if (_this->egl_data->egl_swapinterval == 0) {
+        flip_flags |= DRM_MODE_PAGE_FLIP_ASYNC;
+    }
+
     ret = KMSDRM_drmModePageFlip(viddata->drm_fd, dispdata->crtc->crtc_id,
-	     fb_info->fb_id, DRM_MODE_PAGE_FLIP_EVENT, &windata->waiting_for_flip);
+	     fb_info->fb_id, flip_flags, &windata->waiting_for_flip);
 
     if (ret == 0) {
-        if (_this->egl_data->egl_swapinterval == 1) {
-	    windata->waiting_for_flip = SDL_TRUE;
-        }
+	windata->waiting_for_flip = SDL_TRUE;
     } else {
 	SDL_LogError(SDL_LOG_CATEGORY_VIDEO, "Could not queue pageflip: %d", ret);
+	printf("Could not queue pageflip: %s\n", strerror(errno));
     }
 
     /* If we are in double-buffer mode, wait immediately for vsync
@@ -179,7 +181,7 @@ KMSDRM_GLES_SwapWindow(_THIS, SDL_Window * window) {
        Run your SDL2 program with "SDL_KMSDRM_DOUBLE_BUFFER=1 <program_name>"
        to enable this. */
     if (_this->egl_data->egl_swapinterval == 1 && windata->double_buffer) {
-	KMSDRM_WaitPageFlip(_this, windata, -1);
+	KMSDRM_WaitPageFlip(_this, windata);
     }
 
     return 0;
diff --git a/src/video/kmsdrm/SDL_kmsdrmvideo.c b/src/video/kmsdrm/SDL_kmsdrmvideo.c
index 1099c35..b47e90f 100644
--- a/src/video/kmsdrm/SDL_kmsdrmvideo.c
+++ b/src/video/kmsdrm/SDL_kmsdrmvideo.c
@@ -339,11 +339,13 @@ KMSDRM_FlipHandler(int fd, unsigned int frame, unsigned int sec, unsigned int us
 }
 
 SDL_bool
-KMSDRM_WaitPageFlip(_THIS, SDL_WindowData *windata, int timeout) {
+KMSDRM_WaitPageFlip(_THIS, SDL_WindowData *windata) {
     SDL_VideoData *viddata = ((SDL_VideoData *)_this->driverdata);
     drmEventContext ev = {0};
     struct pollfd pfd = {0};
-
+    /* If the pageflip hasn't completed after 10 seconds, it nevel will. */
+    uint32_t timeout = 10000;
+ 
     ev.version = DRM_EVENT_CONTEXT_VERSION;
     ev.page_flip_handler = KMSDRM_FlipHandler;
 
@@ -353,21 +355,25 @@ KMSDRM_WaitPageFlip(_THIS, SDL_WindowData *windata, int timeout) {
     while (windata->waiting_for_flip) {
         pfd.revents = 0;
 
+        /* poll() waits for events arriving on the FD, and returns < 0 if timeout
+           passes with no events.  */
         if (poll(&pfd, 1, timeout) < 0) {
             SDL_LogError(SDL_LOG_CATEGORY_VIDEO, "DRM poll error");
             return SDL_FALSE;
         }
 
         if (pfd.revents & (POLLHUP | POLLERR)) {
+            /* An event arrived on the FD in time, but it's an error. */
             SDL_LogError(SDL_LOG_CATEGORY_VIDEO, "DRM poll hup or error");
             return SDL_FALSE;
         }
 
         if (pfd.revents & POLLIN) {
-            /* Page flip? If so, drmHandleEvent will unset windata->waiting_for_flip */
+            /* There is data to read on the FD!
+               Is the event a pageflip? If so, drmHandleEvent will
+               unset windata->waiting_for_flip */
             KMSDRM_drmHandleEvent(viddata->drm_fd, &ev);
         } else {
-            /* Timed out and page flip didn't happen */
             SDL_LogDebug(SDL_LOG_CATEGORY_VIDEO, "Dropping frame while waiting_for_flip");
             return SDL_FALSE;
         }
@@ -650,7 +656,7 @@ KMSDRM_DestroySurfaces(_THIS, SDL_Window *window)
     /**********************************************/
     /* Wait for last issued pageflip to complete. */
     /**********************************************/
-    KMSDRM_WaitPageFlip(_this, windata, -1);
+    KMSDRM_WaitPageFlip(_this, windata);
 
     /***********************************************************************/
     /* Restore the original CRTC configuration: configue the crtc with the */
diff --git a/src/video/kmsdrm/SDL_kmsdrmvideo.h b/src/video/kmsdrm/SDL_kmsdrmvideo.h
index d089e10..aeff052 100644
--- a/src/video/kmsdrm/SDL_kmsdrmvideo.h
+++ b/src/video/kmsdrm/SDL_kmsdrmvideo.h
@@ -114,7 +114,7 @@ typedef struct KMSDRM_FBInfo
 int KMSDRM_CreateSurfaces(_THIS, SDL_Window * window);
 KMSDRM_FBInfo *KMSDRM_FBFromBO(_THIS, struct gbm_bo *bo);
 KMSDRM_FBInfo *KMSDRM_FBFromBO2(_THIS, struct gbm_bo *bo, int w, int h);
-SDL_bool KMSDRM_WaitPageFlip(_THIS, SDL_WindowData *windata, int timeout);
+SDL_bool KMSDRM_WaitPageFlip(_THIS, SDL_WindowData *windata);
 
 /****************************************************************************/
 /* SDL_VideoDevice functions declaration                                    */