[Video/KMSDRM]: Add Vulkan support to the KMSDRM backend.

This commit is contained in:
Manuel Alfayate Corchete 2020-12-18 22:53:51 +01:00
parent cbe13d232d
commit f60f8d5d84
8 changed files with 1230 additions and 564 deletions

View File

@ -19,6 +19,7 @@
misrepresented as being the original software.
3. This notice may not be removed or altered from any source distribution.
*/
#include "../../SDL_internal.h"
#if SDL_VIDEO_DRIVER_KMSDRM
@ -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"
@ -54,8 +56,10 @@ drm_atomic_movecursor(KMSDRM_CursorData *curdata, uint16_t x, uint16_t y)
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);
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;
}
@ -66,8 +70,9 @@ drm_atomic_movecursor(KMSDRM_CursorData *curdata, uint16_t x, uint16_t y)
/* 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. */
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;
@ -93,35 +98,28 @@ KMSDRM_CreateDefaultCursor(void)
return SDL_CreateCursor(default_cdata, default_cmask, DEFAULT_CWIDTH, DEFAULT_CHEIGHT, DEFAULT_CHOTX, DEFAULT_CHOTY);
}
/* Create a GBM cursor from a surface, which means creating a hardware cursor.
Most programs use software cursors, but protracker-clone for example uses
an optional hardware cursor. */
/* This simply copies over the cursor bitmap from the SDLSurface we receive to the
cursor GBM BO. Setting up the cursor plane, creating the cursor FB BO, etc.. is
done in KMSDRM_InitMouse(): when we get here. everything must be ready. */
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_DisplayData *dispdata = (SDL_DisplayData *)SDL_GetDisplayDriverData(0);
KMSDRM_CursorData *curdata;
SDL_Cursor *cursor;
uint64_t usable_cursor_w, usable_cursor_h;
uint32_t bo_stride, pixel;
uint32_t *buffer = NULL;
size_t bufsize;
unsigned int i, j;
/* 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
/* 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);
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 NULL;
}
cursor = (SDL_Cursor *) SDL_calloc(1, sizeof(*cursor));
if (!cursor) {
SDL_OutOfMemory();
@ -134,34 +132,13 @@ KMSDRM_CreateCursor(SDL_Surface * surface, int hot_x, int hot_y)
return NULL;
}
/* Find out what GBM cursor size is recommended by the driver. */
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;
}
/* 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 = dispdata->cursor_w;
curdata->h = dispdata->cursor_h;
curdata->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 (!curdata->bo) {
SDL_SetError("Could not create GBM cursor BO");
goto cleanup;
}
bo_stride = KMSDRM_gbm_bo_get_stride(curdata->bo);
bo_stride = KMSDRM_gbm_bo_get_stride(dispdata->cursor_bo);
bufsize = bo_stride * curdata->h;
/* Always use a temp buffer: it serves the purpose of storing the
@ -197,7 +174,7 @@ KMSDRM_CreateCursor(SDL_Surface * surface, int hot_x, int hot_y)
SDL_UnlockSurface(surface);
}
if (KMSDRM_gbm_bo_write(curdata->bo, buffer, bufsize)) {
if (KMSDRM_gbm_bo_write(dispdata->cursor_bo, buffer, bufsize)) {
SDL_SetError("Could not write to GBM cursor BO");
goto cleanup;
}
@ -218,11 +195,9 @@ cleanup:
SDL_free(cursor);
}
if (curdata) {
if (curdata->bo) {
KMSDRM_gbm_bo_destroy(curdata->bo);
}
SDL_free(curdata);
}
return NULL;
}
@ -263,7 +238,8 @@ KMSDRM_ShowCursor(SDL_Cursor * cursor)
and SDL is stored in mouse->cur_cursor. */
if (mouse->cur_cursor && mouse->cur_cursor->driverdata) {
if (dispdata && dispdata->cursor_plane) {
info.plane = dispdata->cursor_plane; /* The rest of the members are zeroed. */
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.");
@ -288,18 +264,14 @@ KMSDRM_ShowCursor(SDL_Cursor * cursor)
curdata = (KMSDRM_CursorData *) cursor->driverdata;
if (!curdata || !curdata->bo) {
if (!curdata || !dispdata->cursor_bo) {
return SDL_SetError("Cursor not initialized properly.");
}
curdata->crtc_id = dispdata->crtc->crtc->crtc_id;
curdata->plane = dispdata->cursor_plane;
curdata->video = video_device;
fb = KMSDRM_FBFromBO(curdata->video, curdata->bo);
fb = KMSDRM_FBFromBO(video_device, dispdata->cursor_bo);
info.plane = dispdata->cursor_plane;
info.crtc_id = curdata->crtc_id;
info.crtc_id = dispdata->crtc->crtc->crtc_id;
info.fb_id = fb->fb_id;
info.src_w = curdata->w;
info.src_h = curdata->h;
@ -313,39 +285,17 @@ KMSDRM_ShowCursor(SDL_Cursor * cursor)
if (drm_atomic_commit(display->device, SDL_TRUE)) {
return SDL_SetError("Failed atomic commit in KMSDRM_ShowCursor.");
}
return 0;
}
/* Unset the cursor from the cursor plane, and ONLY WHEN THAT'S DONE,
DONE FOR REAL, and not only requested, destroy it by destroying the curso BO.
Destroying the cursor BO is an special an delicate situation,
because drm_atomic_set_plane_props() returns immediately, and we DON'T
want to get to gbm_bo_destroy() before the prop changes requested
in drm_atomic_set_plane_props() have effectively been done. So we
issue a BLOCKING atomic_commit here to avoid that situation.
REMEMBER you yan issue an atomic_commit whenever you want, and
the changes requested until that moment (for any planes, crtcs, etc.)
will be done. */
/* 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 = NULL;
SDL_VideoDevice *video_device = SDL_GetVideoDevice();
KMSDRM_PlaneInfo info = {0};
/* Even if the cursor is not ours, free it. */
if (cursor) {
curdata = (KMSDRM_CursorData *) cursor->driverdata;
if (video_device && curdata->bo && curdata->plane) {
info.plane = curdata->plane; /* The other members are zeroed. */
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_FreeCursor.");
}
KMSDRM_gbm_bo_destroy(curdata->bo);
curdata->bo = NULL;
}
/* Even if the cursor is not ours, free it. */
SDL_free(cursor->driverdata);
SDL_free(cursor);
}
@ -365,6 +315,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. */
@ -372,7 +323,7 @@ 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 (dispdata->cursor_bo) {
if (drm_atomic_movecursor(curdata, x, y)) {
return SDL_SetError("drm_atomic_movecursor() failed.");
}
@ -382,7 +333,8 @@ KMSDRM_WarpMouseGlobal(int x, int y)
} else {
return SDL_SetError("No mouse or current cursor.");
}
return 0;
return 0;
}
void
@ -392,8 +344,11 @@ KMSDRM_InitMouse(_THIS)
* 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;
@ -402,20 +357,96 @@ KMSDRM_InitMouse(_THIS)
mouse->WarpMouse = KMSDRM_WarpMouse;
mouse->WarpMouseGlobal = KMSDRM_WarpMouseGlobal;
/* Init cursor plane, if we haven't yet. */
/***************************************************************************/
/* 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)
{
/* Free the plane on which the cursor was being shown. */
SDL_VideoDevice *video_device = SDL_GetVideoDevice();
SDL_DisplayData *dispdata = (SDL_DisplayData *)SDL_GetDisplayDriverData(0);
free_plane(&dispdata->cursor_plane);
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 */
@ -429,11 +460,13 @@ KMSDRM_MoveCursor(SDL_Cursor * cursor)
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;
/* 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() 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. */
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.");

