kmsdrm: greatly improve comments in SwapBuffersFenced() for future reference.

Manuel Alfayate Corchete 2020-09-10 23:26:02 +02:00
parent 3c6004feb7
commit 47e2d0304e
1 changed files with 88 additions and 50 deletions

View File

@ -100,7 +100,13 @@ static EGLSyncKHR create_fence(int fd, _THIS)
return fence;
}
static int
/***********************************************************************************/
/* Comments about buffer access protection mechanism (=fences) are the ones boxed. */
/* Also, DON'T remove the asserts: if a fence-related call fails, it's better that */
/* program exits immediately, or we could leave KMS waiting for a failed/missing */
/* fence forevever. */
/***********************************************************************************/
int
KMSDRM_GLES_SwapWindowFenced(_THIS, SDL_Window * window)
{
SDL_WindowData *windata = ((SDL_WindowData *) window->driverdata);
@ -108,36 +114,47 @@ KMSDRM_GLES_SwapWindowFenced(_THIS, SDL_Window * window)
KMSDRM_FBInfo *fb;
KMSDRM_PlaneInfo info = {0};
/*************************************************************************/
/* Block for telling KMS to wait for GPU rendering of the current frame */
/* before applying the KMS changes requested in the atomic ioctl. */
/*************************************************************************/
/* 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. */
/******************************************************************/
/* Create the GPU-side FENCE OBJECT. It will be inserted into the */
/* GL 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);
/* 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. */
/******************************************************************/
/* eglSwapBuffers flushes the fence down the GL CMDSTREAM, so we */
/* know for sure it's there now. */
/* Also it marks, at EGL level, the buffer that we want to become */
/* the new front buffer. (Remember that won't really happen until */
/* we request a pageflip at the KMS level and it completes. */
/******************************************************************/
if (! _this->egl_data->eglSwapBuffers(_this->egl_data->egl_display, windata->egl_surface)) {
return SDL_EGL_SetError("Failed to swap EGL buffers", "eglSwapBuffers");
}
/******************************************************************/
/* EXPORT the GPU-side FENCE OBJECT to the fence INPUT FD, so we */
/* can pass it into the kernel. Atomic ioctl will pass the */
/* in-fence fd into the kernel, thus telling KMS that it has to */
/* wait for GPU to finish rendering the frame (remember where we */
/* put the fence in the GL CMDSTREAM) before doing the changes */
/* requested in the atomic ioct (the pageflip in this case). */
/* (We export the GPU-side FENCE OJECT to the fence INPUT FD now, */
/* not sooner, because now we are sure that the GPU-side fence is */
/* in the CMDSTREAM to be lifted when the CMDSTREAM to this point */
/* is completed). */
/******************************************************************/
dispdata->kms_in_fence_fd = _this->egl_data->eglDupNativeFenceFDANDROID
(_this->egl_data->egl_display, 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);
/***************/
/* Block ends. */
/***************/
/* 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.
REMEMBER that gbm_surface_lock_front_buffer() ALWAYS has to be called after eglSwapBuffers(). */
/* 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.
REMEMBER that gbm_surface_lock_front_buffer() ALWAYS has to be
called after eglSwapBuffers(). */
windata->next_bo = KMSDRM_gbm_surface_lock_front_buffer(windata->gs);
if (!windata->next_bo) {
return SDL_SetError("Failed to lock frontbuffer");
@ -162,56 +179,77 @@ KMSDRM_GLES_SwapWindowFenced(_THIS, SDL_Window * window)
return SDL_SetError("Failed to request prop changes for setting plane buffer and CRTC");
}
/* Set the IN_FENCE and OUT_FENCE props only here, since this is the only place
on which we're interested in managing who and when should access the buffers
that the display plane uses, and that's what these props are for. */
/*****************************************************************/
/* Tell the display (KMS) that it will have to wait on the fence */
/* for the GPU-side FENCE. */
/* */
/* Since KMS is a kernel thing, we have to pass an FD into */
/* the kernel, and get another FD out of the kernel. */
/* */
/* 1) To pass the GPU-side fence into the kernel, we set the */
/* INPUT FD as the IN_FENCE_FD prop of the PRIMARY PLANE. */
/* This FD tells KMS (the kernel) to wait for the GPU-side fence.*/
/* */
/* 2) To get the KMS-side fence out of the kernel, we set the */
/* OUTPUT FD as the OUT_FEWNCE_FD prop of the CRTC. */
/* This FD will be later imported as a FENCE OBJECT which will be*/
/* used to tell the GPU to wait for KMS to complete the changes */
/* requested in atomic_commit (the pageflip in this case). */
/*****************************************************************/
if (dispdata->kms_in_fence_fd != -1)
{
if (add_crtc_property(dispdata->atomic_req, dispdata->crtc, "OUT_FENCE_PTR",
VOID2U64(&dispdata->kms_out_fence_fd)) < 0)
return SDL_SetError("Failed to set CRTC OUT_FENCE_PTR prop");
if (add_plane_property(dispdata->atomic_req, dispdata->display_plane, "IN_FENCE_FD", dispdata->kms_in_fence_fd) < 0)
if (add_plane_property(dispdata->atomic_req, dispdata->display_plane,
"IN_FENCE_FD", dispdata->kms_in_fence_fd) < 0)
return SDL_SetError("Failed to set plane IN_FENCE_FD prop");
if (add_crtc_property(dispdata->atomic_req, dispdata->crtc,
"OUT_FENCE_PTR", VOID2U64(&dispdata->kms_out_fence_fd)) < 0)
return SDL_SetError("Failed to set CRTC OUT_FENCE_PTR prop");
}
/* Issue the one and only atomic commit where all changes will be requested!.
We need e a non-blocking atomic commit for triple buffering, because we
must not block on this atomic commit so we can re-enter program loop once more. */
/*****************************************************************/
/* Issue a non-blocking atomic commit: for triple buffering, */
/* this must not block so the game can start building another */
/* frame, even if the just-requested pageflip hasnt't completed. */
/*****************************************************************/
if (drm_atomic_commit(_this, SDL_FALSE)) {
return SDL_SetError("Failed to issue atomic commit on pageflip");
}
/* Release the last front buffer so EGL can chose it as back buffer and render on it again. */
/* Release the previous front buffer so EGL can chose it as back buffer
and render on it again. */
if (windata->bo) {
KMSDRM_gbm_surface_release_buffer(windata->gs, windata->bo);
}
/* Take note of current front buffer, so we can free it next time we come here. */
/* Take note of the buffer about to become front buffer, so next
time we come here we can free it like we just did with the previous
front buffer. */
windata->bo = windata->next_bo;
/**************************************************************************/
/* Block for telling GPU to wait for completion of requested KMS changes */
/* before starting cmdstream execution (=next frame rendering). */
/**************************************************************************/
/* Import the kms fence from the out fence fd. We need it to tell GPU to wait for pageflip to complete. */
/****************************************************************/
/* Import the KMS-side FENCE OUTPUT FD from the kernel to the */
/* KMS-side FENCE OBJECT so we can use use it to fence the GPU. */
/****************************************************************/
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. */
/****************************************************************/
/* "Delete" the fence OUTPUT FD, because we already have the */
/* KMS FENCE OBJECT, the fence itself is away from us, on the */
/* kernel side. */
/****************************************************************/
dispdata->kms_out_fence_fd = -1;
/* Tell the GPU to wait until the requested pageflip has completed. */
/*****************************************************************/
/* Tell the GPU to wait on the fence for the KMS-side FENCE, */
/* which means waiting until the requested pageflip is completed.*/
/*****************************************************************/
_this->egl_data->eglWaitSyncKHR(_this->egl_data->egl_display, dispdata->kms_fence, 0);
/***************/
/* Block ends. */
/***************/
return 0;
}
static int
int
KMSDRM_GLES_SwapWindowDoubleBuffered(_THIS, SDL_Window * window)
{
SDL_WindowData *windata = ((SDL_WindowData *) window->driverdata);
@ -220,10 +258,11 @@ KMSDRM_GLES_SwapWindowDoubleBuffered(_THIS, SDL_Window * window)
KMSDRM_PlaneInfo info = {0};
/****************************************************************************************************/
/* In double-buffer mode, atomic commit will always be synchronous/blocking (ie: won't return until */
/* In double-buffer mode, atomic_commit will always be synchronous/blocking (ie: won't return until */
/* the requested changes are really 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. */
/* (hence not building or executing a new cmdstring) until pageflip is done, so we don't need to */
/* protect the KMS/GPU access to the buffer. */
/****************************************************************************************************/
/* Mark, at EGL level, the buffer that we want to become the new front buffer.
@ -312,7 +351,6 @@ KMSDRM_GLES_SwapWindow(_THIS, SDL_Window * window)
return windata->swap_window(_this, window);
}
/***************************************/
/* End of Atomic functions block */
/***************************************/