Skip to content

Commit

Permalink
Fixed bug 3690 - SDL2 KMS/DRM render context support
Browse files Browse the repository at this point in the history
Manuel

The attached patch adds support for KMS/DRM context graphics.

It builds with no problem on X86_64 GNU/Linux systems, provided the needed libraries are present, and on ARM GNU/Linux systems that have KMS/DRM support and a GLES2 implementation.
Tested on Raspberry Pi: KMS/DRM is what the Raspberry Pi will use as default in the near future, once the propietary DispmanX API by Broadcom is overtaken by open graphics stack, it's possible to boot current Raspbian system in KMS mode by adding "dtoverlay=vc4-kms-v3d" to config.txt on Raspbian's boot partition.
X86 systems use KMS right away in every current GNU/Linux system.

Simple build instructions:

$./autogen.sh
$./configure --enable-video-kmsdrm
$make
  • Loading branch information
slouken committed Aug 2, 2017
1 parent 2ffd6d0 commit 56363eb
Show file tree
Hide file tree
Showing 28 changed files with 2,059 additions and 39 deletions.
3 changes: 3 additions & 0 deletions CMakeLists.txt
Expand Up @@ -314,6 +314,8 @@ set_option(VIDEO_COCOA "Use Cocoa video driver" ${APPLE})
set_option(DIRECTX "Use DirectX for Windows audio/video" ${WINDOWS})
set_option(RENDER_D3D "Enable the Direct3D render driver" ${WINDOWS})
set_option(VIDEO_VIVANTE "Use Vivante EGL video driver" ${UNIX_SYS})
set_option(VIDEO_KMSDRM "Use KMS DRM video driver" ${UNIX_SYS})
dep_option(KMSDRM_SHARED "Dynamically load KMS DRM support" ON "VIDEO_KMSDRM" OFF)

# TODO: We should (should we?) respect cmake's ${BUILD_SHARED_LIBS} flag here
# The options below are for compatibility to configure's default behaviour.
Expand Down Expand Up @@ -917,6 +919,7 @@ elseif(UNIX AND NOT APPLE AND NOT ANDROID)
CheckOpenGLESX11()
CheckWayland()
CheckVivante()
CheckKMSDRM()
endif()

if(LINUX)
Expand Down
43 changes: 43 additions & 0 deletions cmake/sdlchecks.cmake
Expand Up @@ -1149,3 +1149,46 @@ macro(CheckRPI)
endif(SDL_VIDEO AND HAVE_VIDEO_RPI)
endif(VIDEO_RPI)
endmacro(CheckRPI)

# Requires:
# - EGL
# - PkgCheckModules
# Optional:
# - KMSDRM_SHARED opt
# - HAVE_DLOPEN opt
macro(CheckKMSDRM)
if(VIDEO_KMSDRM)
pkg_check_modules(KMSDRM libdrm gbm egl)
if(KMSDRM_FOUND)
link_directories(
${KMSDRM_LIBRARY_DIRS}
)
include_directories(
${KMSDRM_INCLUDE_DIRS}
)
set(HAVE_VIDEO_KMSDRM TRUE)
set(HAVE_SDL_VIDEO TRUE)