View File

@ -33,19 +33,13 @@
/* Driverdata with driver-side info about the cursor. */
typedef struct _KMSDRM_CursorData
{
struct gbm_bo *bo;
struct plane *plane;
uint32_t crtc_id;
uint16_t hot_x, hot_y;
uint16_t w, h;
/* The video devide implemented on SDL_kmsdrmvideo.c
* to be used as _THIS pointer in SDL_kmsdrmvideo.c
* functions that need it. */
SDL_VideoDevice *video;
} KMSDRM_CursorData;
extern void KMSDRM_InitMouse(_THIS);
extern void KMSDRM_QuitMouse(_THIS);
extern void KMSDRM_DeinitMouse(_THIS);
#endif /* SDL_KMSDRM_mouse_h_ */

View File

@ -59,11 +59,24 @@ KMSDRM_GLES_DefaultProfileConfig(_THIS, int *mask, int *major, int *minor)
#endif
}
int
KMSDRM_GLES_LoadLibrary(_THIS, const char *path) {
/* 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)
@ -94,6 +107,7 @@ static EGLSyncKHR create_fence(int fd, _THIS)
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);
@ -342,16 +356,6 @@ KMSDRM_GLES_SwapWindow(_THIS, SDL_Window * window)
{
SDL_WindowData *windata = ((SDL_WindowData *) window->driverdata);
/* Get the EGL context, now that SDL_CreateRenderer() has already been called,
and call eglMakeCurrent() on it and the EGL surface. */
#if SDL_VIDEO_OPENGL_EGL
if (windata->egl_context_pending) {
EGLContext egl_context = (EGLContext)SDL_GL_GetCurrentContext();
SDL_EGL_MakeCurrent(_this, windata->egl_surface, egl_context);
windata->egl_context_pending = SDL_FALSE;
}
#endif
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)) ||

