kmsdrm: reimplement modesetting for fullscreen window scaling and AR-correction.

This commit is contained in:
Manuel Alfayate Corchete 2020-09-12 04:52:56 +02:00
parent 4575c6942a
commit 9e9227add3
3 changed files with 93 additions and 34 deletions

View File

@ -179,6 +179,23 @@ KMSDRM_GLES_SwapWindowFenced(_THIS, SDL_Window * window)
return SDL_SetError("Failed to request prop changes for setting plane buffer and CRTC");
}
/* Do we have a pending modesetting? If so, set the necessary
props so it's included in the incoming atomic commit. */
if (dispdata->modeset_pending) {
SDL_VideoData *viddata = (SDL_VideoData *)_this->driverdata;
uint32_t blob_id;
dispdata->atomic_flags |= DRM_MODE_ATOMIC_ALLOW_MODESET;
if (add_connector_property(dispdata->atomic_req, dispdata->connector, "CRTC_ID", dispdata->crtc->crtc->crtc_id) < 0)
return -1;
if (KMSDRM_drmModeCreatePropertyBlob(viddata->drm_fd, &dispdata->mode, sizeof(dispdata->mode), &blob_id) != 0)
return -1;
if (add_crtc_property(dispdata->atomic_req, dispdata->crtc, "MODE_ID", blob_id) < 0)
return -1;
if (add_crtc_property(dispdata->atomic_req, dispdata->crtc, "ACTIVE", 1) < 0)
return -1;
dispdata->modeset_pending = SDL_FALSE;
}
/*****************************************************************/
/* Tell the display (KMS) that it will have to wait on the fence */
/* for the GPU-side FENCE. */

View File

