early-access version 1617
This commit is contained in:
+1
-3
@@ -1,6 +1,7 @@
|
||||
/*
|
||||
Simple DirectMedia Layer
|
||||
Copyright (C) 1997-2020 Sam Lantinga <slouken@libsdl.org>
|
||||
Atomic KMSDRM backend by Manuel Alfayate Corchete <redwindwanderer@gmail.com>
|
||||
|
||||
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,9 +28,6 @@
|
||||
|
||||
#include "SDL_kmsdrmdyn.h"
|
||||
|
||||
#if DEBUG_DYNAMIC_KMSDRM
|
||||
#include "SDL_log.h"
|
||||
#endif
|
||||
|
||||
#ifdef SDL_VIDEO_DRIVER_KMSDRM_DYNAMIC
|
||||
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
/*
|
||||
Simple DirectMedia Layer
|
||||
Copyright (C) 1997-2020 Sam Lantinga <slouken@libsdl.org>
|
||||
Atomic KMSDRM backend by Manuel Alfayate Corchete <redwindwanderer@gmail.com>
|
||||
|
||||
This software is provided 'as-is', without any express or implied
|
||||
warranty. In no event will the authors be held liable for any damages
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
/*
|
||||
Simple DirectMedia Layer
|
||||
Copyright (C) 1997-2020 Sam Lantinga <slouken@libsdl.org>
|
||||
Atomic KMSDRM backend by Manuel Alfayate Corchete <redwindwanderer@gmail.com>
|
||||
|
||||
This software is provided 'as-is', without any express or implied
|
||||
warranty. In no event will the authors be held liable for any damages
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
/*
|
||||
Simple DirectMedia Layer
|
||||
Copyright (C) 1997-2020 Sam Lantinga <slouken@libsdl.org>
|
||||
Atomic KMSDRM backend by Manuel Alfayate Corchete <redwindwanderer@gmail.com>
|
||||
|
||||
This software is provided 'as-is', without any express or implied
|
||||
warranty. In no event will the authors be held liable for any damages
|
||||
|
||||
+330
-292
@@ -1,6 +1,7 @@
|
||||
/*
|
||||
Simple DirectMedia Layer
|
||||
Copyright (C) 1997-2020 Sam Lantinga <slouken@libsdl.org>
|
||||
Atomic KMSDRM backend by Manuel Alfayate Corchete <redwindwanderer@gmail.com>
|
||||
|
||||
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,6 +27,7 @@
|
||||
#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"
|
||||
@@ -38,276 +40,194 @@ 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. */
|
||||
/**********************************/
|
||||
|
||||
int
|
||||
drm_atomic_movecursor(KMSDRM_CursorData *curdata, uint16_t x, uint16_t y)
|
||||
{
|
||||
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;
|
||||
}
|
||||
|
||||
/***************************************/
|
||||
/* 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) {
|
||||
|
||||
uint32_t A, R, G, B;
|
||||
|
||||
/* Component bytes extraction. */
|
||||
A = (*pixel >> (3 << 3)) & 0xFF;
|
||||
R = (*pixel >> (2 << 3)) & 0xFF;
|
||||
G = (*pixel >> (1 << 3)) & 0xFF;
|
||||
B = (*pixel >> (0 << 3)) & 0xFF;
|
||||
|
||||
/* Alpha pre-multiplication of each component. */
|
||||
R = (float)A * ((float)R /255);
|
||||
G = (float)A * ((float)G /255);
|
||||
B = (float)A * ((float)B /255);
|
||||
|
||||
/* ARGB8888 pixel recomposition. */
|
||||
(*pixel) = (((uint32_t)A << 24) | ((uint32_t)R << 16) | ((uint32_t)G << 8)) | ((uint32_t)B << 0);
|
||||
}
|
||||
|
||||
static SDL_Cursor *
|
||||
KMSDRM_CreateDefaultCursor(void)
|
||||
{
|
||||
return SDL_CreateCursor(default_cdata, default_cmask, DEFAULT_CWIDTH, DEFAULT_CHEIGHT, DEFAULT_CHOTX, DEFAULT_CHOTY);
|
||||
}
|
||||
|
||||
/* Evaluate if a given cursor size is supported or not. Notably, current Intel gfx only support 64x64 and up. */
|
||||
static SDL_bool
|
||||
KMSDRM_IsCursorSizeSupported (int w, int h, uint32_t bo_format) {
|
||||
|
||||
SDL_VideoDevice *dev = SDL_GetVideoDevice();
|
||||
SDL_VideoData *viddata = ((SDL_VideoData *)dev->driverdata);
|
||||
SDL_DisplayData *dispdata = (SDL_DisplayData *)SDL_GetDisplayDriverData(0);
|
||||
|
||||
int ret;
|
||||
uint32_t bo_handle;
|
||||
struct gbm_bo *bo = KMSDRM_gbm_bo_create(viddata->gbm, w, h, bo_format,
|
||||
GBM_BO_USE_CURSOR | GBM_BO_USE_WRITE);
|
||||
|
||||
if (!bo) {
|
||||
SDL_SetError("Could not create GBM cursor BO width size %dx%d for size testing", w, h);
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
bo_handle = KMSDRM_gbm_bo_get_handle(bo).u32;
|
||||
ret = KMSDRM_drmModeSetCursor(viddata->drm_fd, dispdata->crtc_id, bo_handle, w, h);
|
||||
|
||||
if (ret) {
|
||||
goto cleanup;
|
||||
}
|
||||
else {
|
||||
KMSDRM_gbm_bo_destroy(bo);
|
||||
return SDL_TRUE;
|
||||
}
|
||||
|
||||
cleanup:
|
||||
if (bo) {
|
||||
KMSDRM_gbm_bo_destroy(bo);
|
||||
}
|
||||
return SDL_FALSE;
|
||||
}
|
||||
|
||||
/* Create a cursor from a surface */
|
||||
/* 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)
|
||||
{
|
||||
SDL_VideoDevice *dev = SDL_GetVideoDevice();
|
||||
SDL_VideoData *viddata = ((SDL_VideoData *)dev->driverdata);
|
||||
SDL_PixelFormat *pixlfmt = surface->format;
|
||||
KMSDRM_CursorData *curdata;
|
||||
SDL_Cursor *cursor;
|
||||
SDL_bool cursor_supported = SDL_FALSE;
|
||||
int i, ret, usable_cursor_w, usable_cursor_h;
|
||||
uint32_t bo_format, bo_stride;
|
||||
char *buffer = NULL;
|
||||
size_t bufsize;
|
||||
SDL_Cursor *cursor, *ret;
|
||||
|
||||
switch(pixlfmt->format) {
|
||||
case SDL_PIXELFORMAT_RGB332:
|
||||
bo_format = GBM_FORMAT_RGB332;
|
||||
break;
|
||||
case SDL_PIXELFORMAT_ARGB4444:
|
||||
bo_format = GBM_FORMAT_ARGB4444;
|
||||
break;
|
||||
case SDL_PIXELFORMAT_RGBA4444:
|
||||
bo_format = GBM_FORMAT_RGBA4444;
|
||||
break;
|
||||
case SDL_PIXELFORMAT_ABGR4444:
|
||||
bo_format = GBM_FORMAT_ABGR4444;
|
||||
break;
|
||||
case SDL_PIXELFORMAT_BGRA4444:
|
||||
bo_format = GBM_FORMAT_BGRA4444;
|
||||
break;
|
||||
case SDL_PIXELFORMAT_ARGB1555:
|
||||
bo_format = GBM_FORMAT_ARGB1555;
|
||||
break;
|
||||
case SDL_PIXELFORMAT_RGBA5551:
|
||||
bo_format = GBM_FORMAT_RGBA5551;
|
||||
break;
|
||||
case SDL_PIXELFORMAT_ABGR1555:
|
||||
bo_format = GBM_FORMAT_ABGR1555;
|
||||
break;
|
||||
case SDL_PIXELFORMAT_BGRA5551:
|
||||
bo_format = GBM_FORMAT_BGRA5551;
|
||||
break;
|
||||
case SDL_PIXELFORMAT_RGB565:
|
||||
bo_format = GBM_FORMAT_RGB565;
|
||||
break;
|
||||
case SDL_PIXELFORMAT_BGR565:
|
||||
bo_format = GBM_FORMAT_BGR565;
|
||||
break;
|
||||
case SDL_PIXELFORMAT_RGB888:
|
||||
case SDL_PIXELFORMAT_RGB24:
|
||||
bo_format = GBM_FORMAT_RGB888;
|
||||
break;
|
||||
case SDL_PIXELFORMAT_BGR888:
|
||||
case SDL_PIXELFORMAT_BGR24:
|
||||
bo_format = GBM_FORMAT_BGR888;
|
||||
break;
|
||||
case SDL_PIXELFORMAT_RGBX8888:
|
||||
bo_format = GBM_FORMAT_RGBX8888;
|
||||
break;
|
||||
case SDL_PIXELFORMAT_BGRX8888:
|
||||
bo_format = GBM_FORMAT_BGRX8888;
|
||||
break;
|
||||
case SDL_PIXELFORMAT_ARGB8888:
|
||||
bo_format = GBM_FORMAT_ARGB8888;
|
||||
break;
|
||||
case SDL_PIXELFORMAT_RGBA8888:
|
||||
bo_format = GBM_FORMAT_RGBA8888;
|
||||
break;
|
||||
case SDL_PIXELFORMAT_ABGR8888:
|
||||
bo_format = GBM_FORMAT_ABGR8888;
|
||||
break;
|
||||
case SDL_PIXELFORMAT_BGRA8888:
|
||||
bo_format = GBM_FORMAT_BGRA8888;
|
||||
break;
|
||||
case SDL_PIXELFORMAT_ARGB2101010:
|
||||
bo_format = GBM_FORMAT_ARGB2101010;
|
||||
break;
|
||||
default:
|
||||
SDL_SetError("Unsupported pixel format for cursor");
|
||||
return NULL;
|
||||
}
|
||||
curdata = NULL;
|
||||
ret = NULL;
|
||||
|
||||
if (!KMSDRM_gbm_device_is_format_supported(viddata->gbm, bo_format, GBM_BO_USE_CURSOR | GBM_BO_USE_WRITE)) {
|
||||
SDL_SetError("Unsupported pixel format for cursor");
|
||||
return NULL;
|
||||
}
|
||||
/* All code below assumes ARGB8888 format for the cursor surface,
|
||||
like other backends do. Also, the GBM BO pixels have to be
|
||||
alpha-premultiplied, but the SDL surface we receive has
|
||||
straight-alpha pixels, so we always have to convert. */
|
||||
SDL_assert(surface->format->format == SDL_PIXELFORMAT_ARGB8888);
|
||||
SDL_assert(surface->pitch == surface->w * 4);
|
||||
|
||||
cursor = (SDL_Cursor *) SDL_calloc(1, sizeof(*cursor));
|
||||
if (!cursor) {
|
||||
SDL_OutOfMemory();
|
||||
return NULL;
|
||||
goto cleanup;
|
||||
}
|
||||
curdata = (KMSDRM_CursorData *) SDL_calloc(1, sizeof(*curdata));
|
||||
if (!curdata) {
|
||||
SDL_OutOfMemory();
|
||||
SDL_free(cursor);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* We have to know beforehand if a cursor with the same size as the surface is supported.
|
||||
* If it's not, we have to find an usable cursor size and use an intermediate and clean buffer.
|
||||
* If we can't find a cursor size supported by the hardware, we won't go on trying to
|
||||
* call SDL_SetCursor() later. */
|
||||
|
||||
usable_cursor_w = surface->w;
|
||||
usable_cursor_h = surface->h;
|
||||
|
||||
while (usable_cursor_w <= MAX_CURSOR_W && usable_cursor_h <= MAX_CURSOR_H) {
|
||||
if (KMSDRM_IsCursorSizeSupported(usable_cursor_w, usable_cursor_h, bo_format)) {
|
||||
cursor_supported = SDL_TRUE;
|
||||
break;
|
||||
}
|
||||
usable_cursor_w += usable_cursor_w;
|
||||
usable_cursor_h += usable_cursor_h;
|
||||
}
|
||||
|
||||
if (!cursor_supported) {
|
||||
SDL_SetError("Could not find a cursor size supported by the kernel driver");
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
/* hox_x and hot_y are the coordinates of the "tip of the cursor" from it's base. */
|
||||
curdata->hot_x = hot_x;
|
||||
curdata->hot_y = hot_y;
|
||||
curdata->w = usable_cursor_w;
|
||||
curdata->h = usable_cursor_h;
|
||||
curdata->w = surface->w;
|
||||
curdata->h = surface->h;
|
||||
curdata->buffer = NULL;
|
||||
|
||||
curdata->bo = KMSDRM_gbm_bo_create(viddata->gbm, usable_cursor_w, usable_cursor_h, bo_format,
|
||||
GBM_BO_USE_CURSOR | GBM_BO_USE_WRITE);
|
||||
/* Configure the cursor buffer info.
|
||||
This buffer has the original size of the cursor surface we are given. */
|
||||
curdata->buffer_pitch = surface->pitch;
|
||||
curdata->buffer_size = surface->pitch * surface->h;
|
||||
curdata->buffer = (uint32_t*)SDL_malloc(curdata->buffer_size);
|
||||
|
||||
if (!curdata->bo) {
|
||||
SDL_SetError("Could not create GBM cursor BO");
|
||||
if (!curdata->buffer) {
|
||||
SDL_OutOfMemory();
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
bo_stride = KMSDRM_gbm_bo_get_stride(curdata->bo);
|
||||
bufsize = bo_stride * curdata->h;
|
||||
|
||||
if (surface->pitch != bo_stride) {
|
||||
/* pitch doesn't match stride, must be copied to temp buffer */
|
||||
buffer = SDL_malloc(bufsize);
|
||||
if (!buffer) {
|
||||
SDL_OutOfMemory();
|
||||
if (SDL_MUSTLOCK(surface)) {
|
||||
if (SDL_LockSurface(surface) < 0) {
|
||||
/* Could not lock surface */
|
||||
goto cleanup;
|
||||
}
|
||||
}
|
||||
|
||||
if (SDL_MUSTLOCK(surface)) {
|
||||
if (SDL_LockSurface(surface) < 0) {
|
||||
/* Could not lock surface */
|
||||
goto cleanup;
|
||||
}
|
||||
}
|
||||
/* Copy the surface pixels to the cursor buffer, for future use in ShowCursor() */
|
||||
SDL_memcpy(curdata->buffer, surface->pixels, curdata->buffer_size);
|
||||
|
||||
/* Clean the whole temporary buffer */
|
||||
SDL_memset(buffer, 0x00, bo_stride * curdata->h);
|
||||
|
||||
/* Copy to temporary buffer */
|
||||
for (i = 0; i < surface->h; i++) {
|
||||
SDL_memcpy(buffer + (i * bo_stride),
|
||||
((char *)surface->pixels) + (i * surface->pitch),
|
||||
surface->w * pixlfmt->BytesPerPixel);
|
||||
}
|
||||
|
||||
if (SDL_MUSTLOCK(surface)) {
|
||||
SDL_UnlockSurface(surface);
|
||||
}
|
||||
|
||||
if (KMSDRM_gbm_bo_write(curdata->bo, buffer, bufsize)) {
|
||||
SDL_SetError("Could not write to GBM cursor BO");
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
/* Free temporary buffer */
|
||||
SDL_free(buffer);
|
||||
buffer = NULL;
|
||||
} else {
|
||||
/* surface matches BO format */
|
||||
if (SDL_MUSTLOCK(surface)) {
|
||||
if (SDL_LockSurface(surface) < 0) {
|
||||
/* Could not lock surface */
|
||||
goto cleanup;
|
||||
}
|
||||
}
|
||||
|
||||
ret = KMSDRM_gbm_bo_write(curdata->bo, surface->pixels, bufsize);
|
||||
|
||||
if (SDL_MUSTLOCK(surface)) {
|
||||
SDL_UnlockSurface(surface);
|
||||
}
|
||||
|
||||
if (ret) {
|
||||
SDL_SetError("Could not write to GBM cursor BO");
|
||||
goto cleanup;
|
||||
}
|
||||
if (SDL_MUSTLOCK(surface)) {
|
||||
SDL_UnlockSurface(surface);
|
||||
}
|
||||
|
||||
cursor->driverdata = curdata;
|
||||
|
||||
return cursor;
|
||||
ret = cursor;
|
||||
|
||||
cleanup:
|
||||
if (buffer) {
|
||||
SDL_free(buffer);
|
||||
if (ret == NULL) {
|
||||
if (curdata) {
|
||||
if (curdata->buffer) {
|
||||
SDL_free(curdata->buffer);
|
||||
}
|
||||
SDL_free(curdata);
|
||||
}
|
||||
if (cursor) {
|
||||
SDL_free(cursor);
|
||||
}
|
||||
}
|
||||
if (cursor) {
|
||||
SDL_free(cursor);
|
||||
}
|
||||
if (curdata) {
|
||||
if (curdata->bo) {
|
||||
KMSDRM_gbm_bo_destroy(curdata->bo);
|
||||
}
|
||||
SDL_free(curdata);
|
||||
}
|
||||
return NULL;
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* Show the specified cursor, or hide if cursor is NULL */
|
||||
/* 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
|
||||
*/
|
||||
static int
|
||||
KMSDRM_ShowCursor(SDL_Cursor * cursor)
|
||||
{
|
||||
SDL_VideoDevice *dev = SDL_GetVideoDevice();
|
||||
SDL_VideoData *viddata = ((SDL_VideoData *)dev->driverdata);
|
||||
SDL_VideoDevice *video_device = SDL_GetVideoDevice();
|
||||
//SDL_VideoData *viddata = ((SDL_VideoData *)dev->driverdata);
|
||||
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 i,j;
|
||||
int ret;
|
||||
uint32_t bo_handle;
|
||||
|
||||
mouse = SDL_GetMouse();
|
||||
if (!mouse) {
|
||||
@@ -321,87 +241,121 @@ KMSDRM_ShowCursor(SDL_Cursor * cursor)
|
||||
}
|
||||
}
|
||||
|
||||
/**********************************/
|
||||
/* if cursor == NULL, HIDE cursor */
|
||||
/**********************************/
|
||||
if (!cursor) {
|
||||
/* Hide current 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) {
|
||||
curdata = (KMSDRM_CursorData *) mouse->cur_cursor->driverdata;
|
||||
|
||||
if (curdata->crtc_id != 0) {
|
||||
ret = KMSDRM_drmModeSetCursor(viddata->drm_fd, curdata->crtc_id, 0, 0, 0);
|
||||
if (ret) {
|
||||
SDL_SetError("Could not hide current cursor with drmModeSetCursor().");
|
||||
return ret;
|
||||
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.");
|
||||
}
|
||||
/* Mark previous cursor as not-displayed */
|
||||
curdata->crtc_id = 0;
|
||||
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
/* otherwise if possible, hide global cursor */
|
||||
if (dispdata && dispdata->crtc_id != 0) {
|
||||
ret = KMSDRM_drmModeSetCursor(viddata->drm_fd, dispdata->crtc_id, 0, 0, 0);
|
||||
if (ret) {
|
||||
SDL_SetError("Could not hide display's cursor with drmModeSetCursor().");
|
||||
return ret;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
return SDL_SetError("Couldn't find cursor to hide.");
|
||||
}
|
||||
/* If cursor != NULL, show new cursor on 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 || !curdata->bo) {
|
||||
|
||||
if (!curdata || !dispdata->cursor_bo) {
|
||||
return SDL_SetError("Cursor not initialized properly.");
|
||||
}
|
||||
|
||||
bo_handle = KMSDRM_gbm_bo_get_handle(curdata->bo).u32;
|
||||
if (curdata->hot_x == 0 && curdata->hot_y == 0) {
|
||||
ret = KMSDRM_drmModeSetCursor(viddata->drm_fd, dispdata->crtc_id, bo_handle,
|
||||
curdata->w, curdata->h);
|
||||
} else {
|
||||
ret = KMSDRM_drmModeSetCursor2(viddata->drm_fd, dispdata->crtc_id, bo_handle,
|
||||
curdata->w, curdata->h, curdata->hot_x, curdata->hot_y);
|
||||
}
|
||||
if (ret) {
|
||||
SDL_SetError("drmModeSetCursor failed.");
|
||||
return ret;
|
||||
/* 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;
|
||||
}
|
||||
|
||||
curdata->crtc_id = dispdata->crtc_id;
|
||||
/* Clean the whole buffer we are preparing. */
|
||||
SDL_memset(ready_buffer, 0x00, bo_stride * curdata->h);
|
||||
|
||||
return 0;
|
||||
/* 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;
|
||||
}
|
||||
|
||||
/* Free a window manager cursor */
|
||||
/* 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;
|
||||
int drm_fd;
|
||||
|
||||
/* Even if the cursor is not ours, free it. */
|
||||
if (cursor) {
|
||||
curdata = (KMSDRM_CursorData *) cursor->driverdata;
|
||||
|
||||
if (curdata) {
|
||||
if (curdata->bo) {
|
||||
if (curdata->crtc_id != 0) {
|
||||
drm_fd = KMSDRM_gbm_device_get_fd(KMSDRM_gbm_bo_get_device(curdata->bo));
|
||||
/* Hide the cursor if previously shown on a CRTC */
|
||||
KMSDRM_drmModeSetCursor(drm_fd, curdata->crtc_id, 0, 0, 0);
|
||||
curdata->crtc_id = 0;
|
||||
}
|
||||
KMSDRM_gbm_bo_destroy(curdata->bo);
|
||||
curdata->bo = NULL;
|
||||
}
|
||||
/* 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);
|
||||
@@ -422,6 +376,7 @@ 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) {
|
||||
/* Update internal mouse position. */
|
||||
@@ -429,20 +384,9 @@ KMSDRM_WarpMouseGlobal(int x, int y)
|
||||
|
||||
/* And now update the cursor graphic position on screen. */
|
||||
curdata = (KMSDRM_CursorData *) mouse->cur_cursor->driverdata;
|
||||
if (curdata->bo) {
|
||||
|
||||
if (curdata->crtc_id != 0) {
|
||||
int ret, drm_fd;
|
||||
drm_fd = KMSDRM_gbm_device_get_fd(KMSDRM_gbm_bo_get_device(curdata->bo));
|
||||
ret = KMSDRM_drmModeMoveCursor(drm_fd, curdata->crtc_id, x, y);
|
||||
|
||||
if (ret) {
|
||||
SDL_SetError("drmModeMoveCursor() failed.");
|
||||
}
|
||||
|
||||
return ret;
|
||||
} else {
|
||||
return SDL_SetError("Cursor is not currently shown.");
|
||||
if (dispdata->cursor_bo) {
|
||||
if (drm_atomic_movecursor(curdata, x, y)) {
|
||||
return SDL_SetError("drm_atomic_movecursor() failed.");
|
||||
}
|
||||
} else {
|
||||
return SDL_SetError("Cursor not initialized properly.");
|
||||
@@ -450,6 +394,8 @@ KMSDRM_WarpMouseGlobal(int x, int y)
|
||||
} else {
|
||||
return SDL_SetError("No mouse or current cursor.");
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void
|
||||
@@ -458,7 +404,12 @@ KMSDRM_InitMouse(_THIS)
|
||||
/* 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;
|
||||
|
||||
mouse->CreateCursor = KMSDRM_CreateCursor;
|
||||
mouse->ShowCursor = KMSDRM_ShowCursor;
|
||||
@@ -467,13 +418,96 @@ 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;
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
KMSDRM_QuitMouse(_THIS)
|
||||
KMSDRM_DeinitMouse(_THIS)
|
||||
{
|
||||
/* TODO: ? */
|
||||
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);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/* This is called when a mouse motion event occurs */
|
||||
@@ -482,17 +516,21 @@ KMSDRM_MoveCursor(SDL_Cursor * cursor)
|
||||
{
|
||||
SDL_Mouse *mouse = SDL_GetMouse();
|
||||
KMSDRM_CursorData *curdata;
|
||||
int drm_fd, ret;
|
||||
|
||||
/* 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;
|
||||
drm_fd = KMSDRM_gbm_device_get_fd(KMSDRM_gbm_bo_get_device(curdata->bo));
|
||||
ret = KMSDRM_drmModeMoveCursor(drm_fd, curdata->crtc_id, mouse->x, mouse->y);
|
||||
|
||||
if (ret) {
|
||||
SDL_SetError("drmModeMoveCursor() failed.");
|
||||
/* 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. */
|
||||
|
||||
if (drm_atomic_movecursor(curdata, mouse->x, mouse->y)) {
|
||||
SDL_SetError("drm_atomic_movecursor() failed.");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+14
-5
@@ -1,6 +1,7 @@
|
||||
/*
|
||||
Simple DirectMedia Layer
|
||||
Copyright (C) 1997-2020 Sam Lantinga <slouken@libsdl.org>
|
||||
Atomic KMSDRM backend by Manuel Alfayate Corchete <redwindwanderer@gmail.com>
|
||||
|
||||
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,16 +30,24 @@
|
||||
#define MAX_CURSOR_W 512
|
||||
#define MAX_CURSOR_H 512
|
||||
|
||||
/* Driverdata with driver-side info about the cursor. */
|
||||
typedef struct _KMSDRM_CursorData
|
||||
{
|
||||
struct gbm_bo *bo;
|
||||
uint32_t crtc_id;
|
||||
int hot_x, hot_y;
|
||||
int w, h;
|
||||
uint16_t hot_x, hot_y;
|
||||
uint16_t 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().*/
|
||||
uint32_t *buffer;
|
||||
size_t buffer_size;
|
||||
size_t buffer_pitch;
|
||||
|
||||
} KMSDRM_CursorData;
|
||||
|
||||
extern void KMSDRM_InitMouse(_THIS);
|
||||
extern void KMSDRM_QuitMouse(_THIS);
|
||||
extern void KMSDRM_DeinitMouse(_THIS);
|
||||
|
||||
extern void KMSDRM_InitCursor();
|
||||
|
||||
#endif /* SDL_KMSDRM_mouse_h_ */
|
||||
|
||||
|
||||
+301
-72
@@ -1,6 +1,7 @@
|
||||
/*
|
||||
Simple DirectMedia Layer
|
||||
Copyright (C) 1997-2020 Sam Lantinga <slouken@libsdl.org>
|
||||
Atomic KMSDRM backend by Manuel Alfayate Corchete <redwindwanderer@gmail.com>
|
||||
|
||||
This software is provided 'as-is', without any express or implied
|
||||
warranty. In no event will the authors be held liable for any damages
|
||||
@@ -23,22 +24,59 @@
|
||||
|
||||
#if SDL_VIDEO_DRIVER_KMSDRM && SDL_VIDEO_OPENGL_EGL
|
||||
|
||||
#include "SDL_log.h"
|
||||
|
||||
#include "SDL_kmsdrmvideo.h"
|
||||
#include "SDL_kmsdrmopengles.h"
|
||||
#include "SDL_kmsdrmdyn.h"
|
||||
#include "SDL_hints.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
|
||||
KMSDRM_GLES_DefaultProfileConfig(_THIS, int *mask, int *major, int *minor)
|
||||
{
|
||||
/* if SDL was _also_ built with the Raspberry Pi driver (so we're
|
||||
definitely a Pi device), default to GLES2. */
|
||||
#if SDL_VIDEO_DRIVER_RPI
|
||||
*mask = SDL_GL_CONTEXT_PROFILE_ES;
|
||||
*major = 2;
|
||||
*minor = 0;
|
||||
#endif
|
||||
}
|
||||
|
||||
int
|
||||
KMSDRM_GLES_LoadLibrary(_THIS, const char *path) {
|
||||
NativeDisplayType display = (NativeDisplayType)((SDL_VideoData *)_this->driverdata)->gbm;
|
||||
/* Just pretend you do this here, but don't do it until KMSDRM_CreateWindow(),
|
||||
where we do the same library load we would normally do here.
|
||||
because this gets called by SDL_CreateWindow() before KMSDR_CreateWindow(),
|
||||
so gbm dev isn't yet created when this is called, AND we can't alter the
|
||||
call order in SDL_CreateWindow(). */
|
||||
#if 0
|
||||
NativeDisplayType display = (NativeDisplayType)((SDL_VideoData *)_this->driverdata)->gbm_dev;
|
||||
return SDL_EGL_LoadLibrary(_this, path, display, EGL_PLATFORM_GBM_MESA);
|
||||
#endif
|
||||
return 0;
|
||||
}
|
||||
|
||||
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. */
|
||||
}
|
||||
|
||||
SDL_EGL_CreateContext_impl(KMSDRM)
|
||||
@@ -57,94 +95,285 @@ 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_SwapWindow(_THIS, SDL_Window * window) {
|
||||
KMSDRM_GLES_SwapWindowFenced(_THIS, SDL_Window * window)
|
||||
{
|
||||
SDL_WindowData *windata = ((SDL_WindowData *) window->driverdata);
|
||||
SDL_DisplayData *dispdata = (SDL_DisplayData *) SDL_GetDisplayForWindow(window)->driverdata;
|
||||
SDL_VideoData *viddata = ((SDL_VideoData *)_this->driverdata);
|
||||
KMSDRM_FBInfo *fb_info;
|
||||
int ret, timeout;
|
||||
KMSDRM_FBInfo *fb;
|
||||
KMSDRM_PlaneInfo info = {0};
|
||||
|
||||
/* Recreate the GBM / EGL surfaces if the display mode has changed */
|
||||
if (windata->egl_surface_dirty) {
|
||||
KMSDRM_CreateSurfaces(_this, window);
|
||||
/******************************************************************/
|
||||
/* 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);
|
||||
|
||||
/******************************************************************/
|
||||
/* 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 */
|
||||
timeout = 0;
|
||||
if (_this->egl_data->egl_swapinterval == 1) {
|
||||
timeout = -1;
|
||||
}
|
||||
if (!KMSDRM_WaitPageFlip(_this, windata, timeout)) {
|
||||
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);
|
||||
|
||||
/* Release the previous front buffer */
|
||||
if (windata->curr_bo) {
|
||||
KMSDRM_gbm_surface_release_buffer(windata->gs, windata->curr_bo);
|
||||
/* SDL_LogDebug(SDL_LOG_CATEGORY_VIDEO, "Released GBM surface %p", (void *)windata->curr_bo); */
|
||||
windata->curr_bo = NULL;
|
||||
}
|
||||
|
||||
windata->curr_bo = windata->next_bo;
|
||||
|
||||
/* Make the current back buffer the next front buffer */
|
||||
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 next front buffer so it can't be allocated as a back buffer */
|
||||
/* 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) {
|
||||
SDL_LogError(SDL_LOG_CATEGORY_VIDEO, "Could not lock GBM surface front buffer");
|
||||
return 0;
|
||||
/* } else {
|
||||
SDL_LogDebug(SDL_LOG_CATEGORY_VIDEO, "Locked GBM surface %p", (void *)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");
|
||||
}
|
||||
|
||||
fb_info = KMSDRM_FBFromBO(_this, windata->next_bo);
|
||||
if (!fb_info) {
|
||||
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;
|
||||
|
||||
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));
|
||||
}
|
||||
|
||||
if (!windata->curr_bo) {
|
||||
/* On the first swap, immediately present the new front buffer. Before
|
||||
drmModePageFlip can be used the CRTC has to be configured to use
|
||||
the current connector and mode with drmModeSetCrtc */
|
||||
ret = KMSDRM_drmModeSetCrtc(viddata->drm_fd, dispdata->crtc_id, fb_info->fb_id, 0,
|
||||
0, &dispdata->conn->connector_id, 1, &dispdata->mode);
|
||||
/* 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;
|
||||
|
||||
if (ret) {
|
||||
SDL_LogError(SDL_LOG_CATEGORY_VIDEO, "Could not configure CRTC");
|
||||
}
|
||||
} else {
|
||||
/* On subsequent swaps, queue the new front buffer to be flipped during
|
||||
the next vertical blank */
|
||||
ret = KMSDRM_drmModePageFlip(viddata->drm_fd, dispdata->crtc_id, fb_info->fb_id,
|
||||
DRM_MODE_PAGE_FLIP_EVENT, &windata->waiting_for_flip);
|
||||
/* SDL_LogDebug(SDL_LOG_CATEGORY_VIDEO, "drmModePageFlip(%d, %u, %u, DRM_MODE_PAGE_FLIP_EVENT, &windata->waiting_for_flip)",
|
||||
viddata->drm_fd, displaydata->crtc_id, fb_info->fb_id); */
|
||||
|
||||
if (_this->egl_data->egl_swapinterval == 1) {
|
||||
if (ret == 0) {
|
||||
windata->waiting_for_flip = SDL_TRUE;
|
||||
} else {
|
||||
SDL_LogError(SDL_LOG_CATEGORY_VIDEO, "Could not queue pageflip: %d", ret);
|
||||
}
|
||||
}
|
||||
|
||||
/* Wait immediately for vsync (as if we only had two buffers), for low input-lag scenarios.
|
||||
Run your SDL2 program with "SDL_KMSDRM_DOUBLE_BUFFER=1 <program_name>" to enable this. */
|
||||
if (_this->egl_data->egl_swapinterval == 1 && windata->double_buffer) {
|
||||
KMSDRM_WaitPageFlip(_this, windata, -1);
|
||||
}
|
||||
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. */
|
||||
if (windata->bo) {
|
||||
KMSDRM_gbm_surface_release_buffer(windata->gs, windata->bo);
|
||||
}
|
||||
|
||||
/* Take note of the buffer about to become front buffer, so next
|
||||
time we come here we can free it like we just did with the previous
|
||||
front buffer. */
|
||||
windata->bo = windata->next_bo;
|
||||
|
||||
/****************************************************************/
|
||||
/* Import the KMS-side FENCE OUTPUT FD from the kernel to the */
|
||||
/* KMS-side FENCE OBJECT so we can use use it to fence the GPU. */
|
||||
/****************************************************************/
|
||||
dispdata->kms_fence = create_fence(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");
|
||||
}
|
||||
|
||||
/* Lock the buffer that is marked by eglSwapBuffers() to become the next front buffer (so it can not
|
||||
be chosen by EGL as back buffer to draw on), and get a handle to it to request the pageflip on it. */
|
||||
windata->next_bo = KMSDRM_gbm_surface_lock_front_buffer(windata->gs);
|
||||
if (!windata->next_bo) {
|
||||
return SDL_SetError("Failed to lock frontbuffer");
|
||||
}
|
||||
fb = KMSDRM_FBFromBO(_this, windata->next_bo);
|
||||
if (!fb) {
|
||||
return SDL_SetError("Failed to get a new framebuffer BO");
|
||||
}
|
||||
|
||||
/* 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);
|
||||
|
||||
/* Do we have a pending modesetting? If so, set the necessary
|
||||
props so it's included in the incoming atomic commit. */
|
||||
if (dispdata->modeset_pending) {
|
||||
SDL_VideoData *viddata = (SDL_VideoData *)_this->driverdata;
|
||||
uint32_t blob_id;
|
||||
dispdata->atomic_flags |= DRM_MODE_ATOMIC_ALLOW_MODESET;
|
||||
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 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");
|
||||
}
|
||||
|
||||
/* Release last front buffer so EGL can chose it as back buffer and render on it again. */
|
||||
if (windata->bo) {
|
||||
KMSDRM_gbm_surface_release_buffer(windata->gs, windata->bo);
|
||||
}
|
||||
|
||||
/* Take note of current front buffer, so we can free it next time we come here. */
|
||||
windata->bo = windata->next_bo;
|
||||
|
||||
return 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;
|
||||
}
|
||||
}
|
||||
|
||||
return windata->swap_window(_this, window);
|
||||
}
|
||||
|
||||
/***************************************/
|
||||
/* End of Atomic functions block */
|
||||
/***************************************/
|
||||
|
||||
SDL_EGL_MakeCurrent_impl(KMSDRM)
|
||||
|
||||
#endif /* SDL_VIDEO_DRIVER_KMSDRM && SDL_VIDEO_OPENGL_EGL */
|
||||
|
||||
+2
-1
@@ -1,6 +1,7 @@
|
||||
/*
|
||||
Simple DirectMedia Layer
|
||||
Copyright (C) 1997-2020 Sam Lantinga <slouken@libsdl.org>
|
||||
Atomic KMSDRM backend by Manuel Alfayate Corchete <redwindwanderer@gmail.com>
|
||||
|
||||
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,10 +32,10 @@
|
||||
/* OpenGLES functions */
|
||||
#define KMSDRM_GLES_GetAttribute SDL_EGL_GetAttribute
|
||||
#define KMSDRM_GLES_GetProcAddress SDL_EGL_GetProcAddress
|
||||
#define KMSDRM_GLES_UnloadLibrary SDL_EGL_UnloadLibrary
|
||||
#define KMSDRM_GLES_DeleteContext SDL_EGL_DeleteContext
|
||||
#define KMSDRM_GLES_GetSwapInterval SDL_EGL_GetSwapInterval
|
||||
|
||||
extern void KMSDRM_GLES_DefaultProfileConfig(_THIS, int *mask, int *major, int *minor);
|
||||
extern int KMSDRM_GLES_SetSwapInterval(_THIS, int interval);
|
||||
extern int KMSDRM_GLES_LoadLibrary(_THIS, const char *path);
|
||||
extern SDL_GLContext KMSDRM_GLES_CreateContext(_THIS, SDL_Window * window);
|
||||
|
||||
+40
-12
@@ -40,28 +40,51 @@ SDL_KMSDRM_SYM(void,drmModeFreeFB,(drmModeFBPtr ptr))
|
||||
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(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))
|
||||
|
||||
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))
|
||||
|
||||
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,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 */
|
||||
|
||||
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))
|
||||
SDL_KMSDRM_SYM(drmModeObjectPropertiesPtr,drmModeObjectGetProperties,(int fd,uint32_t object_id,uint32_t object_type))
|
||||
SDL_KMSDRM_SYM(drmModePropertyPtr,drmModeGetProperty,(int fd, uint32_t propertyId))
|
||||
|
||||
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_MODULE(GBM)
|
||||
SDL_KMSDRM_SYM(int,gbm_device_get_fd,(struct gbm_device *gbm))
|
||||
@@ -72,6 +95,11 @@ 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))
|
||||
|
||||
+1367
-348
File diff suppressed because it is too large
Load Diff
+132
-30
@@ -1,6 +1,7 @@
|
||||
/*
|
||||
Simple DirectMedia Layer
|
||||
Copyright (C) 1997-2020 Sam Lantinga <slouken@libsdl.org>
|
||||
Atomic KMSDRM backend by Manuel Alfayate Corchete <redwindwanderer@gmail.com>
|
||||
|
||||
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,63 +31,163 @@
|
||||
#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
|
||||
{
|
||||
int devindex; /* device index that was passed on creation */
|
||||
int drm_fd; /* DRM file desc */
|
||||
struct gbm_device *gbm;
|
||||
char devpath[32]; /* DRM dev path. */
|
||||
|
||||
struct gbm_device *gbm_dev;
|
||||
|
||||
SDL_Window **windows;
|
||||
int max_windows;
|
||||
int num_windows;
|
||||
unsigned int max_windows;
|
||||
unsigned int num_windows;
|
||||
|
||||
SDL_bool video_init; /* Has VideoInit succeeded? */
|
||||
|
||||
} 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 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
|
||||
{
|
||||
drmModeModeInfo mode;
|
||||
drmModeModeInfo preferred_mode;
|
||||
uint32_t atomic_flags;
|
||||
|
||||
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;
|
||||
|
||||
/* 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,
|
||||
where we may not have an SDL_Cursor at all (so no SDL_Cursor driverdata).
|
||||
There's only one cursor GBM BO because we only support one cursor. */
|
||||
struct gbm_bo *cursor_bo;
|
||||
uint64_t cursor_w, cursor_h;
|
||||
|
||||
} SDL_DisplayData;
|
||||
|
||||
/* Driverdata info that gives KMSDRM-side support and substance to the SDL_Window. */
|
||||
typedef struct SDL_WindowData
|
||||
{
|
||||
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_bo *bo;
|
||||
struct gbm_bo *next_bo;
|
||||
|
||||
#if SDL_VIDEO_OPENGL_EGL
|
||||
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);
|
||||
|
||||
} SDL_WindowData;
|
||||
|
||||
typedef struct SDL_DisplayModeData
|
||||
{
|
||||
int mode_index;
|
||||
} SDL_DisplayModeData;
|
||||
|
||||
|
||||
typedef struct SDL_DisplayData
|
||||
{
|
||||
uint32_t crtc_id;
|
||||
drmModeConnector *conn;
|
||||
drmModeModeInfo mode;
|
||||
drmModeCrtc *saved_crtc; /* CRTC to restore on quit */
|
||||
} SDL_DisplayData;
|
||||
|
||||
|
||||
typedef struct SDL_WindowData
|
||||
{
|
||||
SDL_VideoData *viddata;
|
||||
struct gbm_surface *gs;
|
||||
struct gbm_bo *curr_bo;
|
||||
struct gbm_bo *next_bo;
|
||||
struct gbm_bo *crtc_bo;
|
||||
SDL_bool waiting_for_flip;
|
||||
SDL_bool double_buffer;
|
||||
#if SDL_VIDEO_OPENGL_EGL
|
||||
int egl_surface_dirty;
|
||||
EGLSurface egl_surface;
|
||||
#endif
|
||||
} SDL_WindowData;
|
||||
|
||||
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_CreateSurfaces(_THIS, SDL_Window * window);
|
||||
int KMSDRM_CreateEGLSurface(_THIS, SDL_Window * window);
|
||||
KMSDRM_FBInfo *KMSDRM_FBFromBO(_THIS, struct gbm_bo *bo);
|
||||
SDL_bool KMSDRM_WaitPageFlip(_THIS, SDL_WindowData *windata, int timeout);
|
||||
|
||||
/* 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);
|
||||
|
||||
/****************************************************************************/
|
||||
/* SDL_VideoDevice functions declaration */
|
||||
@@ -103,6 +204,7 @@ void KMSDRM_SetWindowTitle(_THIS, SDL_Window * window);
|
||||
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);
|
||||
void KMSDRM_ShowWindow(_THIS, SDL_Window * window);
|
||||
void KMSDRM_HideWindow(_THIS, SDL_Window * window);
|
||||
void KMSDRM_RaiseWindow(_THIS, SDL_Window * window);
|
||||
|
||||
+408
@@ -0,0 +1,408 @@
|
||||
/*
|
||||
Simple DirectMedia Layer
|
||||
Copyright (C) 1997-2020 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
|
||||
arising from the use of this software.
|
||||
|
||||
Permission is granted to anyone to use this software for any purpose,
|
||||
including commercial applications, and to alter it and redistribute it
|
||||
freely, subject to the following restrictions:
|
||||
|
||||
1. The origin of this software must not be misrepresented; you must not
|
||||
claim that you wrote the original software. If you use this software
|
||||
in a product, an acknowledgment in the product documentation would be
|
||||
appreciated but is not required.
|
||||
2. Altered source versions must be plainly marked as such, and must not be
|
||||
misrepresented as being the original software.
|
||||
3. This notice may not be removed or altered from any source distribution.
|
||||
*/
|
||||
|
||||
/*
|
||||
* @author Manuel Alfayate Corchere <redwindwanderer@gmail.com>.
|
||||
* Based on Jacob Lifshay's SDL_x11vulkan.c.
|
||||
*/
|
||||
|
||||
#include "../../SDL_internal.h"
|
||||
|
||||
#if SDL_VIDEO_VULKAN && SDL_VIDEO_DRIVER_KMSDRM
|
||||
|
||||
#include "SDL_kmsdrmvideo.h"
|
||||
#include "SDL_kmsdrmdyn.h"
|
||||
#include "SDL_assert.h"
|
||||
|
||||
#include "SDL_loadso.h"
|
||||
#include "SDL_kmsdrmvulkan.h"
|
||||
#include "SDL_syswm.h"
|
||||
#include "sys/ioctl.h"
|
||||
|
||||
#if defined(__OpenBSD__)
|
||||
#define DEFAULT_VULKAN "libvulkan.so"
|
||||
#else
|
||||
#define DEFAULT_VULKAN "libvulkan.so.1"
|
||||
#endif
|
||||
|
||||
int KMSDRM_Vulkan_LoadLibrary(_THIS, const char *path)
|
||||
{
|
||||
VkExtensionProperties *extensions = NULL;
|
||||
Uint32 i, extensionCount = 0;
|
||||
SDL_bool hasSurfaceExtension = SDL_FALSE;
|
||||
SDL_bool hasDisplayExtension = SDL_FALSE;
|
||||
PFN_vkGetInstanceProcAddr vkGetInstanceProcAddr = NULL;
|
||||
|
||||
if(_this->vulkan_config.loader_handle)
|
||||
return SDL_SetError("Vulkan already loaded");
|
||||
|
||||
/* Load the Vulkan library */
|
||||
if(!path)
|
||||
path = SDL_getenv("SDL_VULKAN_LIBRARY");
|
||||
if(!path)
|
||||
path = DEFAULT_VULKAN;
|
||||
|
||||
_this->vulkan_config.loader_handle = SDL_LoadObject(path);
|
||||
|
||||
if(!_this->vulkan_config.loader_handle)
|
||||
return -1;
|
||||
|
||||
SDL_strlcpy(_this->vulkan_config.loader_path, path,
|
||||
SDL_arraysize(_this->vulkan_config.loader_path));
|
||||
|
||||
vkGetInstanceProcAddr = (PFN_vkGetInstanceProcAddr)SDL_LoadFunction(
|
||||
_this->vulkan_config.loader_handle, "vkGetInstanceProcAddr");
|
||||
|
||||
if(!vkGetInstanceProcAddr)
|
||||
goto fail;
|
||||
|
||||
_this->vulkan_config.vkGetInstanceProcAddr = (void *)vkGetInstanceProcAddr;
|
||||
_this->vulkan_config.vkEnumerateInstanceExtensionProperties =
|
||||
(void *)((PFN_vkGetInstanceProcAddr)_this->vulkan_config.vkGetInstanceProcAddr)(
|
||||
VK_NULL_HANDLE, "vkEnumerateInstanceExtensionProperties");
|
||||
|
||||
if(!_this->vulkan_config.vkEnumerateInstanceExtensionProperties)
|
||||
goto fail;
|
||||
|
||||
extensions = SDL_Vulkan_CreateInstanceExtensionsList(
|
||||
(PFN_vkEnumerateInstanceExtensionProperties)
|
||||
_this->vulkan_config.vkEnumerateInstanceExtensionProperties,
|
||||
&extensionCount);
|
||||
|
||||
if(!extensions)
|
||||
goto fail;
|
||||
|
||||
for(i = 0; i < extensionCount; i++)
|
||||
{
|
||||
if(SDL_strcmp(VK_KHR_SURFACE_EXTENSION_NAME, extensions[i].extensionName) == 0)
|
||||
hasSurfaceExtension = SDL_TRUE;
|
||||
else if(SDL_strcmp(VK_KHR_DISPLAY_EXTENSION_NAME, extensions[i].extensionName) == 0)
|
||||
hasDisplayExtension = SDL_TRUE;
|
||||
}
|
||||
|
||||
SDL_free(extensions);
|
||||
|
||||
if(!hasSurfaceExtension)
|
||||
{
|
||||
SDL_SetError("Installed Vulkan doesn't implement the "
|
||||
VK_KHR_SURFACE_EXTENSION_NAME " extension");
|
||||
goto fail;
|
||||
}
|
||||
else if(!hasDisplayExtension)
|
||||
{
|
||||
SDL_SetError("Installed Vulkan doesn't implement the "
|
||||
VK_KHR_DISPLAY_EXTENSION_NAME "extension");
|
||||
goto fail;
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
fail:
|
||||
SDL_UnloadObject(_this->vulkan_config.loader_handle);
|
||||
_this->vulkan_config.loader_handle = NULL;
|
||||
return -1;
|
||||
}
|
||||
|
||||
void KMSDRM_Vulkan_UnloadLibrary(_THIS)
|
||||
{
|
||||
if(_this->vulkan_config.loader_handle)
|
||||
{
|
||||
SDL_UnloadObject(_this->vulkan_config.loader_handle);
|
||||
_this->vulkan_config.loader_handle = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
/*********************************************************************/
|
||||
/* Here we can put whatever Vulkan extensions we want to be enabled */
|
||||
/* at instance creation, which is done in the programs, not in SDL. */
|
||||
/* So: programs call SDL_Vulkan_GetInstanceExtensions() and here */
|
||||
/* we put the extensions specific to this backend so the programs */
|
||||
/* get a list with the extension we want, so they can include that */
|
||||
/* list in the ppEnabledExtensionNames and EnabledExtensionCount */
|
||||
/* members of the VkInstanceCreateInfo struct passed to */
|
||||
/* vkCreateInstance(). */
|
||||
/*********************************************************************/
|
||||
SDL_bool KMSDRM_Vulkan_GetInstanceExtensions(_THIS,
|
||||
SDL_Window *window,
|
||||
unsigned *count,
|
||||
const char **names)
|
||||
{
|
||||
static const char *const extensionsForKMSDRM[] = {
|
||||
VK_KHR_SURFACE_EXTENSION_NAME, VK_KHR_DISPLAY_EXTENSION_NAME
|
||||
};
|
||||
if(!_this->vulkan_config.loader_handle)
|
||||
{
|
||||
SDL_SetError("Vulkan is not loaded");
|
||||
return SDL_FALSE;
|
||||
}
|
||||
return SDL_Vulkan_GetInstanceExtensions_Helper(
|
||||
count, names, SDL_arraysize(extensionsForKMSDRM),
|
||||
extensionsForKMSDRM);
|
||||
}
|
||||
|
||||
void KMSDRM_Vulkan_GetDrawableSize(_THIS, SDL_Window *window, int *w, int *h)
|
||||
{
|
||||
if (w) {
|
||||
*w = window->w;
|
||||
}
|
||||
|
||||
if (h) {
|
||||
*h = window->h;
|
||||
}
|
||||
}
|
||||
|
||||
/***********************************************************************/
|
||||
/* First thing to know is that we don't call vkCreateInstance() here. */
|
||||
/* Instead, programs using SDL and Vulkan create their Vulkan instance */
|
||||
/* and we get it here, ready to use. */
|
||||
/* Extensions specific for this platform are activated in */
|
||||
/* KMSDRM_Vulkan_GetInstanceExtensions(), like we do with */
|
||||
/* VK_KHR_DISPLAY_EXTENSION_NAME, which is what we need for x-less VK. */
|
||||
/***********************************************************************/
|
||||
SDL_bool KMSDRM_Vulkan_CreateSurface(_THIS,
|
||||
SDL_Window *window,
|
||||
VkInstance instance,
|
||||
VkSurfaceKHR *surface)
|
||||
{
|
||||
VkPhysicalDevice gpu;
|
||||
uint32_t gpu_count;
|
||||
uint32_t display_count;
|
||||
uint32_t mode_count;
|
||||
uint32_t plane_count;
|
||||
|
||||
VkPhysicalDevice *physical_devices = NULL;
|
||||
VkDisplayPropertiesKHR *displays_props = NULL;
|
||||
VkDisplayModePropertiesKHR *modes_props = NULL;
|
||||
VkDisplayPlanePropertiesKHR *planes_props = NULL;
|
||||
|
||||
VkDisplayModeCreateInfoKHR display_mode_create_info;
|
||||
VkDisplaySurfaceCreateInfoKHR display_plane_surface_create_info;
|
||||
|
||||
VkExtent2D image_size;
|
||||
VkDisplayModeKHR display_mode;
|
||||
VkDisplayModePropertiesKHR display_mode_props = {0};
|
||||
|
||||
VkResult result;
|
||||
SDL_bool ret = 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;
|
||||
|
||||
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;
|
||||
|
||||
PFN_vkCreateDisplayPlaneSurfaceKHR vkCreateDisplayPlaneSurfaceKHR =
|
||||
(PFN_vkCreateDisplayPlaneSurfaceKHR)vkGetInstanceProcAddr(
|
||||
instance, "vkCreateDisplayPlaneSurfaceKHR");
|
||||
|
||||
PFN_vkEnumeratePhysicalDevices vkEnumeratePhysicalDevices =
|
||||
(PFN_vkEnumeratePhysicalDevices)vkGetInstanceProcAddr(
|
||||
instance, "vkEnumeratePhysicalDevices");
|
||||
|
||||
PFN_vkGetPhysicalDeviceDisplayPropertiesKHR vkGetPhysicalDeviceDisplayPropertiesKHR =
|
||||
(PFN_vkGetPhysicalDeviceDisplayPropertiesKHR)vkGetInstanceProcAddr(
|
||||
instance, "vkGetPhysicalDeviceDisplayPropertiesKHR");
|
||||
|
||||
PFN_vkGetDisplayModePropertiesKHR vkGetDisplayModePropertiesKHR =
|
||||
(PFN_vkGetDisplayModePropertiesKHR)vkGetInstanceProcAddr(
|
||||
instance, "vkGetDisplayModePropertiesKHR");
|
||||
|
||||
PFN_vkGetPhysicalDeviceDisplayPlanePropertiesKHR vkGetPhysicalDeviceDisplayPlanePropertiesKHR =
|
||||
(PFN_vkGetPhysicalDeviceDisplayPlanePropertiesKHR)vkGetInstanceProcAddr(
|
||||
instance, "vkGetPhysicalDeviceDisplayPlanePropertiesKHR");
|
||||
|
||||
/*PFN_vkGetDisplayPlaneSupportedDisplaysKHR vkGetDisplayPlaneSupportedDisplaysKHR =
|
||||
(PFN_vkGetDisplayPlaneSupportedDisplaysKHR)vkGetInstanceProcAddr(
|
||||
instance, "vkGetDisplayPlaneSupportedDisplaysKHR");
|
||||
|
||||
PFN_vkGetDisplayPlaneCapabilitiesKHR vkGetDisplayPlaneCapabilitiesKHR =
|
||||
(PFN_vkGetDisplayPlaneCapabilitiesKHR)vkGetInstanceProcAddr(
|
||||
instance, "vkGetDisplayPlaneCapabilitiesKHR");
|
||||
*/
|
||||
|
||||
PFN_vkCreateDisplayModeKHR vkCreateDisplayModeKHR =
|
||||
(PFN_vkCreateDisplayModeKHR)vkGetInstanceProcAddr(
|
||||
instance, "vkCreateDisplayModeKHR");
|
||||
|
||||
if(!_this->vulkan_config.loader_handle)
|
||||
{
|
||||
SDL_SetError("Vulkan is not loaded");
|
||||
goto clean;
|
||||
}
|
||||
|
||||
/*************************************/
|
||||
/* Block for vulkan surface creation */
|
||||
/*************************************/
|
||||
|
||||
/****************************************************************/
|
||||
/* If we got vkCreateDisplayPlaneSurfaceKHR() pointer, it means */
|
||||
/* that the VK_KHR_Display extension is active on the instance. */
|
||||
/* That's the central extension we need for x-less VK! */
|
||||
/****************************************************************/
|
||||
if(!vkCreateDisplayPlaneSurfaceKHR)
|
||||
{
|
||||
SDL_SetError(VK_KHR_DISPLAY_EXTENSION_NAME
|
||||
" extension is not enabled in the Vulkan instance.");
|
||||
goto clean;
|
||||
}
|
||||
|
||||
/* Get the physical device count. */
|
||||
vkEnumeratePhysicalDevices(instance, &gpu_count, NULL);
|
||||
|
||||
if (gpu_count == 0) {
|
||||
SDL_SetError("Vulkan can't find physical devices (gpus).");
|
||||
goto clean;
|
||||
}
|
||||
|
||||
/* Get the physical devices. */
|
||||
physical_devices = malloc(sizeof(VkPhysicalDevice) * gpu_count);
|
||||
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];
|
||||
|
||||
/* A display is a video output. 1 GPU can have N displays.
|
||||
Vulkan only counts the connected displays.
|
||||
Get the display count of the GPU. */
|
||||
vkGetPhysicalDeviceDisplayPropertiesKHR(gpu, &display_count, NULL);
|
||||
if (display_count == 0) {
|
||||
SDL_SetError("Vulkan can't find any displays.");
|
||||
goto clean;
|
||||
}
|
||||
|
||||
/* Get the props of the displays of the physical device. */
|
||||
displays_props = (VkDisplayPropertiesKHR *) malloc(display_count * sizeof(*displays_props));
|
||||
vkGetPhysicalDeviceDisplayPropertiesKHR(gpu,
|
||||
&display_count,
|
||||
displays_props);
|
||||
|
||||
/* Get the videomode count for the first display. */
|
||||
vkGetDisplayModePropertiesKHR(gpu,
|
||||
displays_props[display_index].display,
|
||||
&mode_count, NULL);
|
||||
|
||||
if (mode_count == 0) {
|
||||
SDL_SetError("Vulkan can't find any video modes for display %i (%s)\n", 0,
|
||||
displays_props[display_index].displayName);
|
||||
goto clean;
|
||||
}
|
||||
|
||||
/* Get the props of the videomodes for the first display. */
|
||||
modes_props = (VkDisplayModePropertiesKHR *) malloc(mode_count * sizeof(*modes_props));
|
||||
vkGetDisplayModePropertiesKHR(gpu,
|
||||
displays_props[display_index].display,
|
||||
&mode_count, modes_props);
|
||||
|
||||
/* Get the planes count of the physical device. */
|
||||
vkGetPhysicalDeviceDisplayPlanePropertiesKHR(gpu, &plane_count, NULL);
|
||||
if (plane_count == 0) {
|
||||
SDL_SetError("Vulkan can't find any planes.");
|
||||
goto clean;
|
||||
}
|
||||
|
||||
/* Get the props of the planes for the physical device. */
|
||||
planes_props = 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,
|
||||
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)
|
||||
{
|
||||
display_mode_props = modes_props[i];
|
||||
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;
|
||||
}
|
||||
|
||||
/* 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;
|
||||
}
|
||||
|
||||
/* Let's finally create the Vulkan surface! */
|
||||
|
||||
image_size.width = window->w;
|
||||
image_size.height = window->h;
|
||||
|
||||
display_plane_surface_create_info.sType = VK_STRUCTURE_TYPE_DISPLAY_SURFACE_CREATE_INFO_KHR;
|
||||
display_plane_surface_create_info.displayMode = display_mode;
|
||||
/* For now, simply use the first plane. */
|
||||
display_plane_surface_create_info.planeIndex = 0;
|
||||
display_plane_surface_create_info.imageExtent = image_size;
|
||||
result = vkCreateDisplayPlaneSurfaceKHR(instance,
|
||||
&display_plane_surface_create_info,
|
||||
NULL,
|
||||
surface);
|
||||
if(result != VK_SUCCESS)
|
||||
{
|
||||
SDL_SetError("vkCreateKMSDRMSurfaceKHR failed: %s",
|
||||
SDL_Vulkan_GetResultString(result));
|
||||
goto clean;
|
||||
}
|
||||
|
||||
ret = SDL_TRUE;
|
||||
|
||||
clean:
|
||||
if (physical_devices)
|
||||
free (physical_devices);
|
||||
if (displays_props)
|
||||
free (displays_props);
|
||||
if (planes_props)
|
||||
free (planes_props);
|
||||
if (modes_props)
|
||||
free (modes_props);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
/* vim: set ts=4 sw=4 expandtab: */
|
||||
+53
@@ -0,0 +1,53 @@
|
||||
/*
|
||||
Simple DirectMedia Layer
|
||||
Copyright (C) 1997-2020 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
|
||||
arising from the use of this software.
|
||||
|
||||
Permission is granted to anyone to use this software for any purpose,
|
||||
including commercial applications, and to alter it and redistribute it
|
||||
freely, subject to the following restrictions:
|
||||
|
||||
1. The origin of this software must not be misrepresented; you must not
|
||||
claim that you wrote the original software. If you use this software
|
||||
in a product, an acknowledgment in the product documentation would be
|
||||
appreciated but is not required.
|
||||
2. Altered source versions must be plainly marked as such, and must not be
|
||||
misrepresented as being the original software.
|
||||
3. This notice may not be removed or altered from any source distribution.
|
||||
*/
|
||||
|
||||
/*
|
||||
* @author Manuel Alfayate Corchere <redwindwanderer@gmail.com>.
|
||||
* Based on Jacob Lifshay's SDL_x11vulkan.c.
|
||||
*/
|
||||
|
||||
#include "../../SDL_internal.h"
|
||||
|
||||
#ifndef SDL_kmsdrmvulkan_h_
|
||||
#define SDL_kmsdrmvulkan_h_
|
||||
|
||||
#include "../SDL_vulkan_internal.h"
|
||||
#include "../SDL_sysvideo.h"
|
||||
|
||||
#if SDL_VIDEO_VULKAN && SDL_VIDEO_DRIVER_KMSDRM
|
||||
|
||||
int KMSDRM_Vulkan_LoadLibrary(_THIS, const char *path);
|
||||
void KMSDRM_Vulkan_UnloadLibrary(_THIS);
|
||||
SDL_bool KMSDRM_Vulkan_GetInstanceExtensions(_THIS,
|
||||
SDL_Window *window,
|
||||
unsigned *count,
|
||||
const char **names);
|
||||
void KMSDRM_Vulkan_GetDrawableSize(_THIS, SDL_Window *window, int *w, int *h);
|
||||
SDL_bool KMSDRM_Vulkan_CreateSurface(_THIS,
|
||||
SDL_Window *window,
|
||||
VkInstance instance,
|
||||
VkSurfaceKHR *surface);
|
||||
|
||||
#endif
|
||||
|
||||
#endif /* SDL_kmsdrmvulkan_h_ */
|
||||
|
||||
/* vi: set ts=4 sw=4 expandtab: */
|
||||
Reference in New Issue
Block a user