View File

@ -32,7 +32,6 @@
/* 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

File diff suppressed because it is too large Load Diff

View File

@ -85,6 +85,8 @@ typedef struct SDL_VideoData
{
int devindex; /* device index that was passed on creation */
int drm_fd; /* DRM file desc */
char devpath[32]; /* DRM dev path. */
struct gbm_device *gbm_dev;
SDL_Window **windows;
@ -117,6 +119,7 @@ typedef struct connector {
typedef struct SDL_DisplayData
{
drmModeModeInfo mode;
drmModeModeInfo preferred_mode;
uint32_t atomic_flags;
plane *display_plane;
@ -141,6 +144,14 @@ typedef struct SDL_DisplayData
dumb_buffer *dumb_buffer;
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;
@ -167,12 +178,9 @@ typedef struct SDL_WindowData
int32_t output_h;
int32_t output_x;
/* This is for deferred eglMakeCurrent() call: we can't call it until
the EGL context is available, but we need the EGL surface sooner. */
SDL_bool egl_context_pending;
/* This dictates what approach we'll use for SwapBuffers. */
int (*swap_window)(_THIS, SDL_Window * window);
} SDL_WindowData;
typedef struct SDL_DisplayModeData

View File

@ -0,0 +1,395 @@
/*
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;
/* 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);
/* For now, just grab the first physical device (gpu = physical_device in vkcube example).*/
/* TODO What happens on video systems with multiple display ports like the Rpi4 ? */
gpu = physical_devices[0];
/* Get the display count of the phsysical device. */
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. */
/* TODO What happens on video systems with multiple display ports like the Rpi4 ? */
vkGetDisplayModePropertiesKHR(gpu,
displays_props[0].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[0].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[0].display,
&mode_count, modes_props);
/* Get the planes count of the physical device. */
/* TODO: find out if we need other planes. */
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 matching 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 (int 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[0].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;
display_plane_surface_create_info.planeIndex = 0; /* For now, simply use the first plane. */
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: */

View File

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