[KMS/DRM] Go back to the LEGACY interface only because using planes breaks compatibility with HW, so no advantage on using ATOMIC.

This commit is contained in:
Manuel Alfayate Corchete 2021-01-08 18:57:12 +01:00
parent 940e1b8dd9
commit b24494734b
16 changed files with 545 additions and 1475 deletions

View File

@ -1182,8 +1182,7 @@ macro(CheckKMSDRM)
set(HAVE_SDL_VIDEO TRUE)
file(GLOB KMSDRM_SOURCES ${SDL2_SOURCE_DIR}/src/video/kmsdrm/*.c)
file(GLOB KMSDRM_LEGACY_SOURCES ${SDL2_SOURCE_DIR}/src/video/kmsdrm_legacy/*.c)
set(SOURCE_FILES ${SOURCE_FILES} ${KMSDRM_SOURCES} ${KMSDRM_LEGACY_SOURCES})
set(SOURCE_FILES ${SOURCE_FILES} ${KMSDRM_SOURCES})
list(APPEND EXTRA_CFLAGS ${KMSDRM_CFLAGS})

View File

@ -2252,7 +2252,6 @@ AS_HELP_STRING([--enable-kmsdrm-shared], [dynamically load kmsdrm support [[defa
AC_DEFINE(SDL_VIDEO_DRIVER_KMSDRM, 1, [ ])
SOURCES="$SOURCES $srcdir/src/video/kmsdrm/*.c"
SOURCES="$SOURCES $srcdir/src/video/kmsdrm_legacy/*.c"
EXTRA_CFLAGS="$EXTRA_CFLAGS $LIBDRM_CFLAGS $LIBGBM_CFLAGS"
AC_MSG_CHECKING(for kmsdrm dynamic loading support)

View File

@ -96,7 +96,6 @@ static VideoBootStrap *bootstrap[] = {
#endif
#if SDL_VIDEO_DRIVER_KMSDRM
&KMSDRM_bootstrap,
&KMSDRM_LEGACY_bootstrap,
#endif
#if SDL_VIDEO_DRIVER_RPI
&RPI_bootstrap,

View File

@ -1,7 +1,6 @@
/*
Simple DirectMedia Layer
Copyright (C) 1997-2021 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
@ -28,7 +27,6 @@
#include "SDL_kmsdrmdyn.h"
#ifdef SDL_VIDEO_DRIVER_KMSDRM_DYNAMIC
#include "SDL_name.h"

View File

@ -1,7 +1,6 @@
/*
Simple DirectMedia Layer
Copyright (C) 1997-2021 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

View File

@ -1,7 +1,6 @@
/*
Simple DirectMedia Layer
Copyright (C) 1997-2021 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

View File

@ -1,7 +1,6 @@
/*
Simple DirectMedia Layer
Copyright (C) 1997-2021 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

View File

@ -1,7 +1,6 @@
/*
Simple DirectMedia Layer
Copyright (C) 1997-2021 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,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"
@ -55,40 +53,18 @@ static int KMSDRM_WarpMouseGlobal(int x, int y);
/* and mouse->cursor_shown is 1. */
/**************************************************************************************/
/**********************************/
/* Atomic helper functions block. */
/**********************************/
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;
@ -107,15 +83,9 @@ 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)
{
return SDL_CreateCursor(default_cdata, default_cmask, DEFAULT_CWIDTH, DEFAULT_CHEIGHT, DEFAULT_CHOTX, DEFAULT_CHOTY);
}
/* 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. */
/* 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)
{
@ -225,13 +195,12 @@ static int
KMSDRM_ShowCursor(SDL_Cursor * cursor)
{
SDL_VideoDevice *video_device = SDL_GetVideoDevice();
//SDL_VideoData *viddata = ((SDL_VideoData *)video_device->driverdata);
SDL_VideoData *viddata = ((SDL_VideoData *)video_device->driverdata);
SDL_DisplayData *dispdata = (SDL_DisplayData *)SDL_GetDisplayDriverData(0);
SDL_Mouse *mouse;
KMSDRM_CursorData *curdata;
KMSDRM_FBInfo *fb;
KMSDRM_PlaneInfo info = {0};
uint32_t bo_handle;
size_t bo_stride;
size_t bufsize;
@ -239,7 +208,7 @@ KMSDRM_ShowCursor(SDL_Cursor * cursor)
uint32_t pixel;
int i,j;
int ret = 0;
int ret;
mouse = SDL_GetMouse();
if (!mouse) {
@ -250,17 +219,13 @@ KMSDRM_ShowCursor(SDL_Cursor * cursor)
/* Hide cursor if it's NULL or it has no focus(=winwow). */
/*********************************************************/
if (!cursor || !mouse->focus) {
if (dispdata->cursor_plane) {
/* Hide the drm cursor with no more considerations because
SDL_VideoQuit() takes us here after disabling the mouse
so there is no mouse->cur_cursor by now. */
info.plane = dispdata->cursor_plane;
/* The rest of the members are zeroed, so this takes away the cursor
from the cursor plane. */
drm_atomic_set_plane_props(&info);
if (drm_atomic_commit(video_device, SDL_TRUE, SDL_FALSE)) {
ret = SDL_SetError("Failed atomic commit in KMSDRM_ShowCursor.");
}
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;
}
@ -268,10 +233,6 @@ KMSDRM_ShowCursor(SDL_Cursor * cursor)
/************************************************/
/* If cursor != NULL, DO show cursor on display */
/************************************************/
if (!dispdata->cursor_plane) {
return SDL_SetError("Hardware cursor plane not initialized.");
}
curdata = (KMSDRM_CursorData *) cursor->driverdata;
if (!curdata || !dispdata->cursor_bo) {
@ -297,7 +258,7 @@ KMSDRM_ShowCursor(SDL_Cursor * cursor)
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);
legacy_alpha_premultiply_ARGB8888 (&pixel);
SDL_memcpy(ready_buffer + (i * dispdata->cursor_w) + j, &pixel, 4);
}
}
@ -308,28 +269,20 @@ KMSDRM_ShowCursor(SDL_Cursor * cursor)
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 = dispdata->cursor_w;
info.src_h = dispdata->cursor_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(video_device, SDL_TRUE, SDL_FALSE)) {
ret = SDL_SetError("Failed atomic commit in KMSDRM_ShowCursor.");
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);
}
ret = 0;
if (ret) {
ret = SDL_SetError("Failed to set DRM cursor.");
goto cleanup;
}
cleanup:
@ -373,7 +326,6 @@ 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);
@ -382,11 +334,18 @@ KMSDRM_WarpMouseGlobal(int x, int y)
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 ret, drm_fd;
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.");
}
@ -403,30 +362,15 @@ KMSDRM_DeinitMouse(_THIS)
{
SDL_VideoDevice *video_device = SDL_GetVideoDevice();
SDL_DisplayData *dispdata = (SDL_DisplayData *)SDL_GetDisplayDriverData(0);
KMSDRM_PlaneInfo info = {0};
/* 1- Destroy the curso GBM BO. */
/* 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_FALSE)) {
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);
}
}
/* Create cursor BO. */
void
KMSDRM_InitMouse(_THIS)
{
@ -442,29 +386,23 @@ 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. */
/************************************************/
/* 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,
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))
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");
goto cleanup;
@ -477,7 +415,7 @@ KMSDRM_InitMouse(_THIS)
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_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");
@ -505,27 +443,28 @@ cleanup:
}
}
void
KMSDRM_QuitMouse(_THIS)
{
/* TODO: ? */
}
/* This is called when a mouse motion event occurs */
static void
KMSDRM_MoveCursor(SDL_Cursor * cursor)
{
SDL_Mouse *mouse = SDL_GetMouse();
KMSDRM_CursorData *curdata;
SDL_DisplayData *dispdata = (SDL_DisplayData *)SDL_GetDisplayDriverData(0);
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(dispdata->cursor_bo));
ret = KMSDRM_drmModeMoveCursor(drm_fd, dispdata->crtc->crtc_id, mouse->x, mouse->y);
/* 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.");
if (ret) {
SDL_SetError("drmModeMoveCursor() failed.");
}
}
}

View File

@ -1,7 +1,6 @@
/*
Simple DirectMedia Layer
Copyright (C) 1997-2021 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,14 +29,14 @@
#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;
@ -46,6 +45,7 @@ typedef struct _KMSDRM_CursorData
extern void KMSDRM_InitMouse(_THIS);
extern void KMSDRM_DeinitMouse(_THIS);
extern void KMSDRM_QuitMouse(_THIS);
extern void KMSDRM_InitCursor();

View File

@ -1,7 +1,6 @@
/*
Simple DirectMedia Layer
Copyright (C) 1997-2021 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
@ -24,27 +23,16 @@
#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"
#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
@ -95,290 +83,81 @@ 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_bool modesetting = SDL_FALSE;
SDL_VideoData *viddata = ((SDL_VideoData *)_this->driverdata);
KMSDRM_FBInfo *fb_info;
int ret, timeout;
/******************************************************************/
/* 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");
/* Recreate the GBM / EGL surfaces if the display mode has changed */
if (windata->egl_surface_dirty) {
KMSDRM_CreateSurfaces(_this, window);
}
/******************************************************************/
/* 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");
/* 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;
}
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;
add_connector_property(dispdata->atomic_req, dispdata->connector, "CRTC_ID", dispdata->crtc->crtc->crtc_id);
KMSDRM_drmModeCreatePropertyBlob(viddata->drm_fd, &dispdata->mode, sizeof(dispdata->mode), &blob_id);
add_crtc_property(dispdata->atomic_req, dispdata->crtc, "MODE_ID", blob_id);
add_crtc_property(dispdata->atomic_req, dispdata->crtc, "active", 1);
modesetting = SDL_TRUE;
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, modesetting)) {
return SDL_SetError("Failed to issue atomic commit on pageflip");
}
/* Release the previous front buffer so EGL can chose it as back buffer
and render on it again. */
if (windata->bo) {
KMSDRM_gbm_surface_release_buffer(windata->gs, windata->bo);
}
/* Take note of the buffer about to become front buffer, so next
time we come here we can free it like we just did with the previous
front buffer. */
windata->bo = windata->next_bo;
/****************************************************************/
/* Import the KMS-side FENCE OUTPUT FD from the kernel to the */
/* KMS-side FENCE OBJECT so we can use use it to fence the GPU. */
/****************************************************************/
dispdata->kms_fence = create_fence(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);
if (!KMSDRM_WaitPageFlip(_this, windata, timeout)) {
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};
SDL_bool modesetting = SDL_FALSE;
/**********************************************************************************/
/* In double-buffer mode, atomic_commit will always be synchronous/blocking (ie: */
/* won't return until the requested changes are really done). */
/* Also, there's no need to fence KMS or the GPU, because we won't be entering */
/* game loop again (hence not building or executing a new cmdstring) until */
/* pageflip is done, so we don't need to protect the KMS/GPU access to the buffer.*/
/**********************************************************************************/
/* Mark, at EGL level, the buffer that we want to become the new front buffer.
It won't really happen until we request a pageflip at the KMS level and it
completes. */
if (! _this->egl_data->eglSwapBuffers(_this->egl_data->egl_display, windata->egl_surface)) {
return SDL_EGL_SetError("Failed to swap EGL buffers", "eglSwapBuffers");
}
/* Lock the buffer that is marked by eglSwapBuffers() to become the next front buffer
(so it can not be chosen by EGL as back buffer to draw on), and get a handle to it,
to request the pageflip on it. */
windata->next_bo = KMSDRM_gbm_surface_lock_front_buffer(windata->gs);
if (!windata->next_bo) {
return SDL_SetError("Failed to lock frontbuffer");
}
fb = KMSDRM_FBFromBO(_this, windata->next_bo);
if (!fb) {
return SDL_SetError("Failed to get a new framebuffer BO");
}
/* 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) {
uint32_t blob_id;
SDL_VideoData *viddata = (SDL_VideoData *)_this->driverdata;
add_connector_property(dispdata->atomic_req, dispdata->connector, "CRTC_ID", dispdata->crtc->crtc->crtc_id);
KMSDRM_drmModeCreatePropertyBlob(viddata->drm_fd, &dispdata->mode, sizeof(dispdata->mode), &blob_id);
add_crtc_property(dispdata->atomic_req, dispdata->crtc, "MODE_ID", blob_id);
add_crtc_property(dispdata->atomic_req, dispdata->crtc, "active", 1);
modesetting = SDL_TRUE;
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, modesetting)) {
return SDL_SetError("Failed to issue atomic commit on pageflip");
}
/* Release last front buffer so EGL can chose it as back buffer and render on it again. */
/* Release the previous front buffer */
if (windata->bo) {
KMSDRM_gbm_surface_release_buffer(windata->gs, windata->bo);
windata->bo = NULL;
}
/* Take note of current front buffer, so we can free it next time we come here. */
windata->bo = windata->next_bo;
/* 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;
}
int
KMSDRM_GLES_SwapWindow(_THIS, SDL_Window * window)
{
SDL_WindowData *windata = ((SDL_WindowData *) window->driverdata);
/* Lock the next front buffer so it can't be allocated as a back buffer */
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;
}
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;
/* Get the fb_info for the next front buffer. */
fb_info = KMSDRM_FBFromBO(_this, windata->next_bo);
if (!fb_info) {
return 0;
}
/* Issue pageflip on the next front buffer.
The pageflip will be done during the next vblank. */
ret = KMSDRM_drmModePageFlip(viddata->drm_fd, dispdata->crtc->crtc_id,
fb_info->fb_id, DRM_MODE_PAGE_FLIP_EVENT, &windata->waiting_for_flip);
if (_this->egl_data->egl_swapinterval == 1) {
if (ret == 0) {
windata->waiting_for_flip = SDL_TRUE;
} else {
windata->swap_window = KMSDRM_GLES_SwapWindowFenced;
SDL_LogError(SDL_LOG_CATEGORY_VIDEO, "Could not queue pageflip: %d", ret);
}
}
return windata->swap_window(_this, window);
/* If we are in double-buffer mode, wait immediately for vsync
(as if we only had two buffers),
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);
}
/***************************************/
/* End of Atomic functions block */
/***************************************/
return 0;
}
SDL_EGL_MakeCurrent_impl(KMSDRM)

