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

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;
KMSDRM_FBInfo *fb;
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 */
@ -128,20 +118,14 @@ KMSDRM_GLES_SwapWindow(_THIS, SDL_Window * window)
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. */
if (dispdata->kms_fence) {
EGLint status;
/* Add the pageflip to te request list. */
drm_atomic_request_pageflip(_this, fb->fb_id);
do {
status = _this->egl_data->eglClientWaitSyncKHR(_this->egl_data->egl_display, dispdata->kms_fence, 0, EGL_FOREVER_KHR);
} while (status != EGL_CONDITION_SATISFIED_KHR);
/* 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. */
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) {
return SDL_SetError("failed to issue atomic commit");
}
@ -180,22 +164,14 @@ int
KMSDRM_GLES_SwapWindowDB(_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 = 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. */
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.
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);
@ -211,10 +187,15 @@ KMSDRM_GLES_SwapWindowDB(_THIS, SDL_Window * window)
return SDL_SetError("Failed to get a new framebuffer BO");
}
/* Issue atomic commit, where we request the pageflip. */
ret = drm_atomic_commit(_this, fb->fb_id, flags);
/* Add the pageflip to te request list. */
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) {
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. */

View File

@ -197,23 +197,23 @@ static int add_crtc_property(drmModeAtomicReq *req, uint32_t obj_id,
static int add_plane_property(drmModeAtomicReq *req, uint32_t obj_id,
const char *name, uint64_t value)
{
SDL_DisplayData *dispdata = (SDL_DisplayData *)SDL_GetDisplayDriverData(0);
unsigned int i;
int prop_id = -1;
SDL_DisplayData *dispdata = (SDL_DisplayData *)SDL_GetDisplayDriverData(0);
unsigned int i;
int prop_id = -1;
for (i = 0 ; i < dispdata->plane_props->count_props ; i++) {
if (strcmp(dispdata->plane_props_info[i]->name, name) == 0) {
prop_id = dispdata->plane_props_info[i]->prop_id;
break;
}
for (i = 0 ; i < dispdata->plane_props->count_props ; i++) {
if (strcmp(dispdata->plane_props_info[i]->name, name) == 0) {
prop_id = dispdata->plane_props_info[i]->prop_id;
break;
}
}
if (prop_id < 0) {
printf("no plane property: %s\n", name);
return -EINVAL;
}
if (prop_id < 0) {
printf("no plane property: %s\n", name);
return -EINVAL;
}
return KMSDRM_drmModeAtomicAddProperty(req, obj_id, prop_id, value);
return KMSDRM_drmModeAtomicAddProperty(req, obj_id, prop_id, value);
}
#if 0
@ -403,51 +403,70 @@ uint32_t get_plane_id(_THIS, drmModeRes *resources)
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_VideoData *viddata = ((SDL_VideoData *)_this->driverdata);
uint32_t plane_id = dispdata->plane->plane_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;
req = KMSDRM_drmModeAtomicAlloc();
if (!blocking)
dispdata->atomic_flags |= DRM_MODE_ATOMIC_NONBLOCK;
if (flags & DRM_MODE_ATOMIC_ALLOW_MODESET) {
if (add_connector_property(req, dispdata->connector->connector_id, "CRTC_ID",
dispdata->crtc_id) < 0)
return -1;
/* Never issue a new atomic commit if previous has not yet completed, or it will error. */
drm_atomic_wait_pending(_this);
if (KMSDRM_drmModeCreatePropertyBlob(viddata->drm_fd, &dispdata->mode, sizeof(dispdata->mode),
&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);
ret = KMSDRM_drmModeAtomicCommit(viddata->drm_fd, dispdata->atomic_req, dispdata->atomic_flags, NULL);
if (ret)
goto out;
@ -457,13 +476,15 @@ int drm_atomic_commit(_THIS, uint32_t fb_id, uint32_t flags)
}
out:
KMSDRM_drmModeAtomicFree(req);
KMSDRM_drmModeAtomicFree(dispdata->atomic_req);
dispdata->atomic_req = NULL;
dispdata->atomic_flags = 0;
return ret;
}
void
wait_pending_atomic(_THIS)
drm_atomic_wait_pending(_THIS)
{
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;
/* Wait for pending atomic commit (like pageflips requested in SwapWindow) to complete. */
wait_pending_atomic(_this);
drm_atomic_wait_pending(_this);
if (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;
#endif
/* We take note here about the need to do a modeset in the atomic_commit(),
called in KMSDRM_GLES_SwapWindow(). */
dispdata->modeset_pending = SDL_TRUE;
/* Add modeset request to the current change request list. */
drm_atomic_request_modeset(_this);
return 0;
}
@ -901,12 +921,13 @@ KMSDRM_VideoInit(_THIS)
/* 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->gpu_fence = NULL;
dispdata->kms_out_fence_fd = -1,
dispdata->kms_in_fence_fd = -1,
dispdata->modeset_pending = SDL_FALSE;
/*********************/
/* Atomic block ends */
@ -1086,20 +1107,18 @@ KMSDRM_VideoQuit(_THIS)
viddata->max_windows = 0;
viddata->num_windows = 0;
/* Restore original videomode. */
/* Restore original buffer. */
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())
and we don't want to issue another atomic commit before previous one is completed. */
wait_pending_atomic(_this);
/* 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);
/* Issue sync/blocking atomic commit that points crtc to original buffer.
SDL_video has already called SetDisplayMode() to set the original display mode at this point.
We are not doing pageflips anymore, so we can't rely on the SwapWindow() atomic commit
so we are explicitly calling it here. */
drm_atomic_request_pageflip(_this, dispdata->crtc->buffer_id);
ret = drm_atomic_commit(_this, SDL_TRUE);
/*********************/
/* Atomic block ends */
@ -1194,6 +1213,7 @@ KMSDRM_SetDisplayMode(_THIS, SDL_VideoDisplay * display, SDL_DisplayMode * mode)
}
dispdata->mode = conn->modes[modedata->mode_index];
drm_atomic_request_modeset(_this);
for (int i = 0; i < viddata->num_windows; i++) {
SDL_Window *window = viddata->windows[i];

View File

@ -60,13 +60,24 @@ typedef struct SDL_DisplayData
drmModeModeInfo mode;
uint32_t plane_id;
uint32_t cusor_plane_id;
uint32_t crtc_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;
drmModeObjectProperties *plane_props;
drmModePropertyRes **plane_props_info;
drmModePlane *cursor_plane;
drmModeObjectProperties *cursor_plane_props;
drmModePropertyRes **cursor_plane_props_info;
drmModeCrtc *crtc;
drmModeObjectProperties *crtc_props;
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 gpu_fence; /* Signaled when GPU rendering is done. */
SDL_bool modeset_pending;
} SDL_DisplayData;
@ -107,10 +117,12 @@ typedef struct KMSDRM_FBInfo
/* Helper functions */
int KMSDRM_CreateSurfaces(_THIS, SDL_Window * window);
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 */
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 */