early-access version 1667

This commit is contained in:
pineappleEA
2021-05-09 11:30:38 +02:00
parent 5e268d25d7
commit 5dbb928ff2
1069 changed files with 38272 additions and 14437 deletions
+1 -3
View File
@@ -1,7 +1,6 @@
/*
Simple DirectMedia Layer
Copyright (C) 1997-2020 Sam Lantinga <slouken@libsdl.org>
Atomic KMSDRM backend by Manuel Alfayate Corchete <redwindwanderer@gmail.com>
Copyright (C) 1997-2021 Sam Lantinga <slouken@libsdl.org>
This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any damages
@@ -28,7 +27,6 @@
#include "SDL_kmsdrmdyn.h"
#ifdef SDL_VIDEO_DRIVER_KMSDRM_DYNAMIC
#include "SDL_name.h"
+1 -2
View File
@@ -1,7 +1,6 @@
/*
Simple DirectMedia Layer
Copyright (C) 1997-2020 Sam Lantinga <slouken@libsdl.org>
Atomic KMSDRM backend by Manuel Alfayate Corchete <redwindwanderer@gmail.com>
Copyright (C) 1997-2021 Sam Lantinga <slouken@libsdl.org>
This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any damages
+5 -2
View File
@@ -1,7 +1,6 @@
/*
Simple DirectMedia Layer
Copyright (C) 1997-2020 Sam Lantinga <slouken@libsdl.org>
Atomic KMSDRM backend by Manuel Alfayate Corchete <redwindwanderer@gmail.com>
Copyright (C) 1997-2021 Sam Lantinga <slouken@libsdl.org>
This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any damages
@@ -29,12 +28,16 @@
#ifdef SDL_INPUT_LINUXEV
#include "../../core/linux/SDL_evdev.h"
#elif defined SDL_INPUT_WSCONS
#include "../../core/openbsd/SDL_wscons.h"
#endif
void KMSDRM_PumpEvents(_THIS)
{
#ifdef SDL_INPUT_LINUXEV
SDL_EVDEV_Poll();
#elif defined SDL_INPUT_WSCONS
SDL_WSCONS_PumpEvents();
#endif
}
+1 -2
View File
@@ -1,7 +1,6 @@
/*
Simple DirectMedia Layer
Copyright (C) 1997-2020 Sam Lantinga <slouken@libsdl.org>
Atomic KMSDRM backend by Manuel Alfayate Corchete <redwindwanderer@gmail.com>
Copyright (C) 1997-2021 Sam Lantinga <slouken@libsdl.org>
This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any damages
+285 -317
View File
@@ -1,7 +1,6 @@
/*
Simple DirectMedia Layer
Copyright (C) 1997-2020 Sam Lantinga <slouken@libsdl.org>
Atomic KMSDRM backend by Manuel Alfayate Corchete <redwindwanderer@gmail.com>
Copyright (C) 1997-2021 Sam Lantinga <slouken@libsdl.org>
This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any damages
@@ -27,7 +26,6 @@
#include "SDL_kmsdrmvideo.h"
#include "SDL_kmsdrmmouse.h"
#include "SDL_kmsdrmdyn.h"
#include "SDL_assert.h"
#include "../../events/SDL_mouse_c.h"
#include "../../events/default_cursor.h"
@@ -40,40 +38,33 @@ static void KMSDRM_FreeCursor(SDL_Cursor * cursor);
static void KMSDRM_WarpMouse(SDL_Window * window, int x, int y);
static int KMSDRM_WarpMouseGlobal(int x, int y);
/**********************************/
/* Atomic helper functions block. */
/**********************************/
/**************************************************************************************/
/* BEFORE CODING ANYTHING MOUSE/CURSOR RELATED, REMEMBER THIS. */
/* How does SDL manage cursors internally? First, mouse =! cursor. The mouse can have */
/* many cursors in mouse->cursors. */
/* -SDL tells us to create a cursor with KMSDRM_CreateCursor(). It can create many */
/* cursosr with this, not only one. */
/* -SDL stores those cursors in a cursors array, in mouse->cursors. */
/* -Whenever it wants (or the programmer wants) takes a cursor from that array */
/* and shows it on screen with KMSDRM_ShowCursor(). */
/* KMSDRM_ShowCursor() simply shows or hides the cursor it receives: it does NOT */
/* mind if it's mouse->cur_cursor, etc. */
/* -If KMSDRM_ShowCursor() returns succesfully, that cursor becomes mouse->cur_cursor */
/* and mouse->cursor_shown is 1. */
/**************************************************************************************/
int
drm_atomic_movecursor(KMSDRM_CursorData *curdata, uint16_t x, uint16_t y)
static SDL_Cursor *
KMSDRM_CreateDefaultCursor(void)
{
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. */
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);
return 0;
return SDL_CreateCursor(default_cdata, default_cmask, DEFAULT_CWIDTH, DEFAULT_CHEIGHT, DEFAULT_CHOTX, DEFAULT_CHOTY);
}
/***************************************/
/* Atomic helper functions block ends. */
/***************************************/
/* Converts a pixel from straight-alpha [AA, RR, GG, BB], which the SDL cursor surface has,
to premultiplied-alpha [AA. AA*RR, AA*GG, AA*BB].
These multiplications have to be done with floats instead of uint32_t's,
and the resulting values have to be converted to be relative to the 0-255 interval,
where 255 is 1.00 and anything between 0 and 255 is 0.xx. */
void alpha_premultiply_ARGB8888 (uint32_t *pixel) {
void legacy_alpha_premultiply_ARGB8888 (uint32_t *pixel) {
uint32_t A, R, G, B;
@@ -92,14 +83,185 @@ void alpha_premultiply_ARGB8888 (uint32_t *pixel) {
(*pixel) = (((uint32_t)A << 24) | ((uint32_t)R << 16) | ((uint32_t)G << 8)) | ((uint32_t)B << 0);
}
static SDL_Cursor *
KMSDRM_CreateDefaultCursor(void)
/* Given a display's driverdata, destroy the cursor BO for it.
To be called from KMSDRM_DestroyWindow(), as that's where we
destroy the driverdata for the window's display. */
void
KMSDRM_DestroyCursorBO (_THIS, SDL_VideoDisplay *display)
{
return SDL_CreateCursor(default_cdata, default_cmask, DEFAULT_CWIDTH, DEFAULT_CHEIGHT, DEFAULT_CHOTX, DEFAULT_CHOTY);
SDL_DisplayData *dispdata = (SDL_DisplayData *) display->driverdata;
/* Destroy the curso GBM BO. */
if (dispdata->cursor_bo) {
KMSDRM_gbm_bo_destroy(dispdata->cursor_bo);
dispdata->cursor_bo = NULL;
}
}
/* This simply gets the cursor soft-buffer ready. We don't copy it to a GBO BO until ShowCursor()
because the cusor GBM BO (living in dispata) is destroyed and recreated when we recreate windows, etc. */
/* Given a display's driverdata, create the cursor BO for it.
To be called from KMSDRM_CreateWindow(), as that's where we
build a window and assign a display to it. */
void
KMSDRM_CreateCursorBO (SDL_VideoDisplay *display) {
SDL_VideoDevice *dev = SDL_GetVideoDevice();
SDL_VideoData *viddata = ((SDL_VideoData *)dev->driverdata);
SDL_DisplayData *dispdata = (SDL_DisplayData *) display->driverdata;
if (!KMSDRM_gbm_device_is_format_supported(viddata->gbm_dev,
GBM_FORMAT_ARGB8888,
GBM_BO_USE_CURSOR | GBM_BO_USE_WRITE))
{
SDL_SetError("Unsupported pixel format for cursor");
return;
}
if (KMSDRM_drmGetCap(viddata->drm_fd,
DRM_CAP_CURSOR_WIDTH, &dispdata->cursor_w) ||
KMSDRM_drmGetCap(viddata->drm_fd, DRM_CAP_CURSOR_HEIGHT,
&dispdata->cursor_h))
{
SDL_SetError("Could not get the recommended GBM cursor size");
return;
}
if (dispdata->cursor_w == 0 || dispdata->cursor_h == 0) {
SDL_SetError("Could not get an usable GBM cursor size");
return;
}
dispdata->cursor_bo = KMSDRM_gbm_bo_create(viddata->gbm_dev,
dispdata->cursor_w, dispdata->cursor_h,
GBM_FORMAT_ARGB8888, GBM_BO_USE_CURSOR | GBM_BO_USE_WRITE | GBM_BO_USE_LINEAR);
if (!dispdata->cursor_bo) {
SDL_SetError("Could not create GBM cursor BO");
return;
}
}
/* Remove a cursor buffer from a display's DRM cursor BO. */
int
KMSDRM_RemoveCursorFromBO(SDL_VideoDisplay *display)
{
int ret = 0;
SDL_DisplayData *dispdata = (SDL_DisplayData *) display->driverdata;
SDL_VideoDevice *video_device = SDL_GetVideoDevice();
SDL_VideoData *viddata = ((SDL_VideoData *)video_device->driverdata);
ret = KMSDRM_drmModeSetCursor(viddata->drm_fd,
dispdata->crtc->crtc_id, 0, 0, 0);
if (ret) {
ret = SDL_SetError("Could not hide current cursor with drmModeSetCursor().");
}
return ret;
}
/* Dump a cursor buffer to a display's DRM cursor BO. */
int
KMSDRM_DumpCursorToBO(SDL_VideoDisplay *display, SDL_Cursor *cursor)
{
SDL_DisplayData *dispdata = (SDL_DisplayData *) display->driverdata;
KMSDRM_CursorData *curdata = (KMSDRM_CursorData *) cursor->driverdata;
SDL_VideoDevice *video_device = SDL_GetVideoDevice();
SDL_VideoData *viddata = ((SDL_VideoData *)video_device->driverdata);
uint32_t bo_handle;
size_t bo_stride;
size_t bufsize;
uint32_t *ready_buffer = NULL;
uint32_t pixel;
int i,j;
int ret;
if (!curdata || !dispdata->cursor_bo) {
return SDL_SetError("Cursor or display not initialized properly.");
}
/* Prepare a buffer we can dump to our GBM BO (different
size, alpha premultiplication...) */
bo_stride = KMSDRM_gbm_bo_get_stride(dispdata->cursor_bo);
bufsize = bo_stride * dispdata->cursor_h;
ready_buffer = (uint32_t*)SDL_calloc(1, bufsize);
if (!ready_buffer) {
ret = SDL_OutOfMemory();
goto cleanup;
}
/* Copy from the cursor buffer to a buffer that we can dump to the GBM BO,
pre-multiplying by alpha each pixel as we go. */
for (i = 0; i < curdata->h; i++) {
for (j = 0; j < curdata->w; j++) {
pixel = ((uint32_t*)curdata->buffer)[i * curdata->w + j];
legacy_alpha_premultiply_ARGB8888 (&pixel);
SDL_memcpy(ready_buffer + (i * dispdata->cursor_w) + j, &pixel, 4);
}
}
/* Dump the cursor buffer to our GBM BO. */
if (KMSDRM_gbm_bo_write(dispdata->cursor_bo, ready_buffer, bufsize)) {
ret = SDL_SetError("Could not write to GBM cursor BO");
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) {
ret = KMSDRM_drmModeSetCursor(viddata->drm_fd, dispdata->crtc->crtc_id,
bo_handle, dispdata->cursor_w, dispdata->cursor_h);
} else {
ret = KMSDRM_drmModeSetCursor2(viddata->drm_fd, dispdata->crtc->crtc_id,
bo_handle, dispdata->cursor_w, dispdata->cursor_h, curdata->hot_x, curdata->hot_y);
}
if (ret) {
ret = SDL_SetError("Failed to set DRM cursor.");
goto cleanup;
}
if (ret) {
ret = SDL_SetError("Failed to reset cursor position.");
goto cleanup;
}
cleanup:
if (ready_buffer) {
SDL_free(ready_buffer);
}
return ret;
}
/* This is only for freeing the SDL_cursor.*/
static void
KMSDRM_FreeCursor(SDL_Cursor * cursor)
{
KMSDRM_CursorData *curdata;
/* Even if the cursor is not ours, free it. */
if (cursor) {
curdata = (KMSDRM_CursorData *) cursor->driverdata;
/* Free cursor buffer */
if (curdata->buffer) {
SDL_free(curdata->buffer);
curdata->buffer = NULL;
}
/* Free cursor itself */
if (cursor->driverdata) {
SDL_free(cursor->driverdata);
}
SDL_free(cursor);
}
}
/* This simply gets the cursor soft-buffer ready.
We don't copy it to a GBO BO until ShowCursor() because the cusor GBM BO (living
in dispata) is destroyed and recreated when we recreate windows, etc. */
static SDL_Cursor *
KMSDRM_CreateCursor(SDL_Surface * surface, int hot_x, int hot_y)
{
@@ -165,203 +327,78 @@ KMSDRM_CreateCursor(SDL_Surface * surface, int hot_x, int hot_y)
cleanup:
if (ret == NULL) {
if (curdata) {
if (curdata->buffer) {
SDL_free(curdata->buffer);
}
SDL_free(curdata);
}
if (cursor) {
SDL_free(cursor);
}
if (curdata) {
if (curdata->buffer) {
SDL_free(curdata->buffer);
}
SDL_free(curdata);
}
if (cursor) {
SDL_free(cursor);
}
}
return ret;
}
/* When we create a window, we have to test if we have to show the cursor,
and explicily do so if necessary.
This is because when we destroy a window, we take the cursor away from the
cursor plane, and destroy the cusror GBM BO. So we have to re-show it,
so to say. */
void
KMSDRM_InitCursor()
{
SDL_Mouse *mouse = NULL;
mouse = SDL_GetMouse();
if (!mouse) {
return;
}
if (!(mouse->cur_cursor)) {
return;
}
if (!(mouse->cursor_shown)) {
return;
}
KMSDRM_ShowCursor(mouse->cur_cursor);
}
/* Show the specified cursor, or hide if cursor is NULL.
cur_cursor is the current cursor, and cursor is the new cursor.
A cursor is displayed on a display, so we have to add a pointer to dispdata
to the driverdata
*/
/* Show the specified cursor, or hide if cursor is NULL or has no focus. */
static int
KMSDRM_ShowCursor(SDL_Cursor * cursor)
{
SDL_VideoDevice *video_device = SDL_GetVideoDevice();
//SDL_VideoData *viddata = ((SDL_VideoData *)dev->driverdata);
SDL_VideoDisplay *display;
SDL_Window *window;
SDL_Mouse *mouse;
KMSDRM_CursorData *curdata;
SDL_VideoDisplay *display = NULL;
SDL_DisplayData *dispdata = NULL;
KMSDRM_FBInfo *fb;
KMSDRM_PlaneInfo info = {0};
size_t bo_stride;
size_t bufsize;
uint32_t *ready_buffer = NULL;
uint32_t pixel;
int num_displays, i;
int ret = 0;
int i,j;
int ret;
/* Get the mouse focused window, if any. */
mouse = SDL_GetMouse();
if (!mouse) {
return SDL_SetError("No mouse.");
}
if (mouse->focus) {
display = SDL_GetDisplayForWindow(mouse->focus);
if (display) {
dispdata = (SDL_DisplayData*) display->driverdata;
}
}
window = mouse->focus;
/**********************************/
/* if cursor == NULL, HIDE cursor */
/**********************************/
if (!cursor) {
/* Hide CURRENT cursor, a cursor that is already on screen
and SDL is stored in mouse->cur_cursor. */
if (mouse->cur_cursor && mouse->cur_cursor->driverdata) {
if (dispdata && dispdata->cursor_plane) {
info.plane = dispdata->cursor_plane;
/* The rest of the members are zeroed. */
drm_atomic_set_plane_props(&info);
if (drm_atomic_commit(display->device, SDL_TRUE))
return SDL_SetError("Failed atomic commit in KMSDRM_ShowCursor.");
}
return 0;
if (!window || !cursor) {
/* If no window is focused by mouse or cursor is NULL,
since we have no window (no mouse->focus) and hence
we have no display, we simply hide mouse on all displays.
This happens on video quit, where we get here after
the mouse focus has been unset, yet SDL wants to
restore the system default cursor (makes no sense here). */
num_displays = SDL_GetNumVideoDisplays();
/* Iterate on the displays hidding the cursor. */
for (i = 0; i < num_displays; i++) {
display = SDL_GetDisplay(i);
ret = KMSDRM_RemoveCursorFromBO(display);
}
return SDL_SetError("Couldn't find cursor to hide.");
} else {
display = SDL_GetDisplayForWindow(window);
if (display) {
if (cursor) {
/* Dump the cursor to the display DRM cursor BO so it becomes visible
on that display. */
ret = KMSDRM_DumpCursorToBO(display, cursor);
} else {
/* Hide the cursor on that display. */
ret = KMSDRM_RemoveCursorFromBO(display);
}
}
}
/************************************************/
/* If cursor != NULL, DO show cursor on display */
/************************************************/
if (!display) {
return SDL_SetError("Could not get display for mouse.");
}
if (!dispdata) {
return SDL_SetError("Could not get display driverdata.");
}
if (!dispdata->cursor_plane) {
return SDL_SetError("Hardware cursor plane not initialized.");
}
curdata = (KMSDRM_CursorData *) cursor->driverdata;
if (!curdata || !dispdata->cursor_bo) {
return SDL_SetError("Cursor not initialized properly.");
}
/* Prepare a buffer we can dump to our GBM BO (different size, alpha premultiplication...) */
bo_stride = KMSDRM_gbm_bo_get_stride(dispdata->cursor_bo);
bufsize = bo_stride * curdata->h;
ready_buffer = (uint32_t*)SDL_malloc(bufsize);
if (!ready_buffer) {
ret = SDL_OutOfMemory();
goto cleanup;
}
/* Clean the whole buffer we are preparing. */
SDL_memset(ready_buffer, 0x00, bo_stride * curdata->h);
/* Copy from the cursor buffer to a buffer that we can dump to the GBM BO,
pre-multiplying by alpha each pixel as we go. */
for (i = 0; i < curdata->h; i++) {
for (j = 0; j < curdata->w; j++) {
pixel = ((uint32_t*)curdata->buffer)[i * curdata->w + j];
alpha_premultiply_ARGB8888 (&pixel);
SDL_memcpy(ready_buffer + (i * dispdata->cursor_w) + j, &pixel, 4);
}
}
/* Dump the cursor buffer to our GBM BO. */
if (KMSDRM_gbm_bo_write(dispdata->cursor_bo, ready_buffer, bufsize)) {
ret = SDL_SetError("Could not write to GBM cursor BO");
goto cleanup;
}
/* Get the fb_id for the GBM BO so we can show it on the cursor plane. */
fb = KMSDRM_FBFromBO(video_device, dispdata->cursor_bo);
/* Show the GBM BO buffer on the cursor plane. */
info.plane = dispdata->cursor_plane;
info.crtc_id = dispdata->crtc->crtc->crtc_id;
info.fb_id = fb->fb_id;
info.src_w = curdata->w;
info.src_h = curdata->h;
info.crtc_x = mouse->x - curdata->hot_x;
info.crtc_y = mouse->y - curdata->hot_y;
info.crtc_w = curdata->w;
info.crtc_h = curdata->h;
drm_atomic_set_plane_props(&info);
if (drm_atomic_commit(display->device, SDL_TRUE)) {
ret = SDL_SetError("Failed atomic commit in KMSDRM_ShowCursor.");
goto cleanup;
}
ret = 0;
cleanup:
if (ready_buffer) {
SDL_free(ready_buffer);
}
return ret;
}
/* We have destroyed the cursor by now, in KMSDRM_DestroyCursor.
This is only for freeing the SDL_cursor.*/
static void
KMSDRM_FreeCursor(SDL_Cursor * cursor)
{
KMSDRM_CursorData *curdata;
/* Even if the cursor is not ours, free it. */
if (cursor) {
curdata = (KMSDRM_CursorData *) cursor->driverdata;
/* Free cursor buffer */
if (curdata->buffer) {
SDL_free(curdata->buffer);
curdata->buffer = NULL;
}
/* Free cursor itself */
if (cursor->driverdata) {
SDL_free(cursor->driverdata);
}
SDL_free(cursor);
}
}
/* Warp the mouse to (x,y) */
static void
KMSDRM_WarpMouse(SDL_Window * window, int x, int y)
@@ -374,23 +411,34 @@ KMSDRM_WarpMouse(SDL_Window * window, int x, int y)
static int
KMSDRM_WarpMouseGlobal(int x, int y)
{
KMSDRM_CursorData *curdata;
SDL_Mouse *mouse = SDL_GetMouse();
SDL_DisplayData *dispdata = (SDL_DisplayData *)SDL_GetDisplayDriverData(0);
if (mouse && mouse->cur_cursor && mouse->cur_cursor->driverdata) {
if (mouse && mouse->cur_cursor && mouse->focus) {
SDL_Window *window = mouse->focus;
SDL_DisplayData *dispdata = (SDL_DisplayData *) SDL_GetDisplayForWindow(window)->driverdata;
/* Update internal mouse position. */
SDL_SendMouseMotion(mouse->focus, mouse->mouseID, 0, x, y);
/* And now update the cursor graphic position on screen. */
curdata = (KMSDRM_CursorData *) mouse->cur_cursor->driverdata;
if (dispdata->cursor_bo) {
if (drm_atomic_movecursor(curdata, x, y)) {
return SDL_SetError("drm_atomic_movecursor() failed.");
int drm_fd;
int ret = 0;
drm_fd = KMSDRM_gbm_device_get_fd(KMSDRM_gbm_bo_get_device(dispdata->cursor_bo));
ret = KMSDRM_drmModeMoveCursor(drm_fd, dispdata->crtc->crtc_id, x, y);
if (ret) {
SDL_SetError("drmModeMoveCursor() failed.");
}
return ret;
} else {
return SDL_SetError("Cursor not initialized properly.");
}
} else {
return SDL_SetError("No mouse or current cursor.");
}
@@ -399,17 +447,10 @@ KMSDRM_WarpMouseGlobal(int x, int y)
}
void
KMSDRM_InitMouse(_THIS)
KMSDRM_InitMouse(_THIS, SDL_VideoDisplay *display)
{
/* FIXME: Using UDEV it should be possible to scan all mice
* but there's no point in doing so as there's no multimice support...yet!
*/
SDL_VideoDevice *dev = SDL_GetVideoDevice();
SDL_VideoData *viddata = ((SDL_VideoData *)dev->driverdata);
SDL_DisplayData *dispdata = (SDL_DisplayData *)SDL_GetDisplayDriverData(0);
SDL_Mouse *mouse = SDL_GetMouse();
uint64_t usable_cursor_w, usable_cursor_h;
SDL_DisplayData *dispdata = (SDL_DisplayData *) display->driverdata;
mouse->CreateCursor = KMSDRM_CreateCursor;
mouse->ShowCursor = KMSDRM_ShowCursor;
@@ -418,96 +459,18 @@ KMSDRM_InitMouse(_THIS)
mouse->WarpMouse = KMSDRM_WarpMouse;
mouse->WarpMouseGlobal = KMSDRM_WarpMouseGlobal;
/***************************************************************************/
/* REMEMBER TO BE SURE OF UNDOING ALL THESE STEPS PROPERLY BEFORE CALLING */
/* gbm_device_destroy, OR YOU WON'T BE ABLE TO CREATE A NEW ONE (ERROR -13 */
/* ON gbm_create_device). */
/***************************************************************************/
/* 1- Init cursor plane, if we haven't yet. */
if (!dispdata->cursor_plane) {
setup_plane(_this, &(dispdata->cursor_plane), DRM_PLANE_TYPE_CURSOR);
}
/* 2- Create the cursor GBM BO, if we haven't yet. */
if (!dispdata->cursor_bo) {
if (!KMSDRM_gbm_device_is_format_supported(viddata->gbm_dev, GBM_FORMAT_ARGB8888,
GBM_BO_USE_CURSOR | GBM_BO_USE_WRITE))
{
SDL_SetError("Unsupported pixel format for cursor");
return;
}
if (KMSDRM_drmGetCap(viddata->drm_fd, DRM_CAP_CURSOR_WIDTH, &usable_cursor_w) ||
KMSDRM_drmGetCap(viddata->drm_fd, DRM_CAP_CURSOR_HEIGHT, &usable_cursor_h))
{
SDL_SetError("Could not get the recommended GBM cursor size");
goto cleanup;
}
if (usable_cursor_w == 0 || usable_cursor_h == 0) {
SDL_SetError("Could not get an usable GBM cursor size");
goto cleanup;
}
dispdata->cursor_w = usable_cursor_w;
dispdata->cursor_h = usable_cursor_h;
dispdata->cursor_bo = KMSDRM_gbm_bo_create(viddata->gbm_dev,
usable_cursor_w, usable_cursor_h,
GBM_FORMAT_ARGB8888, GBM_BO_USE_CURSOR | GBM_BO_USE_WRITE);
if (!dispdata->cursor_bo) {
SDL_SetError("Could not create GBM cursor BO");
goto cleanup;
}
}
/* SDL expects to set the default cursor on screen when we init the mouse. */
SDL_SetDefaultCursor(KMSDRM_CreateDefaultCursor());
return;
cleanup:
if (dispdata->cursor_bo) {
KMSDRM_gbm_bo_destroy(dispdata->cursor_bo);
dispdata->cursor_bo = NULL;
/* Only create the default cursor for this display if we haven't done so before,
we don't want several cursors to be created for the same display. */
if (!dispdata->default_cursor_init) {
SDL_SetDefaultCursor(KMSDRM_CreateDefaultCursor());
dispdata->default_cursor_init = SDL_TRUE;
}
}
void
KMSDRM_DeinitMouse(_THIS)
KMSDRM_QuitMouse(_THIS)
{
SDL_VideoDevice *video_device = SDL_GetVideoDevice();
SDL_DisplayData *dispdata = (SDL_DisplayData *)SDL_GetDisplayDriverData(0);
KMSDRM_PlaneInfo info = {0};
/*******************************************/
/* UNDO WHAT WE DID IN KMSDRM_InitMouse(). */
/*******************************************/
/* 1- Destroy the curso GBM BO. */
if (video_device && dispdata->cursor_bo) {
/* Unsethe the cursor BO from the cursor plane.
(The other members of the plane info are zeroed). */
info.plane = dispdata->cursor_plane;
drm_atomic_set_plane_props(&info);
/* Wait until the cursor is unset from the cursor plane
before destroying it's BO. */
if (drm_atomic_commit(video_device, SDL_TRUE)) {
SDL_SetError("Failed atomic commit in KMSDRM_DenitMouse.");
}
/* ..and finally destroy the cursor DRM BO! */
KMSDRM_gbm_bo_destroy(dispdata->cursor_bo);
dispdata->cursor_bo = NULL;
}
/* 2- Free the cursor plane, on which the cursor was being shown. */
if (dispdata->cursor_plane) {
free_plane(&dispdata->cursor_plane);
}
/* TODO: ? */
}
/* This is called when a mouse motion event occurs */
@@ -515,22 +478,27 @@ static void
KMSDRM_MoveCursor(SDL_Cursor * cursor)
{
SDL_Mouse *mouse = SDL_GetMouse();
KMSDRM_CursorData *curdata;
int drm_fd;
int ret = 0;
/* We must NOT call SDL_SendMouseMotion() here or we will enter recursivity!
That's why we move the cursor graphic ONLY. */
if (mouse && mouse->cur_cursor && mouse->cur_cursor->driverdata) {
curdata = (KMSDRM_CursorData *) mouse->cur_cursor->driverdata;
if (mouse && mouse->cur_cursor && mouse->focus) {
/* 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. */
SDL_Window *window = mouse->focus;
SDL_DisplayData *dispdata = (SDL_DisplayData *) SDL_GetDisplayForWindow(window)->driverdata;
if (drm_atomic_movecursor(curdata, mouse->x, mouse->y)) {
SDL_SetError("drm_atomic_movecursor() failed.");
if (!dispdata->cursor_bo) {
SDL_SetError("Cursor not initialized properly.");
return;
}
drm_fd = KMSDRM_gbm_device_get_fd(KMSDRM_gbm_bo_get_device(dispdata->cursor_bo));
ret = KMSDRM_drmModeMoveCursor(drm_fd, dispdata->crtc->crtc_id, mouse->x, mouse->y);
if (ret) {
SDL_SetError("drmModeMoveCursor() failed.");
}
}
}
+9 -8
View File
@@ -1,7 +1,6 @@
/*
Simple DirectMedia Layer
Copyright (C) 1997-2020 Sam Lantinga <slouken@libsdl.org>
Atomic KMSDRM backend by Manuel Alfayate Corchete <redwindwanderer@gmail.com>
Copyright (C) 1997-2021 Sam Lantinga <slouken@libsdl.org>
This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any damages
@@ -30,23 +29,25 @@
#define MAX_CURSOR_W 512
#define MAX_CURSOR_H 512
/* Driverdata with driver-side info about the cursor. */
typedef struct _KMSDRM_CursorData
{
uint16_t hot_x, hot_y;
uint16_t w, h;
int hot_x, hot_y;
int w, h;
/* The buffer where we store the mouse bitmap ready to be used.
We get it ready and filled in CreateCursor(), and copy it to a GBM BO in ShowCursor().*/
We get it ready and filled in CreateCursor(), and copy it
to a GBM BO in ShowCursor().*/
uint32_t *buffer;
size_t buffer_size;
size_t buffer_pitch;
} KMSDRM_CursorData;
extern void KMSDRM_InitMouse(_THIS);
extern void KMSDRM_DeinitMouse(_THIS);
extern void KMSDRM_InitMouse(_THIS, SDL_VideoDisplay *display);
extern void KMSDRM_QuitMouse(_THIS);
extern void KMSDRM_CreateCursorBO(SDL_VideoDisplay *display);
extern void KMSDRM_DestroyCursorBO(_THIS, SDL_VideoDisplay *display);
extern void KMSDRM_InitCursor();
#endif /* SDL_KMSDRM_mouse_h_ */
+91 -265
View File
@@ -1,7 +1,6 @@
/*
Simple DirectMedia Layer
Copyright (C) 1997-2020 Sam Lantinga <slouken@libsdl.org>
Atomic KMSDRM backend by Manuel Alfayate Corchete <redwindwanderer@gmail.com>
Copyright (C) 1997-2021 Sam Lantinga <slouken@libsdl.org>
This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any damages
@@ -22,29 +21,19 @@
#include "../../SDL_internal.h"
#if SDL_VIDEO_DRIVER_KMSDRM && SDL_VIDEO_OPENGL_EGL
#if SDL_VIDEO_DRIVER_KMSDRM
#include "SDL_log.h"
#include "SDL_kmsdrmvideo.h"
#include "SDL_kmsdrmopengles.h"
#include "SDL_kmsdrmdyn.h"
#include "SDL_hints.h"
#include <errno.h>
#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
@@ -75,13 +64,14 @@ KMSDRM_GLES_LoadLibrary(_THIS, const char *path) {
void
KMSDRM_GLES_UnloadLibrary(_THIS) {
/* As with KMSDRM_GLES_LoadLibrary(), we define our own unloading function so
we manually unload the library whenever we want. */
/* As with KMSDRM_GLES_LoadLibrary(), we define our own "dummy" unloading function
so we manually unload the library whenever we want. */
}
SDL_EGL_CreateContext_impl(KMSDRM)
int KMSDRM_GLES_SetSwapInterval(_THIS, int interval) {
if (!_this->egl_data) {
return SDL_SetError("EGL not initialized");
}
@@ -95,287 +85,123 @@ int KMSDRM_GLES_SetSwapInterval(_THIS, int interval) {
return 0;
}
/*********************************/
/* Atomic functions block */
/*********************************/
#define VOID2U64(x) ((uint64_t)(unsigned long)(x))
static EGLSyncKHR create_fence(int fd, _THIS)
{
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);
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 forevever. */
/***********************************************************************************/
int
KMSDRM_GLES_SwapWindowFenced(_THIS, SDL_Window * window)
{
KMSDRM_GLES_SwapWindow(_THIS, SDL_Window * window) {
SDL_WindowData *windata = ((SDL_WindowData *) window->driverdata);
SDL_DisplayData *dispdata = (SDL_DisplayData *) SDL_GetDisplayForWindow(window)->driverdata;
KMSDRM_FBInfo *fb;
KMSDRM_PlaneInfo info = {0};
SDL_VideoData *viddata = ((SDL_VideoData *)_this->driverdata);
KMSDRM_FBInfo *fb_info;
int ret = 0;
/******************************************************************/
/* 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(EGL_NO_NATIVE_FENCE_FD_ANDROID, _this);
assert(dispdata->gpu_fence);
/* Always wait for the previous issued flip before issuing a new one,
even if you do async flips. */
uint32_t flip_flags = DRM_MODE_PAGE_FLIP_EVENT;
/******************************************************************/
/* 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");
/* Wait for confirmation that the next front buffer has been flipped, at which
point the previous front buffer can be released */
if (!KMSDRM_WaitPageflip(_this, windata)) {
SDL_LogError(SDL_LOG_CATEGORY_VIDEO, "Wait for previous pageflip failed");
return 0;
}
/******************************************************************/
/* 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);
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");
}
/* 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 = windata->src_w;
info.src_h = windata->src_h;
info.crtc_w = windata->output_w;
info.crtc_h = windata->output_h;
info.crtc_x = windata->output_x;
drm_atomic_set_plane_props(&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 (dispdata->modeset_pending) {
uint32_t blob_id;
SDL_VideoData *viddata = (SDL_VideoData *)_this->driverdata;
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, &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);
dispdata->modeset_pending = SDL_FALSE;
}
/*****************************************************************/
/* 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, SDL_FALSE)) {
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. */
/* Release the previous front buffer */
if (windata->bo) {
KMSDRM_gbm_surface_release_buffer(windata->gs, windata->bo);
windata->bo = NULL;
}
/* 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(dispdata->kms_out_fence_fd, _this);
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 0;
}
int
KMSDRM_GLES_SwapWindowDoubleBuffered(_THIS, SDL_Window * window)
{
SDL_WindowData *windata = ((SDL_WindowData *) window->driverdata);
SDL_DisplayData *dispdata = (SDL_DisplayData *) SDL_GetDisplayForWindow(window)->driverdata;
KMSDRM_FBInfo *fb;
KMSDRM_PlaneInfo info = {0};
/****************************************************************************************************/
/* 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.
However, 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");
/* Mark a buffer to becume the next front buffer.
This won't happen until pagelip completes. */
if (!(_this->egl_data->eglSwapBuffers(_this->egl_data->egl_display,
windata->egl_surface))) {
SDL_LogError(SDL_LOG_CATEGORY_VIDEO, "eglSwapBuffers failed");
return 0;
}
/* 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. */
/* From the GBM surface, get the next BO to become the next front buffer,
and lock it so it can't be allocated as a back buffer (to prevent EGL
from drawing into 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");
SDL_LogError(SDL_LOG_CATEGORY_VIDEO, "Could not lock front buffer on GBM surface");
return 0;
}
/* 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;
/* Get an actual usable fb for the next front buffer. */
fb_info = KMSDRM_FBFromBO(_this, windata->next_bo);
if (!fb_info) {
SDL_LogError(SDL_LOG_CATEGORY_VIDEO, "Could not get a framebuffer");
return 0;
}
info.src_w = windata->src_w;
info.src_h = windata->src_h;
info.crtc_w = windata->output_w;
info.crtc_h = windata->output_h;
info.crtc_x = windata->output_x;
drm_atomic_set_plane_props(&info);
/* Do we have a pending modesetting? If so, set the necessary
props so it's included in the incoming atomic commit. */
/* Do we have a modeset pending? If so, configure the new mode on the CRTC.
Has to be done before next pageflip issues, so the buffer with the
new size is big enough for preventing CRTC from reading out of bounds. */
if (dispdata->modeset_pending) {
SDL_VideoData *viddata = (SDL_VideoData *)_this->driverdata;
uint32_t blob_id;
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, &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);
ret = KMSDRM_drmModeSetCrtc(viddata->drm_fd,
dispdata->crtc->crtc_id, fb_info->fb_id, 0, 0,
&dispdata->connector->connector_id, 1, &dispdata->mode);
dispdata->modeset_pending = SDL_FALSE;
if (ret) {
SDL_LogError(SDL_LOG_CATEGORY_VIDEO, "Could not set videomode on CRTC.");
return 0;
}
/* It's OK to return now if we have done a drmModeSetCrtc(),
it has done the pageflip and blocked until it was done. */
return 1;
}
/* 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, SDL_TRUE)) {
return SDL_SetError("Failed to issue atomic commit");
/* Issue pageflip on the next front buffer.
Remember: drmModePageFlip() never blocks, it just issues the flip,
which will be done during the next vblank, or immediately if
we pass the DRM_MODE_PAGE_FLIP_ASYNC flag.
Since calling drmModePageFlip() will return EBUSY if we call it
without having completed the last issued flip, we must pass the
DRM_MODE_PAGE_FLIP_ASYNC if we don't block on EGL (egl_swapinterval = 0).
That makes it flip immediately, without waiting for the next vblank
to do so, so even if we don't block on EGL, the flip will have completed
when we get here again. */
if (_this->egl_data->egl_swapinterval == 0 && viddata->async_pageflip_support) {
flip_flags |= DRM_MODE_PAGE_FLIP_ASYNC;
}
/* 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);
ret = KMSDRM_drmModePageFlip(viddata->drm_fd, dispdata->crtc->crtc_id,
fb_info->fb_id, flip_flags, &windata->waiting_for_flip);
if (ret == 0) {
windata->waiting_for_flip = SDL_TRUE;
} else {
SDL_LogError(SDL_LOG_CATEGORY_VIDEO, "Could not queue pageflip: %d", ret);
}
/* Take note of current front buffer, so we can free it next time we come here. */
windata->bo = windata->next_bo;
return 0;
}
int
KMSDRM_GLES_SwapWindow(_THIS, SDL_Window * window)
{
SDL_WindowData *windata = ((SDL_WindowData *) window->driverdata);
if (windata->swap_window == NULL) {
/* We want the fenced version by default, but it needs extensions. */
if ( (SDL_GetHintBoolean(SDL_HINT_VIDEO_DOUBLE_BUFFER, SDL_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;
/* Wait immediately for vsync (as if we only had two buffers).
Even if we are already doing a WaitPageflip at the begining of this
function, this is NOT redundant because here we wait immediately
after submitting the image to the screen, reducing lag, and if
we have waited here, there won't be a pending pageflip so the
WaitPageflip at the beggining of this function will be a no-op.
Just leave it here and don't worry.
Run your SDL2 program with "SDL_KMSDRM_DOUBLE_BUFFER=1 <program_name>"
to enable this. */
if (windata->double_buffer) {
if (!KMSDRM_WaitPageflip(_this, windata)) {
SDL_LogError(SDL_LOG_CATEGORY_VIDEO, "Immediate wait for previous pageflip failed");
return 0;
}
}
return windata->swap_window(_this, window);
return 1;
}
/***************************************/
/* End of Atomic functions block */
/***************************************/
SDL_EGL_MakeCurrent_impl(KMSDRM)
#endif /* SDL_VIDEO_DRIVER_KMSDRM && SDL_VIDEO_OPENGL_EGL */
#endif /* SDL_VIDEO_DRIVER_KMSDRM */
/* vi: set ts=4 sw=4 expandtab: */
+3 -4
View File
@@ -1,7 +1,6 @@
/*
Simple DirectMedia Layer
Copyright (C) 1997-2020 Sam Lantinga <slouken@libsdl.org>
Atomic KMSDRM backend by Manuel Alfayate Corchete <redwindwanderer@gmail.com>
Copyright (C) 1997-2021 Sam Lantinga <slouken@libsdl.org>
This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any damages
@@ -24,7 +23,7 @@
#ifndef SDL_kmsdrmopengles_h_
#define SDL_kmsdrmopengles_h_
#if SDL_VIDEO_DRIVER_KMSDRM && SDL_VIDEO_OPENGL_EGL
#if SDL_VIDEO_DRIVER_KMSDRM
#include "../SDL_sysvideo.h"
#include "../SDL_egl_c.h"
@@ -42,7 +41,7 @@ extern SDL_GLContext KMSDRM_GLES_CreateContext(_THIS, SDL_Window * window);
extern int KMSDRM_GLES_SwapWindow(_THIS, SDL_Window * window);
extern int KMSDRM_GLES_MakeCurrent(_THIS, SDL_Window * window, SDL_GLContext context);
#endif /* SDL_VIDEO_DRIVER_KMSDRM && SDL_VIDEO_OPENGL_EGL */
#endif /* SDL_VIDEO_DRIVER_KMSDRM */
#endif /* SDL_kmsdrmopengles_h_ */
+32 -27
View File
@@ -1,6 +1,6 @@
/*
Simple DirectMedia Layer
Copyright (C) 1997-2020 Sam Lantinga <slouken@libsdl.org>
Copyright (C) 1997-2021 Sam Lantinga <slouken@libsdl.org>
This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any damages
@@ -41,32 +41,41 @@ SDL_KMSDRM_SYM(void,drmModeFreeCrtc,(drmModeCrtcPtr ptr))
SDL_KMSDRM_SYM(void,drmModeFreeConnector,(drmModeConnectorPtr ptr))
SDL_KMSDRM_SYM(void,drmModeFreeEncoder,(drmModeEncoderPtr ptr))
SDL_KMSDRM_SYM(int,drmGetCap,(int fd, uint64_t capability, uint64_t *value))
SDL_KMSDRM_SYM(int,drmIoctl,(int fd, unsigned long request, void *arg))
SDL_KMSDRM_SYM(int,drmSetMaster,(int fd))
SDL_KMSDRM_SYM(int,drmAuthMagic,(int fd, drm_magic_t magic))
SDL_KMSDRM_SYM(drmModeResPtr,drmModeGetResources,(int fd))
SDL_KMSDRM_SYM(int,drmModeAddFB,(int fd, uint32_t width, uint32_t height, uint8_t depth,
uint8_t bpp, uint32_t pitch, uint32_t bo_handle,
uint32_t *buf_id))
uint8_t bpp, uint32_t pitch, uint32_t bo_handle,
uint32_t *buf_id))
SDL_KMSDRM_SYM(int,drmModeAddFB2,(int fd, uint32_t width, uint32_t height,
uint32_t pixel_format, const uint32_t bo_handles[4],
const uint32_t pitches[4], const uint32_t offsets[4],
uint32_t *buf_id, uint32_t flags))
SDL_KMSDRM_SYM(int,drmModeAddFB2WithModifiers,(int fd, uint32_t width, uint32_t height,
uint32_t pixel_format, const uint32_t bo_handles[4],
const uint32_t pitches[4], const uint32_t offsets[4],
const uint64_t modifier[4], uint32_t *buf_id,
uint32_t flags))
uint32_t pixel_format, const uint32_t bo_handles[4],
const uint32_t pitches[4], const uint32_t offsets[4],
uint32_t *buf_id, uint32_t flags))
SDL_KMSDRM_SYM(int,drmModeRmFB,(int fd, uint32_t bufferId))
SDL_KMSDRM_SYM(drmModeFBPtr,drmModeGetFB,(int fd, uint32_t buf))
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,drmModeCrtcGetGamma,(int fd, uint32_t crtc_id, uint32_t size,
uint16_t *red, uint16_t *green, uint16_t *blue))
SDL_KMSDRM_SYM(int,drmModeCrtcSetGamma,(int fd, uint32_t crtc_id, uint32_t size,
uint16_t *red, uint16_t *green, uint16_t *blue))
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(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 */
/* Planes stuff. */
SDL_KMSDRM_SYM(int,drmSetClientCap,(int fd, uint64_t capability, uint64_t value))
SDL_KMSDRM_SYM(drmModePlaneResPtr,drmModeGetPlaneResources,(int fd))
SDL_KMSDRM_SYM(drmModePlanePtr,drmModeGetPlane,(int fd, uint32_t plane_id))
@@ -77,14 +86,13 @@ SDL_KMSDRM_SYM(void,drmModeFreeProperty,(drmModePropertyPtr ptr))
SDL_KMSDRM_SYM(void,drmModeFreeObjectProperties,(drmModeObjectPropertiesPtr ptr))
SDL_KMSDRM_SYM(void,drmModeFreePlane,(drmModePlanePtr ptr))
SDL_KMSDRM_SYM(void,drmModeFreePlaneResources,(drmModePlaneResPtr ptr))
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))
/* End of atomic fns */
SDL_KMSDRM_SYM(int,drmModeSetPlane,(int fd, uint32_t plane_id, uint32_t crtc_id,
uint32_t fb_id, uint32_t flags,
int32_t crtc_x, int32_t crtc_y,
uint32_t crtc_w, uint32_t crtc_h,
uint32_t src_x, uint32_t src_y,
uint32_t src_w, uint32_t src_h))
/* Planes stuff ends. */
SDL_KMSDRM_MODULE(GBM)
SDL_KMSDRM_SYM(int,gbm_device_get_fd,(struct gbm_device *gbm))
@@ -95,11 +103,8 @@ SDL_KMSDRM_SYM(struct gbm_device *,gbm_create_device,(int fd))
SDL_KMSDRM_SYM(unsigned int,gbm_bo_get_width,(struct gbm_bo *bo))
SDL_KMSDRM_SYM(unsigned int,gbm_bo_get_height,(struct gbm_bo *bo))
SDL_KMSDRM_SYM(uint32_t,gbm_bo_get_stride,(struct gbm_bo *bo))
SDL_KMSDRM_SYM(uint32_t,gbm_bo_get_stride_for_plane,(struct gbm_bo *bo,int plane))
SDL_KMSDRM_SYM(uint32_t,gbm_bo_get_format,(struct gbm_bo *bo))
SDL_KMSDRM_SYM(uint32_t,gbm_bo_get_offset,(struct gbm_bo *bo, int plane))
SDL_KMSDRM_SYM(int,gbm_bo_get_plane_count,(struct gbm_bo *bo))
SDL_KMSDRM_SYM(union gbm_bo_handle,gbm_bo_get_handle,(struct gbm_bo *bo))
SDL_KMSDRM_SYM(int,gbm_bo_write,(struct gbm_bo *bo, const void *buf, size_t count))
SDL_KMSDRM_SYM(struct gbm_device *,gbm_bo_get_device,(struct gbm_bo *bo))
File diff suppressed because it is too large Load Diff
+36 -104
View File
@@ -1,7 +1,6 @@
/*
Simple DirectMedia Layer
Copyright (C) 1997-2020 Sam Lantinga <slouken@libsdl.org>
Atomic KMSDRM backend by Manuel Alfayate Corchete <redwindwanderer@gmail.com>
Copyright (C) 1997-2021 Sam Lantinga <slouken@libsdl.org>
This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any damages
@@ -31,19 +30,8 @@
#include <unistd.h>
#include <xf86drm.h>
#include <xf86drmMode.h>
#include <gbm.h>
#include <assert.h>
#if SDL_VIDEO_OPENGL_EGL
#include <EGL/egl.h>
#include <EGL/eglext.h>
#endif
/****************************************************************************************/
/* Driverdata pointers are void struct* used to store backend-specific variables */
/* and info that supports the SDL-side structs like SDL Display Devices, SDL_Windows... */
/* which need to be "supported" with backend-side info and mechanisms to work. */
/****************************************************************************************/
typedef struct SDL_VideoData
{
@@ -53,60 +41,36 @@ typedef struct SDL_VideoData
struct gbm_device *gbm_dev;
SDL_Window **windows;
unsigned int max_windows;
unsigned int num_windows;
SDL_bool video_init; /* Has VideoInit succeeded? */
SDL_bool vulkan_mode; /* Are we in Vulkan mode? One VK window is enough to be. */
SDL_bool async_pageflip_support; /* Does the hardware support async. pageflips? */
SDL_bool video_init; /* Has VideoInit succeeded? */
SDL_Window **windows;
int max_windows;
int num_windows;
/* Even if we have several displays, we only have to
open 1 FD and create 1 gbm device. */
SDL_bool gbm_init;
} SDL_VideoData;
typedef struct plane {
drmModePlane *plane;
drmModeObjectProperties *props;
drmModePropertyRes **props_info;
} plane;
typedef struct crtc {
drmModeCrtc *crtc;
drmModeObjectProperties *props;
drmModePropertyRes **props_info;
} crtc;
typedef struct SDL_DisplayModeData
{
int mode_index;
} SDL_DisplayModeData;
typedef struct connector {
drmModeConnector *connector;
drmModeObjectProperties *props;
drmModePropertyRes **props_info;
} connector;
/* More general driverdata info that gives support and substance to the SDL_Display. */
typedef struct SDL_DisplayData
{
drmModeConnector *connector;
drmModeCrtc *crtc;
drmModeModeInfo mode;
drmModeModeInfo preferred_mode;
uint32_t atomic_flags;
drmModeModeInfo original_mode;
drmModeModeInfo next_mode; /* New mode to be set on the CRTC. */
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;
EGLSyncKHR gpu_fence;
#if SDL_VIDEO_OPENGL_EGL
EGLSurface old_egl_surface;
#endif
SDL_bool modeset_pending;
SDL_bool gbm_init;
drmModeCrtc *saved_crtc; /* CRTC to restore on quit */
/* DRM & GBM cursor stuff lives here, not in an SDL_Cursor's driverdata struct,
because setting/unsetting up these is done on window creation/destruction,
@@ -115,9 +79,11 @@ typedef struct SDL_DisplayData
struct gbm_bo *cursor_bo;
uint64_t cursor_w, cursor_h;
SDL_bool default_cursor_init;
SDL_bool modeset_pending;
} SDL_DisplayData;
/* Driverdata info that gives KMSDRM-side support and substance to the SDL_Window. */
typedef struct SDL_WindowData
{
SDL_VideoData *viddata;
@@ -129,65 +95,30 @@ typedef struct SDL_WindowData
struct gbm_bo *bo;
struct gbm_bo *next_bo;
#if SDL_VIDEO_OPENGL_EGL
SDL_bool waiting_for_flip;
SDL_bool double_buffer;
EGLSurface egl_surface;
#endif
/* For scaling and AR correction. */
int32_t src_w;
int32_t src_h;
int32_t output_w;
int32_t output_h;
int32_t output_x;
/* This dictates what approach we'll use for SwapBuffers. */
int (*swap_window)(_THIS, SDL_Window * window);
/* The size we chose for the GBM surface. REMEMBER that the CRTC must always have
a mode with the same size configured before trying to flip to a buffer of that
surface or drmModePageFlip() will return -28. */
uint32_t surface_w;
uint32_t surface_h;
} SDL_WindowData;
typedef struct SDL_DisplayModeData
{
int mode_index;
} SDL_DisplayModeData;
typedef struct KMSDRM_FBInfo
{
int drm_fd; /* DRM file desc */
uint32_t fb_id; /* DRM framebuffer ID */
} KMSDRM_FBInfo;
typedef struct KMSDRM_PlaneInfo
{
struct 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 */
int KMSDRM_CreateEGLSurface(_THIS, SDL_Window * window);
int KMSDRM_CreateSurfaces(_THIS, SDL_Window * window);
KMSDRM_FBInfo *KMSDRM_FBFromBO(_THIS, struct gbm_bo *bo);
/* Atomic functions that are used from SDL_kmsdrmopengles.c and SDL_kmsdrmmouse.c */
void drm_atomic_set_plane_props(struct KMSDRM_PlaneInfo *info);
void drm_atomic_waitpending(_THIS);
int drm_atomic_commit(_THIS, SDL_bool blocking);
int add_plane_property(drmModeAtomicReq *req, struct plane *plane,
const char *name, uint64_t value);
int add_crtc_property(drmModeAtomicReq *req, struct crtc *crtc,
const char *name, uint64_t value);
int add_connector_property(drmModeAtomicReq *req, struct connector *connector,
const char *name, uint64_t value);
int setup_plane(_THIS, struct plane **plane, uint32_t plane_type);
void free_plane(struct plane **plane);
KMSDRM_FBInfo *KMSDRM_FBFromBO2(_THIS, struct gbm_bo *bo, int w, int h);
SDL_bool KMSDRM_WaitPageflip(_THIS, SDL_WindowData *windata);
/****************************************************************************/
/* SDL_VideoDevice functions declaration */
@@ -205,13 +136,14 @@ void KMSDRM_SetWindowIcon(_THIS, SDL_Window * window, SDL_Surface * icon);
void KMSDRM_SetWindowPosition(_THIS, SDL_Window * window);
void KMSDRM_SetWindowSize(_THIS, SDL_Window * window);
void KMSDRM_SetWindowFullscreen(_THIS, SDL_Window * window, SDL_VideoDisplay * _display, SDL_bool fullscreen);
int KMSDRM_SetWindowGammaRamp(_THIS, SDL_Window * window, const Uint16 * ramp);
int KMSDRM_GetWindowGammaRamp(_THIS, SDL_Window * window, Uint16 * ramp);
void KMSDRM_ShowWindow(_THIS, SDL_Window * window);
void KMSDRM_HideWindow(_THIS, SDL_Window * window);
void KMSDRM_RaiseWindow(_THIS, SDL_Window * window);
void KMSDRM_MaximizeWindow(_THIS, SDL_Window * window);
void KMSDRM_MinimizeWindow(_THIS, SDL_Window * window);
void KMSDRM_RestoreWindow(_THIS, SDL_Window * window);
void KMSDRM_SetWindowGrab(_THIS, SDL_Window * window, SDL_bool grabbed);
void KMSDRM_DestroyWindow(_THIS, SDL_Window * window);
/* Window manager function */
+94 -45
View File
@@ -1,6 +1,6 @@
/*
Simple DirectMedia Layer
Copyright (C) 1997-2020 Sam Lantinga <slouken@libsdl.org>
Copyright (C) 1997-2021 Sam Lantinga <slouken@libsdl.org>
This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any damages
@@ -189,6 +189,7 @@ SDL_bool KMSDRM_Vulkan_CreateSurface(_THIS,
uint32_t plane_count;
VkPhysicalDevice *physical_devices = NULL;
VkPhysicalDeviceProperties *device_props = NULL;
VkDisplayPropertiesKHR *displays_props = NULL;
VkDisplayModePropertiesKHR *modes_props = NULL;
VkDisplayPlanePropertiesKHR *planes_props = NULL;
@@ -197,23 +198,19 @@ SDL_bool KMSDRM_Vulkan_CreateSurface(_THIS,
VkDisplaySurfaceCreateInfoKHR display_plane_surface_create_info;
VkExtent2D image_size;
VkDisplayModeKHR display_mode;
VkDisplayModeKHR display_mode = (VkDisplayModeKHR)0;
VkDisplayModePropertiesKHR display_mode_props = {0};
VkDisplayModeParametersKHR new_mode_parameters = { {0, 0}, 0};
VkResult result;
SDL_bool ret = SDL_FALSE;
SDL_bool valid_gpu = SDL_FALSE;
SDL_bool mode_found = SDL_FALSE;
/* We don't receive a display index in KMSDRM_CreateDevice(), only
a device index, which determines the GPU to use, but not the output.
So we simply use the first connected output (ie, the first connected
video output) for now.
In other words, change this index to select a different output. Easy! */
int display_index = 0;
/* Get the display index from the display being used by the window. */
int display_index = SDL_atoi(SDL_GetDisplayForWindow(window)->name);
int i;
SDL_VideoData *viddata = ((SDL_VideoData *)_this->driverdata);
/* Get the function pointers for the functions we will use. */
PFN_vkGetInstanceProcAddr vkGetInstanceProcAddr =
(PFN_vkGetInstanceProcAddr)_this->vulkan_config.vkGetInstanceProcAddr;
@@ -226,6 +223,10 @@ SDL_bool KMSDRM_Vulkan_CreateSurface(_THIS,
(PFN_vkEnumeratePhysicalDevices)vkGetInstanceProcAddr(
instance, "vkEnumeratePhysicalDevices");
PFN_vkGetPhysicalDeviceProperties vkGetPhysicalDeviceProperties =
(PFN_vkGetPhysicalDeviceProperties)vkGetInstanceProcAddr(
instance, "vkGetPhysicalDeviceProperties");
PFN_vkGetPhysicalDeviceDisplayPropertiesKHR vkGetPhysicalDeviceDisplayPropertiesKHR =
(PFN_vkGetPhysicalDeviceDisplayPropertiesKHR)vkGetInstanceProcAddr(
instance, "vkGetPhysicalDeviceDisplayPropertiesKHR");
@@ -273,6 +274,14 @@ SDL_bool KMSDRM_Vulkan_CreateSurface(_THIS,
goto clean;
}
/* A GPU (or physical_device, in vkcube terms) is a physical GPU.
A machine with more than one video output doesn't need to have more than one GPU,
like the Pi4 which has 1 GPU and 2 video outputs.
Just in case, we test that the GPU we choose is Vulkan-capable.
If there are new reports about VK init failures, hardcode
gpu = physical_devices[0], instead of probing, and go with that.
*/
/* Get the physical device count. */
vkEnumeratePhysicalDevices(instance, &gpu_count, NULL);
@@ -282,14 +291,33 @@ SDL_bool KMSDRM_Vulkan_CreateSurface(_THIS,
}
/* Get the physical devices. */
physical_devices = malloc(sizeof(VkPhysicalDevice) * gpu_count);
physical_devices = SDL_malloc(sizeof(VkPhysicalDevice) * gpu_count);
device_props = SDL_malloc(sizeof(VkPhysicalDeviceProperties));
vkEnumeratePhysicalDevices(instance, &gpu_count, physical_devices);
/* A GPU (or physical_device, in vkcube terms) is a GPU. A machine with more
than one video output doen't need to have more than one GPU, like the Pi4
which has 1 GPU and 2 video outputs.
We grab the GPU/physical_device with the index we got in KMSDR_CreateDevice(). */
gpu = physical_devices[viddata->devindex];
/* Iterate on the physical devices. */
for (i = 0; i < gpu_count; i++) {
/* Get the physical device properties. */
vkGetPhysicalDeviceProperties(
physical_devices[i],
device_props
);
/* Is this device a real GPU that supports API version 1 at least? */
if (device_props->apiVersion >= 1 &&
(device_props->deviceType == 1 || device_props->deviceType == 2))
{
gpu = physical_devices[i];
valid_gpu = SDL_TRUE;
break;
}
}
if (!valid_gpu) {
SDL_SetError("Vulkan can't find a valid physical device (gpu).");
goto clean;
}
/* A display is a video output. 1 GPU can have N displays.
Vulkan only counts the connected displays.
@@ -301,7 +329,7 @@ SDL_bool KMSDRM_Vulkan_CreateSurface(_THIS,
}
/* Get the props of the displays of the physical device. */
displays_props = (VkDisplayPropertiesKHR *) malloc(display_count * sizeof(*displays_props));
displays_props = (VkDisplayPropertiesKHR *) SDL_malloc(display_count * sizeof(*displays_props));
vkGetPhysicalDeviceDisplayPropertiesKHR(gpu,
&display_count,
displays_props);
@@ -317,8 +345,8 @@ SDL_bool KMSDRM_Vulkan_CreateSurface(_THIS,
goto clean;
}
/* Get the props of the videomodes for the first display. */
modes_props = (VkDisplayModePropertiesKHR *) malloc(mode_count * sizeof(*modes_props));
/* Get the props of the videomodes for the display. */
modes_props = (VkDisplayModePropertiesKHR *) SDL_malloc(mode_count * sizeof(*modes_props));
vkGetDisplayModePropertiesKHR(gpu,
displays_props[display_index].display,
&mode_count, modes_props);
@@ -331,43 +359,62 @@ SDL_bool KMSDRM_Vulkan_CreateSurface(_THIS,
}
/* Get the props of the planes for the physical device. */
planes_props = malloc(sizeof(VkDisplayPlanePropertiesKHR) * plane_count);
planes_props = SDL_malloc(sizeof(VkDisplayPlanePropertiesKHR) * plane_count);
vkGetPhysicalDeviceDisplayPlanePropertiesKHR(gpu, &plane_count, planes_props);
/* Get a video mode equal or smaller than the window size. REMEMBER:
We have to get a small enough videomode for the window size,
/* Get a video mode equal to the window size among the predefined ones,
if possible.
REMEMBER: We have to get a small enough videomode for the window size,
because videomode determines how big the scanout region is and we can't
scanout a region bigger than the window (we would be reading past the
buffer, and Vulkan would give us a confusing VK_ERROR_SURFACE_LOST_KHR). */
for (i = 0; i < mode_count; i++) {
if (modes_props[i].parameters.visibleRegion.width <= window->w &&
modes_props[i].parameters.visibleRegion.height <= window->h)
if (modes_props[i].parameters.visibleRegion.width == window->w &&
modes_props[i].parameters.visibleRegion.height == window->h)
{
display_mode_props = modes_props[i];
mode_found = SDL_TRUE;
break;
}
}
if (display_mode_props.parameters.visibleRegion.width == 0
|| display_mode_props.parameters.visibleRegion.height == 0)
{
SDL_SetError("Vulkan can't find a proper display mode for the window size.");
goto clean;
if (mode_found &&
display_mode_props.parameters.visibleRegion.width > 0 &&
display_mode_props.parameters.visibleRegion.height > 0 ) {
/* Found a suitable mode among the predefined ones. Use that. */
display_mode = display_mode_props.displayMode;
} else {
/* Couldn't find a suitable mode among the predefined ones, so try to create our own.
This won't work for some video chips atm (like Pi's VideoCore) so these are limited
to supported resolutions. Don't try to use "closest" resolutions either, because
those are often bigger than the window size, thus causing out-of-bunds scanout. */
new_mode_parameters.visibleRegion.width = window->w;
new_mode_parameters.visibleRegion.height = window->h;
/* SDL (and DRM, if we look at drmModeModeInfo vrefresh) uses plain integer Hz for
display mode refresh rate, but Vulkan expects higher precision. */
new_mode_parameters.refreshRate = window->fullscreen_mode.refresh_rate * 1000;
display_mode_create_info.sType = VK_STRUCTURE_TYPE_DISPLAY_MODE_CREATE_INFO_KHR;
display_mode_create_info.parameters = new_mode_parameters;
result = vkCreateDisplayModeKHR(gpu,
displays_props[display_index].display,
&display_mode_create_info,
NULL, &display_mode);
if (result != VK_SUCCESS) {
SDL_SetError("Vulkan couldn't find a predefined mode for that window size and couldn't create a suitable mode.");
goto clean;
}
}
/* We have the props of the display mode, but we need an actual display mode. */
display_mode_create_info.sType = VK_STRUCTURE_TYPE_DISPLAY_MODE_CREATE_INFO_KHR;
display_mode_create_info.parameters = display_mode_props.parameters;
result = vkCreateDisplayModeKHR(gpu,
displays_props[display_index].display,
&display_mode_create_info,
NULL, &display_mode);
if (result != VK_SUCCESS) {
SDL_SetError("Vulkan can't create the display mode.");
goto clean;
/* Just in case we get here without a display_mode. */
if (!display_mode) {
SDL_SetError("Vulkan couldn't get a display mode.");
goto clean;
}
/********************************************/
/* Let's finally create the Vulkan surface! */
/********************************************/
image_size.width = window->w;
image_size.height = window->h;
@@ -383,7 +430,7 @@ SDL_bool KMSDRM_Vulkan_CreateSurface(_THIS,
surface);
if(result != VK_SUCCESS)
{
SDL_SetError("vkCreateKMSDRMSurfaceKHR failed: %s",
SDL_SetError("vkCreateDisplayPlaneSurfaceKHR failed: %s",
SDL_Vulkan_GetResultString(result));
goto clean;
}
@@ -392,13 +439,15 @@ SDL_bool KMSDRM_Vulkan_CreateSurface(_THIS,
clean:
if (physical_devices)
free (physical_devices);
SDL_free (physical_devices);
if (displays_props)
free (displays_props);
SDL_free (displays_props);
if (device_props)
SDL_free (device_props);
if (planes_props)
free (planes_props);
SDL_free (planes_props);
if (modes_props)
free (modes_props);
SDL_free (modes_props);
return ret;
}
+4 -4
View File
@@ -1,6 +1,6 @@
/*
Simple DirectMedia Layer
Copyright (C) 1997-2020 Sam Lantinga <slouken@libsdl.org>
Copyright (C) 1997-2021 Sam Lantinga <slouken@libsdl.org>
This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any damages
@@ -26,8 +26,8 @@
#include "../../SDL_internal.h"
#ifndef SDL_kmsdrmvulkan_h_
#define SDL_kmsdrmvulkan_h_
#ifndef SDL_kmsdrm_vulkan_h_
#define SDL_kmsdrm_vulkan_h_
#include "../SDL_vulkan_internal.h"
#include "../SDL_sysvideo.h"
@@ -48,6 +48,6 @@ SDL_bool KMSDRM_Vulkan_CreateSurface(_THIS,
#endif
#endif /* SDL_kmsdrmvulkan_h_ */
#endif /* SDL_kmsdrm_vulkan_h_ */
/* vi: set ts=4 sw=4 expandtab: */