kmsdrm: wait for pending atomic commits before restoring videomode and crtc->buffer on VideoQuit, and simplify double-buffer SwapWindow() implementation.

This commit is contained in:
Manuel Alfayate Corchete 2020-08-06 01:36:56 +02:00
parent 09692b6170
commit 96c99693a2
2 changed files with 39 additions and 58 deletions

View File

@ -101,7 +101,7 @@ KMSDRM_GLES_SwapWindow(_THIS, SDL_Window * window)
dispdata->gpu_fence = create_fence(EGL_NO_NATIVE_FENCE_FD_ANDROID, _this); dispdata->gpu_fence = create_fence(EGL_NO_NATIVE_FENCE_FD_ANDROID, _this);
assert(dispdata->gpu_fence); assert(dispdata->gpu_fence);
/* Mark in the EGL level the buffer that we want to become the new front buffer. /* Mark, at EGL level, the buffer that we want to become the new front buffer.
However, it won't really happen until we request a pageflip at the KMS level and it completes. */ However, it won't really happen until we request a pageflip at the KMS level and it completes. */
_this->egl_data->eglSwapBuffers(_this->egl_data->egl_display, windata->egl_surface); _this->egl_data->eglSwapBuffers(_this->egl_data->egl_display, windata->egl_surface);
@ -187,33 +187,18 @@ KMSDRM_GLES_SwapWindowDB(_THIS, SDL_Window * window)
int ret; int ret;
uint32_t flags = 0; uint32_t flags = 0;
/* In double-buffer mode, atomic commit will always be synchronous/blocking (ie: won't return until
the requested changes are done).
Also, there's no need to fence KMS or the GPU, because we won't be entering game loop again
(hence not building or executing a new cmdstring) until pageflip is done. */
/* Do we need to set video mode this time? If yes, pass the right flag and issue a blocking atomic ioctl. */ /* Do we need to set video mode this time? If yes, pass the right flag and issue a blocking atomic ioctl. */
if (dispdata->modeset_pending) { if (dispdata->modeset_pending) {
flags |= DRM_MODE_ATOMIC_ALLOW_MODESET; flags |= DRM_MODE_ATOMIC_ALLOW_MODESET;
dispdata->modeset_pending = SDL_FALSE; dispdata->modeset_pending = SDL_FALSE;
} }
else {
flags |= DRM_MODE_ATOMIC_NONBLOCK;
}
/* Create the fence that will be inserted in the cmdstream exactly at the end /* Mark, at EGL level, the buffer that we want to become the new front buffer.
of the gl commands that form a frame. KMS will have to wait on it before doing a pageflip.
(NOTE this fence is not really neeeded in double-buffer mode because the program will be
blocked in eglClientWaitSyncKMHR() until pageflip completes, but we need an in-fence FD anyway
to issue the atomic ioctl).
*/
dispdata->gpu_fence = create_fence(EGL_NO_NATIVE_FENCE_FD_ANDROID, _this);
assert(dispdata->gpu_fence);
/* It's safe to get the gpu_fence FD now, because eglSwapBuffers flushes it down the cmdstream,
so it's now in place in the cmdstream.
Atomic ioctl will pass the in-fence fd into the kernel, telling KMS that it has to wait for GPU to
finish rendering the frame before doing the changes requested in the atomic ioct (pageflip in this case). */
dispdata->kms_in_fence_fd = _this->egl_data->eglDupNativeFenceFDANDROID(_this->egl_data->egl_display, dispdata->gpu_fence);
_this->egl_data->eglDestroySyncKHR(_this->egl_data->egl_display, dispdata->gpu_fence);
assert(dispdata->kms_in_fence_fd != -1);
/* Mark in the EGL level the buffer that we want to become the new front buffer.
However, it won't really happen until we request a pageflip at the KMS level and it completes. */ However, it won't really happen until we request a pageflip at the KMS level and it completes. */
_this->egl_data->eglSwapBuffers(_this->egl_data->egl_display, windata->egl_surface); _this->egl_data->eglSwapBuffers(_this->egl_data->egl_display, windata->egl_surface);
@ -245,24 +230,6 @@ KMSDRM_GLES_SwapWindowDB(_THIS, SDL_Window * window)
/* Take note of current front buffer, so we can free it next time we come here. */ /* Take note of current front buffer, so we can free it next time we come here. */
windata->bo = windata->next_bo; windata->bo = windata->next_bo;
/* Import the kms fence from the out fence fd. We need it to tell GPU to wait for pageflip to complete. */
dispdata->kms_fence = create_fence(dispdata->kms_out_fence_fd, _this);
assert(dispdata->kms_fence);
/* Reset out fence FD value because the fence is now away from us, on the driver side. */
dispdata->kms_out_fence_fd = -1;
/* Wait for pageflip to complete, and destroy kms_fence. */
if (dispdata->kms_fence) {
EGLint status;
do {
status = _this->egl_data->eglClientWaitSyncKHR(_this->egl_data->egl_display, dispdata->kms_fence, 0, EGL_FOREVER_KHR);
} while (status != EGL_CONDITION_SATISFIED_KHR);
_this->egl_data->eglDestroySyncKHR(_this->egl_data->egl_display, dispdata->kms_fence);
}
return ret; return ret;
} }

