kmsdrm: Init cursor surface on SetCursor() ONLY. Removal of dynamic modeset because it causes A LOT of problems with some kernel video drivers. Some refactoring and cleanups.

Manuel Alfayate Corchete 2020-08-23 02:58:57 +02:00
parent 0d0ba111ae
commit 16c04f266a
4 changed files with 133 additions and 340 deletions

View File

@ -111,11 +111,11 @@ KMSDRM_GLES_SwapWindow(_THIS, SDL_Window * window)
be chosen by EGL as back buffer to draw on), and get a handle to it to request the pageflip on it. */ 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); windata->next_bo = KMSDRM_gbm_surface_lock_front_buffer(windata->gs);
if (!windata->next_bo) { if (!windata->next_bo) {
return SDL_SetError("Failed to lock frontbuffer"); return SDL_SetError("Failed to lock frontbuffer on GBM surface destruction");
} }
fb = KMSDRM_FBFromBO(_this, windata->next_bo); fb = KMSDRM_FBFromBO(_this, windata->next_bo);
if (!fb) { if (!fb) {
return SDL_SetError("Failed to get a new framebuffer"); return SDL_SetError("Failed to get a new framebuffer on GBM surface destruction");
} }
/* Add the pageflip to te request list. */ /* Add the pageflip to te request list. */
@ -125,9 +125,8 @@ KMSDRM_GLES_SwapWindow(_THIS, SDL_Window * window)
We need e a non-blocking atomic commit for triple buffering, because we We need e a non-blocking atomic commit for triple buffering, because we
must not block on this atomic commit so we can re-enter program loop once more. */ must not block on this atomic commit so we can re-enter program loop once more. */
ret = drm_atomic_commit(_this, SDL_FALSE); ret = drm_atomic_commit(_this, SDL_FALSE);
if (ret) { if (ret) {
return SDL_SetError("failed to issue atomic commit"); return SDL_SetError("failed to issue atomic commit on GBM surface destruction");
} }
/* Release the last front buffer so EGL can chose it as back buffer and render on it again. */ /* Release the last front buffer so EGL can chose it as back buffer and render on it again. */

View File

