mirror of
https://github.com/libsdl-org/SDL.git
synced 2026-03-23 18:53:08 +08:00
kmsdrm: Restore atomic support.
This commit is contained in:
@@ -2473,6 +2473,28 @@ extern "C" {
|
||||
*/
|
||||
#define SDL_HINT_KMSDRM_REQUIRE_DRM_MASTER "SDL_KMSDRM_REQUIRE_DRM_MASTER"
|
||||
|
||||
/**
|
||||
* A variable that controls whether KMSDRM will use "atomic" functionality.
|
||||
*
|
||||
* The KMSDRM backend can use atomic commits, if both DRM_CLIENT_CAP_ATOMIC
|
||||
* and DRM_CLIENT_CAP_UNIVERSAL_PLANES is supported by the system. As of
|
||||
* SDL 3.4.0, it will favor this functionality, but in case this doesn't
|
||||
* work well on a given system or other surprises, this hint can be used
|
||||
* to disable it.
|
||||
*
|
||||
* This hint can not enable the functionality if it isn't available.
|
||||
*
|
||||
* The variable can be set to the following values:
|
||||
*
|
||||
* - "0": SDL will not use the KMSDRM "atomic" functionality.
|
||||
* - "1": SDL will allow usage of the KMSDRM "atomic" functionality. (default)
|
||||
*
|
||||
* This hint should be set before SDL is initialized.
|
||||
*
|
||||
* \since This hint is available since SDL 3.4.0.
|
||||
*/
|
||||
#define SDL_HINT_KMSDRM_ATOMIC "SDL_KMSDRM_ATOMIC"
|
||||
|
||||
/**
|
||||
* A variable controlling the default SDL log levels.
|
||||
*
|
||||
|
||||
@@ -67,6 +67,23 @@ void KMSDRM_DestroyCursorBO(SDL_VideoDevice *_this, SDL_VideoDisplay *display)
|
||||
|
||||
// Destroy the curso GBM BO.
|
||||
if (dispdata->cursor_bo) {
|
||||
SDL_VideoData *viddata = (SDL_VideoData *) _this->internal;
|
||||
if (viddata->is_atomic) {
|
||||
if (dispdata->cursor_plane) {
|
||||
// Unset the the cursor BO from the cursor plane.
|
||||
KMSDRM_PlaneInfo info;
|
||||
SDL_zero(info);
|
||||
info.plane = dispdata->cursor_plane;
|
||||
drm_atomic_set_plane_props(dispdata, &info);
|
||||
// Wait until the cursor is unset from the cursor plane before destroying it's BO.
|
||||
if (drm_atomic_commit(_this, dispdata, true, false)) {
|
||||
SDL_SetError("Failed atomic commit in KMSDRM_DenitMouse.");
|
||||
}
|
||||
// Free the cursor plane, on which the cursor was being shown.
|
||||
free_plane(&dispdata->cursor_plane);
|
||||
}
|
||||
}
|
||||
|
||||
KMSDRM_gbm_bo_destroy(dispdata->cursor_bo);
|
||||
dispdata->cursor_bo = NULL;
|
||||
dispdata->cursor_bo_drm_fd = -1;
|
||||
@@ -78,11 +95,14 @@ void KMSDRM_DestroyCursorBO(SDL_VideoDevice *_this, SDL_VideoDisplay *display)
|
||||
build a window and assign a display to it. */
|
||||
bool KMSDRM_CreateCursorBO(SDL_VideoDisplay *display)
|
||||
{
|
||||
|
||||
SDL_VideoDevice *dev = SDL_GetVideoDevice();
|
||||
SDL_VideoData *viddata = dev->internal;
|
||||
SDL_DisplayData *dispdata = display->internal;
|
||||
|
||||
if (viddata->is_atomic) {
|
||||
setup_plane(dev, dispdata, &dispdata->cursor_plane, DRM_PLANE_TYPE_CURSOR);
|
||||
}
|
||||
|
||||
if (!KMSDRM_gbm_device_is_format_supported(viddata->gbm_dev,
|
||||
GBM_FORMAT_ARGB8888,
|
||||
GBM_BO_USE_CURSOR | GBM_BO_USE_WRITE)) {
|
||||
@@ -121,15 +141,29 @@ static bool KMSDRM_RemoveCursorFromBO(SDL_VideoDisplay *display)
|
||||
SDL_VideoDevice *video_device = SDL_GetVideoDevice();
|
||||
SDL_VideoData *viddata = video_device->internal;
|
||||
|
||||
const int rc = KMSDRM_drmModeSetCursor(viddata->drm_fd, dispdata->crtc->crtc_id, 0, 0, 0);
|
||||
if (rc < 0) {
|
||||
result = SDL_SetError("drmModeSetCursor() failed: %s", strerror(-rc));
|
||||
if (viddata->is_atomic) {
|
||||
if (dispdata->cursor_plane) {
|
||||
KMSDRM_PlaneInfo info;
|
||||
SDL_zero(info);
|
||||
info.plane = dispdata->cursor_plane;
|
||||
// The rest of the members are zeroed, so this takes away the cursor from the cursor plane.
|
||||
drm_atomic_set_plane_props(dispdata, &info);
|
||||
if (drm_atomic_commit(video_device, dispdata, true, false)) {
|
||||
result = SDL_SetError("Failed atomic commit in KMSDRM_ShowCursor.");
|
||||
}
|
||||
}
|
||||
} else {
|
||||
const int rc = KMSDRM_drmModeSetCursor(viddata->drm_fd, dispdata->crtc.crtc->crtc_id, 0, 0, 0);
|
||||
if (rc < 0) {
|
||||
result = SDL_SetError("drmModeSetCursor() failed: %s", strerror(-rc));
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
// Dump a cursor buffer to a display's DRM cursor BO.
|
||||
static bool KMSDRM_DumpCursorToBO(SDL_VideoDisplay *display, SDL_Cursor *cursor)
|
||||
static bool KMSDRM_DumpCursorToBO(SDL_VideoDisplay *display, SDL_Mouse *mouse, SDL_Cursor *cursor)
|
||||
{
|
||||
SDL_DisplayData *dispdata = display->internal;
|
||||
SDL_CursorData *curdata = cursor->internal;
|
||||
@@ -173,22 +207,42 @@ static bool KMSDRM_DumpCursorToBO(SDL_VideoDisplay *display, SDL_Cursor *cursor)
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
// Put the GBM BO buffer on screen using the DRM interface.
|
||||
bo_handle = KMSDRM_gbm_bo_get_handle(dispdata->cursor_bo).u32;
|
||||
if (curdata->hot_x == 0 && curdata->hot_y == 0) {
|
||||
rc = KMSDRM_drmModeSetCursor(viddata->drm_fd, dispdata->crtc->crtc_id,
|
||||
bo_handle, dispdata->cursor_w, dispdata->cursor_h);
|
||||
if (viddata->is_atomic) {
|
||||
// Get the fb_id for the GBM BO so we can show it on the cursor plane.
|
||||
KMSDRM_FBInfo *fb = KMSDRM_FBFromBO(video_device, dispdata->cursor_bo);
|
||||
KMSDRM_PlaneInfo info;
|
||||
|
||||
// Show the GBM BO buffer on the cursor plane.
|
||||
SDL_zero(info);
|
||||
info.plane = dispdata->cursor_plane;
|
||||
info.crtc_id = dispdata->crtc.crtc->crtc_id;
|
||||
info.fb_id = fb->fb_id;
|
||||
info.src_w = dispdata->cursor_w;
|
||||
info.src_h = dispdata->cursor_h;
|
||||
info.crtc_x = ((int32_t) SDL_roundf(mouse->x)) - curdata->hot_x;
|
||||
info.crtc_y = ((int32_t) SDL_roundf(mouse->y)) - curdata->hot_y;
|
||||
info.crtc_w = curdata->w;
|
||||
info.crtc_h = curdata->h;
|
||||
drm_atomic_set_plane_props(dispdata, &info);
|
||||
if (drm_atomic_commit(video_device, dispdata, true, false)) {
|
||||
result = SDL_SetError("Failed atomic commit in KMSDRM_ShowCursor.");
|
||||
goto cleanup;
|
||||
}
|
||||
} else {
|
||||
rc = KMSDRM_drmModeSetCursor2(viddata->drm_fd, dispdata->crtc->crtc_id,
|
||||
bo_handle, dispdata->cursor_w, dispdata->cursor_h, curdata->hot_x, curdata->hot_y);
|
||||
}
|
||||
if (rc < 0) {
|
||||
result = SDL_SetError("Failed to set DRM cursor: %s", strerror(-rc));
|
||||
goto cleanup;
|
||||
// Put the GBM BO buffer on screen using the DRM interface.
|
||||
bo_handle = KMSDRM_gbm_bo_get_handle(dispdata->cursor_bo).u32;
|
||||
if (curdata->hot_x == 0 && curdata->hot_y == 0) {
|
||||
rc = KMSDRM_drmModeSetCursor(viddata->drm_fd, dispdata->crtc.crtc->crtc_id, bo_handle, dispdata->cursor_w, dispdata->cursor_h);
|
||||
} else {
|
||||
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);
|
||||
}
|
||||
if (rc < 0) {
|
||||
result = SDL_SetError("Failed to set DRM cursor: %s", strerror(-rc));
|
||||
goto cleanup;
|
||||
}
|
||||
}
|
||||
|
||||
cleanup:
|
||||
|
||||
if (ready_buffer) {
|
||||
SDL_free(ready_buffer);
|
||||
}
|
||||
@@ -316,7 +370,7 @@ static bool KMSDRM_ShowCursor(SDL_Cursor *cursor)
|
||||
if (cursor) {
|
||||
/* Dump the cursor to the display DRM cursor BO so it becomes visible
|
||||
on that display. */
|
||||
result = KMSDRM_DumpCursorToBO(display, cursor);
|
||||
result = KMSDRM_DumpCursorToBO(display, mouse, cursor);
|
||||
} else {
|
||||
// Hide the cursor on that display.
|
||||
result = KMSDRM_RemoveCursorFromBO(display);
|
||||
@@ -327,6 +381,18 @@ static bool KMSDRM_ShowCursor(SDL_Cursor *cursor)
|
||||
return result;
|
||||
}
|
||||
|
||||
static void drm_atomic_movecursor(SDL_DisplayData *dispdata, const SDL_CursorData *curdata, uint16_t x, uint16_t y)
|
||||
{
|
||||
if (dispdata->cursor_plane) { // We can't move a non-existing cursor, but that's ok.
|
||||
// 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, dispdata->cursor_plane, "CRTC_X", x - curdata->hot_x);
|
||||
add_plane_property(dispdata->atomic_req, dispdata->cursor_plane, "CRTC_Y", y - curdata->hot_y);
|
||||
}
|
||||
}
|
||||
|
||||
static bool KMSDRM_WarpMouseGlobal(float x, float y)
|
||||
{
|
||||
SDL_Mouse *mouse = SDL_GetMouse();
|
||||
@@ -340,17 +406,25 @@ static bool KMSDRM_WarpMouseGlobal(float x, float y)
|
||||
|
||||
// And now update the cursor graphic position on screen.
|
||||
if (dispdata->cursor_bo) {
|
||||
const int rc = KMSDRM_drmModeMoveCursor(dispdata->cursor_bo_drm_fd, dispdata->crtc->crtc_id, (int)x, (int)y);
|
||||
if (rc < 0) {
|
||||
return SDL_SetError("drmModeMoveCursor() failed: %s", strerror(-rc));
|
||||
SDL_VideoDevice *dev = SDL_GetVideoDevice();
|
||||
SDL_VideoData *viddata = dev->internal;
|
||||
if (viddata->is_atomic) {
|
||||
const SDL_CursorData *curdata = (const SDL_CursorData *) mouse->cur_cursor->internal;
|
||||
drm_atomic_movecursor(dispdata, curdata, (uint16_t) (int) x, (uint16_t) (int) y);
|
||||
} else {
|
||||
const int rc = KMSDRM_drmModeMoveCursor(dispdata->cursor_bo_drm_fd, dispdata->crtc.crtc->crtc_id, (int)x, (int)y);
|
||||
if (rc < 0) {
|
||||
return SDL_SetError("drmModeMoveCursor() failed: %s", strerror(-rc));
|
||||
}
|
||||
}
|
||||
return true;
|
||||
} else {
|
||||
return SDL_SetError("Cursor not initialized properly.");
|
||||
}
|
||||
} else {
|
||||
return SDL_SetError("No mouse or current cursor.");
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool KMSDRM_WarpMouse(SDL_Window *window, float x, float y)
|
||||
@@ -394,14 +468,27 @@ static bool KMSDRM_MoveCursor(SDL_Cursor *cursor)
|
||||
if (mouse && mouse->cur_cursor && mouse->focus) {
|
||||
SDL_Window *window = mouse->focus;
|
||||
SDL_DisplayData *dispdata = SDL_GetDisplayDriverDataForWindow(window);
|
||||
SDL_VideoDevice *dev = SDL_GetVideoDevice();
|
||||
SDL_VideoData *viddata = dev->internal;
|
||||
|
||||
if (!dispdata->cursor_bo) {
|
||||
return SDL_SetError("Cursor not initialized properly.");
|
||||
}
|
||||
|
||||
const int rc = KMSDRM_drmModeMoveCursor(dispdata->cursor_bo_drm_fd, dispdata->crtc->crtc_id, (int)mouse->x, (int)mouse->y);
|
||||
if (rc < 0) {
|
||||
return SDL_SetError("drmModeMoveCursor() failed: %s", strerror(-rc));
|
||||
if (viddata->is_atomic) {
|
||||
/* !!! FIXME: Some programs expect cursor movement even while they don't do SwapWindow() calls,
|
||||
and since we ride on the atomic_commit() in SwapWindow() for cursor movement,
|
||||
cursor won't move in these situations. We could do an atomic_commit() here
|
||||
for each cursor movement request, but it cripples the movement to 30FPS,
|
||||
so a future solution is needed. SDLPoP "QUIT?" menu is an example of this
|
||||
situation. */
|
||||
const SDL_CursorData *curdata = (const SDL_CursorData *) mouse->cur_cursor->internal;
|
||||
drm_atomic_movecursor(dispdata, curdata, (uint16_t) (int) mouse->x, (uint16_t) (int) mouse->y);
|
||||
} else {
|
||||
const int rc = KMSDRM_drmModeMoveCursor(dispdata->cursor_bo_drm_fd, dispdata->crtc.crtc->crtc_id, (int)mouse->x, (int)mouse->y);
|
||||
if (rc < 0) {
|
||||
return SDL_SetError("drmModeMoveCursor() failed: %s", strerror(-rc));
|
||||
}
|
||||
}
|
||||
}
|
||||
return true;
|
||||
|
||||
@@ -28,10 +28,25 @@
|
||||
#include "SDL_kmsdrmdyn.h"
|
||||
#include <errno.h>
|
||||
|
||||
#define VOID2U64(x) ((uint64_t)(size_t)(x))
|
||||
|
||||
#ifndef EGL_PLATFORM_GBM_MESA
|
||||
#define EGL_PLATFORM_GBM_MESA 0x31D7
|
||||
#endif
|
||||
|
||||
#ifndef EGL_SYNC_NATIVE_FENCE_ANDROID
|
||||
#define EGL_SYNC_NATIVE_FENCE_ANDROID 0x3144
|
||||
#endif
|
||||
|
||||
#ifndef EGL_SYNC_NATIVE_FENCE_FD_ANDROID
|
||||
#define EGL_SYNC_NATIVE_FENCE_FD_ANDROID 0x3145
|
||||
#endif
|
||||
|
||||
#ifndef EGL_NO_NATIVE_FENCE_FD_ANDROID
|
||||
#define EGL_NO_NATIVE_FENCE_FD_ANDROID -1
|
||||
#endif
|
||||
|
||||
|
||||
// EGL implementation of SDL OpenGL support
|
||||
|
||||
void KMSDRM_GLES_DefaultProfileConfig(SDL_VideoDevice *_this, int *mask, int *major, int *minor)
|
||||
@@ -83,7 +98,291 @@ bool KMSDRM_GLES_SetSwapInterval(SDL_VideoDevice *_this, int interval)
|
||||
return true;
|
||||
}
|
||||
|
||||
bool KMSDRM_GLES_SwapWindow(SDL_VideoDevice *_this, SDL_Window *window)
|
||||
static EGLSyncKHR create_fence(SDL_VideoDevice *_this, int fd)
|
||||
{
|
||||
EGLint attrib_list[] = {
|
||||
EGL_SYNC_NATIVE_FENCE_FD_ANDROID, fd,
|
||||
EGL_NONE,
|
||||
};
|
||||
|
||||
EGLSyncKHR fence = _this->egl_data->eglCreateSyncKHR(_this->egl_data->egl_display, EGL_SYNC_NATIVE_FENCE_ANDROID, attrib_list);
|
||||
|
||||
SDL_assert(fence);
|
||||
return fence;
|
||||
}
|
||||
|
||||
/***********************************************************************************/
|
||||
/* Comments about buffer access protection mechanism (=fences) are the ones boxed. */
|
||||
/* Also, DON'T remove the asserts: if a fence-related call fails, it's better that */
|
||||
/* program exits immediately, or we could leave KMS waiting for a failed/missing */
|
||||
/* fence forever. */
|
||||
/***********************************************************************************/
|
||||
static bool KMSDRM_GLES_SwapWindowFenced(SDL_VideoDevice *_this, SDL_Window * window)
|
||||
{
|
||||
SDL_WindowData *windata = ((SDL_WindowData *) window->internal);
|
||||
SDL_DisplayData *dispdata = SDL_GetDisplayDriverDataForWindow(window);
|
||||
KMSDRM_FBInfo *fb;
|
||||
KMSDRM_PlaneInfo info;
|
||||
bool modesetting = false;
|
||||
|
||||
SDL_zero(info);
|
||||
|
||||
/******************************************************************/
|
||||
/* Create the GPU-side FENCE OBJECT. It will be inserted into the */
|
||||
/* GL CMDSTREAM exactly at the end of the gl commands that form a */
|
||||
/* frame.(KMS will have to wait on it before doing a pageflip.) */
|
||||
/******************************************************************/
|
||||
dispdata->gpu_fence = create_fence(_this, EGL_NO_NATIVE_FENCE_FD_ANDROID);
|
||||
SDL_assert(dispdata->gpu_fence);
|
||||
|
||||
/******************************************************************/
|
||||
/* eglSwapBuffers flushes the fence down the GL CMDSTREAM, so we */
|
||||
/* know for sure it's there now. */
|
||||
/* Also it marks, at EGL level, the buffer that we want to become */
|
||||
/* the new front buffer. (Remember that won't really happen until */
|
||||
/* we request a pageflip at the KMS level and it completes. */
|
||||
/******************************************************************/
|
||||
if (! _this->egl_data->eglSwapBuffers(_this->egl_data->egl_display, windata->egl_surface)) {
|
||||
return SDL_EGL_SetError("Failed to swap EGL buffers", "eglSwapBuffers");
|
||||
}
|
||||
|
||||
/******************************************************************/
|
||||
/* EXPORT the GPU-side FENCE OBJECT to the fence INPUT FD, so we */
|
||||
/* can pass it into the kernel. Atomic ioctl will pass the */
|
||||
/* in-fence fd into the kernel, thus telling KMS that it has to */
|
||||
/* wait for GPU to finish rendering the frame (remember where we */
|
||||
/* put the fence in the GL CMDSTREAM) before doing the changes */
|
||||
/* requested in the atomic ioct (the pageflip in this case). */
|
||||
/* (We export the GPU-side FENCE OJECT to the fence INPUT FD now, */
|
||||
/* not sooner, because now we are sure that the GPU-side fence is */
|
||||
/* in the CMDSTREAM to be lifted when the CMDSTREAM to this point */
|
||||
/* is completed). */
|
||||
/******************************************************************/
|
||||
dispdata->kms_in_fence_fd = _this->egl_data->eglDupNativeFenceFDANDROID (_this->egl_data->egl_display, dispdata->gpu_fence);
|
||||
|
||||
_this->egl_data->eglDestroySyncKHR(_this->egl_data->egl_display, dispdata->gpu_fence);
|
||||
SDL_assert(dispdata->kms_in_fence_fd != -1);
|
||||
|
||||
/* Lock the buffer that is marked by eglSwapBuffers() to become the
|
||||
next front buffer (so it can not be chosen by EGL as back buffer
|
||||
to draw on), and get a handle to it to request the pageflip on it.
|
||||
REMEMBER that gbm_surface_lock_front_buffer() ALWAYS has to be
|
||||
called after eglSwapBuffers(). */
|
||||
windata->next_bo = KMSDRM_gbm_surface_lock_front_buffer(windata->gs);
|
||||
if (!windata->next_bo) {
|
||||
return SDL_SetError("Failed to lock frontbuffer");
|
||||
}
|
||||
fb = KMSDRM_FBFromBO(_this, windata->next_bo);
|
||||
if (!fb) {
|
||||
return SDL_SetError("Failed to get a new framebuffer from BO");
|
||||
}
|
||||
|
||||
if (!windata->bo) {
|
||||
/* On the first swap, immediately present the new front buffer. Before
|
||||
drmModePageFlip can be used the CRTC has to be configured to use
|
||||
the current connector and mode with drmModeSetCrtc */
|
||||
SDL_VideoData *viddata = _this->internal;
|
||||
const int ret = KMSDRM_drmModeSetCrtc(viddata->drm_fd,
|
||||
dispdata->crtc.crtc->crtc_id, fb->fb_id, 0, 0,
|
||||
&dispdata->connector.connector->connector_id, 1, &dispdata->mode);
|
||||
|
||||
if (ret) {
|
||||
return SDL_SetError("Could not set videomode on CRTC.");
|
||||
}
|
||||
}
|
||||
|
||||
/* Add the pageflip to the request list. */
|
||||
info.plane = dispdata->display_plane;
|
||||
info.crtc_id = dispdata->crtc.crtc->crtc_id;
|
||||
info.fb_id = fb->fb_id;
|
||||
info.src_w = window->w; // !!! FIXME: was windata->src_w in the original atomic patch
|
||||
info.src_h = window->h; // !!! FIXME: was windata->src_h in the original atomic patch
|
||||
info.crtc_w = dispdata->mode.hdisplay; // !!! FIXME: was windata->output_w in the original atomic patch
|
||||
info.crtc_h = dispdata->mode.vdisplay; // !!! FIXME: was windata->output_h in the original atomic patch
|
||||
info.crtc_x = 0; // !!! FIXME: was windata->output_x in the original atomic patch
|
||||
|
||||
drm_atomic_set_plane_props(dispdata, &info);
|
||||
|
||||
/*****************************************************************/
|
||||
/* Tell the display (KMS) that it will have to wait on the fence */
|
||||
/* for the GPU-side FENCE. */
|
||||
/* */
|
||||
/* Since KMS is a kernel thing, we have to pass an FD into */
|
||||
/* the kernel, and get another FD out of the kernel. */
|
||||
/* */
|
||||
/* 1) To pass the GPU-side fence into the kernel, we set the */
|
||||
/* INPUT FD as the IN_FENCE_FD prop of the PRIMARY PLANE. */
|
||||
/* This FD tells KMS (the kernel) to wait for the GPU-side fence.*/
|
||||
/* */
|
||||
/* 2) To get the KMS-side fence out of the kernel, we set the */
|
||||
/* OUTPUT FD as the OUT_FEWNCE_FD prop of the CRTC. */
|
||||
/* This FD will be later imported as a FENCE OBJECT which will be*/
|
||||
/* used to tell the GPU to wait for KMS to complete the changes */
|
||||
/* requested in atomic_commit (the pageflip in this case). */
|
||||
/*****************************************************************/
|
||||
if (dispdata->kms_in_fence_fd != -1)
|
||||
{
|
||||
add_plane_property(dispdata->atomic_req, dispdata->display_plane,
|
||||
"IN_FENCE_FD", dispdata->kms_in_fence_fd);
|
||||
add_crtc_property(dispdata->atomic_req, &dispdata->crtc,
|
||||
"OUT_FENCE_PTR", VOID2U64(&dispdata->kms_out_fence_fd));
|
||||
}
|
||||
|
||||
/* Do we have a pending modesetting? If so, set the necessary
|
||||
props so it's included in the incoming atomic commit. */
|
||||
if (windata->egl_surface_dirty) {
|
||||
// !!! FIXME: this CreateSurfaces call is what the legacy path does; it's not clear to me if the atomic paths need to do it too.
|
||||
KMSDRM_CreateSurfaces(_this, window);
|
||||
|
||||
uint32_t blob_id;
|
||||
SDL_VideoData *viddata = (SDL_VideoData *)_this->internal;
|
||||
|
||||
add_connector_property(dispdata->atomic_req, &dispdata->connector, "CRTC_ID", dispdata->crtc.crtc->crtc_id);
|
||||
KMSDRM_drmModeCreatePropertyBlob(viddata->drm_fd, &dispdata->mode, sizeof(dispdata->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);
|
||||
modesetting = true;
|
||||
}
|
||||
|
||||
/*****************************************************************/
|
||||
/* Issue a non-blocking atomic commit: for triple buffering, */
|
||||
/* this must not block so the game can start building another */
|
||||
/* frame, even if the just-requested pageflip hasnt't completed. */
|
||||
/*****************************************************************/
|
||||
if (drm_atomic_commit(_this, dispdata, false, modesetting)) {
|
||||
return SDL_SetError("Failed to issue atomic commit on pageflip");
|
||||
}
|
||||
|
||||
/* Release the previous front buffer so EGL can chose it as back buffer
|
||||
and render on it again. */
|
||||
if (windata->bo) {
|
||||
KMSDRM_gbm_surface_release_buffer(windata->gs, windata->bo);
|
||||
}
|
||||
/* Take note of the buffer about to become front buffer, so next
|
||||
time we come here we can free it like we just did with the previous
|
||||
front buffer. */
|
||||
windata->bo = windata->next_bo;
|
||||
|
||||
/****************************************************************/
|
||||
/* Import the KMS-side FENCE OUTPUT FD from the kernel to the */
|
||||
/* KMS-side FENCE OBJECT so we can use use it to fence the GPU. */
|
||||
/****************************************************************/
|
||||
dispdata->kms_fence = create_fence(_this, dispdata->kms_out_fence_fd);
|
||||
SDL_assert(dispdata->kms_fence);
|
||||
|
||||
/****************************************************************/
|
||||
/* "Delete" the fence OUTPUT FD, because we already have the */
|
||||
/* KMS FENCE OBJECT, the fence itself is away from us, on the */
|
||||
/* kernel side. */
|
||||
/****************************************************************/
|
||||
dispdata->kms_out_fence_fd = -1;
|
||||
|
||||
/*****************************************************************/
|
||||
/* Tell the GPU to wait on the fence for the KMS-side FENCE, */
|
||||
/* which means waiting until the requested pageflip is completed.*/
|
||||
/*****************************************************************/
|
||||
_this->egl_data->eglWaitSyncKHR(_this->egl_data->egl_display, dispdata->kms_fence, 0);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool KMSDRM_GLES_SwapWindowDoubleBuffered(SDL_VideoDevice *_this, SDL_Window * window)
|
||||
{
|
||||
SDL_WindowData *windata = ((SDL_WindowData *) window->internal);
|
||||
SDL_DisplayData *dispdata = SDL_GetDisplayDriverDataForWindow(window);
|
||||
KMSDRM_FBInfo *fb;
|
||||
KMSDRM_PlaneInfo info;
|
||||
bool modesetting = false;
|
||||
|
||||
SDL_zero(info);
|
||||
|
||||
/**********************************************************************************/
|
||||
/* In double-buffer mode, atomic_commit will always be synchronous/blocking (ie: */
|
||||
/* won't return until the requested changes are really 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, so we don't need to protect the KMS/GPU access to the buffer.*/
|
||||
/**********************************************************************************/
|
||||
|
||||
/* Mark, at EGL level, the buffer that we want to become the new front buffer.
|
||||
It won't really happen until we request a pageflip at the KMS level and it
|
||||
completes. */
|
||||
if (! _this->egl_data->eglSwapBuffers(_this->egl_data->egl_display, windata->egl_surface)) {
|
||||
return SDL_EGL_SetError("Failed to swap EGL buffers", "eglSwapBuffers");
|
||||
}
|
||||
/* Lock the buffer that is marked by eglSwapBuffers() to become the next front buffer
|
||||
(so it can not be chosen by EGL as back buffer to draw on), and get a handle to it,
|
||||
to request the pageflip on it. */
|
||||
windata->next_bo = KMSDRM_gbm_surface_lock_front_buffer(windata->gs);
|
||||
if (!windata->next_bo) {
|
||||
return SDL_SetError("Failed to lock frontbuffer");
|
||||
}
|
||||
fb = KMSDRM_FBFromBO(_this, windata->next_bo);
|
||||
if (!fb) {
|
||||
return SDL_SetError("Failed to get a new framebuffer BO");
|
||||
}
|
||||
|
||||
if (!windata->bo) {
|
||||
/* On the first swap, immediately present the new front buffer. Before
|
||||
drmModePageFlip can be used the CRTC has to be configured to use
|
||||
the current connector and mode with drmModeSetCrtc */
|
||||
SDL_VideoData *viddata = _this->internal;
|
||||
const int ret = KMSDRM_drmModeSetCrtc(viddata->drm_fd,
|
||||
dispdata->crtc.crtc->crtc_id, fb->fb_id, 0, 0,
|
||||
&dispdata->connector.connector->connector_id, 1, &dispdata->mode);
|
||||
|
||||
if (ret) {
|
||||
return SDL_SetError("Could not set videomode on CRTC.");
|
||||
}
|
||||
}
|
||||
|
||||
/* Add the pageflip to the request list. */
|
||||
info.plane = dispdata->display_plane;
|
||||
info.crtc_id = dispdata->crtc.crtc->crtc_id;
|
||||
info.fb_id = fb->fb_id;
|
||||
info.src_w = window->w; // !!! FIXME: was windata->src_w in the original atomic patch
|
||||
info.src_h = window->h; // !!! FIXME: was windata->src_h in the original atomic patch
|
||||
info.crtc_w = dispdata->mode.hdisplay; // !!! FIXME: was windata->output_w in the original atomic patch
|
||||
info.crtc_h = dispdata->mode.vdisplay; // !!! FIXME: was windata->output_h in the original atomic patch
|
||||
info.crtc_x = 0; // !!! FIXME: was windata->output_x in the original atomic patch
|
||||
|
||||
drm_atomic_set_plane_props(dispdata, &info);
|
||||
|
||||
/* Do we have a pending modesetting? If so, set the necessary
|
||||
props so it's included in the incoming atomic commit. */
|
||||
if (windata->egl_surface_dirty) {
|
||||
// !!! FIXME: this CreateSurfaces call is what the legacy path does; it's not clear to me if the atomic paths need to do it too.
|
||||
KMSDRM_CreateSurfaces(_this, window);
|
||||
|
||||
uint32_t blob_id;
|
||||
|
||||
SDL_VideoData *viddata = (SDL_VideoData *)_this->internal;
|
||||
|
||||
add_connector_property(dispdata->atomic_req, &dispdata->connector, "CRTC_ID", dispdata->crtc.crtc->crtc_id);
|
||||
KMSDRM_drmModeCreatePropertyBlob(viddata->drm_fd, &dispdata->mode, sizeof(dispdata->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);
|
||||
modesetting = true;
|
||||
}
|
||||
|
||||
/* Issue the one and only atomic commit where all changes will be requested!
|
||||
Blocking for double buffering: won't return until completed. */
|
||||
if (drm_atomic_commit(_this, dispdata, true, modesetting)) {
|
||||
return SDL_SetError("Failed to issue atomic commit on pageflip");
|
||||
}
|
||||
|
||||
/* Release last front buffer so EGL can chose it as back buffer and render on it again. */
|
||||
if (windata->bo) {
|
||||
KMSDRM_gbm_surface_release_buffer(windata->gs, windata->bo);
|
||||
}
|
||||
|
||||
/* Take note of current front buffer, so we can free it next time we come here. */
|
||||
windata->bo = windata->next_bo;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool KMSDRM_GLES_SwapWindowLegacy(SDL_VideoDevice *_this, SDL_Window *window)
|
||||
{
|
||||
SDL_WindowData *windata = window->internal;
|
||||
SDL_DisplayData *dispdata = SDL_GetDisplayDriverDataForWindow(window);
|
||||
@@ -116,13 +415,12 @@ bool KMSDRM_GLES_SwapWindow(SDL_VideoDevice *_this, SDL_Window *window)
|
||||
// Release the previous front buffer
|
||||
if (windata->bo) {
|
||||
KMSDRM_gbm_surface_release_buffer(windata->gs, windata->bo);
|
||||
windata->bo = NULL;
|
||||
}
|
||||
|
||||
windata->bo = windata->next_bo;
|
||||
|
||||
/* Mark a buffer to become the next front buffer.
|
||||
This won't happen until pagelip completes. */
|
||||
This won't happen until pageflip completes. */
|
||||
if (!(_this->egl_data->eglSwapBuffers(_this->egl_data->egl_display,
|
||||
windata->egl_surface))) {
|
||||
return SDL_SetError("eglSwapBuffers failed");
|
||||
@@ -147,8 +445,8 @@ bool KMSDRM_GLES_SwapWindow(SDL_VideoDevice *_this, SDL_Window *window)
|
||||
drmModePageFlip can be used the CRTC has to be configured to use
|
||||
the current connector and mode with drmModeSetCrtc */
|
||||
ret = KMSDRM_drmModeSetCrtc(viddata->drm_fd,
|
||||
dispdata->crtc->crtc_id, fb_info->fb_id, 0, 0,
|
||||
&dispdata->connector->connector_id, 1, &dispdata->mode);
|
||||
dispdata->crtc.crtc->crtc_id, fb_info->fb_id, 0, 0,
|
||||
&dispdata->connector.connector->connector_id, 1, &dispdata->mode);
|
||||
|
||||
if (ret) {
|
||||
return SDL_SetError("Could not set videomode on CRTC.");
|
||||
@@ -170,7 +468,7 @@ bool KMSDRM_GLES_SwapWindow(SDL_VideoDevice *_this, SDL_Window *window)
|
||||
flip_flags |= DRM_MODE_PAGE_FLIP_ASYNC;
|
||||
}
|
||||
|
||||
ret = KMSDRM_drmModePageFlip(viddata->drm_fd, dispdata->crtc->crtc_id,
|
||||
ret = KMSDRM_drmModePageFlip(viddata->drm_fd, dispdata->crtc.crtc->crtc_id,
|
||||
fb_info->fb_id, flip_flags, &windata->waiting_for_flip);
|
||||
|
||||
if (ret == 0) {
|
||||
@@ -198,6 +496,26 @@ bool KMSDRM_GLES_SwapWindow(SDL_VideoDevice *_this, SDL_Window *window)
|
||||
return true;
|
||||
}
|
||||
|
||||
bool KMSDRM_GLES_SwapWindow(SDL_VideoDevice *_this, SDL_Window * window)
|
||||
{
|
||||
SDL_WindowData *windata = (SDL_WindowData *) window->internal;
|
||||
|
||||
if (windata->swap_window == NULL) {
|
||||
SDL_VideoData *viddata = _this->internal;
|
||||
if (viddata->is_atomic) {
|
||||
// We want the fenced version by default, but it needs extensions.
|
||||
if ( (SDL_GetHintBoolean(SDL_HINT_VIDEO_DOUBLE_BUFFER, false)) || (!SDL_EGL_HasExtension(_this, SDL_EGL_DISPLAY_EXTENSION, "EGL_ANDROID_native_fence_sync")) ) {
|
||||
windata->swap_window = KMSDRM_GLES_SwapWindowDoubleBuffered;
|
||||
} else {
|
||||
windata->swap_window = KMSDRM_GLES_SwapWindowFenced;
|
||||
}
|
||||
} else {
|
||||
windata->swap_window = KMSDRM_GLES_SwapWindowLegacy;
|
||||
}
|
||||
}
|
||||
return windata->swap_window(_this, window);
|
||||
}
|
||||
|
||||
SDL_EGL_MakeCurrent_impl(KMSDRM)
|
||||
|
||||
#endif // SDL_VIDEO_DRIVER_KMSDRM
|
||||
|
||||
@@ -137,6 +137,16 @@ SDL_KMSDRM_SYM_OPT(uint32_t,gbm_bo_get_offset,(struct gbm_bo *bo, int plane))
|
||||
SDL_KMSDRM_SYM_OPT(uint32_t,gbm_bo_get_stride_for_plane,(struct gbm_bo *bo, int plane))
|
||||
SDL_KMSDRM_SYM_OPT(union gbm_bo_handle,gbm_bo_get_handle_for_plane,(struct gbm_bo *bo, int plane))
|
||||
|
||||
|
||||
SDL_KMSDRM_MODULE(ATOMIC)
|
||||
SDL_KMSDRM_SYM(int,drmIoctl,(int fd, unsigned long request, void *arg))
|
||||
SDL_KMSDRM_SYM(drmModeAtomicReqPtr,drmModeAtomicAlloc,(void))
|
||||
SDL_KMSDRM_SYM(void,drmModeAtomicFree,(drmModeAtomicReqPtr req))
|
||||
SDL_KMSDRM_SYM(int,drmModeAtomicCommit,(int fd,drmModeAtomicReqPtr req,uint32_t flags,void *user_data))
|
||||
SDL_KMSDRM_SYM(int,drmModeAtomicAddProperty,(drmModeAtomicReqPtr req,uint32_t object_id,uint32_t property_id,uint64_t value))
|
||||
SDL_KMSDRM_SYM(int,drmModeCreatePropertyBlob,(int fd,const void *data,size_t size,uint32_t *id))
|
||||
|
||||
|
||||
#undef SDL_KMSDRM_MODULE
|
||||
#undef SDL_KMSDRM_SYM
|
||||
#undef SDL_KMSDRM_SYM_CONST
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -19,6 +19,8 @@
|
||||
3. This notice may not be removed or altered from any source distribution.
|
||||
*/
|
||||
|
||||
// Atomic KMSDRM backend originally written by Manuel Alfayate Corchete <redwindwanderer@gmail.com>
|
||||
|
||||
#include "SDL_internal.h"
|
||||
|
||||
#ifndef SDL_kmsdrmvideo_h
|
||||
@@ -32,6 +34,7 @@
|
||||
#include <xf86drmMode.h>
|
||||
#include <gbm.h>
|
||||
#include <EGL/egl.h>
|
||||
#include <EGL/eglext.h>
|
||||
|
||||
#ifndef DRM_FORMAT_MOD_INVALID
|
||||
#define DRM_FORMAT_MOD_INVALID 0x00ffffffffffffffULL
|
||||
@@ -72,6 +75,27 @@
|
||||
#define GBM_BO_USE_LINEAR (1 << 4)
|
||||
#endif
|
||||
|
||||
typedef struct KMSDRM_plane
|
||||
{
|
||||
drmModePlane *plane;
|
||||
drmModeObjectProperties *props;
|
||||
drmModePropertyRes **props_info;
|
||||
} KMSDRM_plane;
|
||||
|
||||
typedef struct KMSDRM_crtc
|
||||
{
|
||||
drmModeCrtc *crtc;
|
||||
drmModeObjectProperties *props;
|
||||
drmModePropertyRes **props_info;
|
||||
} KMSDRM_crtc;
|
||||
|
||||
typedef struct KMSDRM_connector
|
||||
{
|
||||
drmModeConnector *connector;
|
||||
drmModeObjectProperties *props;
|
||||
drmModePropertyRes **props_info;
|
||||
} KMSDRM_connector;
|
||||
|
||||
struct SDL_VideoData
|
||||
{
|
||||
int devindex; // device index that was passed on creation
|
||||
@@ -92,6 +116,7 @@ struct SDL_VideoData
|
||||
open 1 FD and create 1 gbm device. */
|
||||
bool gbm_init;
|
||||
|
||||
bool is_atomic; // true if atomic interfaces are supported.
|
||||
};
|
||||
|
||||
struct SDL_DisplayModeData
|
||||
@@ -101,8 +126,11 @@ struct SDL_DisplayModeData
|
||||
|
||||
struct SDL_DisplayData
|
||||
{
|
||||
drmModeConnector *connector;
|
||||
drmModeCrtc *crtc;
|
||||
KMSDRM_plane *display_plane;
|
||||
KMSDRM_plane *cursor_plane;
|
||||
KMSDRM_crtc crtc;
|
||||
KMSDRM_connector connector;
|
||||
|
||||
drmModeModeInfo mode;
|
||||
drmModeModeInfo original_mode;
|
||||
drmModeModeInfo fullscreen_mode;
|
||||
@@ -118,6 +146,15 @@ struct SDL_DisplayData
|
||||
int cursor_bo_drm_fd;
|
||||
uint64_t cursor_w, cursor_h;
|
||||
|
||||
/* 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;
|
||||
EGLSyncKHR gpu_fence;
|
||||
|
||||
bool default_cursor_init;
|
||||
};
|
||||
|
||||
@@ -137,6 +174,9 @@ struct SDL_WindowData
|
||||
|
||||
EGLSurface egl_surface;
|
||||
bool egl_surface_dirty;
|
||||
|
||||
/* This dictates what approach we'll use for SwapBuffers. */
|
||||
bool (*swap_window)(SDL_VideoDevice *_this, SDL_Window *window);
|
||||
};
|
||||
|
||||
typedef struct KMSDRM_FBInfo
|
||||
@@ -145,12 +185,37 @@ typedef struct KMSDRM_FBInfo
|
||||
uint32_t fb_id; // DRM framebuffer ID
|
||||
} KMSDRM_FBInfo;
|
||||
|
||||
typedef struct KMSDRM_PlaneInfo
|
||||
{
|
||||
struct KMSDRM_plane *plane;
|
||||
uint32_t fb_id;
|
||||
uint32_t crtc_id;
|
||||
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
|
||||
extern bool KMSDRM_CreateSurfaces(SDL_VideoDevice *_this, SDL_Window *window);
|
||||
extern KMSDRM_FBInfo *KMSDRM_FBFromBO(SDL_VideoDevice *_this, struct gbm_bo *bo);
|
||||
extern KMSDRM_FBInfo *KMSDRM_FBFromBO2(SDL_VideoDevice *_this, struct gbm_bo *bo, int w, int h);
|
||||
extern bool KMSDRM_WaitPageflip(SDL_VideoDevice *_this, SDL_WindowData *windata);
|
||||
|
||||
// Atomic functions that are used from SDL_kmsdrmopengles.c and SDL_kmsdrmmouse.c
|
||||
void drm_atomic_set_plane_props(SDL_DisplayData *dispdata, struct KMSDRM_PlaneInfo *info);
|
||||
void drm_atomic_waitpending(SDL_VideoDevice *_this, SDL_DisplayData *dispdata);
|
||||
int drm_atomic_commit(SDL_VideoDevice *_this, SDL_DisplayData *dispdata, bool blocking, bool allow_modeset);
|
||||
int add_plane_property(drmModeAtomicReq *req, struct KMSDRM_plane *plane, const char *name, uint64_t value);
|
||||
int add_crtc_property(drmModeAtomicReq *req, struct KMSDRM_crtc *crtc, const char *name, uint64_t value);
|
||||
int add_connector_property(drmModeAtomicReq *req, struct KMSDRM_connector *connector, const char *name, uint64_t value);
|
||||
bool setup_plane(SDL_VideoDevice *_this, SDL_DisplayData *dispdata, struct KMSDRM_plane **plane, uint32_t plane_type);
|
||||
void free_plane(struct KMSDRM_plane **plane);
|
||||
|
||||
/****************************************************************************/
|
||||
// SDL_VideoDevice functions declaration
|
||||
/****************************************************************************/
|
||||
|
||||
@@ -177,7 +177,7 @@ bool KMSDRM_Vulkan_CreateSurface(SDL_VideoDevice *_this,
|
||||
uint32_t display_count;
|
||||
uint32_t mode_count;
|
||||
uint32_t plane_count;
|
||||
uint32_t plane = UINT32_MAX;
|
||||
uint32_t _plane = UINT32_MAX;
|
||||
|
||||
VkPhysicalDevice *physical_devices = NULL;
|
||||
VkPhysicalDeviceProperties *device_props = NULL;
|
||||
@@ -456,13 +456,13 @@ bool KMSDRM_Vulkan_CreateSurface(SDL_VideoDevice *_this,
|
||||
vkGetDisplayPlaneCapabilitiesKHR(gpu, display_mode, i, &plane_caps);
|
||||
if (plane_caps.supportedAlpha == alpha_mode) {
|
||||
// Yep, this plane is alright.
|
||||
plane = i;
|
||||
_plane = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// If we couldn't find an appropriate plane, error out.
|
||||
if (plane == UINT32_MAX) {
|
||||
if (_plane == UINT32_MAX) {
|
||||
SDL_SetError("Vulkan couldn't find an appropriate plane.");
|
||||
goto clean;
|
||||
}
|
||||
@@ -477,7 +477,7 @@ bool KMSDRM_Vulkan_CreateSurface(SDL_VideoDevice *_this,
|
||||
SDL_zero(display_plane_surface_create_info);
|
||||
display_plane_surface_create_info.sType = VK_STRUCTURE_TYPE_DISPLAY_SURFACE_CREATE_INFO_KHR;
|
||||
display_plane_surface_create_info.displayMode = display_mode;
|
||||
display_plane_surface_create_info.planeIndex = plane;
|
||||
display_plane_surface_create_info.planeIndex = _plane;
|
||||
display_plane_surface_create_info.imageExtent = image_size;
|
||||
display_plane_surface_create_info.transform = VK_SURFACE_TRANSFORM_IDENTITY_BIT_KHR;
|
||||
display_plane_surface_create_info.alphaMode = alpha_mode;
|
||||
|
||||
Reference in New Issue
Block a user