View File

@ -1051,27 +1051,14 @@ cleanup:
return ret; return ret;
} }
/* Fn to restore original video mode and crtc buffer on quit, using the atomic interface. */
/*int
restore_video (_THIS)
{
SDL_DisplayData *dispdata = (SDL_DisplayData *)SDL_GetDisplayDriverData(0);
ret = drm_atomic_commit(_this, fb->fb_id, flags);
}*/
void void
KMSDRM_VideoQuit(_THIS) KMSDRM_VideoQuit(_THIS)
{ {
int ret;
SDL_VideoData *viddata = ((SDL_VideoData *)_this->driverdata); SDL_VideoData *viddata = ((SDL_VideoData *)_this->driverdata);
SDL_DisplayData *dispdata = (SDL_DisplayData *)SDL_GetDisplayDriverData(0); SDL_DisplayData *dispdata = (SDL_DisplayData *)SDL_GetDisplayDriverData(0);
SDL_LogDebug(SDL_LOG_CATEGORY_VIDEO, "KMSDRM_VideoQuit()"); SDL_LogDebug(SDL_LOG_CATEGORY_VIDEO, "KMSDRM_VideoQuit()");
if (_this->gl_config.driver_loaded) {
SDL_GL_UnloadLibrary();
}
/* Clear out the window list */ /* Clear out the window list */
SDL_free(viddata->windows); SDL_free(viddata->windows);
viddata->windows = NULL; viddata->windows = NULL;
@ -1082,15 +1069,41 @@ KMSDRM_VideoQuit(_THIS)
if (viddata->drm_fd >= 0 && dispdata && dispdata->connector && dispdata->crtc) { if (viddata->drm_fd >= 0 && dispdata && dispdata->connector && dispdata->crtc) {
uint32_t flags = DRM_MODE_ATOMIC_ALLOW_MODESET; uint32_t flags = DRM_MODE_ATOMIC_ALLOW_MODESET;
int ret = drm_atomic_commit(_this, dispdata->crtc->buffer_id, flags); /***********************************************************/
/* Atomic block for video mode and crt->buffer restoration */
/***********************************************************/
/* It could happen that we will get here after an async atomic commit (as it's in triple buffer
SwapWindow()) and we don't want to issue another atomic commit before previous one is completed. */
if (dispdata->kms_fence) {
EGLint status;
do {
status = _this->egl_data->eglClientWaitSyncKHR(_this->egl_data->egl_display,
dispdata->kms_fence, 0, EGL_FOREVER_KHR);
} while (status != EGL_CONDITION_SATISFIED_KHR);
_this->egl_data->eglDestroySyncKHR(_this->egl_data->egl_display, dispdata->kms_fence);
}
/* Issue sync/blocking atomic commit that restores original video mode and points crtc to original buffer. */
ret = drm_atomic_commit(_this, dispdata->crtc->buffer_id, flags);
/*********************/
/* Atomic block ends */
/*********************/
if (ret != 0) { if (ret != 0) {
SDL_LogWarn(SDL_LOG_CATEGORY_VIDEO, "Could not restore original videomode"); SDL_LogWarn(SDL_LOG_CATEGORY_VIDEO, "Could not restore original videomode");
} }
} }
/****************/
/* Atomic block */ if (_this->gl_config.driver_loaded) {
/****************/ SDL_GL_UnloadLibrary();
}
/**************************************************/
/* Atomic block for freeing up property pointers. */
/**************************************************/
if (dispdata && dispdata->connector_props_info) { if (dispdata && dispdata->connector_props_info) {
SDL_free(dispdata->connector_props_info); SDL_free(dispdata->connector_props_info);
dispdata->connector_props_info = NULL; dispdata->connector_props_info = NULL;
@ -1106,6 +1119,7 @@ KMSDRM_VideoQuit(_THIS)
/*********************/ /*********************/
/* Atomic block ends */ /* Atomic block ends */
/*********************/ /*********************/
if (dispdata && dispdata->connector) { if (dispdata && dispdata->connector) {
KMSDRM_drmModeFreeConnector(dispdata->connector); KMSDRM_drmModeFreeConnector(dispdata->connector);
dispdata->connector = NULL; dispdata->connector = NULL;