From f7d739058bb5dd71dc1bb3b36b0f908aa1a22f6a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Vladim=C3=ADr=20Vondru=C5=A1?= Date: Thu, 10 Oct 2019 17:30:54 +0200 Subject: [PATCH] Platform: device selection in WindowlessEglApplication. --- doc/changelog.dox | 13 ++-- src/Magnum/GL/Context.h | 4 ++ .../Platform/WindowlessEglApplication.cpp | 63 ++++++++++++++----- .../Platform/WindowlessEglApplication.h | 40 +++++++++++- 4 files changed, 96 insertions(+), 24 deletions(-) diff --git a/doc/changelog.dox b/doc/changelog.dox index 3248155ec..4278e8300 100644 --- a/doc/changelog.dox +++ b/doc/changelog.dox @@ -272,6 +272,13 @@ See also: in @ref Platform::GlfwApplication and @ref Platform::EmscriptenApplication) for changing window title at runtime as opposed to setting them on application startup +- @ref Platform::WindowlessEglApplication now uses 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`, working better on headless + setups and allowing selection of an arbitrary GPU device on multi-GPU + systems @subsubsection changelog-latest-new-primitives Primitives library @@ -484,12 +491,6 @@ 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/GL/Context.h b/src/Magnum/GL/Context.h index e2aac201d..b603a14fe 100644 --- a/src/Magnum/GL/Context.h +++ b/src/Magnum/GL/Context.h @@ -153,6 +153,10 @@ options passed to the application itself. Options that don't have this prefix are completely ignored, see documentation of the @ref Utility-Arguments-delegating "Utility::Arguments" class for details. +Particular application implementations add more options for DPI scaling or +GPU selection, see @ref Platform::Sdl2Application, @ref Platform::GlfwApplication +and @ref Platform::WindowlessEglApplication for details. + @section GL-Context-multithreading Thread safety If Corrade is compiled with @ref CORRADE_BUILD_MULTITHREADED (the default), the diff --git a/src/Magnum/Platform/WindowlessEglApplication.cpp b/src/Magnum/Platform/WindowlessEglApplication.cpp index 427556def..0d725b089 100644 --- a/src/Magnum/Platform/WindowlessEglApplication.cpp +++ b/src/Magnum/Platform/WindowlessEglApplication.cpp @@ -27,6 +27,7 @@ #include #include +#include #include #include #include @@ -37,13 +38,10 @@ #include "Implementation/Egl.h" -#if defined(MAGNUM_TARGET_GLES) && !defined(MAGNUM_TARGET_WEBGL) -#include -#endif - /* None of this is in the Emscripten emulation layer, so no need to include that there */ #ifndef MAGNUM_TARGET_WEBGL +#include #ifndef EGL_EXT_device_base typedef void *EGLDeviceEXT; @@ -138,16 +136,21 @@ WindowlessEglContext::WindowlessEglContext(const Configuration& configuration, G return; } + if(configuration.device() >= UnsignedInt(count)) { + Error{} << "Platform::WindowlessEglContext: requested EGL device" << configuration.device() << "but found only" << count; + return; + } + if(magnumContext && (magnumContext->internalFlags() >= GL::Context::InternalFlag::DisplayVerboseInitializationLog)) { - Debug{} << "Platform::WindowlessEglApplication: found" << count << "EGL devices, choosing the first one"; + Debug{} << "Platform::WindowlessEglApplication: found" << count << "EGL devices, choosing device" << configuration.device(); } /* Assuming the same thing won't suddenly start failing when called the second time */ - EGLDeviceEXT device; - CORRADE_INTERNAL_ASSERT_OUTPUT(eglQueryDevices(1, &device, &count)); + Containers::Array devices{configuration.device() + 1}; + CORRADE_INTERNAL_ASSERT_OUTPUT(eglQueryDevices(configuration.device() + 1, devices, &count)); - if(!(_display = reinterpret_cast(eglGetProcAddress("eglGetPlatformDisplayEXT"))(EGL_PLATFORM_DEVICE_EXT, device, nullptr))) { + if(!(_display = reinterpret_cast(eglGetProcAddress("eglGetPlatformDisplayEXT"))(EGL_PLATFORM_DEVICE_EXT, devices[configuration.device()], nullptr))) { Error{} << "Platform::WindowlessEglApplication::tryCreateContext(): cannot get platform display for a device:" << Implementation::eglErrorString(eglGetError()); return; } @@ -155,9 +158,18 @@ WindowlessEglContext::WindowlessEglContext(const Configuration& configuration, G #endif /* Otherwise initialize the classic way. WebGL doesn't have any of the above, so no need to compile that at all. */ - if(!(_display = eglGetDisplay(EGL_DEFAULT_DISPLAY))) { - Error{} << "Platform::WindowlessEglApplication::tryCreateContext(): cannot get default EGL display:" << Implementation::eglErrorString(eglGetError()); - return; + { + #ifndef MAGNUM_TARGET_WEBGL + if(configuration.device() != 0) { + Error{} << "Platform::WindowlessEglContext: requested EGL device" << configuration.device() << "but EGL_EXT_platform_device is not supported and there's just the default one"; + return; + } + #endif + + if(!(_display = eglGetDisplay(EGL_DEFAULT_DISPLAY))) { + Error{} << "Platform::WindowlessEglApplication::tryCreateContext(): cannot get default EGL display:" << Implementation::eglErrorString(eglGetError()); + return; + } } if(!eglInitialize(_display, nullptr, nullptr)) { @@ -327,9 +339,9 @@ bool WindowlessEglContext::makeCurrent() { WindowlessEglContext::Configuration::Configuration() #ifndef MAGNUM_TARGET_GLES - : _flags{Flag::ForwardCompatible} + : _flags{Flag::ForwardCompatible}, _device{} #elif !defined(MAGNUM_TARGET_WEBGL) - : _flags{} + : _flags{}, _device{} #endif {} @@ -341,7 +353,21 @@ WindowlessEglApplication::WindowlessEglApplication(const Arguments& arguments, c createContext(configuration); } -WindowlessEglApplication::WindowlessEglApplication(const Arguments& arguments, NoCreateT): _glContext{NoCreate}, _context{new GLContext{NoCreate, arguments.argc, arguments.argv}} {} +WindowlessEglApplication::WindowlessEglApplication(const Arguments& arguments, NoCreateT): _glContext{NoCreate} { + Utility::Arguments args{"magnum"}; + #ifndef MAGNUM_TARGET_WEBGL + args.addOption("device", "").setHelp("device", "GPU device to use", "N") + .setFromEnvironment("device"); + #endif + _context.reset(new GLContext{NoCreate, args, arguments.argc, arguments.argv}); + + #ifndef MAGNUM_TARGET_WEBGL + if(args.value("device").empty()) + _commandLineDevice = 0; + else + _commandLineDevice = args.value("device"); + #endif +} void WindowlessEglApplication::createContext() { createContext({}); } @@ -352,7 +378,14 @@ void WindowlessEglApplication::createContext(const Configuration& configuration) bool WindowlessEglApplication::tryCreateContext(const Configuration& configuration) { CORRADE_ASSERT(_context->version() == GL::Version::None, "Platform::WindowlessEglApplication::tryCreateContext(): context already created", false); - WindowlessEglContext glContext{configuration, _context.get()}; + /* Command-line arguments override what's set programatically */ + Configuration mergedConfiguration{configuration}; + #ifndef MAGNUM_TARGET_WEBGL + if(!mergedConfiguration.device()) + mergedConfiguration.setDevice(_commandLineDevice); + #endif + + WindowlessEglContext glContext{mergedConfiguration, _context.get()}; if(!glContext.isCreated() || !glContext.makeCurrent() || !_context->tryCreate()) return false; diff --git a/src/Magnum/Platform/WindowlessEglApplication.h b/src/Magnum/Platform/WindowlessEglApplication.h index cfce477a8..d67e612ae 100644 --- a/src/Magnum/Platform/WindowlessEglApplication.h +++ b/src/Magnum/Platform/WindowlessEglApplication.h @@ -234,11 +234,34 @@ class WindowlessEglContext::Configuration { _flags &= ~flags; return *this; } + + /** + * @brief Device ID to use + * + * @requires_gles Device selection is not available in WebGL. + */ + UnsignedInt device() const { return _device; } + + /** + * @brief Set device ID to use + * @return Reference to self (for method chaining) + * + * The device ID is expected to be smaller than the count of devices + * reported by EGL. When using @ref WindowlessEglApplication, this is + * also exposed as a `--magnum-device` command-line option and a + * `MAGNUM_DEVICE` environment variable. + * @requires_gles Device selection is not available in WebGL. + */ + Configuration& setDevice(UnsignedInt id) { + _device = id; + return *this; + } #endif private: #ifndef MAGNUM_TARGET_WEBGL Flags _flags; + UnsignedInt _device; #endif }; @@ -350,14 +373,20 @@ 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 +@section Platform-WindowlessEglApplication-device-selection GPU device selection 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. +extensions where available instead of `EGL_DEFAULT_DISPLAY` to work better on +headless setups. The application chooses the first found device by default, you +can override that either with @ref Configuration::setDevice() or using a +`--magnum-device` command-line option (and the `MAGNUM_DEVICE` environment +variable). Unfortunately EGL doesn't provide any reasonable way to enumerate or +filter named devices, so the best you can do is checking reported device count +printed by the `--magnum-log verbose` @ref GL-Context-command-line "command-line option", +and then going from `0` up to figure out the desired device ID. @m_class{m-block m-danger} @@ -492,6 +521,11 @@ class WindowlessEglApplication { private: WindowlessEglContext _glContext; Containers::Pointer _context; + + #ifndef MAGNUM_TARGET_WEBGL + /* These are saved from command-line arguments */ + UnsignedInt _commandLineDevice; + #endif }; /** @hideinitializer