kmsdrm: separate requests in different functions so we only need one atomic commit for everything, as expected by atomic design.

This commit is contained in:
Manuel Alfayate Corchete 2020-08-08 14:27:55 +02:00
parent 3b9f1073c6
commit c437729b21
3 changed files with 117 additions and 104 deletions

View File

@ -80,16 +80,6 @@ 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;
KMSDRM_FBInfo *fb; KMSDRM_FBInfo *fb;
int ret; int ret;
uint32_t flags = 0;
/* 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) {
flags |= DRM_MODE_ATOMIC_ALLOW_MODESET;
dispdata->modeset_pending = SDL_FALSE;
}
else {
flags |= DRM_MODE_ATOMIC_NONBLOCK;
}
/*************************************************************************/ /*************************************************************************/
/* Block for telling KMS to wait for GPU rendering of the current frame */ /* Block for telling KMS to wait for GPU rendering of the current frame */
@ -128,20 +118,14 @@ KMSDRM_GLES_SwapWindow(_THIS, SDL_Window * window)
return SDL_SetError("Failed to get a new framebuffer BO"); return SDL_SetError("Failed to get a new framebuffer BO");
} }
/* Don't issue another atomic ioctl until previous one has completed: it will cause errors. */ /* Add the pageflip to te request list. */
if (dispdata->kms_fence) { drm_atomic_request_pageflip(_this, fb->fb_id);
EGLint status;
do { /* Issue the one and only atomic commit where all changes will be requested!.
status = _this->egl_data->eglClientWaitSyncKHR(_this->egl_data->egl_display, dispdata->kms_fence, 0, EGL_FOREVER_KHR); We need e a non-blocking atomic commit for triple buffering, because we
} while (status != EGL_CONDITION_SATISFIED_KHR); must not block on this atomic commit so we can re-enter program loop once more. */
ret = drm_atomic_commit(_this, SDL_FALSE);
_this->egl_data->eglDestroySyncKHR(_this->egl_data->egl_display, dispdata->kms_fence);
dispdata->kms_fence = NULL;
}
/* Issue atomic commit, where we request the pageflip. */
ret = drm_atomic_commit(_this, fb->fb_id, flags);
if (ret) { if (ret) {
return SDL_SetError("failed to issue atomic commit"); return SDL_SetError("failed to issue atomic commit");
} }
@ -180,22 +164,14 @@ int
KMSDRM_GLES_SwapWindowDB(_THIS, SDL_Window * window) KMSDRM_GLES_SwapWindowDB(_THIS, SDL_Window * window)
{ {
SDL_WindowData *windata = ((SDL_WindowData *) window->driverdata); SDL_WindowData *windata = ((SDL_WindowData *) window->driverdata);
SDL_DisplayData *dispdata = (SDL_DisplayData *) SDL_GetDisplayForWindow(window)->driverdata;
KMSDRM_FBInfo *fb; KMSDRM_FBInfo *fb;
int ret; int ret;
uint32_t flags = 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 done). 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 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. */
/* 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) {
flags |= DRM_MODE_ATOMIC_ALLOW_MODESET;
dispdata->modeset_pending = SDL_FALSE;
}
/* Mark, at 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);
@ -211,10 +187,15 @@ KMSDRM_GLES_SwapWindowDB(_THIS, SDL_Window * window)
return SDL_SetError("Failed to get a new framebuffer BO"); return SDL_SetError("Failed to get a new framebuffer BO");
} }
/* Issue atomic commit, where we request the pageflip. */ /* Add the pageflip to te request list. */
ret = drm_atomic_commit(_this, fb->fb_id, flags); drm_atomic_request_pageflip(_this, fb->fb_id);
/* Issue the one and only atomic commit where all changes will be requested!.
Blocking for double buffering: won't return until completed. */
ret = drm_atomic_commit(_this, SDL_TRUE);
if (ret) { if (ret) {
return SDL_SetError("failed to do atomic commit"); return SDL_SetError("failed to issue atomic commit");
} }
/* Release last front buffer so EGL can chose it as back buffer and render on it again. */ /* Release last front buffer so EGL can chose it as back buffer and render on it again. */

View File

@ -403,51 +403,70 @@ uint32_t get_plane_id(_THIS, drmModeRes *resources)
return ret; return ret;
} }
int drm_atomic_commit(_THIS, uint32_t fb_id, uint32_t flags) void
drm_atomic_request_pageflip(_THIS, uint32_t fb_id)
{
SDL_DisplayData *dispdata = (SDL_DisplayData *)SDL_GetDisplayDriverData(0);
uint32_t plane_id = dispdata->plane->plane_id;
/* Do we have a set of changes already in the making? If not, allocate a new one. */
if (!dispdata->atomic_req)
dispdata->atomic_req = KMSDRM_drmModeAtomicAlloc();
add_plane_property(dispdata->atomic_req, plane_id, "FB_ID", fb_id);
add_plane_property(dispdata->atomic_req, plane_id, "CRTC_ID", dispdata->crtc_id);
add_plane_property(dispdata->atomic_req, plane_id, "SRC_X", 0);
add_plane_property(dispdata->atomic_req, plane_id, "SRC_Y", 0);
add_plane_property(dispdata->atomic_req, plane_id, "SRC_W", dispdata->mode.hdisplay << 16);
add_plane_property(dispdata->atomic_req, plane_id, "SRC_H", dispdata->mode.vdisplay << 16);
add_plane_property(dispdata->atomic_req, plane_id, "CRTC_X", 0);
add_plane_property(dispdata->atomic_req, plane_id, "CRTC_Y", 0);
add_plane_property(dispdata->atomic_req, plane_id, "CRTC_W", dispdata->mode.hdisplay);
add_plane_property(dispdata->atomic_req, plane_id, "CRTC_H", dispdata->mode.vdisplay);
if (dispdata->kms_in_fence_fd != -1) {
add_crtc_property(dispdata->atomic_req, dispdata->crtc_id, "OUT_FENCE_PTR",
VOID2U64(&dispdata->kms_out_fence_fd));
add_plane_property(dispdata->atomic_req, plane_id, "IN_FENCE_FD", dispdata->kms_in_fence_fd);
}
}
void
drm_atomic_request_modeset(_THIS)
{ {
SDL_DisplayData *dispdata = (SDL_DisplayData *)SDL_GetDisplayDriverData(0); SDL_DisplayData *dispdata = (SDL_DisplayData *)SDL_GetDisplayDriverData(0);
SDL_VideoData *viddata = ((SDL_VideoData *)_this->driverdata); SDL_VideoData *viddata = ((SDL_VideoData *)_this->driverdata);
uint32_t plane_id = dispdata->plane->plane_id;
uint32_t blob_id; uint32_t blob_id;
drmModeAtomicReq *req;
/* Do we have a set of changes already in the making? If not, allocate a new one. */
if (!dispdata->atomic_req)
dispdata->atomic_req = KMSDRM_drmModeAtomicAlloc();
dispdata->atomic_flags |= DRM_MODE_ATOMIC_ALLOW_MODESET;
add_connector_property(dispdata->atomic_req, dispdata->connector->connector_id, "CRTC_ID", dispdata->crtc_id);
KMSDRM_drmModeCreatePropertyBlob(viddata->drm_fd, &dispdata->mode, sizeof(dispdata->mode), &blob_id);
add_crtc_property(dispdata->atomic_req, dispdata->crtc_id, "MODE_ID", blob_id);
add_crtc_property(dispdata->atomic_req, dispdata->crtc_id, "ACTIVE", 1);
}
int drm_atomic_commit(_THIS, SDL_bool blocking)
{
SDL_DisplayData *dispdata = (SDL_DisplayData *)SDL_GetDisplayDriverData(0);
SDL_VideoData *viddata = ((SDL_VideoData *)_this->driverdata);
int ret; int ret;
req = KMSDRM_drmModeAtomicAlloc(); if (!blocking)
dispdata->atomic_flags |= DRM_MODE_ATOMIC_NONBLOCK;
if (flags & DRM_MODE_ATOMIC_ALLOW_MODESET) { /* Never issue a new atomic commit if previous has not yet completed, or it will error. */
if (add_connector_property(req, dispdata->connector->connector_id, "CRTC_ID", drm_atomic_wait_pending(_this);
dispdata->crtc_id) < 0)
return -1;
if (KMSDRM_drmModeCreatePropertyBlob(viddata->drm_fd, &dispdata->mode, sizeof(dispdata->mode), ret = KMSDRM_drmModeAtomicCommit(viddata->drm_fd, dispdata->atomic_req, dispdata->atomic_flags, NULL);
&blob_id) != 0)
return -1;
if (add_crtc_property(req, dispdata->crtc_id, "MODE_ID", blob_id) < 0)
return -1;
if (add_crtc_property(req, dispdata->crtc_id, "ACTIVE", 1) < 0)
return -1;
}
add_plane_property(req, plane_id, "FB_ID", fb_id);
add_plane_property(req, plane_id, "CRTC_ID", dispdata->crtc_id);
add_plane_property(req, plane_id, "SRC_X", 0);
add_plane_property(req, plane_id, "SRC_Y", 0);
add_plane_property(req, plane_id, "SRC_W", dispdata->mode.hdisplay << 16);
add_plane_property(req, plane_id, "SRC_H", dispdata->mode.vdisplay << 16);
add_plane_property(req, plane_id, "CRTC_X", 0);
add_plane_property(req, plane_id, "CRTC_Y", 0);
add_plane_property(req, plane_id, "CRTC_W", dispdata->mode.hdisplay);
add_plane_property(req, plane_id, "CRTC_H", dispdata->mode.vdisplay);
if (dispdata->kms_in_fence_fd != -1) {
add_crtc_property(req, dispdata->crtc_id, "OUT_FENCE_PTR",
VOID2U64(&dispdata->kms_out_fence_fd));
add_plane_property(req, plane_id, "IN_FENCE_FD", dispdata->kms_in_fence_fd);
}
ret = KMSDRM_drmModeAtomicCommit(viddata->drm_fd, req, flags, NULL);
if (ret) if (ret)
goto out; goto out;
@ -457,13 +476,15 @@ int drm_atomic_commit(_THIS, uint32_t fb_id, uint32_t flags)
} }
out: out:
KMSDRM_drmModeAtomicFree(req); KMSDRM_drmModeAtomicFree(dispdata->atomic_req);
dispdata->atomic_req = NULL;
dispdata->atomic_flags = 0;
return ret; return ret;
} }
void void
wait_pending_atomic(_THIS) drm_atomic_wait_pending(_THIS)
{ {
SDL_DisplayData *dispdata = (SDL_DisplayData *)SDL_GetDisplayDriverData(0); SDL_DisplayData *dispdata = (SDL_DisplayData *)SDL_GetDisplayDriverData(0);
@ -676,7 +697,7 @@ KMSDRM_DestroySurfaces(_THIS, SDL_Window * window)
SDL_WindowData *windata = (SDL_WindowData *)window->driverdata; SDL_WindowData *windata = (SDL_WindowData *)window->driverdata;
/* Wait for pending atomic commit (like pageflips requested in SwapWindow) to complete. */ /* Wait for pending atomic commit (like pageflips requested in SwapWindow) to complete. */
wait_pending_atomic(_this); drm_atomic_wait_pending(_this);
if (windata->bo) { if (windata->bo) {
KMSDRM_gbm_surface_release_buffer(windata->gs, windata->bo); KMSDRM_gbm_surface_release_buffer(windata->gs, windata->bo);
@ -746,9 +767,8 @@ KMSDRM_CreateSurfaces(_THIS, SDL_Window * window)
windata->egl_surface_dirty = SDL_FALSE; windata->egl_surface_dirty = SDL_FALSE;
#endif #endif
/* We take note here about the need to do a modeset in the atomic_commit(), /* Add modeset request to the current change request list. */
called in KMSDRM_GLES_SwapWindow(). */ drm_atomic_request_modeset(_this);
dispdata->modeset_pending = SDL_TRUE;
return 0; return 0;
} }
@ -901,12 +921,13 @@ KMSDRM_VideoInit(_THIS)
/* Atomic block */ /* Atomic block */
/****************/ /****************/
/* Initialize the fences and their fds: */ /* Initialize the atomic dispdata members. */
dispdata->atomic_flags = 0;
dispdata->atomic_req = NULL;
dispdata->kms_fence = NULL; dispdata->kms_fence = NULL;
dispdata->gpu_fence = NULL; dispdata->gpu_fence = NULL;
dispdata->kms_out_fence_fd = -1, dispdata->kms_out_fence_fd = -1,
dispdata->kms_in_fence_fd = -1, dispdata->kms_in_fence_fd = -1,
dispdata->modeset_pending = SDL_FALSE;
/*********************/ /*********************/
/* Atomic block ends */ /* Atomic block ends */
@ -1086,20 +1107,18 @@ KMSDRM_VideoQuit(_THIS)
viddata->max_windows = 0; viddata->max_windows = 0;
viddata->num_windows = 0; viddata->num_windows = 0;
/* Restore original videomode. */ /* Restore original buffer. */
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;
/***********************************************************/ /***********************************************************/
/* Atomic block for video mode and crt->buffer restoration */ /* Atomic block for original crt->buffer restoration */
/***********************************************************/ /***********************************************************/
/* We could get here after an async atomic commit (as it's in triple buffer SwapWindow()) /* Issue sync/blocking atomic commit that points crtc to original buffer.
and we don't want to issue another atomic commit before previous one is completed. */ SDL_video has already called SetDisplayMode() to set the original display mode at this point.
wait_pending_atomic(_this); We are not doing pageflips anymore, so we can't rely on the SwapWindow() atomic commit
so we are explicitly calling it here. */
/* Issue sync/blocking atomic commit that restores original video mode and points crtc to original buffer. */ drm_atomic_request_pageflip(_this, dispdata->crtc->buffer_id);
ret = drm_atomic_commit(_this, dispdata->crtc->buffer_id, flags); ret = drm_atomic_commit(_this, SDL_TRUE);
/*********************/ /*********************/
/* Atomic block ends */ /* Atomic block ends */
@ -1194,6 +1213,7 @@ KMSDRM_SetDisplayMode(_THIS, SDL_VideoDisplay * display, SDL_DisplayMode * mode)
} }
dispdata->mode = conn->modes[modedata->mode_index]; dispdata->mode = conn->modes[modedata->mode_index];
drm_atomic_request_modeset(_this);
for (int i = 0; i < viddata->num_windows; i++) { for (int i = 0; i < viddata->num_windows; i++) {
SDL_Window *window = viddata->windows[i]; SDL_Window *window = viddata->windows[i];

View File

@ -60,13 +60,24 @@ typedef struct SDL_DisplayData
drmModeModeInfo mode; drmModeModeInfo mode;
uint32_t plane_id; uint32_t plane_id;
uint32_t cusor_plane_id;
uint32_t crtc_id; uint32_t crtc_id;
uint32_t connector_id; uint32_t connector_id;
uint32_t atomic_flags;
/* All changes will be requested via this one and only atomic request,
that will be sent to the kernel in the one and only atomic_commit() call
that takes place in SwapWindow(). */
drmModeAtomicReq *atomic_req;
drmModePlane *plane; drmModePlane *plane;
drmModeObjectProperties *plane_props; drmModeObjectProperties *plane_props;
drmModePropertyRes **plane_props_info; drmModePropertyRes **plane_props_info;
drmModePlane *cursor_plane;
drmModeObjectProperties *cursor_plane_props;
drmModePropertyRes **cursor_plane_props_info;
drmModeCrtc *crtc; drmModeCrtc *crtc;
drmModeObjectProperties *crtc_props; drmModeObjectProperties *crtc_props;
drmModePropertyRes **crtc_props_info; drmModePropertyRes **crtc_props_info;
@ -81,7 +92,6 @@ typedef struct SDL_DisplayData
EGLSyncKHR kms_fence; /* Signaled when kms completes changes requested in atomic iotcl (pageflip, etc). */ EGLSyncKHR kms_fence; /* Signaled when kms completes changes requested in atomic iotcl (pageflip, etc). */
EGLSyncKHR gpu_fence; /* Signaled when GPU rendering is done. */ EGLSyncKHR gpu_fence; /* Signaled when GPU rendering is done. */
SDL_bool modeset_pending;
} SDL_DisplayData; } SDL_DisplayData;
@ -107,10 +117,12 @@ typedef struct KMSDRM_FBInfo
/* Helper functions */ /* Helper functions */
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);
SDL_bool KMSDRM_WaitPageFlip(_THIS, SDL_WindowData *windata, int timeout);
/* Atomic functions that are used from SDL_kmsdrmopengles.c */ /* Atomic functions that are used from SDL_kmsdrmopengles.c */
int drm_atomic_commit(_THIS, uint32_t fb_id, uint32_t flags); void drm_atomic_request_modeset(_THIS);
void drm_atomic_request_pageflip(_THIS, uint32_t fb_id);
int drm_atomic_commit(_THIS, SDL_bool blocking);
void drm_atomic_wait_pending(_THIS);
/****************************************************************************/ /****************************************************************************/
/* SDL_VideoDevice functions declaration */ /* SDL_VideoDevice functions declaration */