Browse Source

Platform: only initialize/terminate EGLDisplay once in a shared set.

Co-authored-by: Vladimír Vondruš <mosra@centrum.cz>
pull/432/merge
aspioupiou 6 years ago committed by Vladimír Vondruš
parent
commit
64d0702277
  1. 69
      src/Magnum/Platform/WindowlessEglApplication.cpp
  2. 46
      src/Magnum/Platform/WindowlessEglApplication.h

69
src/Magnum/Platform/WindowlessEglApplication.cpp

@ -82,18 +82,30 @@ bool extensionSupported(const char* const extensions, Containers::ArrayView<cons
#endif #endif
WindowlessEglContext::WindowlessEglContext(const Configuration& configuration, GLContext* const magnumContext) { WindowlessEglContext::WindowlessEglContext(const Configuration& configuration, GLContext* const magnumContext) {
#ifndef MAGNUM_TARGET_WEBGL
/* The user provided a shared context, use the associated display
directly. We don't call eglInitialize() in this case either -- the
context we share with already did that on the provided display */
if(configuration.sharedContext() != EGL_NO_CONTEXT && configuration.sharedDisplay() != EGL_NO_DISPLAY) {
_display = configuration.sharedDisplay();
_sharedContext = true;
} else
#endif
/* Otherwise find the display and initialize EGL */
{
#ifndef MAGNUM_TARGET_WEBGL #ifndef MAGNUM_TARGET_WEBGL
/* If relevant extensions are supported, try to find some display using /* If relevant extensions are supported, try to find some display using
those APIs, as that works reliably also when running headless. This 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 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, 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 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. */ version parsing from a string. Not feeling like doing that today,
no. */
const char* const extensions = eglQueryString(EGL_NO_DISPLAY, EGL_EXTENSIONS); const char* const extensions = eglQueryString(EGL_NO_DISPLAY, EGL_EXTENSIONS);
if(extensions && if(extensions &&
/* eglQueryDevicesEXT(). NVidia exposes only EGL_EXT_device_base, which /* eglQueryDevicesEXT(). NVidia exposes only EGL_EXT_device_base,
is an older version of EGL_EXT_device_enumeration before it got which is an older version of EGL_EXT_device_enumeration before
split to that and EGL_EXT_device_query, so test for both. */ it got split to that and EGL_EXT_device_query, so test for both. */
(extensionSupported(extensions, "EGL_EXT_device_enumeration") || extensionSupported(extensions, "EGL_EXT_device_base")) && (extensionSupported(extensions, "EGL_EXT_device_enumeration") || extensionSupported(extensions, "EGL_EXT_device_base")) &&
/* eglGetPlatformDisplayEXT() */ /* eglGetPlatformDisplayEXT() */
@ -106,9 +118,9 @@ WindowlessEglContext::WindowlessEglContext(const Configuration& configuration, G
/* When libEGL_nvidia.so is present on a system w/o a NV GPU, /* When libEGL_nvidia.so is present on a system w/o a NV GPU,
eglQueryDevicesEXT() fails there with EGL_BAD_ALLOC, but that is eglQueryDevicesEXT() fails there with EGL_BAD_ALLOC, but that is
never propagated to the glvnd wrapper. Enable debug output if never propagated to the glvnd wrapper. Enable debug output if
--magnum-gpu-validation is enabled because otherwise it's fucking --magnum-gpu-validation is enabled because otherwise it's
hard to discover what's to blame (lost > 3 hours already). See class fucking hard to discover what's to blame (lost > 3 hours
docs for more info and a workaround. */ already). See class docs for more info and a workaround. */
if(extensionSupported(extensions, "EGL_KHR_debug") && magnumContext && (magnumContext->internalFlags() & GL::Context::InternalFlag::GpuValidation)) { if(extensionSupported(extensions, "EGL_KHR_debug") && magnumContext && (magnumContext->internalFlags() & GL::Context::InternalFlag::GpuValidation)) {
auto eglDebugMessageControl = reinterpret_cast<EGLint(*)(EGLDEBUGPROCKHR, const EGLAttrib*)>(eglGetProcAddress("eglDebugMessageControlKHR")); auto eglDebugMessageControl = reinterpret_cast<EGLint(*)(EGLDEBUGPROCKHR, const EGLAttrib*)>(eglGetProcAddress("eglDebugMessageControlKHR"));
const EGLAttrib debugAttribs[] = { const EGLAttrib debugAttribs[] = {
@ -145,8 +157,8 @@ WindowlessEglContext::WindowlessEglContext(const Configuration& configuration, G
Debug{} << "Platform::WindowlessEglApplication: found" << count << "EGL devices, choosing device" << configuration.device(); Debug{} << "Platform::WindowlessEglApplication: found" << count << "EGL devices, choosing device" << configuration.device();
} }
/* Assuming the same thing won't suddenly start failing when called the /* Assuming the same thing won't suddenly start failing when called
second time */ the second time */
Containers::Array<EGLDeviceEXT> devices{configuration.device() + 1}; Containers::Array<EGLDeviceEXT> devices{configuration.device() + 1};
CORRADE_INTERNAL_ASSERT_OUTPUT(eglQueryDevices(configuration.device() + 1, devices, &count)); CORRADE_INTERNAL_ASSERT_OUTPUT(eglQueryDevices(configuration.device() + 1, devices, &count));
@ -154,10 +166,10 @@ WindowlessEglContext::WindowlessEglContext(const Configuration& configuration, G
Error{} << "Platform::WindowlessEglApplication::tryCreateContext(): cannot get platform display for a device:" << Implementation::eglErrorString(eglGetError()); Error{} << "Platform::WindowlessEglApplication::tryCreateContext(): cannot get platform display for a device:" << Implementation::eglErrorString(eglGetError());
return; return;
} }
} else }
#endif
/* Otherwise initialize the classic way. WebGL doesn't have any of the /* Otherwise initialize the classic way. WebGL doesn't have any of the
above, so no need to compile that at all. */ above, so no need to compile that at all. */
#endif
{ {
#ifndef MAGNUM_TARGET_WEBGL #ifndef MAGNUM_TARGET_WEBGL
if(configuration.device() != 0) { if(configuration.device() != 0) {
@ -176,6 +188,7 @@ WindowlessEglContext::WindowlessEglContext(const Configuration& configuration, G
Error() << "Platform::WindowlessEglApplication::tryCreateContext(): cannot initialize EGL:" << Implementation::eglErrorString(eglGetError()); Error() << "Platform::WindowlessEglApplication::tryCreateContext(): cannot initialize EGL:" << Implementation::eglErrorString(eglGetError());
return; return;
} }
}
const EGLenum api = const EGLenum api =
#ifndef MAGNUM_TARGET_GLES #ifndef MAGNUM_TARGET_GLES
@ -299,7 +312,15 @@ WindowlessEglContext::WindowlessEglContext(const Configuration& configuration, G
#endif #endif
} }
WindowlessEglContext::WindowlessEglContext(WindowlessEglContext&& other): _display{other._display}, _context{other._context} { WindowlessEglContext::WindowlessEglContext(WindowlessEglContext&& other):
#ifndef MAGNUM_TARGET_WEBGL
_sharedContext{other._sharedContext},
#endif
_display{other._display}, _context{other._context}
{
#ifndef MAGNUM_TARGET_WEBGL
other._sharedContext = false;
#endif
other._display = {}; other._display = {};
other._context = {}; other._context = {};
} }
@ -309,11 +330,23 @@ WindowlessEglContext::~WindowlessEglContext() {
#if defined(MAGNUM_TARGET_GLES) && !defined(MAGNUM_TARGET_WEBGL) #if defined(MAGNUM_TARGET_GLES) && !defined(MAGNUM_TARGET_WEBGL)
if(_surface) eglDestroySurface(_display, _surface); if(_surface) eglDestroySurface(_display, _surface);
#endif #endif
if(_display) eglTerminate(_display);
/* Don't terminate EGL if we're a shared context as it would kill all
others as well. In case of a shared context it's expected that the
first instance of WindowlessEglContext in the shared chain is destroyed
last, calling eglTerminate() after all others are gone. */
if(
#ifndef MAGNUM_TARGET_WEBGL
!_sharedContext &&
#endif
_display) eglTerminate(_display);
} }
WindowlessEglContext& WindowlessEglContext::operator=(WindowlessEglContext&& other) { WindowlessEglContext& WindowlessEglContext::operator=(WindowlessEglContext&& other) {
using std::swap; using std::swap;
#ifndef MAGNUM_TARGET_WEBGL
swap(other._sharedContext, _sharedContext);
#endif
swap(other._display, _display); swap(other._display, _display);
swap(other._context, _context); swap(other._context, _context);
return *this; return *this;
@ -390,6 +423,16 @@ bool WindowlessEglApplication::tryCreateContext(const Configuration& configurati
return true; return true;
} }
#ifndef MAGNUM_TARGET_WEBGL
auto WindowlessEglContext::Configuration::setSharedContext(EGLDisplay display, EGLContext context) -> Configuration& {
CORRADE_ASSERT((context == EGL_NO_CONTEXT) == (display == EGL_NO_DISPLAY),
"Platform::WindowlessEglContext::Configuration::setSharedContext(): either both the context and the display have to be valid or both null", *this);
_sharedDisplay = display;
_sharedContext = context;
return *this;
}
#endif
WindowlessEglApplication::~WindowlessEglApplication() = default; WindowlessEglApplication::~WindowlessEglApplication() = default;
}} }}

46
src/Magnum/Platform/WindowlessEglApplication.h

@ -132,6 +132,9 @@ class WindowlessEglContext {
EGLContext glContext() { return _context; } EGLContext glContext() { return _context; }
private: private:
#ifndef MAGNUM_TARGET_WEBGL
bool _sharedContext = false;
#endif
EGLDisplay _display{}; EGLDisplay _display{};
EGLContext _context{}; EGLContext _context{};
#if defined(MAGNUM_TARGET_GLES) && !defined(MAGNUM_TARGET_WEBGL) #if defined(MAGNUM_TARGET_GLES) && !defined(MAGNUM_TARGET_WEBGL)
@ -259,7 +262,9 @@ class WindowlessEglContext::Configuration {
* The device ID is expected to be smaller than the count of devices * The device ID is expected to be smaller than the count of devices
* reported by EGL. When using @ref WindowlessEglApplication, this is * reported by EGL. When using @ref WindowlessEglApplication, this is
* also exposed as a `--magnum-device` command-line option and a * also exposed as a `--magnum-device` command-line option and a
* `MAGNUM_DEVICE` environment variable. * `MAGNUM_DEVICE` environment variable. If @ref setSharedContext() is
* set, this value is ignored and the device is picked to be the same
* as in the shared context instead.
* @requires_gles Device selection is not available in WebGL. * @requires_gles Device selection is not available in WebGL.
*/ */
Configuration& setDevice(UnsignedInt id) { Configuration& setDevice(UnsignedInt id) {
@ -273,18 +278,25 @@ class WindowlessEglContext::Configuration {
* @m_since_latest * @m_since_latest
* *
* When set, the created context will share a subset of OpenGL objects * When set, the created context will share a subset of OpenGL objects
* with @p context, instead of being independent. Many caveats and * with @p context and its associated @p display, instead of being
* limitations apply to shared OpenGL contexts, please consult the * independent. Many caveats and limitations apply to shared OpenGL
* OpenGL specification for details. Default is `EGL_NO_CONTEXT`, i.e. * contexts, please consult the OpenGL specification for details.
* no sharing. * Default is `EGL_NO_CONTEXT`, i.e. no sharing. See
* @ref Platform-WindowlessEglApplication-shared-contexts for more
* information.
* @see @ref WindowlessEglContext::glContext(), * @see @ref WindowlessEglContext::glContext(),
* @ref WindowlessEglApplication::glContext() * @ref WindowlessEglApplication::glContext()
* @requires_gles Context sharing is not available in WebGL. * @requires_gles Context sharing is not available in WebGL.
*/ */
Configuration& setSharedContext(EGLContext context) { Configuration& setSharedContext(EGLDisplay display, EGLContext context);
_sharedContext = context;
return *this; /**
} * @brief Shared display
* @m_since_latest
*
* @requires_gles Context sharing is not available in WebGL.
*/
EGLContext sharedDisplay() const { return _sharedDisplay; }
/** /**
* @brief Shared context * @brief Shared context
@ -299,6 +311,7 @@ class WindowlessEglContext::Configuration {
#ifndef MAGNUM_TARGET_WEBGL #ifndef MAGNUM_TARGET_WEBGL
Flags _flags; Flags _flags;
UnsignedInt _device; UnsignedInt _device;
EGLDisplay _sharedDisplay = EGL_NO_DISPLAY;
EGLContext _sharedContext = EGL_NO_CONTEXT; EGLContext _sharedContext = EGL_NO_CONTEXT;
#endif #endif
}; };
@ -465,6 +478,21 @@ using the `__EGL_VENDOR_LIBRARY_FILENAMES` environment variable, for example:
__EGL_VENDOR_LIBRARY_FILENAMES=/usr/share/glvnd/egl_vendor.d/50_mesa.json ./my-application __EGL_VENDOR_LIBRARY_FILENAMES=/usr/share/glvnd/egl_vendor.d/50_mesa.json ./my-application
@endcode @endcode
@endparblock @endparblock
@section Platform-WindowlessEglApplication-shared-contexts Shared EGL contexts
Unlike with @ref WindowlessGlxApplication and @ref WindowlessWglApplication,
you're expected to supply both the display and the context in
@ref Configuration::setSharedContext(). This is done in order to ensure the
same `EGLDisplay` is used for all shared contexts, especially when a
non-default GPU device is selected via @ref Configuration::setDevice().
Moreover, since `eglInitialize()` and `eglTerminate()` is expected to be called
just once on a particular display, EGL initialization and termination is only
done in the case of a non-shared @ref WindowlessEglApplication (or the first
one created in a shared chain). Shared instances then reuse the already
initialized `EGLDisplay` and expect that it's terminated only after all shared
instances are gone.
*/ */
class WindowlessEglApplication { class WindowlessEglApplication {
public: public:

Loading…
Cancel
Save