@ -61,21 +61,8 @@ SDL_KMSDRM_SYM(int,drmModeAddFB2WithModifiers,(int fd, uint32_t width, uint32_t
SDL_KMSDRM_SYM(int,drmModeRmFB,(int fd, uint32_t bufferId)) SDL_KMSDRM_SYM(int,drmModeRmFB,(int fd, uint32_t bufferId))
SDL_KMSDRM_SYM(drmModeFBPtr,drmModeGetFB,(int fd, uint32_t buf)) SDL_KMSDRM_SYM(drmModeFBPtr,drmModeGetFB,(int fd, uint32_t buf))
SDL_KMSDRM_SYM(drmModeCrtcPtr,drmModeGetCrtc,(int fd, uint32_t crtcId)) SDL_KMSDRM_SYM(drmModeCrtcPtr,drmModeGetCrtc,(int fd, uint32_t crtcId))
SDL_KMSDRM_SYM(int,drmModeSetCrtc,(int fd, uint32_t crtcId, uint32_t bufferId,
uint32_t x, uint32_t y, uint32_t *connectors, int count,
drmModeModeInfoPtr mode))
SDL_KMSDRM_SYM(int,drmModeSetCursor,(int fd, uint32_t crtcId, uint32_t bo_handle,
uint32_t width, uint32_t height))
SDL_KMSDRM_SYM(int,drmModeSetCursor2,(int fd, uint32_t crtcId, uint32_t bo_handle,
uint32_t width, uint32_t height,
int32_t hot_x, int32_t hot_y))
SDL_KMSDRM_SYM(int,drmModeMoveCursor,(int fd, uint32_t crtcId, int x, int y))
SDL_KMSDRM_SYM(drmModeEncoderPtr,drmModeGetEncoder,(int fd, uint32_t encoder_id)) SDL_KMSDRM_SYM(drmModeEncoderPtr,drmModeGetEncoder,(int fd, uint32_t encoder_id))
SDL_KMSDRM_SYM(drmModeConnectorPtr,drmModeGetConnector,(int fd, uint32_t connector_id)) SDL_KMSDRM_SYM(drmModeConnectorPtr,drmModeGetConnector,(int fd, uint32_t connector_id))
SDL_KMSDRM_SYM(int,drmHandleEvent,(int fd,drmEventContextPtr evctx))
SDL_KMSDRM_SYM(int,drmModePageFlip,(int fd, uint32_t crtc_id, uint32_t fb_id,
uint32_t flags, void *user_data))
/* Atomic functions */ /* Atomic functions */

View File

@ -150,6 +150,7 @@ get_driindex(void)
#define VOID2U64(x) ((uint64_t)(unsigned long)(x)) #define VOID2U64(x) ((uint64_t)(unsigned long)(x))
#if 0
static int add_connector_property(drmModeAtomicReq *req, struct connector *connector, static int add_connector_property(drmModeAtomicReq *req, struct connector *connector,
const char *name, uint64_t value) const char *name, uint64_t value)
{ {
@ -170,6 +171,7 @@ static int add_connector_property(drmModeAtomicReq *req, struct connector *conne
return KMSDRM_drmModeAtomicAddProperty(req, connector->connector->connector_id, prop_id, value); return KMSDRM_drmModeAtomicAddProperty(req, connector->connector->connector_id, prop_id, value);
} }
#endif
static int add_crtc_property(drmModeAtomicReq *req, struct crtc *crtc, static int add_crtc_property(drmModeAtomicReq *req, struct crtc *crtc,
const char *name, uint64_t value) const char *name, uint64_t value)
@ -313,9 +315,10 @@ void get_planes_info(_THIS)
/* Get the plane_id of a plane that is of the specified plane type (primary, /* Get the plane_id of a plane that is of the specified plane type (primary,
overlay, cursor...) and can use the CRTC we have chosen previously. */ overlay, cursor...) and can use the CRTC we have chosen previously. */
static uint32_t get_plane_id(_THIS, drmModeRes *resources, uint32_t plane_type) static uint32_t get_plane_id(_THIS, uint32_t plane_type)
{ {
drmModePlaneResPtr plane_resources; drmModeRes *resources = NULL;
drmModePlaneResPtr plane_resources = NULL;
uint32_t i, j; uint32_t i, j;
uint32_t crtc_index = 0; uint32_t crtc_index = 0;
uint32_t ret = -EINVAL; uint32_t ret = -EINVAL;
@ -324,6 +327,8 @@ static uint32_t get_plane_id(_THIS, drmModeRes *resources, uint32_t plane_type)
SDL_VideoData *viddata = ((SDL_VideoData *)_this->driverdata); SDL_VideoData *viddata = ((SDL_VideoData *)_this->driverdata);
SDL_DisplayData *dispdata = (SDL_DisplayData *)SDL_GetDisplayDriverData(0); SDL_DisplayData *dispdata = (SDL_DisplayData *)SDL_GetDisplayDriverData(0);
resources = KMSDRM_drmModeGetResources(viddata->drm_fd);
/* Get the crtc_index for the current CRTC. /* Get the crtc_index for the current CRTC.
It's needed to find out if a plane supports the CRTC. */ It's needed to find out if a plane supports the CRTC. */
for (i = 0; i < resources->count_crtcs; i++) { for (i = 0; i < resources->count_crtcs; i++) {
@ -335,8 +340,7 @@ static uint32_t get_plane_id(_THIS, drmModeRes *resources, uint32_t plane_type)
plane_resources = KMSDRM_drmModeGetPlaneResources(viddata->drm_fd); plane_resources = KMSDRM_drmModeGetPlaneResources(viddata->drm_fd);
if (!plane_resources) { if (!plane_resources) {
printf("drmModeGetPlaneResources failed: %s\n", strerror(errno)); return SDL_SetError("drmModeGetPlaneResources failed.");
return -1;
} }
/* Iterate on all the available planes. */ /* Iterate on all the available planes. */
@ -346,7 +350,6 @@ static uint32_t get_plane_id(_THIS, drmModeRes *resources, uint32_t plane_type)
drmModePlanePtr plane = KMSDRM_drmModeGetPlane(viddata->drm_fd, plane_id); drmModePlanePtr plane = KMSDRM_drmModeGetPlane(viddata->drm_fd, plane_id);
if (!plane) { if (!plane) {
printf("drmModeGetPlane(%u) failed: %s\n", plane_id, strerror(errno));
continue; continue;
} }
@ -380,10 +383,71 @@ static uint32_t get_plane_id(_THIS, drmModeRes *resources, uint32_t plane_type)
} }
KMSDRM_drmModeFreePlaneResources(plane_resources); KMSDRM_drmModeFreePlaneResources(plane_resources);
KMSDRM_drmModeFreeResources(resources);
return ret; return ret;
} }
/* Setup cursor plane and it's props. */
static int
setup_plane(_THIS, struct plane **plane, uint32_t plane_type ) {
uint32_t plane_id;
SDL_VideoData *viddata = ((SDL_VideoData *)_this->driverdata);
*plane = SDL_calloc(1, sizeof(*plane));
/* Get plane ID. */
plane_id = get_plane_id(_this, plane_type);
if (!plane_id) {
goto cleanup;
}
/* Get the DRM plane itself. */
(*plane)->plane = KMSDRM_drmModeGetPlane(viddata->drm_fd, plane_id);
/* Get the DRM plane properties. */
if ((*plane)->plane) {
(*plane)->props = KMSDRM_drmModeObjectGetProperties(viddata->drm_fd,
(*plane)->plane->plane_id, DRM_MODE_OBJECT_PLANE);
(*plane)->props_info = SDL_calloc((*plane)->props->count_props,
sizeof((*plane)->props_info));
for (int i = 0; i < (*plane)->props->count_props; i++) {
(*plane)->props_info[i] = KMSDRM_drmModeGetProperty(viddata->drm_fd,
(*plane)->props->props[i]);
}
}
return 0;
cleanup:
SDL_free(*plane);
*plane = NULL;
return -1;
}
/* Free a plane and it's props. */
static void
free_plane(struct plane **plane) {
SDL_DisplayData *dispdata = (SDL_DisplayData *)SDL_GetDisplayDriverData(0);
if (dispdata && (*plane)) {
if ((*plane)->plane) {
KMSDRM_drmModeFreePlane((*plane)->plane);
(*plane)->plane = NULL;
}
if ((*plane)->props_info) {
SDL_free((*plane)->props_info);
(*plane)->props_info = NULL;
}
SDL_free(*plane);
*plane = NULL;
}
}
/**********************************************************************************/ /**********************************************************************************/
/* The most important ATOMIC fn of the backend. */ /* The most important ATOMIC fn of the backend. */
/* A PLANE reads a BUFFER, and a CRTC reads a PLANE and sends it's contents */ /* A PLANE reads a BUFFER, and a CRTC reads a PLANE and sends it's contents */
@ -429,33 +493,36 @@ drm_atomic_setbuffer(_THIS, struct plane *plane, uint32_t fb_id)
} }
void void
drm_atomic_modeset(_THIS, int mode_index) drm_atomic_unsetbuffer(_THIS, struct plane *plane)
{ {
SDL_DisplayData *dispdata = (SDL_DisplayData *)SDL_GetDisplayDriverData(0); SDL_DisplayData *dispdata = (SDL_DisplayData *)SDL_GetDisplayDriverData(0);
SDL_VideoData *viddata = ((SDL_VideoData *)_this->driverdata);
drmModeModeInfo mode;
uint32_t blob_id;
mode = dispdata->connector->connector->modes[mode_index];
/* Do we have a set of changes already in the making? If not, allocate a new one. */ /* Do we have a set of changes already in the making? If not, allocate a new one. */
if (!dispdata->atomic_req) if (!dispdata->atomic_req)
dispdata->atomic_req = KMSDRM_drmModeAtomicAlloc(); dispdata->atomic_req = KMSDRM_drmModeAtomicAlloc();
dispdata->atomic_flags |= DRM_MODE_ATOMIC_ALLOW_MODESET; add_plane_property(dispdata->atomic_req, plane, "FB_ID", 0);
add_plane_property(dispdata->atomic_req, plane, "CRTC_ID", 0);
add_plane_property(dispdata->atomic_req, plane, "SRC_W", 0);
add_plane_property(dispdata->atomic_req, plane, "SRC_H", 0);
add_plane_property(dispdata->atomic_req, plane, "SRC_X", 0);
add_plane_property(dispdata->atomic_req, plane, "SRC_Y", 0);
add_plane_property(dispdata->atomic_req, plane, "CRTC_W", 0);
add_plane_property(dispdata->atomic_req, plane, "CRTC_H", 0);
add_plane_property(dispdata->atomic_req, plane, "CRTC_X", 0);
add_plane_property(dispdata->atomic_req, plane, "CRTC_Y", 0);
add_connector_property(dispdata->atomic_req, dispdata->connector, "CRTC_ID", dispdata->crtc->crtc->crtc_id); if (dispdata->kms_in_fence_fd != -1) {
add_crtc_property(dispdata->atomic_req, dispdata->crtc, "ACTIVE", 1); add_crtc_property(dispdata->atomic_req, dispdata->crtc, "OUT_FENCE_PTR",
KMSDRM_drmModeCreatePropertyBlob(viddata->drm_fd, &mode, sizeof(mode), &blob_id); VOID2U64(&dispdata->kms_out_fence_fd));
add_crtc_property(dispdata->atomic_req, dispdata->crtc, "MODE_ID", blob_id); add_plane_property(dispdata->atomic_req, plane, "IN_FENCE_FD", dispdata->kms_in_fence_fd);
}
/* Update video mode in use. */
dispdata->mode = mode;
} }
int int
drm_atomic_setcursor(KMSDRM_CursorData *curdata, int x, int y) drm_atomic_setcursor(KMSDRM_CursorData *curdata, int x, int y)
{ {
KMSDRM_FBInfo *fb;
SDL_DisplayData *dispdata = (SDL_DisplayData *)SDL_GetDisplayDriverData(0); SDL_DisplayData *dispdata = (SDL_DisplayData *)SDL_GetDisplayDriverData(0);
/* Do we have a set of changes already in the making? If not, allocate a new one. */ /* Do we have a set of changes already in the making? If not, allocate a new one. */
@ -464,7 +531,12 @@ drm_atomic_setcursor(KMSDRM_CursorData *curdata, int x, int y)
if (curdata) if (curdata)
{ {
KMSDRM_FBInfo *fb = KMSDRM_FBFromBO(curdata->video, curdata->bo); if (!dispdata->cursor_plane) {
setup_plane(curdata->video, &(dispdata->cursor_plane), DRM_PLANE_TYPE_CURSOR);
/* "curdata->video" is the _THIS pointer, which points to an SDL_Display, as passed from kmsdrmmouse.c */
}
fb = KMSDRM_FBFromBO(curdata->video, curdata->bo);
add_plane_property(dispdata->atomic_req, dispdata->cursor_plane, "FB_ID", fb->fb_id); add_plane_property(dispdata->atomic_req, dispdata->cursor_plane, "FB_ID", fb->fb_id);
add_plane_property(dispdata->atomic_req, dispdata->cursor_plane, "CRTC_ID", dispdata->crtc->crtc->crtc_id); add_plane_property(dispdata->atomic_req, dispdata->cursor_plane, "CRTC_ID", dispdata->crtc->crtc->crtc_id);
@ -477,7 +549,7 @@ drm_atomic_setcursor(KMSDRM_CursorData *curdata, int x, int y)
add_plane_property(dispdata->atomic_req, dispdata->cursor_plane, "CRTC_W", curdata->w); add_plane_property(dispdata->atomic_req, dispdata->cursor_plane, "CRTC_W", curdata->w);
add_plane_property(dispdata->atomic_req, dispdata->cursor_plane, "CRTC_H", curdata->h); add_plane_property(dispdata->atomic_req, dispdata->cursor_plane, "CRTC_H", curdata->h);
} }
else else if (dispdata->cursor_plane) /* Don't go further if plane has already been freed. */
{ {
add_plane_property(dispdata->atomic_req, dispdata->cursor_plane, "FB_ID", 0); add_plane_property(dispdata->atomic_req, dispdata->cursor_plane, "FB_ID", 0);
add_plane_property(dispdata->atomic_req, dispdata->cursor_plane, "CRTC_ID", 0); add_plane_property(dispdata->atomic_req, dispdata->cursor_plane, "CRTC_ID", 0);
@ -488,6 +560,8 @@ drm_atomic_setcursor(KMSDRM_CursorData *curdata, int x, int y)
add_plane_property(dispdata->atomic_req, dispdata->cursor_plane, "SRC_H", 0); add_plane_property(dispdata->atomic_req, dispdata->cursor_plane, "SRC_H", 0);
add_plane_property(dispdata->atomic_req, dispdata->cursor_plane, "CRTC_W", 0); add_plane_property(dispdata->atomic_req, dispdata->cursor_plane, "CRTC_W", 0);
add_plane_property(dispdata->atomic_req, dispdata->cursor_plane, "CRTC_H", 0); add_plane_property(dispdata->atomic_req, dispdata->cursor_plane, "CRTC_H", 0);
free_plane(&dispdata->cursor_plane);
} }
/* GPU <-> DISPLAY synchronization is done ON the display_plane, /* GPU <-> DISPLAY synchronization is done ON the display_plane,
@ -502,6 +576,9 @@ drm_atomic_movecursor(KMSDRM_CursorData *curdata, int x, int y)
{ {
SDL_DisplayData *dispdata = (SDL_DisplayData *)SDL_GetDisplayDriverData(0); SDL_DisplayData *dispdata = (SDL_DisplayData *)SDL_GetDisplayDriverData(0);
if (!dispdata->cursor_plane) /* We can't move a non-existing cursor, but that's ok. */
return 0;
/* Do we have a set of changes already in the making? If not, allocate a new one. */ /* Do we have a set of changes already in the making? If not, allocate a new one. */
if (!dispdata->atomic_req) if (!dispdata->atomic_req)
dispdata->atomic_req = KMSDRM_drmModeAtomicAlloc(); dispdata->atomic_req = KMSDRM_drmModeAtomicAlloc();
@ -768,21 +845,15 @@ KMSDRM_DestroySurfaces(_THIS, SDL_Window * window)
SDL_WindowData *windata = (SDL_WindowData *)window->driverdata; SDL_WindowData *windata = (SDL_WindowData *)window->driverdata;
SDL_DisplayData *dispdata = (SDL_DisplayData *) SDL_GetDisplayForWindow(window)->driverdata; SDL_DisplayData *dispdata = (SDL_DisplayData *) SDL_GetDisplayForWindow(window)->driverdata;
/* CAUTION: Before destroying the GBM ane EGL surfaces, we must disconnect the display plane /* CAUTION: Before destroying the GBM ane EGL surfaces, we must disconnect
from the GBM surface buffer it's reading, and make it read the original buffer stored the display plane from the GBM surface buffer it's reading by setting
on the CRTC while we recreate the GBM surface, OR simply set it's CRTC_ID and FB_ID props to 0. it's CRTC_ID and FB_ID props to 0.
Since setting CRTC_ID and FB_ID to 0 does not work on amdgpu, we point to the original
buffer containig the TTY instead, but it may stop working when we use KMSCON instead of
FBCON because there won't be an original TTY FB to go back to.
In the future, when KMSCON is used, just zero the props instead.
But ALWAYS do manual freeing here because some programs call DestroyWindow() when
they want to resize video, and end up here mid-game when the user changes the window size.
*/ */
drm_atomic_setbuffer(_this, dispdata->display_plane, dispdata->crtc->crtc->buffer_id); drm_atomic_unsetbuffer(_this, dispdata->display_plane);
drm_atomic_commit(_this, SDL_TRUE); drm_atomic_commit(_this, SDL_TRUE);
if (windata->bo) { if (windata->bo) {
KMSDRM_gbm_surface_release_buffer(windata->gs, windata->bo); KMSDRM_gbm_surface_release_buffer(windata->gs, windata->bo);
windata->bo = NULL; windata->bo = NULL;
} }
@ -820,6 +891,9 @@ KMSDRM_CreateSurfaces(_THIS, SDL_Window * window)
EGLContext egl_context; EGLContext egl_context;
#endif #endif
/* Always try to destroy previous surfaces before creating new ones. */
KMSDRM_DestroySurfaces(_this, window);
if (!KMSDRM_gbm_device_is_format_supported(viddata->gbm_dev, surface_fmt, surface_flags)) { if (!KMSDRM_gbm_device_is_format_supported(viddata->gbm_dev, surface_fmt, surface_flags)) {
SDL_LogWarn(SDL_LOG_CATEGORY_VIDEO, "GBM surface format not supported. Trying anyway."); SDL_LogWarn(SDL_LOG_CATEGORY_VIDEO, "GBM surface format not supported. Trying anyway.");
} }
@ -844,118 +918,11 @@ KMSDRM_CreateSurfaces(_THIS, SDL_Window * window)
SDL_EGL_MakeCurrent(_this, windata->egl_surface, egl_context); SDL_EGL_MakeCurrent(_this, windata->egl_surface, egl_context);
windata->egl_surface_dirty = SDL_FALSE;
#endif #endif
return 0; return 0;
} }
/* Used for videomode change, where we need to have the display plane pointing */
/* to a buffer of a new GBM surface BEFORE destroying the old GBM surface. */
int
KMSDRM_RecreateSurfaces(_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 surface_fmt = GBM_FORMAT_ARGB8888;
Uint32 surface_flags = GBM_BO_USE_SCANOUT | GBM_BO_USE_RENDERING;
struct gbm_surface *new_gs;
struct gbm_bo *bo;
KMSDRM_FBInfo *fb;
int ret;
#if SDL_VIDEO_OPENGL_EGL
EGLContext egl_context;
#endif
/*********************************************************************/
/* FIRST CREATE THE NEW GBM SURFACE AND MAKE THE PLANE READ */
/* A BUFFER FROM THAT NEW GBM SURFACE... */
/*********************************************************************/
if (!KMSDRM_gbm_device_is_format_supported(viddata->gbm_dev, surface_fmt, surface_flags)) {
SDL_LogWarn(SDL_LOG_CATEGORY_VIDEO, "GBM surface format not supported. Trying anyway.");
}
#if SDL_VIDEO_OPENGL_EGL
SDL_EGL_SetRequiredVisualId(_this, surface_fmt);
egl_context = (EGLContext)SDL_GL_GetCurrentContext();
#endif
new_gs = KMSDRM_gbm_surface_create(viddata->gbm_dev, width, height, surface_fmt, surface_flags);
if (!new_gs) {
return SDL_SetError("Could not create new GBM surface");
}
/********************************************************/
/* ...THEN DESTROY THE OLD GBM SURFACE AND IT'S BUFFERS */
/********************************************************/
if (windata->bo) {
KMSDRM_gbm_surface_release_buffer(windata->gs, windata->bo);
windata->bo = NULL;
}
if (windata->next_bo) {
KMSDRM_gbm_surface_release_buffer(windata->gs, windata->next_bo);
windata->next_bo = NULL;
}
#if SDL_VIDEO_OPENGL_EGL
SDL_EGL_MakeCurrent(_this, EGL_NO_SURFACE, EGL_NO_CONTEXT);
if (windata->egl_surface != EGL_NO_SURFACE) {
SDL_EGL_DestroySurface(_this, windata->egl_surface);
windata->egl_surface = EGL_NO_SURFACE;
}
#endif
if (windata->gs) {
KMSDRM_gbm_surface_destroy(windata->gs);
windata->gs = NULL;
}
/* From now on, we'll be operating our shiny, new GBM surface! */
windata->gs = new_gs;
#if SDL_VIDEO_OPENGL_EGL
windata->egl_surface = SDL_EGL_CreateSurface(_this, (NativeWindowType)new_gs);
if (windata->egl_surface == EGL_NO_SURFACE) {
return SDL_SetError("Could not create EGL window surface");
}
SDL_EGL_MakeCurrent(_this, windata->egl_surface, egl_context);
windata->egl_surface_dirty = SDL_FALSE;
#endif
bo = KMSDRM_gbm_surface_lock_front_buffer(windata->gs);
if (!bo) {
return SDL_SetError("Failed to lock frontbuffer on GBM surface recreation");
}
fb = KMSDRM_FBFromBO(_this, windata->next_bo);
if (!fb) {
return SDL_SetError("Failed to get a new framebuffer on GBM surface recreation");
}
/* Issue sync pageflip to one of the buffers of the new GBM surface. */
drm_atomic_setbuffer(_this, dispdata->display_plane, fb->fb_id);
ret = drm_atomic_commit(_this, SDL_TRUE);
if (ret) {
return SDL_SetError("failed to issue atomic commit on GBM surface recreation");
}
return 0;
}
int int
KMSDRM_VideoInit(_THIS) KMSDRM_VideoInit(_THIS)
{ {
@ -966,11 +933,9 @@ KMSDRM_VideoInit(_THIS)
drmModeEncoder *encoder = NULL; drmModeEncoder *encoder = NULL;
char devname[32]; char devname[32];
SDL_VideoDisplay display = {0}; SDL_VideoDisplay display = {0};
uint32_t display_plane_id, cursor_plane_id;
dispdata = (SDL_DisplayData *) SDL_calloc(1, sizeof(SDL_DisplayData)); dispdata = (SDL_DisplayData *) SDL_calloc(1, sizeof(SDL_DisplayData));
dispdata->display_plane = calloc(1, sizeof(*dispdata->display_plane)); dispdata->display_plane = calloc(1, sizeof(*dispdata->display_plane));
dispdata->cursor_plane = calloc(1, sizeof(*dispdata->cursor_plane));
dispdata->crtc = calloc(1, sizeof(*dispdata->crtc)); dispdata->crtc = calloc(1, sizeof(*dispdata->crtc));
dispdata->connector = calloc(1, sizeof(*dispdata->connector)); dispdata->connector = calloc(1, sizeof(*dispdata->connector));
@ -1129,20 +1094,6 @@ KMSDRM_VideoInit(_THIS)
drmModeFreeFB(fb); drmModeFreeFB(fb);
#endif #endif
/* DRM mode index for the desktop mode is needed to complete desktop mode init NOW,
so look for it in the DRM modes array.
This is needed because SetDisplayMode() uses the mode index, and some programs
change to fullscreen desktop video mode as they start. */
for (int i = 0; i < dispdata->connector->connector->count_modes; i++) {
if (!SDL_memcmp(dispdata->connector->connector->modes + i, &dispdata->crtc->crtc->mode, sizeof(drmModeModeInfo))) {
SDL_DisplayModeData *modedata = SDL_calloc(1, sizeof(SDL_DisplayModeData));
if (modedata) {
modedata->mode_index = i;
display.desktop_mode.driverdata = modedata;
}
}
}
display.current_mode = display.desktop_mode; display.current_mode = display.desktop_mode;
display.driverdata = dispdata; display.driverdata = dispdata;
SDL_AddVideoDisplay(&display); SDL_AddVideoDisplay(&display);
@ -1168,50 +1119,12 @@ KMSDRM_VideoInit(_THIS)
get_planes_info(_this); get_planes_info(_this);
#endif #endif
/* Get DISPLAY PLANE (simply a primary plane that can use our CRTC). /* Setup display plane */
It has to be a PRIMARY plane because there's no guarantee that ret = setup_plane(_this, &(dispdata->display_plane), DRM_PLANE_TYPE_PRIMARY);
we have overlays in every hardware: we can really only count if (ret) {
on having one primary plane). */ ret = SDL_SetError("can't find suitable display plane.");
display_plane_id = get_plane_id(_this, resources, DRM_PLANE_TYPE_PRIMARY);
if (!display_plane_id) {
ret = SDL_SetError("could not find a suitable display plane.");
goto cleanup; goto cleanup;
}
dispdata->display_plane->plane = KMSDRM_drmModeGetPlane(viddata->drm_fd, display_plane_id);
/* Get DISPLAY PLANE properties */
dispdata->display_plane->props = KMSDRM_drmModeObjectGetProperties(viddata->drm_fd,
dispdata->display_plane->plane->plane_id, DRM_MODE_OBJECT_PLANE);
dispdata->display_plane->props_info = SDL_calloc(dispdata->display_plane->props->count_props,
sizeof(dispdata->display_plane->props_info));
for (int i = 0; i < dispdata->display_plane->props->count_props; i++) {
dispdata->display_plane->props_info[i] = KMSDRM_drmModeGetProperty(viddata->drm_fd,
dispdata->display_plane->props->props[i]);
}
/* Get CURSOR PLANE (simply a cursor plane that can use our CRTC).
Not having a cursor plane available should not be fatal, just throw an error.
All functions using this plane should first test if it exists. */
cursor_plane_id = get_plane_id(_this, resources, DRM_PLANE_TYPE_CURSOR);
if (!cursor_plane_id) {
SDL_SetError("could not find a suitable cursor plane.");
}
dispdata->cursor_plane->plane = KMSDRM_drmModeGetPlane(viddata->drm_fd, cursor_plane_id);
/* Get CURSOR PLANE properties */
if (dispdata->cursor_plane->plane) {
dispdata->cursor_plane->props = KMSDRM_drmModeObjectGetProperties(viddata->drm_fd,
dispdata->cursor_plane->plane->plane_id, DRM_MODE_OBJECT_PLANE);
dispdata->cursor_plane->props_info = SDL_calloc(dispdata->cursor_plane->props->count_props,
sizeof(dispdata->cursor_plane->props_info));
for (int i = 0; i < dispdata->cursor_plane->props->count_props; i++) {
dispdata->cursor_plane->props_info[i] = KMSDRM_drmModeGetProperty(viddata->drm_fd,
dispdata->cursor_plane->props->props[i]);
}
} }
/* Get CRTC properties */ /* Get CRTC properties */
@ -1283,7 +1196,6 @@ cleanup:
void void
KMSDRM_VideoQuit(_THIS) KMSDRM_VideoQuit(_THIS)
{ {
int ret;
SDL_VideoData *viddata = ((SDL_VideoData *)_this->driverdata); SDL_VideoData *viddata = ((SDL_VideoData *)_this->driverdata);
SDL_DisplayData *dispdata = (SDL_DisplayData *)SDL_GetDisplayDriverData(0); SDL_DisplayData *dispdata = (SDL_DisplayData *)SDL_GetDisplayDriverData(0);
SDL_LogDebug(SDL_LOG_CATEGORY_VIDEO, "KMSDRM_VideoQuit()"); SDL_LogDebug(SDL_LOG_CATEGORY_VIDEO, "KMSDRM_VideoQuit()");
@ -1294,29 +1206,6 @@ KMSDRM_VideoQuit(_THIS)
viddata->max_windows = 0; viddata->max_windows = 0;
viddata->num_windows = 0; viddata->num_windows = 0;
/* Restore original buffer. Probably not needed
b/c DestroySurfaces also points the display plane
to the original buffer before destrying the GBM surface. */
if (viddata->drm_fd >= 0 && dispdata && dispdata->connector->connector &&
dispdata->crtc->crtc)
{
/* Issue sync/blocking atomic commit that points crtc to original buffer.
SDL_video has already called SetDisplayMode() to set the original
display mode at this point.
We are not doing pageflips anymore, so we can't rely on the SwapWindow()
atomic_commit() call, hence we are explicitly calling atomic_commit() here.
*/
drm_atomic_setbuffer(_this, dispdata->display_plane, dispdata->crtc->crtc->buffer_id);
ret = drm_atomic_commit(_this, SDL_TRUE);
if (ret != 0)
{
SDL_LogWarn(SDL_LOG_CATEGORY_VIDEO, "Could not restore original buffer and videomode");
}
}
if (_this->gl_config.driver_loaded) { if (_this->gl_config.driver_loaded) {
SDL_GL_UnloadLibrary(); SDL_GL_UnloadLibrary();
} }
@ -1349,25 +1238,11 @@ KMSDRM_VideoQuit(_THIS)
dispdata->crtc = NULL; dispdata->crtc = NULL;
} }
/* Free main display plane */ /* Free display plane */
if (dispdata && dispdata->display_plane) { free_plane(&dispdata->display_plane);
if (dispdata->display_plane->props_info) {
SDL_free(dispdata->display_plane->props_info);
dispdata->display_plane->props_info = NULL;
}
SDL_free(dispdata->display_plane);
dispdata->display_plane = NULL;
}
/* Free main cursor plane */ /* Free cursor plane (if still not freed) */
if (dispdata && dispdata->cursor_plane) { free_plane(&dispdata->cursor_plane);
if (dispdata->cursor_plane->props_info) {
SDL_free(dispdata->cursor_plane->props_info);
dispdata->cursor_plane->props_info = NULL;
}
SDL_free(dispdata->cursor_plane);
dispdata->cursor_plane = NULL;
}
/* Destroy GBM device. GBM surface is destroyed by DestroySurfaces(). */ /* Destroy GBM device. GBM surface is destroyed by DestroySurfaces(). */
if (viddata->gbm_dev) { if (viddata->gbm_dev) {
@ -1387,51 +1262,13 @@ KMSDRM_VideoQuit(_THIS)
void void
KMSDRM_GetDisplayModes(_THIS, SDL_VideoDisplay * display) KMSDRM_GetDisplayModes(_THIS, SDL_VideoDisplay * display)
{ {
SDL_DisplayData *dispdata = display->driverdata; /* Only one display mode available: the current one */
drmModeConnector *conn = dispdata->connector->connector; SDL_AddDisplayMode(display, &display->current_mode);
SDL_DisplayMode mode;
for (int i = 0; i < conn->count_modes; i++) {
SDL_DisplayModeData *modedata = SDL_calloc(1, sizeof(SDL_DisplayModeData));
if (modedata) {
modedata->mode_index = i;
}
mode.w = conn->modes[i].hdisplay;
mode.h = conn->modes[i].vdisplay;
mode.refresh_rate = conn->modes[i].vrefresh;
mode.format = SDL_PIXELFORMAT_ARGB8888;
mode.driverdata = modedata;
if (!SDL_AddDisplayMode(display, &mode)) {
SDL_free(modedata);
}
}
} }
int int
KMSDRM_SetDisplayMode(_THIS, SDL_VideoDisplay * display, SDL_DisplayMode * mode) KMSDRM_SetDisplayMode(_THIS, SDL_VideoDisplay * display, SDL_DisplayMode * mode)
{ {
SDL_VideoData *viddata = (SDL_VideoData *)_this->driverdata;
SDL_DisplayModeData *modedata = (SDL_DisplayModeData *)mode->driverdata;
if (!modedata) {
return SDL_SetError("Mode doesn't have an associated index");
}
/* Set new videomode for the connector in use. */
drm_atomic_modeset(_this, modedata->mode_index);
for (int i = 0; i < viddata->num_windows; i++) {
SDL_Window *window = viddata->windows[i];
/* Re-create GBM and EGL surfaces everytime we change the display mode. */
KMSDRM_RecreateSurfaces(_this, window);
/* Tell app about the resize */
SDL_SendWindowEvent(window, SDL_WINDOWEVENT_RESIZED, mode->w, mode->h);
}
return 0; return 0;
} }
@ -1439,7 +1276,8 @@ int
KMSDRM_CreateWindow(_THIS, SDL_Window * window) KMSDRM_CreateWindow(_THIS, SDL_Window * window)
{ {
SDL_VideoData *viddata = (SDL_VideoData *)_this->driverdata; SDL_VideoData *viddata = (SDL_VideoData *)_this->driverdata;
SDL_WindowData *windata; SDL_VideoDisplay *display = NULL;
SDL_WindowData *windata = NULL;
#if SDL_VIDEO_OPENGL_EGL #if SDL_VIDEO_OPENGL_EGL
if (!_this->egl_data) { if (!_this->egl_data) {
@ -1452,40 +1290,15 @@ KMSDRM_CreateWindow(_THIS, SDL_Window * window)
/* Allocate window internal data */ /* Allocate window internal data */
windata = (SDL_WindowData *)SDL_calloc(1, sizeof(SDL_WindowData)); windata = (SDL_WindowData *)SDL_calloc(1, sizeof(SDL_WindowData));
if (!windata) { display = SDL_GetDisplayForWindow(window);
SDL_OutOfMemory();
goto error;
}
/* Init fields. */
windata->egl_surface_dirty = SDL_FALSE;
/* First remember that certain functions in SDL_Video.c will call *_SetDisplayMode when the
SDL_WINDOW_FULLSCREEN is set and SDL_WINDOW_FULLSCREEN_DESKTOP is not set.
So I am determining here that the behavior when creating an SDL_Window() in KMSDRM, is:
-Creating a normal non-fullscreen window won't change the display mode.
They won't cover the full screen area, either, because that breaks the image aspect ratio.
-Creating a SDL_WINDOW_FULLSCREEN window will change the display mode,
because SDL_WINDOW_FULLSCREEN flag is set.
-Creating a SDL_WINDOW_FULLSCREEN_DESKTOP window will not change the display mode,
because even if the SDL_WINDOW_FULLSCREEN flag is set, SDL_WINDOW_FULLSCREEN_DESKTOP prevents it.
If we ever decide that we want to have normal windows (non-SDL_WINDOW_FULLSCREEN) should cause a display
mode change, we could force the SDL_WINDOW_FULLSCREEN flag again on every window.
But remember that it will break games that check if a window is FULLSCREEN or not before setting
a fullscreen mode with SDL_SetWindowFullscreen(), like sm64ex (sm64 pc port).
If we ever decide that we want normal windows to cover the whole screen area, we can force window->w
and window->h to the original display mode dimensions.
Commented reference code for all this:
window->flags |= (SDL_WINDOW_FULLSCREEN | SDL_WINDOW_OPENGL);
SDL_VideoDisplay *display = SDL_GetDisplayForWindow(window);
/* Windows have one size for now */
window->w = display->desktop_mode.w; window->w = display->desktop_mode.w;
window->h = display->desktop_mode.h; */ window->h = display->desktop_mode.h;
/* Don't force fullscreen on all windows: it confuses programs that try
to set a window fullscreen after creating it as non-fullscreen (sm64ex) */
// window->flags |= SDL_WINDOW_FULLSCREEN;
/* Setup driver data for this window */ /* Setup driver data for this window */
windata->viddata = viddata; windata->viddata = viddata;
@ -1548,10 +1361,6 @@ KMSDRM_DestroyWindow(_THIS, SDL_Window * window)
} }
} }
/* This is the only place where we simply destroy surfaces. On SetDisplayMode(),
we re-create them instead, and we don't destroy the window, just resize it.
DestroyWindow() is ONLY called on program quit, or if a program calls it
explicitly, but not on videomode change. */
KMSDRM_DestroySurfaces(_this, window); KMSDRM_DestroySurfaces(_this, window);
window->driverdata = NULL; window->driverdata = NULL;

View File

@ -53,11 +53,6 @@ typedef struct SDL_VideoData
int num_windows; int num_windows;
} SDL_VideoData; } SDL_VideoData;
typedef struct SDL_DisplayModeData
{
int mode_index;
} SDL_DisplayModeData;
struct plane { struct plane {
drmModePlane *plane; drmModePlane *plane;
drmModeObjectProperties *props; drmModeObjectProperties *props;
@ -105,12 +100,15 @@ typedef struct SDL_DisplayData
typedef struct SDL_WindowData typedef struct SDL_WindowData
{ {
SDL_VideoData *viddata; SDL_VideoData *viddata;
/* SDL internals expect EGL surface to be here, and in KMSDRM the GBM surface is
what supports the EGL surface on the driver side, so all these surfaces and buffers
are expected to be here, in the struct pointed by SDL_Window driverdata pointer: this one.
So don't try to move these to dispdata! */
struct gbm_surface *gs; struct gbm_surface *gs;
struct gbm_bo *bo; struct gbm_bo *bo;
struct gbm_bo *next_bo; struct gbm_bo *next_bo;
struct gbm_bo *crtc_bo;
#if SDL_VIDEO_OPENGL_EGL #if SDL_VIDEO_OPENGL_EGL
SDL_bool egl_surface_dirty;
EGLSurface egl_surface; EGLSurface egl_surface;
#endif #endif
} SDL_WindowData; } SDL_WindowData;