kmsdrm: reimplement modesetting for fullscreen window scaling and AR-correction.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259
diff --git a/src/video/kmsdrm/SDL_kmsdrmopengles.c b/src/video/kmsdrm/SDL_kmsdrmopengles.c
index a536b0a..f7c2d85 100644
--- a/src/video/kmsdrm/SDL_kmsdrmopengles.c
+++ b/src/video/kmsdrm/SDL_kmsdrmopengles.c
@@ -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. */
diff --git a/src/video/kmsdrm/SDL_kmsdrmvideo.c b/src/video/kmsdrm/SDL_kmsdrmvideo.c
index 0fe093b..00113b6 100644
--- a/src/video/kmsdrm/SDL_kmsdrmvideo.c
+++ b/src/video/kmsdrm/SDL_kmsdrmvideo.c
@@ -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
diff --git a/src/video/kmsdrm/SDL_kmsdrmvideo.h b/src/video/kmsdrm/SDL_kmsdrmvideo.h
index d152792..bb197fd 100644
--- a/src/video/kmsdrm/SDL_kmsdrmvideo.h
+++ b/src/video/kmsdrm/SDL_kmsdrmvideo.h
@@ -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;