[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 @@
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 @@
} 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 @@
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 @@
}
/* 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 @@
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 @@
}
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 @@
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 @@
/**********************************************/
/* 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 @@
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 */