diff --git a/src/video/kmsdrm/SDL_kmsdrmopengles.c b/src/video/kmsdrm/SDL_kmsdrmopengles.c index a20a49449..888190707 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 " 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 1099c3502..b47e90f3d 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 d089e1041..aeff052c1 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 */