[KMS/DRM] Enable async pageflips.

This commit is contained in:
Manuel Alfayate Corchete 2021-01-13 15:54:26 +01:00
parent 9384e59561
commit aac74db685
3 changed files with 35 additions and 27 deletions

View File

@ -72,12 +72,6 @@ SDL_EGL_CreateContext_impl(KMSDRM)
int KMSDRM_GLES_SetSwapInterval(_THIS, int interval) { 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) { if (!_this->egl_data) {
return SDL_SetError("EGL not initialized"); return SDL_SetError("EGL not initialized");
} }
@ -87,9 +81,6 @@ int KMSDRM_GLES_SetSwapInterval(_THIS, int interval) {
} else { } else {
return SDL_SetError("Only swap intervals of 0 or 1 are supported"); return SDL_SetError("Only swap intervals of 0 or 1 are supported");
} }
#endif
_this->egl_data->egl_swapinterval = 1;
return 0; return 0;
} }
@ -100,15 +91,15 @@ KMSDRM_GLES_SwapWindow(_THIS, SDL_Window * window) {
SDL_DisplayData *dispdata = (SDL_DisplayData *) SDL_GetDisplayForWindow(window)->driverdata; SDL_DisplayData *dispdata = (SDL_DisplayData *) SDL_GetDisplayForWindow(window)->driverdata;
SDL_VideoData *viddata = ((SDL_VideoData *)_this->driverdata); SDL_VideoData *viddata = ((SDL_VideoData *)_this->driverdata);
KMSDRM_FBInfo *fb_info; 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 /* Wait for confirmation that the next front buffer has been flipped, at which
point the previous front buffer can be released */ point the previous front buffer can be released */
timeout = 0; if (!KMSDRM_WaitPageFlip(_this, windata)) {
if (_this->egl_data->egl_swapinterval == 1) {
timeout = -1;
}
if (!KMSDRM_WaitPageFlip(_this, windata, timeout)) {
return 0; return 0;
} }
@ -162,16 +153,27 @@ KMSDRM_GLES_SwapWindow(_THIS, SDL_Window * window) {
} }
/* Issue pageflip on the next front buffer. /* 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, 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 (ret == 0) {
if (_this->egl_data->egl_swapinterval == 1) {
windata->waiting_for_flip = SDL_TRUE; windata->waiting_for_flip = SDL_TRUE;
}
} else { } else {
SDL_LogError(SDL_LOG_CATEGORY_VIDEO, "Could not queue pageflip: %d", ret); 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 /* 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>" Run your SDL2 program with "SDL_KMSDRM_DOUBLE_BUFFER=1 <program_name>"
to enable this. */ to enable this. */
if (_this->egl_data->egl_swapinterval == 1 && windata->double_buffer) { if (_this->egl_data->egl_swapinterval == 1 && windata->double_buffer) {
KMSDRM_WaitPageFlip(_this, windata, -1); KMSDRM_WaitPageFlip(_this, windata);
} }
return 0; return 0;

View File

@ -339,10 +339,12 @@ KMSDRM_FlipHandler(int fd, unsigned int frame, unsigned int sec, unsigned int us
} }
SDL_bool SDL_bool
KMSDRM_WaitPageFlip(_THIS, SDL_WindowData *windata, int timeout) { KMSDRM_WaitPageFlip(_THIS, SDL_WindowData *windata) {
SDL_VideoData *viddata = ((SDL_VideoData *)_this->driverdata); SDL_VideoData *viddata = ((SDL_VideoData *)_this->driverdata);
drmEventContext ev = {0}; drmEventContext ev = {0};
struct pollfd pfd = {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.version = DRM_EVENT_CONTEXT_VERSION;
ev.page_flip_handler = KMSDRM_FlipHandler; ev.page_flip_handler = KMSDRM_FlipHandler;
@ -353,21 +355,25 @@ KMSDRM_WaitPageFlip(_THIS, SDL_WindowData *windata, int timeout) {
while (windata->waiting_for_flip) { while (windata->waiting_for_flip) {
pfd.revents = 0; 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) { if (poll(&pfd, 1, timeout) < 0) {
SDL_LogError(SDL_LOG_CATEGORY_VIDEO, "DRM poll error"); SDL_LogError(SDL_LOG_CATEGORY_VIDEO, "DRM poll error");
return SDL_FALSE; return SDL_FALSE;
} }
if (pfd.revents & (POLLHUP | POLLERR)) { 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"); SDL_LogError(SDL_LOG_CATEGORY_VIDEO, "DRM poll hup or error");
return SDL_FALSE; return SDL_FALSE;
} }
if (pfd.revents & POLLIN) { 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); KMSDRM_drmHandleEvent(viddata->drm_fd, &ev);
} else { } else {
/* Timed out and page flip didn't happen */
SDL_LogDebug(SDL_LOG_CATEGORY_VIDEO, "Dropping frame while waiting_for_flip"); SDL_LogDebug(SDL_LOG_CATEGORY_VIDEO, "Dropping frame while waiting_for_flip");
return SDL_FALSE; return SDL_FALSE;
} }
@ -650,7 +656,7 @@ KMSDRM_DestroySurfaces(_THIS, SDL_Window *window)
/**********************************************/ /**********************************************/
/* Wait for last issued pageflip to complete. */ /* 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 */ /* Restore the original CRTC configuration: configue the crtc with the */

View File

@ -114,7 +114,7 @@ typedef struct KMSDRM_FBInfo
int KMSDRM_CreateSurfaces(_THIS, SDL_Window * window); int KMSDRM_CreateSurfaces(_THIS, SDL_Window * window);
KMSDRM_FBInfo *KMSDRM_FBFromBO(_THIS, struct gbm_bo *bo); KMSDRM_FBInfo *KMSDRM_FBFromBO(_THIS, struct gbm_bo *bo);
KMSDRM_FBInfo *KMSDRM_FBFromBO2(_THIS, struct gbm_bo *bo, int w, int h); 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 */ /* SDL_VideoDevice functions declaration */