@ -978,11 +978,12 @@ KMSDRM_CreateSurfaces(_THIS, SDL_Window * window)
/* Destroy the surfaces and buffers before creating the new ones. */
KMSDRM_DestroySurfaces(_this, window);
if ((window->flags & SDL_WINDOW_FULLSCREEN_DESKTOP) == SDL_WINDOW_FULLSCREEN_DESKTOP) {
if (((window->flags & SDL_WINDOW_FULLSCREEN_DESKTOP) == SDL_WINDOW_FULLSCREEN_DESKTOP) ||
((window->flags & SDL_WINDOW_FULLSCREEN) == SDL_WINDOW_FULLSCREEN)) {
width = dispdata->mode.hdisplay;
height = dispdata->mode.vdisplay;
}
else {
} else {
width = window->w;
height = window->h;
}
@ -1064,25 +1065,31 @@ KMSDRM_ReconfigureWindow( _THIS, SDL_Window * window) {
SDL_DisplayData *dispdata = (SDL_DisplayData *) SDL_GetDisplayForWindow(window)->driverdata;
float ratio;
if ((window->flags & SDL_WINDOW_FULLSCREEN_DESKTOP) == SDL_WINDOW_FULLSCREEN_DESKTOP) {
if (((window->flags & SDL_WINDOW_FULLSCREEN_DESKTOP) == SDL_WINDOW_FULLSCREEN_DESKTOP) ||
((window->flags & SDL_WINDOW_FULLSCREEN) == SDL_WINDOW_FULLSCREEN)) {
windata->src_w = dispdata->mode.hdisplay;
windata->src_h = dispdata->mode.vdisplay;
windata->output_w = dispdata->mode.hdisplay;
windata->output_h = dispdata->mode.vdisplay;
windata->output_x = 0;
} else {
/* Get output (CRTC) size and position, for AR correction. */
/* Normal non-fullscreen windows are scaled using the CRTC,
so get output (CRTC) size and position, for AR correction. */
ratio = (float)window->w / (float)window->h;
windata->src_w = window->w;
windata->src_h = window->h;
windata->output_w = dispdata->mode.vdisplay * ratio;
windata->output_h = dispdata->mode.vdisplay;
windata->output_x = (dispdata->mode.hdisplay - windata->output_w) / 2;
}
if (KMSDRM_CreateSurfaces(_this, window)) {
return -1;
}
}
return 0;
}
@ -1109,6 +1116,7 @@ KMSDRM_VideoInit(_THIS)
dispdata->gpu_fence = NULL;
dispdata->kms_out_fence_fd = -1;
dispdata->dumb_buffer = NULL;
dispdata->modeset_pending = SDL_FALSE;
if (!dispdata) {
return SDL_OutOfMemory();
@ -1373,6 +1381,8 @@ KMSDRM_VideoQuit(_THIS)
SDL_VideoData *viddata = ((SDL_VideoData *)_this->driverdata);
SDL_DisplayData *dispdata = (SDL_DisplayData *)SDL_GetDisplayDriverData(0);
KMSDRM_PlaneInfo plane_info = {0};
drmModeModeInfo mode = dispdata->crtc->crtc->mode;
uint32_t blob_id;
/*****************************************************************/
/* */
@ -1409,10 +1419,10 @@ KMSDRM_VideoQuit(_THIS)
plane_info.plane = dispdata->display_plane;
plane_info.crtc_id = dispdata->crtc->crtc->crtc_id;
plane_info.fb_id = dispdata->crtc->crtc->buffer_id;
plane_info.src_w = dispdata->mode.hdisplay;
plane_info.src_h = dispdata->mode.vdisplay;
plane_info.crtc_w = dispdata->mode.hdisplay;
plane_info.crtc_h = dispdata->mode.vdisplay;
plane_info.src_w = mode.hdisplay;
plane_info.src_h = mode.vdisplay;
plane_info.crtc_w = mode.hdisplay;
plane_info.crtc_h = mode.vdisplay;
drm_atomic_set_plane_props(&plane_info);
@ -1432,6 +1442,13 @@ KMSDRM_VideoQuit(_THIS)
#endif
/* Set props that restore the original video mode. */
dispdata->atomic_flags |= DRM_MODE_ATOMIC_ALLOW_MODESET;
add_connector_property(dispdata->atomic_req, dispdata->connector, "CRTC_ID", dispdata->crtc->crtc->crtc_id);
KMSDRM_drmModeCreatePropertyBlob(viddata->drm_fd, &mode, sizeof(mode), &blob_id);
add_crtc_property(dispdata->atomic_req, dispdata->crtc, "MODE_ID", blob_id);
add_crtc_property(dispdata->atomic_req, dispdata->crtc, "ACTIVE", 1);
/* Issue blocking atomic commit. */
if (drm_atomic_commit(_this, SDL_TRUE)) {
SDL_SetError("Failed to issue atomic commit on DestroyWindow().");
@ -1518,12 +1535,9 @@ KMSDRM_GetDisplayModes(_THIS, SDL_VideoDisplay * display)
}
#endif
/* We are NOT really changing the physical display mode, but using
the PRIMARY PLANE and CRTC to scale as we please. But we need that SDL
has knowledge of the video modes we are going to use for fullscreen
window sizes, even if we are faking their use. If not, SDL only considers
the in-use video mode as available, and sets every window to that size
before we get to CreateWindow or ReconfigureWindow. */
/* We only change the video mode for FULLSCREEN windows
that are not FULLSCREEN_DESKTOP.
Normal non-fullscreen windows are scaled using the CRTC. */
void
KMSDRM_GetDisplayModes(_THIS, SDL_VideoDisplay * display)
{
@ -1553,12 +1567,35 @@ KMSDRM_GetDisplayModes(_THIS, SDL_VideoDisplay * display)
int
KMSDRM_SetDisplayMode(_THIS, SDL_VideoDisplay * display, SDL_DisplayMode * mode)
{
/************************************************************************/
/* DO NOT add dynamic videomode changes. It makes NO SENSE, since the */
/* PRIMARY PLANE and the CRTC can be used to scale image, so any window */
/* will appear fullscren with AR correction with NO extra video memory */
/* bandwidth usage. */
/************************************************************************/
/* Set the dispdata->mode to the new mode and leave actual modesetting
pending to be done on SwapWindow(), to be included on next atomic
commit changeset. */
SDL_VideoData *viddata = (SDL_VideoData *)_this->driverdata;
SDL_DisplayData *dispdata = (SDL_DisplayData *)display->driverdata;
SDL_DisplayModeData *modedata = (SDL_DisplayModeData *)mode->driverdata;
drmModeConnector *conn = dispdata->connector->connector;
if (!modedata) {
return SDL_SetError("Mode doesn't have an associated index");
}
/* Take note of the new mode. It will be used in SwapWindow to
set the props needed for mode setting. */
dispdata->mode = conn->modes[modedata->mode_index];
dispdata->modeset_pending = SDL_TRUE;
for (int i = 0; i < viddata->num_windows; i++) {
SDL_Window *window = viddata->windows[i];
if (KMSDRM_CreateSurfaces(_this, window)) {
return -1;
}
/* Tell app about the window resize */
SDL_SendWindowEvent(window, SDL_WINDOWEVENT_RESIZED, mode->w, mode->h);
}
return 0;
}
@ -1586,20 +1623,26 @@ KMSDRM_CreateWindow(_THIS, SDL_Window * window)
display = SDL_GetDisplayForWindow(window);
dispdata = display->driverdata;
if ((window->flags & SDL_WINDOW_FULLSCREEN_DESKTOP) == SDL_WINDOW_FULLSCREEN_DESKTOP) {
if (((window->flags & SDL_WINDOW_FULLSCREEN_DESKTOP) == SDL_WINDOW_FULLSCREEN_DESKTOP) ||
((window->flags & SDL_WINDOW_FULLSCREEN) == SDL_WINDOW_FULLSCREEN)) {
windata->src_w = dispdata->mode.hdisplay;
windata->src_h = dispdata->mode.vdisplay;
windata->output_w = dispdata->mode.hdisplay;
windata->output_h = dispdata->mode.vdisplay;
windata->output_x = 0;
} else {
/* Get output (CRTC) size and position, for AR correction. */
/* Normal non-fullscreen windows are scaled using the CRTC,
so get output (CRTC) size and position, for AR correction. */
ratio = (float)window->w / (float)window->h;
windata->src_w = window->w;
windata->src_h = window->h;
windata->output_w = dispdata->mode.vdisplay * ratio;
windata->output_h = dispdata->mode.vdisplay;
windata->output_x = (dispdata->mode.hdisplay - windata->output_w) / 2;
}
/* Don't force fullscreen on all windows: it confuses programs that try

View File

@ -116,29 +116,28 @@ typedef struct SDL_DisplayData
drmModeModeInfo mode;
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;
plane *display_plane;
plane *cursor_plane;
crtc *crtc;
connector *connector;
/* Central atomic request list, used for the prop
changeset related to pageflip in SwapWindow. */
drmModeAtomicReq *atomic_req;
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. */
EGLSyncKHR kms_fence;
EGLSyncKHR gpu_fence;
#if SDL_VIDEO_OPENGL_EGL
EGLSurface old_egl_surface;
#endif
dumb_buffer *dumb_buffer; /* Aux dumb buffer to keep the PRIMARY PLANE
entertained with when we destroy GBM surface. */
dumb_buffer *dumb_buffer;
SDL_bool modeset_pending;
} SDL_DisplayData;