From 53e86dd041fcaea5f67ce93a58d3f117364a3807 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Vladim=C3=ADr=20Vondru=C5=A1?= Date: Tue, 20 Aug 2019 20:57:18 +0200 Subject: [PATCH] Platform: prefer EGL_EXT_device_enumeration to find an EGL display. Makes things work on Mesa 19.2 when DISPLAY is not defined. And opens the possibilities for *sane* GPU selection. No more environment variables, globally exported symbols or other BAD SHIT. --- doc/changelog.dox | 6 ++ .../Platform/WindowlessEglApplication.cpp | 74 ++++++++++++++++++- .../Platform/WindowlessEglApplication.h | 9 +++ 3 files changed, 87 insertions(+), 2 deletions(-) diff --git a/doc/changelog.dox b/doc/changelog.dox index 48eb8edfd..23fd67dfd 100644 --- a/doc/changelog.dox +++ b/doc/changelog.dox @@ -401,6 +401,12 @@ See also: application. Users are encouraged to call @ref Platform::BasicScreen::hasApplication() to check if the application is accessible. +- @ref Platform::WindowlessEglApplication now prefers to use the + @m_class{m-doc-external} [EGL_EXT_device_enumeration](https://www.khronos.org/registry/EGL/extensions/EXT/EGL_EXT_device_enumeration.txt), + @m_class{m-doc-external} [EGL_EXT_platform_base](https://www.khronos.org/registry/EGL/extensions/EXT/EGL_EXT_platform_base.txt) and + @m_class{m-doc-external} [EGL_EXT_platform_device](https://www.khronos.org/registry/EGL/extensions/EXT/EGL_EXT_platform_device.txt) extensions where + available instead of `EGL_DEFAULT_DISPLAY` to work better on headless + setups @subsubsection changelog-latest-changes-text Text library diff --git a/src/Magnum/Platform/WindowlessEglApplication.cpp b/src/Magnum/Platform/WindowlessEglApplication.cpp index c4c8ce2df..a206ff0b5 100644 --- a/src/Magnum/Platform/WindowlessEglApplication.cpp +++ b/src/Magnum/Platform/WindowlessEglApplication.cpp @@ -25,6 +25,7 @@ #include "WindowlessEglApplication.h" +#include #include #include #include @@ -38,11 +39,80 @@ #include #endif +#ifndef EGL_EXT_device_base +typedef void *EGLDeviceEXT; +#endif + +#ifndef EGL_EXT_platform_device +#define EGL_PLATFORM_DEVICE_EXT 0x313F +#endif + namespace Magnum { namespace Platform { +namespace { + +bool extensionSupported(const char* const extensions, Containers::ArrayView extension) { + CORRADE_INTERNAL_ASSERT(extensions); + const char* pos = std::strstr(extensions, extension); + /* Extension is supported if its string is delimited by a space or end of + the extension list. The extension.size() is the whole C array including + a 0-terminator, so subtract 1 to look at one character after. */ + return pos && (pos[extension.size() - 1] == ' ' || pos[extension.size() - 1] == '\0'); +} + +} + WindowlessEglContext::WindowlessEglContext(const Configuration& configuration, GLContext* const magnumContext) { - /* Initialize */ - _display = eglGetDisplay(EGL_DEFAULT_DISPLAY); + /* If relevant extensions are supported, try to find some display using + those APIs, as that works reliably also when running headless. This + would ideally use EGL 1.5 APIs but since we still want to support + systems which either have old EGL headers or old EGL implementation, + we'd need to have a code path for 1.4 *and* 1.5, plus do complicated + version parsing from a string. Not feeling like doing that today, no. */ + const char* const extensions = eglQueryString(EGL_NO_DISPLAY, EGL_EXTENSIONS); + if(extensions && + /* eglQueryDevicesEXT() */ + extensionSupported(extensions, "EGL_EXT_device_enumeration") && + + /* eglGetPlatformDisplayEXT() */ + extensionSupported(extensions, "EGL_EXT_platform_base") && + + /* EGL_PLATFORM_DEVICE_EXT (FFS, why it has to be scattered over a + thousand extensions?!). This is supported only since Mesa 19.2. */ + extensionSupported(extensions, "EGL_EXT_platform_device") + ) { + EGLint count; + auto eglQueryDevices = reinterpret_cast(eglGetProcAddress("eglQueryDevicesEXT")); + if(!eglQueryDevices(0, nullptr, &count)) { + Error{} << "Platform::WindowlessEglApplication::tryCreateContext(): cannot query EGL devices:" << Implementation::eglErrorString(eglGetError()); + return; + } + + if(!count) { + Error{} << "Platform::WindowlessEglApplication::tryCreateContext(): no EGL devices found"; + return; + } + + if(magnumContext && (magnumContext->internalFlags() >= GL::Context::InternalFlag::DisplayVerboseInitializationLog)) { + Debug{} << "Platform::WindowlessEglApplication: found" << count << "EGL devices, choosing the first one"; + } + + /* Assuming the same thing won't suddenly start failing when called the + second time */ + EGLDeviceEXT device; + CORRADE_INTERNAL_ASSERT_OUTPUT(eglQueryDevices(1, &device, &count)); + + if(!(_display = reinterpret_cast(eglGetProcAddress("eglGetPlatformDisplayEXT"))(EGL_PLATFORM_DEVICE_EXT, device, nullptr))) { + Error{} << "Platform::WindowlessEglApplication::tryCreateContext(): cannot get platform display for a device:" << Implementation::eglErrorString(eglGetError()); + return; + } + + /* Otherwise initialize the classic way */ + } else if(!(_display = eglGetDisplay(EGL_DEFAULT_DISPLAY))) { + Error{} << "Platform::WindowlessEglApplication::tryCreateContext(): cannot get default EGL display:" << Implementation::eglErrorString(eglGetError()); + return; + } + if(!eglInitialize(_display, nullptr, nullptr)) { Error() << "Platform::WindowlessEglApplication::tryCreateContext(): cannot initialize EGL:" << Implementation::eglErrorString(eglGetError()); return; diff --git a/src/Magnum/Platform/WindowlessEglApplication.h b/src/Magnum/Platform/WindowlessEglApplication.h index 38366d8c5..0816fc17a 100644 --- a/src/Magnum/Platform/WindowlessEglApplication.h +++ b/src/Magnum/Platform/WindowlessEglApplication.h @@ -349,6 +349,15 @@ MAGNUM_WINDOWLESSEGLAPPLICATION_MAIN(MyApplication) If no other application header is included, this class is also aliased to @cpp Platform::WindowlessApplication @ce and the macro is aliased to @cpp MAGNUM_WINDOWLESSAPPLICATION_MAIN() @ce to simplify porting. + +@subsection Platform-WindowlessEglApplication-usage-device-enumeration EGL device enumeration + +The application prefers to use the @m_class{m-doc-external} +[EGL_EXT_device_enumeration](https://www.khronos.org/registry/EGL/extensions/EXT/EGL_EXT_device_enumeration.txt), +@m_class{m-doc-external} [EGL_EXT_platform_base](https://www.khronos.org/registry/EGL/extensions/EXT/EGL_EXT_platform_base.txt) and +@m_class{m-doc-external} [EGL_EXT_platform_device](https://www.khronos.org/registry/EGL/extensions/EXT/EGL_EXT_platform_device.txt) +where available instead of `EGL_DEFAULT_DISPLAY` to work better on headless +setups. The application always chooses the first found device. */ class WindowlessEglApplication { public: