kmsdrm: update SwapWindow fn, moving it to triple-buffer.

This commit is contained in:
Manuel Alfayate Corchete 2020-08-03 22:24:49 +02:00
parent ea171c0564
commit 8996ee1786
3 changed files with 205 additions and 12 deletions

View file

@ -73,8 +73,21 @@ static EGLSyncKHR create_fence(int fd, _THIS)
return fence;
}
/*
-We have to stop the GPU from executing the cmdstream again because the front butter
has been marked as back buffer so it can be selected by EGL to draw on it BUT
the pageflip has not completed, so it's still on screen, so letting GPU render on it
would cause tearing. (kms_fence)
-We have to stop the DISPLAY from doing the changes we requested in the atomic ioctl,
like the pageflip, until the GPU has completed the cmdstream execution,
because we don't want the pageflip to be done in the middle of a frame rendering.
(gpu_fence).
-We have to stop the program from doing a new atomic ioctl until the previous one
has been finished. (kms_fence)
*/
int
KMSDRM_GLES_SwapWindow(_THIS, SDL_Window * window) {
KMSDRM_GLES_SwapWindowDOUBLE(_THIS, SDL_Window * window) {
SDL_WindowData *windata = ((SDL_WindowData *) window->driverdata);
SDL_DisplayData *dispdata = (SDL_DisplayData *) SDL_GetDisplayForWindow(window)->driverdata;
@ -85,13 +98,13 @@ KMSDRM_GLES_SwapWindow(_THIS, SDL_Window * window) {
uint32_t flags = DRM_MODE_ATOMIC_NONBLOCK;
EGLSyncKHR display_in_fence = NULL;
EGLSyncKHR display_out_fence = NULL;
EGLSyncKHR kms_in_fence = NULL;
EGLSyncKHR kms_out_fence = NULL;
/* Create the display in-fence, and export it as a fence fd to pass into the kernel. */
display_in_fence = create_fence(EGL_NO_NATIVE_FENCE_FD_ANDROID, _this);
assert(display_in_fence);
dispdata->kms_in_fence_fd = _this->egl_data->eglDupNativeFenceFDANDROID(_this->egl_data->egl_display, display_in_fence);
kms_in_fence = create_fence(EGL_NO_NATIVE_FENCE_FD_ANDROID, _this);
assert(kms_in_fence);
dispdata->kms_in_fence_fd = _this->egl_data->eglDupNativeFenceFDANDROID(_this->egl_data->egl_display, kms_in_fence);
/* Mark the back buffer as the next front buffer, and the current front buffer as elegible
by EGL as back buffer to draw into.
@ -124,18 +137,18 @@ KMSDRM_GLES_SwapWindow(_THIS, SDL_Window * window) {
}
/* Get the display out fence, returned by the atomic ioctl. */
display_out_fence = create_fence(dispdata->kms_out_fence_fd, _this);
assert(display_out_fence);
kms_out_fence = create_fence(dispdata->kms_out_fence_fd, _this);
assert(kms_out_fence);
/* Wait on the CPU side for the _previous_ commit to complete before we post the flip through KMS,
* because atomic will reject the commit if we post a new one while the previous one is still pending. */
do {
status = _this->egl_data->eglClientWaitSyncKHR(_this->egl_data->egl_display, display_out_fence, 0, EGL_FOREVER_KHR);
status = _this->egl_data->eglClientWaitSyncKHR(_this->egl_data->egl_display, kms_out_fence, 0, EGL_FOREVER_KHR);
} while (status != EGL_CONDITION_SATISFIED_KHR);
/* Destroy both in and out display fences. */
_this->egl_data->eglDestroySyncKHR(_this->egl_data->egl_display, display_out_fence);
_this->egl_data->eglDestroySyncKHR(_this->egl_data->egl_display, display_in_fence);
_this->egl_data->eglDestroySyncKHR(_this->egl_data->egl_display, kms_out_fence);
_this->egl_data->eglDestroySyncKHR(_this->egl_data->egl_display, kms_in_fence);
/* Now that the pageflip is complete, release last front buffer so EGL can chose it
* as back buffer and render on it again: */
@ -150,6 +163,178 @@ KMSDRM_GLES_SwapWindow(_THIS, SDL_Window * window) {
return ret;
}
int
KMSDRM_GLES_SwapWindow(_THIS, SDL_Window * window) {
SDL_WindowData *windata = ((SDL_WindowData *) window->driverdata);
SDL_DisplayData *dispdata = (SDL_DisplayData *) SDL_GetDisplayForWindow(window)->driverdata;
KMSDRM_FBInfo *fb;
int ret;
uint32_t flags = DRM_MODE_ATOMIC_NONBLOCK;
/* Create the fence that will be inserted in the cmdstream exactly at the end
of the gl commands that form a frame. KMS will have to wait on it before doing a pageflip. */
dispdata->gpu_fence = create_fence(EGL_NO_NATIVE_FENCE_FD_ANDROID, _this);
assert(dispdata->gpu_fence);
_this->egl_data->eglSwapBuffers(_this->egl_data->egl_display, windata->egl_surface);
/* 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. */
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);
/* Lock the buffer that is marked by eglSwapBuffers() to become the next front buffer (so it can not
be chosen by EGL as back buffer to draw on), and get a handle to it to request the pageflip on it. */
windata->next_bo = KMSDRM_gbm_surface_lock_front_buffer(windata->gs);
if (!windata->next_bo) {
printf("Failed to lock frontbuffer\n");
return -1;
}
fb = KMSDRM_FBFromBO(_this, windata->next_bo);
if (!fb) {
printf("Failed to get a new framebuffer BO\n");
return -1;
}
/* Don't issue another atomic ioctl until previous one has 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 atomic commit. */
ret = drm_atomic_commit(_this, fb->fb_id, flags);
if (ret) {
printf("failed to do atomic commit\n");
return -1;
}
/* release last front buffer so EGL can chose it as back buffer and render on it again: */
if (windata->curr_bo) {
KMSDRM_gbm_surface_release_buffer(windata->gs, windata->curr_bo);
windata->curr_bo = NULL;
}
/* Take note of the current front buffer, so it can be freed next time this function is called. */
windata->curr_bo = windata->next_bo;
/* Import out fence from the out fence fd and tell the GPU to wait on it
until the requested pageflip has completed. */
dispdata->kms_fence = create_fence(dispdata->kms_out_fence_fd, _this);
assert(dispdata->kms_fence);
dispdata->kms_out_fence_fd = -1;
_this->egl_data->eglWaitSyncKHR(_this->egl_data->egl_display, dispdata->kms_fence, 0);
return ret;
}
int
KMSDRM_GLES_SwapWindowOLD(_THIS, SDL_Window * window) {
SDL_WindowData *windata = ((SDL_WindowData *) window->driverdata);
SDL_DisplayData *dispdata = (SDL_DisplayData *) SDL_GetDisplayForWindow(window)->driverdata;
KMSDRM_FBInfo *fb;
int ret;
uint32_t flags = DRM_MODE_ATOMIC_NONBLOCK;
dispdata->kms_fence = NULL; /* in-fence to gpu, out-fence from kms */
dispdata->gpu_fence = NULL; /* in-fence to kms, out-fence from gpu, */
/* Allow modeset (which is done inside atomic_commit). */
flags |= DRM_MODE_ATOMIC_ALLOW_MODESET;
/* Import out fence from the out fence fd and tell the GPU to wait on it
until the requested pageflip has completed. */
dispdata->kms_fence = create_fence(dispdata->kms_out_fence_fd, _this);
assert(dispdata->kms_fence);
dispdata->kms_out_fence_fd = -1;
_this->egl_data->eglWaitSyncKHR(_this->egl_data->egl_display, dispdata->kms_fence, 0);
/* GL DRAW */
/* Create the gpu fence here so it's inserted in the cmdstream exactly
at the end of the gl commands that form a frame. */
dispdata->gpu_fence = create_fence(EGL_NO_NATIVE_FENCE_FD_ANDROID, _this);
assert(dispdata->gpu_fence);
_this->egl_data->eglSwapBuffers(_this->egl_data->egl_display, windata->egl_surface);
/* It's safe to get the gpu_fence fd now, because eglSwapBuffers() flushes the fd. */
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);
windata->next_bo = KMSDRM_gbm_surface_lock_front_buffer(windata->gs);
if (!windata->next_bo) {
printf("Failed to lock frontbuffer\n");
return -1;
}
fb = KMSDRM_FBFromBO(_this, windata->next_bo);
if (!fb) {
printf("Failed to get a new framebuffer BO\n");
return -1;
}
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 atomic commit. */
ret = drm_atomic_commit(_this, fb->fb_id, flags);
if (ret) {
printf("failed to do atomic commit\n");
return -1;
}
/* release last front buffer so EGL can chose it as back buffer and render on it again: */
if (windata->curr_bo) {
KMSDRM_gbm_surface_release_buffer(windata->gs, windata->curr_bo);
windata->curr_bo = NULL;
}
/* Take note of the current front buffer, so it can be freed next time this function is called. */
windata->curr_bo = windata->next_bo;
/* Allow a modeset change for the first commit only. */
flags &= ~(DRM_MODE_ATOMIC_ALLOW_MODESET);
return ret;
}
/***************************************/
/* End of Atomic functions block */
/***************************************/

View file

@ -874,8 +874,11 @@ KMSDRM_VideoInit(_THIS)
}
}
/* Initialize the fence fd: */
/* Initialize the fences and their fds: */
dispdata->kms_fence = NULL;
dispdata->gpu_fence = NULL;
dispdata->kms_out_fence_fd = -1,
dispdata->kms_in_fence_fd = -1,
/*********************/
/* Atomic block ends */

View file

@ -34,6 +34,7 @@
#include <assert.h>
#if SDL_VIDEO_OPENGL_EGL
#include <EGL/egl.h>
#include <EGL/eglext.h>
#endif
typedef struct SDL_VideoData
@ -75,9 +76,13 @@ typedef struct SDL_DisplayData
drmModePropertyRes **connector_props_info;
int crtc_index;
int kms_in_fence_fd;
int kms_out_fence_fd;
EGLSyncKHR kms_fence; /* Signaled when kms completes changes requested in atomic iotcl (pageflip, etc). */
EGLSyncKHR gpu_fence; /* Signaled when GPU rendering is done. */
} SDL_DisplayData;