From 31b179453468ae7913c193fcf8e3876b221fcc5c Mon Sep 17 00:00:00 2001 From: Manuel Alfayate Corchete Date: Fri, 28 Aug 2020 22:38:26 +0200 Subject: [PATCH] kmsdrm: use PLANE and CRTC to do hardware-driven window scaling and AR-correction. --- src/video/kmsdrm/SDL_kmsdrmopengles.c | 20 ++++---- src/video/kmsdrm/SDL_kmsdrmvideo.c | 68 ++++++++++++++++++++++----- src/video/kmsdrm/SDL_kmsdrmvideo.h | 25 ++++++---- 3 files changed, 83 insertions(+), 30 deletions(-) diff --git a/src/video/kmsdrm/SDL_kmsdrmopengles.c b/src/video/kmsdrm/SDL_kmsdrmopengles.c index 53c6fcdf1..01d5a8b6c 100644 --- a/src/video/kmsdrm/SDL_kmsdrmopengles.c +++ b/src/video/kmsdrm/SDL_kmsdrmopengles.c @@ -142,10 +142,12 @@ KMSDRM_GLES_SwapWindow(_THIS, SDL_Window * window) info.plane = dispdata->display_plane; info.crtc_id = dispdata->crtc->crtc->crtc_id; info.fb_id = fb->fb_id; - info.src_w = dispdata->mode.hdisplay; - info.src_h = dispdata->mode.vdisplay; - info.crtc_w = dispdata->mode.hdisplay; - info.crtc_h = dispdata->mode.vdisplay; + + info.src_w = window->w; + info.src_h = window->h; + info.crtc_w = windata->output_w; + info.crtc_h = windata->output_h; + info.crtc_x = windata->output_x; ret = drm_atomic_set_plane_props(&info); if (ret) { @@ -237,10 +239,12 @@ KMSDRM_GLES_SwapWindowDB(_THIS, SDL_Window * window) info.plane = dispdata->display_plane; info.crtc_id = dispdata->crtc->crtc->crtc_id; info.fb_id = fb->fb_id; - info.src_w = dispdata->mode.hdisplay; - info.src_h = dispdata->mode.vdisplay; - info.crtc_w = dispdata->mode.hdisplay; - info.crtc_h = dispdata->mode.vdisplay; + + info.src_w = window->w; + info.src_h = window->h; + info.crtc_w = windata->output_w; + info.crtc_h = windata->output_h; + info.crtc_x = windata->output_x; ret = drm_atomic_set_plane_props(&info); if (ret) { diff --git a/src/video/kmsdrm/SDL_kmsdrmvideo.c b/src/video/kmsdrm/SDL_kmsdrmvideo.c index 76788120b..4756a8414 100644 --- a/src/video/kmsdrm/SDL_kmsdrmvideo.c +++ b/src/video/kmsdrm/SDL_kmsdrmvideo.c @@ -629,6 +629,7 @@ KMSDRM_CreateDevice(int devindex) device->SetWindowIcon = KMSDRM_SetWindowIcon; device->SetWindowPosition = KMSDRM_SetWindowPosition; device->SetWindowSize = KMSDRM_SetWindowSize; + device->SetWindowFullscreen = KMSDRM_SetWindowFullscreen; device->ShowWindow = KMSDRM_ShowWindow; device->HideWindow = KMSDRM_HideWindow; device->RaiseWindow = KMSDRM_RaiseWindow; @@ -814,9 +815,8 @@ KMSDRM_CreateSurfaces(_THIS, SDL_Window * window) { SDL_VideoData *viddata = ((SDL_VideoData *)_this->driverdata); SDL_WindowData *windata = (SDL_WindowData *)window->driverdata; - SDL_DisplayData *dispdata = (SDL_DisplayData *) SDL_GetDisplayForWindow(window)->driverdata; - Uint32 width = dispdata->mode.hdisplay; - Uint32 height = dispdata->mode.vdisplay; + Uint32 width = window->w; + Uint32 height = window->h; Uint32 surface_fmt = GBM_FORMAT_ARGB8888; Uint32 surface_flags = GBM_BO_USE_SCANOUT | GBM_BO_USE_RENDERING; #if SDL_VIDEO_OPENGL_EGL @@ -1273,12 +1273,12 @@ int KMSDRM_SetDisplayMode(_THIS, SDL_VideoDisplay * display, SDL_DisplayMode * mode) { /************************************************************************/ - /* DO NOT add dynamic videomode changes unless you can REALLY test */ - /* on all available KMS drivers and fix them in-kernel, and also test */ - /* all SDL2 software: things will fail one way or another, and it */ - /* greatly increases backend complexiity thus compromising it's */ - /* maintenance. It's NOT as easy as reconstructing GBM and EGL surfaces.*/ - /************************************************************************/ + /* DO NOT add dynamic videomode changes. It makes NO SENSE since the */ + /* PRIMARY PLANE and the CRTC reading it can be used to scale image, */ + /* so any window will appear fullscren with AR correction with NO extra */ + /* video memory bandwidth usage. */ + /************************************************************************/ + return 0; } @@ -1287,7 +1287,9 @@ KMSDRM_CreateWindow(_THIS, SDL_Window * window) { SDL_VideoData *viddata = (SDL_VideoData *)_this->driverdata; SDL_VideoDisplay *display = NULL; + SDL_DisplayData *dispdata = NULL; SDL_WindowData *windata = NULL; + float ratio; #if SDL_VIDEO_OPENGL_EGL if (!_this->egl_data) { @@ -1301,10 +1303,22 @@ KMSDRM_CreateWindow(_THIS, SDL_Window * window) windata = (SDL_WindowData *)SDL_calloc(1, sizeof(SDL_WindowData)); display = SDL_GetDisplayForWindow(window); + dispdata = display->driverdata; - /* Windows have one size for now */ - window->w = display->desktop_mode.w; - window->h = display->desktop_mode.h; + if (window->flags & SDL_WINDOW_FULLSCREEN) { + /* Windows only have one possible size in fullscreen mode. */ + window->w = dispdata->mode.hdisplay; + window->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. */ + ratio = (float)window->w / (float)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 to set a window fullscreen after creating it as non-fullscreen (sm64ex) */ @@ -1401,6 +1415,36 @@ KMSDRM_SetWindowSize(_THIS, SDL_Window * window) { } void +KMSDRM_SetWindowFullscreen(_THIS, SDL_Window * window, SDL_VideoDisplay * display, SDL_bool fullscreen) +{ + + SDL_WindowData *windata = window->driverdata; + SDL_DisplayData *dispdata = display->driverdata; + float ratio; + + KMSDRM_SetPendingSurfacesDestruction(_this, window); + + if (fullscreen) { + /* Windows only have one possible size in fullscreen mode. */ + window->w = dispdata->mode.hdisplay; + window->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. */ + ratio = (float)window->w / (float)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)) { + SDL_SetError("Can't recreate window surfaces on SetWindowFullscreen."); + } +} +void KMSDRM_ShowWindow(_THIS, SDL_Window * window) { } diff --git a/src/video/kmsdrm/SDL_kmsdrmvideo.h b/src/video/kmsdrm/SDL_kmsdrmvideo.h index 0b6543d26..72acb1099 100644 --- a/src/video/kmsdrm/SDL_kmsdrmvideo.h +++ b/src/video/kmsdrm/SDL_kmsdrmvideo.h @@ -125,6 +125,12 @@ typedef struct SDL_WindowData #if SDL_VIDEO_OPENGL_EGL EGLSurface egl_surface; #endif + + /* For scaling and AR correction. */ + int32_t output_w; + int32_t output_h; + int32_t output_x; + } SDL_WindowData; typedef struct KMSDRM_FBInfo @@ -133,21 +139,19 @@ typedef struct KMSDRM_FBInfo uint32_t fb_id; /* DRM framebuffer ID */ } KMSDRM_FBInfo; -/* Info passed to set_plane_props calls. hdisplay and vdisplay in a drm mode are uint16_t, - so that's what we use for sizes and positions here. IDs are uint32_t as always. */ typedef struct KMSDRM_PlaneInfo { struct plane *plane; uint32_t fb_id; uint32_t crtc_id; - uint16_t src_x; - uint16_t src_y; - uint16_t src_w; - uint16_t src_h; - uint16_t crtc_x; - uint16_t crtc_y; - uint16_t crtc_w; - uint16_t crtc_h; + int32_t src_x; + int32_t src_y; + int32_t src_w; + int32_t src_h; + int32_t crtc_x; + int32_t crtc_y; + int32_t crtc_w; + int32_t crtc_h; } KMSDRM_PlaneInfo; /* Helper functions */ @@ -182,6 +186,7 @@ void KMSDRM_SetWindowTitle(_THIS, SDL_Window * window); void KMSDRM_SetWindowIcon(_THIS, SDL_Window * window, SDL_Surface * icon); void KMSDRM_SetWindowPosition(_THIS, SDL_Window * window); void KMSDRM_SetWindowSize(_THIS, SDL_Window * window); +void KMSDRM_SetWindowFullscreen(_THIS, SDL_Window * window, SDL_VideoDisplay * _display, SDL_bool fullscreen); void KMSDRM_ShowWindow(_THIS, SDL_Window * window); void KMSDRM_HideWindow(_THIS, SDL_Window * window); void KMSDRM_RaiseWindow(_THIS, SDL_Window * window);