View File

@ -1,7 +1,6 @@
/*
Simple DirectMedia Layer
Copyright (C) 1997-2021 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

View File

@ -41,32 +41,29 @@ 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 */
/* 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 +74,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 +91,6 @@ 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

View File

@ -1,7 +1,6 @@
/*
Simple DirectMedia Layer
Copyright (C) 1997-2021 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,17 +30,8 @@
#include <unistd.h>
#include <xf86drm.h>
#include <xf86drmMode.h>
#include <gbm.h>
#include <assert.h>
#include <EGL/egl.h>
#include <EGL/eglext.h>
/****************************************************************************************/
/* 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
{
@ -51,56 +41,30 @@ 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_Window **windows;
int max_windows;
int num_windows;
} 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 original_mode;
plane *display_plane;
plane *cursor_plane;
crtc *crtc;
connector *connector;
drmModeCrtc *saved_crtc; /* CRTC to restore on quit */
/* 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;
SDL_bool modeset_pending;
SDL_bool gbm_init;
/* DRM & GBM cursor stuff lives here, not in an SDL_Cursor's driverdata struct,
@ -110,11 +74,12 @@ typedef struct SDL_DisplayData
struct gbm_bo *cursor_bo;
uint64_t cursor_w, cursor_h;
SDL_bool modeset_pending;
SDL_bool set_default_cursor_pending;
} SDL_DisplayData;
/* Driverdata info that gives KMSDRM-side support and substance to the SDL_Window. */
typedef struct SDL_WindowData
{
SDL_VideoData *viddata;
@ -126,6 +91,10 @@ typedef struct SDL_WindowData
struct gbm_bo *bo;
struct gbm_bo *next_bo;
SDL_bool waiting_for_flip;
SDL_bool double_buffer;
int egl_surface_dirty;
EGLSurface egl_surface;
/* For scaling and AR correction. */
@ -135,54 +104,18 @@ typedef struct SDL_WindowData
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 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, SDL_bool allow_modeset);
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_bool KMSDRM_WaitPageFlip(_THIS, SDL_WindowData *windata, int timeout);
/****************************************************************************/
/* SDL_VideoDevice functions declaration */

View File

@ -397,7 +397,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;
}

View File

@ -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: */