Skip to content

Commit 1fe9267

Browse files
committed
kmsdrm: Restore atomic support.
1 parent e4c60c0 commit 1fe9267

File tree

7 files changed

+1109
-82
lines changed

7 files changed

+1109
-82
lines changed

include/SDL3/SDL_hints.h

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2473,6 +2473,28 @@ extern "C" {
24732473
*/
24742474
#define SDL_HINT_KMSDRM_REQUIRE_DRM_MASTER "SDL_KMSDRM_REQUIRE_DRM_MASTER"
24752475

2476+
/**
2477+
* A variable that controls whether KMSDRM will use "atomic" functionality.
2478+
*
2479+
* The KMSDRM backend can use atomic commits, if both DRM_CLIENT_CAP_ATOMIC
2480+
* and DRM_CLIENT_CAP_UNIVERSAL_PLANES is supported by the system. As of
2481+
* SDL 3.4.0, it will favor this functionality, but in case this doesn't
2482+
* work well on a given system or other surprises, this hint can be used
2483+
* to disable it.
2484+
*
2485+
* This hint can not enable the functionality if it isn't available.
2486+
*
2487+
* The variable can be set to the following values:
2488+
*
2489+
* - "0": SDL will not use the KMSDRM "atomic" functionality.
2490+
* - "1": SDL will allow usage of the KMSDRM "atomic" functionality. (default)
2491+
*
2492+
* This hint should be set before SDL is initialized.
2493+
*
2494+
* \since This hint is available since SDL 3.4.0.
2495+
*/
2496+
#define SDL_HINT_KMSDRM_ATOMIC "SDL_KMSDRM_ATOMIC"
2497+
24762498
/**
24772499
* A variable controlling the default SDL log levels.
24782500
*

src/video/kmsdrm/SDL_kmsdrmmouse.c

Lines changed: 112 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -67,6 +67,23 @@ void KMSDRM_DestroyCursorBO(SDL_VideoDevice *_this, SDL_VideoDisplay *display)
6767

6868
// Destroy the curso GBM BO.
6969
if (dispdata->cursor_bo) {
70+
SDL_VideoData *viddata = (SDL_VideoData *) _this->internal;
71+
if (viddata->is_atomic) {
72+
if (dispdata->cursor_plane) {
73+
// Unset the the cursor BO from the cursor plane.
74+
KMSDRM_PlaneInfo info;
75+
SDL_zero(info);
76+
info.plane = dispdata->cursor_plane;
77+
drm_atomic_set_plane_props(dispdata, &info);
78+
// Wait until the cursor is unset from the cursor plane before destroying it's BO.
79+
if (drm_atomic_commit(_this, dispdata, true, false)) {
80+
SDL_SetError("Failed atomic commit in KMSDRM_DenitMouse.");
81+
}
82+
// Free the cursor plane, on which the cursor was being shown.
83+
free_plane(&dispdata->cursor_plane);
84+
}
85+
}
86+
7087
KMSDRM_gbm_bo_destroy(dispdata->cursor_bo);
7188
dispdata->cursor_bo = NULL;
7289
dispdata->cursor_bo_drm_fd = -1;
@@ -78,11 +95,14 @@ void KMSDRM_DestroyCursorBO(SDL_VideoDevice *_this, SDL_VideoDisplay *display)
7895
build a window and assign a display to it. */
7996
bool KMSDRM_CreateCursorBO(SDL_VideoDisplay *display)
8097
{
81-
8298
SDL_VideoDevice *dev = SDL_GetVideoDevice();
8399
SDL_VideoData *viddata = dev->internal;
84100
SDL_DisplayData *dispdata = display->internal;
85101

102+
if (viddata->is_atomic) {
103+
setup_plane(dev, dispdata, &dispdata->cursor_plane, DRM_PLANE_TYPE_CURSOR);
104+
}
105+
86106
if (!KMSDRM_gbm_device_is_format_supported(viddata->gbm_dev,
87107
GBM_FORMAT_ARGB8888,
88108
GBM_BO_USE_CURSOR | GBM_BO_USE_WRITE)) {
@@ -121,15 +141,29 @@ static bool KMSDRM_RemoveCursorFromBO(SDL_VideoDisplay *display)
121141
SDL_VideoDevice *video_device = SDL_GetVideoDevice();
122142
SDL_VideoData *viddata = video_device->internal;
123143

124-
const int rc = KMSDRM_drmModeSetCursor(viddata->drm_fd, dispdata->crtc->crtc_id, 0, 0, 0);
125-
if (rc < 0) {
126-
result = SDL_SetError("drmModeSetCursor() failed: %s", strerror(-rc));
144+
if (viddata->is_atomic) {
145+
if (dispdata->cursor_plane) {
146+
KMSDRM_PlaneInfo info;
147+
SDL_zero(info);
148+
info.plane = dispdata->cursor_plane;
149+
// The rest of the members are zeroed, so this takes away the cursor from the cursor plane.
150+
drm_atomic_set_plane_props(dispdata, &info);
151+
if (drm_atomic_commit(video_device, dispdata, true, false)) {
152+
result = SDL_SetError("Failed atomic commit in KMSDRM_ShowCursor.");
153+
}
154+
}
155+
} else {
156+
const int rc = KMSDRM_drmModeSetCursor(viddata->drm_fd, dispdata->crtc.crtc->crtc_id, 0, 0, 0);
157+
if (rc < 0) {
158+
result = SDL_SetError("drmModeSetCursor() failed: %s", strerror(-rc));
159+
}
127160
}
161+
128162
return result;
129163
}
130164

131165
// Dump a cursor buffer to a display's DRM cursor BO.
132-
static bool KMSDRM_DumpCursorToBO(SDL_VideoDisplay *display, SDL_Cursor *cursor)
166+
static bool KMSDRM_DumpCursorToBO(SDL_VideoDisplay *display, SDL_Mouse *mouse, SDL_Cursor *cursor)
133167
{
134168
SDL_DisplayData *dispdata = display->internal;
135169
SDL_CursorData *curdata = cursor->internal;
@@ -173,22 +207,42 @@ static bool KMSDRM_DumpCursorToBO(SDL_VideoDisplay *display, SDL_Cursor *cursor)
173207
goto cleanup;
174208
}
175209

176-
// Put the GBM BO buffer on screen using the DRM interface.
177-
bo_handle = KMSDRM_gbm_bo_get_handle(dispdata->cursor_bo).u32;
178-
if (curdata->hot_x == 0 && curdata->hot_y == 0) {
179-
rc = KMSDRM_drmModeSetCursor(viddata->drm_fd, dispdata->crtc->crtc_id,
180-
bo_handle, dispdata->cursor_w, dispdata->cursor_h);
210+
if (viddata->is_atomic) {
211+
// Get the fb_id for the GBM BO so we can show it on the cursor plane.
212+
KMSDRM_FBInfo *fb = KMSDRM_FBFromBO(video_device, dispdata->cursor_bo);
213+
KMSDRM_PlaneInfo info;
214+
215+
// Show the GBM BO buffer on the cursor plane.
216+
SDL_zero(info);
217+
info.plane = dispdata->cursor_plane;
218+
info.crtc_id = dispdata->crtc.crtc->crtc_id;
219+
info.fb_id = fb->fb_id;
220+
info.src_w = dispdata->cursor_w;
221+
info.src_h = dispdata->cursor_h;
222+
info.crtc_x = ((int32_t) SDL_roundf(mouse->x)) - curdata->hot_x;
223+
info.crtc_y = ((int32_t) SDL_roundf(mouse->y)) - curdata->hot_y;
224+
info.crtc_w = curdata->w;
225+
info.crtc_h = curdata->h;
226+
drm_atomic_set_plane_props(dispdata, &info);
227+
if (drm_atomic_commit(video_device, dispdata, true, false)) {
228+
result = SDL_SetError("Failed atomic commit in KMSDRM_ShowCursor.");
229+
goto cleanup;
230+
}
181231
} else {
182-
rc = KMSDRM_drmModeSetCursor2(viddata->drm_fd, dispdata->crtc->crtc_id,
183-
bo_handle, dispdata->cursor_w, dispdata->cursor_h, curdata->hot_x, curdata->hot_y);
184-
}
185-
if (rc < 0) {
186-
result = SDL_SetError("Failed to set DRM cursor: %s", strerror(-rc));
187-
goto cleanup;
232+
// Put the GBM BO buffer on screen using the DRM interface.
233+
bo_handle = KMSDRM_gbm_bo_get_handle(dispdata->cursor_bo).u32;
234+
if (curdata->hot_x == 0 && curdata->hot_y == 0) {
235+
rc = KMSDRM_drmModeSetCursor(viddata->drm_fd, dispdata->crtc.crtc->crtc_id, bo_handle, dispdata->cursor_w, dispdata->cursor_h);
236+
} else {
237+
rc = KMSDRM_drmModeSetCursor2(viddata->drm_fd, dispdata->crtc.crtc->crtc_id, bo_handle, dispdata->cursor_w, dispdata->cursor_h, curdata->hot_x, curdata->hot_y);
238+
}
239+
if (rc < 0) {
240+
result = SDL_SetError("Failed to set DRM cursor: %s", strerror(-rc));
241+
goto cleanup;
242+
}
188243
}
189244

190245
cleanup:
191-
192246
if (ready_buffer) {
193247
SDL_free(ready_buffer);
194248
}
@@ -316,7 +370,7 @@ static bool KMSDRM_ShowCursor(SDL_Cursor *cursor)
316370
if (cursor) {
317371
/* Dump the cursor to the display DRM cursor BO so it becomes visible
318372
on that display. */
319-
result = KMSDRM_DumpCursorToBO(display, cursor);
373+
result = KMSDRM_DumpCursorToBO(display, mouse, cursor);
320374
} else {
321375
// Hide the cursor on that display.
322376
result = KMSDRM_RemoveCursorFromBO(display);
@@ -327,6 +381,18 @@ static bool KMSDRM_ShowCursor(SDL_Cursor *cursor)
327381
return result;
328382
}
329383

384+
static void drm_atomic_movecursor(SDL_DisplayData *dispdata, const SDL_CursorData *curdata, uint16_t x, uint16_t y)
385+
{
386+
if (dispdata->cursor_plane) { // We can't move a non-existing cursor, but that's ok.
387+
// Do we have a set of changes already in the making? If not, allocate a new one.
388+
if (!dispdata->atomic_req) {
389+
dispdata->atomic_req = KMSDRM_drmModeAtomicAlloc();
390+
}
391+
add_plane_property(dispdata->atomic_req, dispdata->cursor_plane, "CRTC_X", x - curdata->hot_x);
392+
add_plane_property(dispdata->atomic_req, dispdata->cursor_plane, "CRTC_Y", y - curdata->hot_y);
393+
}
394+
}
395+
330396
static bool KMSDRM_WarpMouseGlobal(float x, float y)
331397
{
332398
SDL_Mouse *mouse = SDL_GetMouse();
@@ -340,17 +406,25 @@ static bool KMSDRM_WarpMouseGlobal(float x, float y)
340406

341407
// And now update the cursor graphic position on screen.
342408
if (dispdata->cursor_bo) {
343-
const int rc = KMSDRM_drmModeMoveCursor(dispdata->cursor_bo_drm_fd, dispdata->crtc->crtc_id, (int)x, (int)y);
344-
if (rc < 0) {
345-
return SDL_SetError("drmModeMoveCursor() failed: %s", strerror(-rc));
409+
SDL_VideoDevice *dev = SDL_GetVideoDevice();
410+
SDL_VideoData *viddata = dev->internal;
411+
if (viddata->is_atomic) {
412+
const SDL_CursorData *curdata = (const SDL_CursorData *) mouse->cur_cursor->internal;
413+
drm_atomic_movecursor(dispdata, curdata, (uint16_t) (int) x, (uint16_t) (int) y);
414+
} else {
415+
const int rc = KMSDRM_drmModeMoveCursor(dispdata->cursor_bo_drm_fd, dispdata->crtc.crtc->crtc_id, (int)x, (int)y);
416+
if (rc < 0) {
417+
return SDL_SetError("drmModeMoveCursor() failed: %s", strerror(-rc));
418+
}
346419
}
347-
return true;
348420
} else {
349421
return SDL_SetError("Cursor not initialized properly.");
350422
}
351423
} else {
352424
return SDL_SetError("No mouse or current cursor.");
353425
}
426+
427+
return true;
354428
}
355429

356430
static bool KMSDRM_WarpMouse(SDL_Window *window, float x, float y)
@@ -394,14 +468,27 @@ static bool KMSDRM_MoveCursor(SDL_Cursor *cursor)
394468
if (mouse && mouse->cur_cursor && mouse->focus) {
395469
SDL_Window *window = mouse->focus;
396470
SDL_DisplayData *dispdata = SDL_GetDisplayDriverDataForWindow(window);
471+
SDL_VideoDevice *dev = SDL_GetVideoDevice();
472+
SDL_VideoData *viddata = dev->internal;
397473

398474
if (!dispdata->cursor_bo) {
399475
return SDL_SetError("Cursor not initialized properly.");
400476
}
401477

402-
const int rc = KMSDRM_drmModeMoveCursor(dispdata->cursor_bo_drm_fd, dispdata->crtc->crtc_id, (int)mouse->x, (int)mouse->y);
403-
if (rc < 0) {
404-
return SDL_SetError("drmModeMoveCursor() failed: %s", strerror(-rc));
478+
if (viddata->is_atomic) {
479+
/* !!! FIXME: Some programs expect cursor movement even while they don't do SwapWindow() calls,
480+
and since we ride on the atomic_commit() in SwapWindow() for cursor movement,
481+
cursor won't move in these situations. We could do an atomic_commit() here
482+
for each cursor movement request, but it cripples the movement to 30FPS,
483+
so a future solution is needed. SDLPoP "QUIT?" menu is an example of this
484+
situation. */
485+
const SDL_CursorData *curdata = (const SDL_CursorData *) mouse->cur_cursor->internal;
486+
drm_atomic_movecursor(dispdata, curdata, (uint16_t) (int) mouse->x, (uint16_t) (int) mouse->y);
487+
} else {
488+
const int rc = KMSDRM_drmModeMoveCursor(dispdata->cursor_bo_drm_fd, dispdata->crtc.crtc->crtc_id, (int)mouse->x, (int)mouse->y);
489+
if (rc < 0) {
490+
return SDL_SetError("drmModeMoveCursor() failed: %s", strerror(-rc));
491+
}
405492
}
406493
}
407494
return true;

0 commit comments

Comments
 (0)