file(GLOB KMSDRM_SOURCES ${SDL2_SOURCE_DIR}/src/video/kmsdrm/*.c)
set(SOURCE_FILES ${SOURCE_FILES} ${KMSDRM_SOURCES})

list(APPEND EXTRA_CFLAGS ${KMSDRM_CLFLAGS})

set(SDL_VIDEO_DRIVER_KMSDRM 1)

if(KMSDRM_SHARED)
if(NOT HAVE_DLOPEN)
message_warn("You must have SDL_LoadObject() support for dynamic KMS/DRM loading")
else()
FindLibraryAndSONAME(drm)
FindLibraryAndSONAME(gbm)
set(SDL_VIDEO_DRIVER_KMSDRM_DYNAMIC "\"${DRM_LIB_SONAME}\"")
set(SDL_VIDEO_DRIVER_KMSDRM_DYNAMIC_GBM "\"${GBM_LIB_SONAME}\"")
set(HAVE_KMSDRM_SHARED TRUE)
endif()
else()
set(EXTRA_LIBS ${KMSDRM_LIBRARIES} ${EXTRA_LIBS})
endif()
endif()
endif()
endmacro()
76 changes: 76 additions & 0 deletions configure.in
Expand Up @@ -2084,6 +2084,81 @@ AC_MSG_WARN("directfb $directfb_lib")
fi
}

dnl Find KMSDRM
CheckKMSDRM()
{
AC_ARG_ENABLE(video-kmsdrm,
AC_HELP_STRING([--enable-video-kmsdrm], [use KMSDRM video driver [[default=no]]]),
, enable_video_kmsdrm=no)

if test x$enable_video = xyes -a x$enable_video_kmsdrm = xyes; then
video_kmsdrm=no
libdrm_avail=no
libgbm_avail=no

LIBDRM_REQUIRED_VERSION=2.4.46
LIBGBM_REQUIRED_VERSION=9.0.0

AC_PATH_PROG(PKG_CONFIG, pkg-config, no)
if test x$PKG_CONFIG != xno; then
if $PKG_CONFIG --atleast-pkgconfig-version 0.7; then
if $PKG_CONFIG --atleast-version $LIBDRM_REQUIRED_VERSION libdrm; then
LIBDRM_CFLAGS=`$PKG_CONFIG --cflags libdrm`
LIBDRM_LIBS=`$PKG_CONFIG --libs libdrm`
LIBDRM_PREFIX=`$PKG_CONFIG --variable=prefix libdrm`
libdrm_avail=yes
fi
if $PKG_CONFIG --atleast-version $LIBGBM_REQUIRED_VERSION gbm; then
LIBGBM_CFLAGS=`$PKG_CONFIG --cflags gbm`
LIBGBM_LIBS=`$PKG_CONFIG --libs gbm`
LIBGBM_PREFIX=`$PKG_CONFIG --variable=prefix gbm`
libgbm_avail=yes
fi
if test x$libdrm_avail = xyes -a x$libgbm_avail = xyes; then
video_kmsdrm=yes
fi

AC_MSG_CHECKING(for libdrm $LIBDRM_REQUIRED_VERSION library for kmsdrm support)
AC_MSG_RESULT($libdrm_avail)
AC_MSG_CHECKING(for libgbm $LIBGBM_REQUIRED_VERSION library for kmsdrm support)
AC_MSG_RESULT($libgbm_avail)

if test x$video_kmsdrm = xyes; then
AC_ARG_ENABLE(kmsdrm-shared,
AC_HELP_STRING([--enable-kmsdrm-shared], [dynamically load kmsdrm support [[default=yes]]]),
, enable_kmsdrm_shared=yes)

AC_DEFINE(SDL_VIDEO_DRIVER_KMSDRM, 1, [ ])
SOURCES="$SOURCES $srcdir/src/video/kmsdrm/*.c"
EXTRA_CFLAGS="$EXTRA_CFLAGS $LIBDRM_CFLAGS $LIBGBM_CFLAGS"

AC_MSG_CHECKING(for kmsdrm dynamic loading support)
kmsdrm_shared=no
drm_lib=[`find_lib "libdrm.so.*" "$DRM_LIBS"`]
gbm_lib=[`find_lib "libgbm.so.*" "$DRM_LIBS"`]
if test x$have_loadso != xyes && \
test x$enable_kmsdrm_shared = xyes; then
AC_MSG_WARN([You must have SDL_LoadObject() support for dynamic kmsdrm loading])
fi
if test x$have_loadso = xyes && \
test x$enable_kmsdrm_shared = xyes && test x$drm_lib != x && test x$gbm_lib != x; then
kmsdrm_shared=yes
AC_DEFINE_UNQUOTED(SDL_VIDEO_DRIVER_KMSDRM_DYNAMIC, "$drm_lib", [ ])
AC_DEFINE_UNQUOTED(SDL_VIDEO_DRIVER_KMSDRM_DYNAMIC_GBM, "$gbm_lib", [ ])
AC_DEFINE_UNQUOTED(HAVE_KMSDRM_SHARED, "TRUE", [ ])
SUMMARY_video="${SUMMARY_video} kmsdrm(dynamic)"
else
EXTRA_LDFLAGS="$EXTRA_LDFLAGS $LIBDRM_LIBS $LIBGBM_LIBS"
SUMMARY_video="${SUMMARY_video} kmsdrm"
fi
AC_MSG_RESULT($kmsdrm_shared)
have_video=yes
fi
fi
fi
fi
}

dnl rcg04172001 Set up the Null video driver.
CheckDummyVideo()
{
Expand Down Expand Up @@ -3148,6 +3223,7 @@ case "$host" in
CheckLibSampleRate
CheckX11
CheckDirectFB
CheckKMSDRM
CheckOpenGLX11
CheckOpenGLESX11
CheckMir
Expand Down
4 changes: 4 additions & 0 deletions include/SDL_config.h.cmake
Expand Up @@ -291,6 +291,10 @@
#cmakedefine SDL_VIDEO_DRIVER_VIVANTE @SDL_VIDEO_DRIVER_VIVANTE@
#cmakedefine SDL_VIDEO_DRIVER_VIVANTE_VDK @SDL_VIDEO_DRIVER_VIVANTE_VDK@

#cmakedefine SDL_VIDEO_DRIVER_KMSDRM @SDL_VIDEO_DRIVER_KMSDRM@
#cmakedefine SDL_VIDEO_DRIVER_KMSDRM_DYNAMIC @SDL_VIDEO_DRIVER_KMSDRM_DYNAMIC@
#cmakedefine SDL_VIDEO_DRIVER_KMSDRM_DYNAMIC_GBM @SDL_VIDEO_DRIVER_KMSDRM_DYNAMIC_GBM@

#cmakedefine SDL_VIDEO_DRIVER_WAYLAND_QT_TOUCH @SDL_VIDEO_DRIVER_WAYLAND_QT_TOUCH@
#cmakedefine SDL_VIDEO_DRIVER_WAYLAND_DYNAMIC @SDL_VIDEO_DRIVER_WAYLAND_DYNAMIC@
#cmakedefine SDL_VIDEO_DRIVER_WAYLAND_DYNAMIC_EGL @SDL_VIDEO_DRIVER_WAYLAND_DYNAMIC_EGL@
Expand Down
3 changes: 3 additions & 0 deletions include/SDL_config.h.in
Expand Up @@ -294,6 +294,9 @@
#undef SDL_VIDEO_DRIVER_MIR_DYNAMIC_XKBCOMMON
#undef SDL_VIDEO_DRIVER_X11
#undef SDL_VIDEO_DRIVER_RPI
#undef SDL_VIDEO_DRIVER_KMSDRM
#undef SDL_VIDEO_DRIVER_KMSDRM_DYNAMIC
#undef SDL_VIDEO_DRIVER_KMSDRM_DYNAMIC_GBM
#undef SDL_VIDEO_DRIVER_ANDROID
#undef SDL_VIDEO_DRIVER_EMSCRIPTEN
#undef SDL_VIDEO_DRIVER_X11_DYNAMIC
Expand Down
140 changes: 112 additions & 28 deletions src/video/SDL_egl.c
Expand Up @@ -30,6 +30,7 @@
#endif

#include "SDL_sysvideo.h"
#include "SDL_log.h"
#include "SDL_egl_c.h"
#include "SDL_loadso.h"
#include "SDL_hints.h"
Expand Down Expand Up @@ -114,38 +115,82 @@ int SDL_EGL_SetErrorEx(const char * message, const char * eglFunctionName, EGLin
}

/* EGL implementation of SDL OpenGL ES support */
#ifdef EGL_KHR_create_context
static int SDL_EGL_HasExtension(_THIS, const char *ext)
typedef enum {
SDL_EGL_DISPLAY_EXTENSION,
SDL_EGL_CLIENT_EXTENSION
} SDL_EGL_ExtensionType;

static SDL_bool SDL_EGL_HasExtension(_THIS, SDL_EGL_ExtensionType type, const char *ext)
{
int i;
int len = 0;
size_t ext_len;
const char *exts;
const char *ext_word;
const char *ext_override;
const char *egl_extstr;
const char *ext_start;

/* Invalid extensions can be rejected early */
if (ext == NULL || *ext == 0 || SDL_strchr(ext, ' ') != NULL) {
/* SDL_LogDebug(SDL_LOG_CATEGORY_VIDEO, "SDL_EGL_HasExtension: Invalid EGL extension"); */
return SDL_FALSE;
}

/* Extensions can be masked with an environment variable.
* Unlike the OpenGL override, this will use the set bits of an integer
* to disable the extension.
* Bit Action
* 0 If set, the display extension is masked and not present to SDL.
* 1 If set, the client extension is masked and not present to SDL.
*/
ext_override = SDL_getenv(ext);
if (ext_override != NULL) {
int disable_ext = SDL_atoi(ext_override);
if (disable_ext & 0x01 && type == SDL_EGL_DISPLAY_EXTENSION) {
return SDL_FALSE;
} else if (disable_ext & 0x02 && type == SDL_EGL_CLIENT_EXTENSION) {
return SDL_FALSE;
}
}

ext_len = SDL_strlen(ext);
exts = _this->egl_data->eglQueryString(_this->egl_data->egl_display, EGL_EXTENSIONS);
switch (type) {
case SDL_EGL_DISPLAY_EXTENSION:
egl_extstr = _this->egl_data->eglQueryString(_this->egl_data->egl_display, EGL_EXTENSIONS);
break;
case SDL_EGL_CLIENT_EXTENSION:
/* EGL_EXT_client_extensions modifies eglQueryString to return client extensions
* if EGL_NO_DISPLAY is passed. Implementations without it are required to return NULL.
* This behavior is included in EGL 1.5.
*/
egl_extstr = _this->egl_data->eglQueryString(EGL_NO_DISPLAY, EGL_EXTENSIONS);
break;
default:
/* SDL_LogDebug(SDL_LOG_CATEGORY_VIDEO, "SDL_EGL_HasExtension: Invalid extension type"); */
return SDL_FALSE;
}

if (exts) {
ext_word = exts;
if (egl_extstr != NULL) {
ext_start = egl_extstr;

for (i = 0; exts[i] != 0; i++) {
if (exts[i] == ' ') {
if (ext_len == len && !SDL_strncmp(ext_word, ext, len)) {
return 1;
while (*ext_start) {
ext_start = SDL_strstr(ext_start, ext);
if (ext_start == NULL) {
return SDL_FALSE;
}
/* Check if the match is not just a substring of one of the extensions */
if (ext_start == egl_extstr || *(ext_start - 1) == ' ') {
if (ext_start[ext_len] == ' ' || ext_start[ext_len] == 0) {
return SDL_TRUE;
}

len = 0;
ext_word = &exts[i + 1];
} else {
len++;
}
/* If the search stopped in the middle of an extension, skip to the end of it */
ext_start += ext_len;
while (*ext_start != ' ' && *ext_start != 0) {
ext_start++;
}
}
}

return 0;
return SDL_FALSE;
}
#endif /* EGL_KHR_create_context */

void *
SDL_EGL_GetProcAddress(_THIS, const char *proc)
Expand Down Expand Up @@ -196,10 +241,11 @@ SDL_EGL_UnloadLibrary(_THIS)
}

int
SDL_EGL_LoadLibrary(_THIS, const char *egl_path, NativeDisplayType native_display)
SDL_EGL_LoadLibrary(_THIS, const char *egl_path, NativeDisplayType native_display, EGLenum platform)
{
void *dll_handle = NULL, *egl_dll_handle = NULL; /* The naming is counter intuitive, but hey, I just work here -- Gabriel */
const char *path = NULL;
int egl_version_major = 0, egl_version_minor = 0;
#if SDL_VIDEO_DRIVER_WINDOWS || SDL_VIDEO_DRIVER_WINRT
const char *d3dcompiler;
#endif
Expand Down Expand Up @@ -305,9 +351,41 @@ SDL_EGL_LoadLibrary(_THIS, const char *egl_path, NativeDisplayType native_displa
LOAD_FUNC(eglQueryString);
LOAD_FUNC(eglGetError);

if (_this->egl_data->eglQueryString) {
/* EGL 1.5 allows querying for client version */
const char *egl_version = _this->egl_data->eglQueryString(EGL_NO_DISPLAY, EGL_VERSION);
if (egl_version != NULL) {
if (SDL_sscanf(egl_version, "%d.%d", &egl_version_major, &egl_version_minor) != 2) {
egl_version_major = 0;
egl_version_minor = 0;
SDL_LogWarn(SDL_LOG_CATEGORY_VIDEO, "Could not parse EGL version string: %s", egl_version);
}
}
}

if (egl_version_major == 1 && egl_version_minor == 5) {
LOAD_FUNC(eglGetPlatformDisplay);
}

_this->egl_data->egl_display = EGL_NO_DISPLAY;
#if !defined(__WINRT__)
_this->egl_data->egl_display = _this->egl_data->eglGetDisplay(native_display);
if (!_this->egl_data->egl_display) {
if (platform) {
if (egl_version_major == 1 && egl_version_minor == 5) {
_this->egl_data->egl_display = _this->egl_data->eglGetPlatformDisplay(platform, native_display, NULL);
} else {
if (SDL_EGL_HasExtension(_this, SDL_EGL_CLIENT_EXTENSION, "EGL_EXT_platform_base")) {
_this->egl_data->eglGetPlatformDisplayEXT = SDL_EGL_GetProcAddress(_this, "eglGetPlatformDisplayEXT");
if (_this->egl_data->eglGetPlatformDisplayEXT) {
_this->egl_data->egl_display = _this->egl_data->eglGetPlatformDisplayEXT(platform, native_display, NULL);
}
}
}
}
/* Try the implementation-specific eglGetDisplay even if eglGetPlatformDisplay fails */
if (_this->egl_data->egl_display == EGL_NO_DISPLAY) {
_this->egl_data->egl_display = _this->egl_data->eglGetDisplay(native_display);
}
if (_this->egl_data->egl_display == EGL_NO_DISPLAY) {
return SDL_SetError("Could not get EGL display");
}

Expand All @@ -328,13 +406,19 @@ SDL_EGL_LoadLibrary(_THIS, const char *egl_path, NativeDisplayType native_displa
int
SDL_EGL_ChooseConfig(_THIS)
{
/* 64 seems nice. */
/* 64 seems nice. */
EGLint attribs[64];
EGLint found_configs = 0, value;
#ifdef SDL_VIDEO_DRIVER_KMSDRM
/* Intel EGL on KMS/DRM (al least) returns invalid configs that confuse the bitdiff search used */
/* later in this function, so we simply use the first one when using the KMSDRM driver for now. */
EGLConfig configs[1];
#else
/* 128 seems even nicer here */
EGLConfig configs[128];
#endif
int i, j, best_bitdiff = -1, bitdiff;

if (!_this->egl_data) {
/* The EGL library wasn't loaded, SDL_GetError() should have info */
return -1;
Expand Down Expand Up @@ -379,7 +463,7 @@ SDL_EGL_ChooseConfig(_THIS)

if (_this->gl_config.framebuffer_srgb_capable) {
#ifdef EGL_KHR_gl_colorspace
if (SDL_EGL_HasExtension(_this, "EGL_KHR_gl_colorspace")) {
if (SDL_EGL_HasExtension(_this, SDL_EGL_DISPLAY_EXTENSION, "EGL_KHR_gl_colorspace")) {
attribs[i++] = EGL_GL_COLORSPACE_KHR;
attribs[i++] = EGL_GL_COLORSPACE_SRGB_KHR;
} else
Expand All @@ -393,7 +477,7 @@ SDL_EGL_ChooseConfig(_THIS)
if (_this->gl_config.profile_mask == SDL_GL_CONTEXT_PROFILE_ES) {
#ifdef EGL_KHR_create_context
if (_this->gl_config.major_version >= 3 &&
SDL_EGL_HasExtension(_this, "EGL_KHR_create_context")) {
SDL_EGL_HasExtension(_this, SDL_EGL_DISPLAY_EXTENSION, "EGL_KHR_create_context")) {
attribs[i++] = EGL_OPENGL_ES3_BIT_KHR;
} else
#endif
Expand Down Expand Up @@ -495,7 +579,7 @@ SDL_EGL_CreateContext(_THIS, EGLSurface egl_surface)
/* The Major/minor version, context profiles, and context flags can
* only be specified when this extension is available.
*/
if (SDL_EGL_HasExtension(_this, "EGL_KHR_create_context")) {
if (SDL_EGL_HasExtension(_this, SDL_EGL_DISPLAY_EXTENSION, "EGL_KHR_create_context")) {
attribs[attr++] = EGL_CONTEXT_MAJOR_VERSION_KHR;
attribs[attr++] = major_version;
attribs[attr++] = EGL_CONTEXT_MINOR_VERSION_KHR;
Expand Down

0 comments on commit 56363eb

Please sign in to comment.