diff --git a/platform.cpp b/platform.cpp index a9e3cd6697..98f8ef6fde 100644 --- a/platform.cpp +++ b/platform.cpp @@ -567,4 +567,14 @@ QString Platform::supportInformation() const return QStringLiteral("Name: %1\n").arg(metaObject()->className()); } +EGLContext Platform::sceneEglGlobalShareContext() const +{ + return m_globalShareContext; +} + +void Platform::setSceneEglGlobalShareContext(EGLContext context) +{ + m_globalShareContext = context; +} + } diff --git a/platform.h b/platform.h index 29b7e9e960..9b96065d9a 100644 --- a/platform.h +++ b/platform.h @@ -118,6 +118,19 @@ public: void setSceneEglContext(EGLContext context) { m_context = context; } + /** + * Returns the compositor-wide shared EGL context. This function may return EGL_NO_CONTEXT + * if the underlying rendering backend does not use EGL. + * + * Note that the returned context should never be made current. Instead, create a context + * that shares with this one and make the new context current. + */ + EGLContext sceneEglGlobalShareContext() const; + /** + * Sets the global share context to @a context. This function is intended to be called only + * by rendering backends. + */ + void setSceneEglGlobalShareContext(EGLContext context); /** * The first (in case of multiple) EGLSurface used by the compositing scene. */ @@ -544,6 +557,7 @@ private: EGLDisplay m_eglDisplay; EGLConfig m_eglConfig = nullptr; EGLContext m_context = EGL_NO_CONTEXT; + EGLContext m_globalShareContext = EGL_NO_CONTEXT; EGLSurface m_surface = EGL_NO_SURFACE; int m_hideCursorCounter = 0; ColorCorrect::Manager *m_colorCorrect = nullptr; diff --git a/platformsupport/scenes/opengl/abstract_egl_backend.cpp b/platformsupport/scenes/opengl/abstract_egl_backend.cpp index 54c32f91ae..5022645598 100644 --- a/platformsupport/scenes/opengl/abstract_egl_backend.cpp +++ b/platformsupport/scenes/opengl/abstract_egl_backend.cpp @@ -40,11 +40,61 @@ eglBindWaylandDisplayWL_func eglBindWaylandDisplayWL = nullptr; eglUnbindWaylandDisplayWL_func eglUnbindWaylandDisplayWL = nullptr; eglQueryWaylandBufferWL_func eglQueryWaylandBufferWL = nullptr; +static EGLContext s_globalShareContext = EGL_NO_CONTEXT; + +static bool isOpenGLES_helper() +{ + if (qstrcmp(qgetenv("KWIN_COMPOSE"), "O2ES") == 0) { + return true; + } + return QOpenGLContext::openGLModuleType() == QOpenGLContext::LibGLES; +} + +static bool ensureGlobalShareContext() +{ + const EGLDisplay eglDisplay = kwinApp()->platform()->sceneEglDisplay(); + const EGLConfig eglConfig = kwinApp()->platform()->sceneEglConfig(); + + if (s_globalShareContext != EGL_NO_CONTEXT) { + return true; + } + + std::vector attribs; + if (isOpenGLES_helper()) { + EglOpenGLESContextAttributeBuilder builder; + builder.setVersion(2); + attribs = builder.build(); + } else { + EglContextAttributeBuilder builder; + attribs = builder.build(); + } + + s_globalShareContext = eglCreateContext(eglDisplay, eglConfig, EGL_NO_CONTEXT, attribs.data()); + if (s_globalShareContext == EGL_NO_CONTEXT) { + qCWarning(KWIN_OPENGL, "Failed to create global share context: 0x%x", eglGetError()); + } + + kwinApp()->platform()->setSceneEglGlobalShareContext(s_globalShareContext); + + return s_globalShareContext != EGL_NO_CONTEXT; +} + +static void destroyGlobalShareContext() +{ + const EGLDisplay eglDisplay = kwinApp()->platform()->sceneEglDisplay(); + if (eglDisplay == EGL_NO_DISPLAY || s_globalShareContext == EGL_NO_CONTEXT) { + return; + } + eglDestroyContext(eglDisplay, s_globalShareContext); + s_globalShareContext = EGL_NO_CONTEXT; + kwinApp()->platform()->setSceneEglGlobalShareContext(EGL_NO_CONTEXT); +} + AbstractEglBackend::AbstractEglBackend() : QObject(nullptr) , OpenGLBackend() { - connect(Compositor::self(), &Compositor::aboutToDestroy, this, &AbstractEglBackend::unbindWaylandDisplay); + connect(Compositor::self(), &Compositor::aboutToDestroy, this, &AbstractEglBackend::teardown); } AbstractEglBackend::~AbstractEglBackend() @@ -52,11 +102,12 @@ AbstractEglBackend::~AbstractEglBackend() delete m_dmaBuf; } -void AbstractEglBackend::unbindWaylandDisplay() +void AbstractEglBackend::teardown() { if (eglUnbindWaylandDisplayWL && m_display != EGL_NO_DISPLAY) { eglUnbindWaylandDisplayWL(m_display, *(WaylandServer::self()->display())); } + destroyGlobalShareContext(); } void AbstractEglBackend::cleanup() @@ -199,14 +250,15 @@ void AbstractEglBackend::doneCurrent() bool AbstractEglBackend::isOpenGLES() const { - if (qstrcmp(qgetenv("KWIN_COMPOSE"), "O2ES") == 0) { - return true; - } - return QOpenGLContext::openGLModuleType() == QOpenGLContext::LibGLES; + return isOpenGLES_helper(); } bool AbstractEglBackend::createContext() { + if (!ensureGlobalShareContext()) { + return false; + } + const bool haveRobustness = hasExtension(QByteArrayLiteral("EGL_EXT_create_context_robustness")); const bool haveCreateContext = hasExtension(QByteArrayLiteral("EGL_KHR_create_context")); const bool haveContextPriority = hasExtension(QByteArrayLiteral("EGL_IMG_context_priority")); @@ -277,7 +329,7 @@ bool AbstractEglBackend::createContext() EGLContext ctx = EGL_NO_CONTEXT; for (auto it = candidates.begin(); it != candidates.end(); it++) { const auto attribs = (*it)->build(); - ctx = eglCreateContext(m_display, config(), EGL_NO_CONTEXT, attribs.data()); + ctx = eglCreateContext(m_display, config(), s_globalShareContext, attribs.data()); if (ctx != EGL_NO_CONTEXT) { qCDebug(KWIN_OPENGL) << "Created EGL context with attributes:" << (*it).get(); break; diff --git a/platformsupport/scenes/opengl/abstract_egl_backend.h b/platformsupport/scenes/opengl/abstract_egl_backend.h index 8c48accc57..d8137f39f5 100644 --- a/platformsupport/scenes/opengl/abstract_egl_backend.h +++ b/platformsupport/scenes/opengl/abstract_egl_backend.h @@ -69,7 +69,7 @@ protected: bool createContext(); private: - void unbindWaylandDisplay(); + void teardown(); EGLDisplay m_display = EGL_NO_DISPLAY; EGLSurface m_surface = EGL_NO_SURFACE; diff --git a/plugins/qpa/eglplatformcontext.cpp b/plugins/qpa/eglplatformcontext.cpp index 40551be42f..70c8ee5dab 100644 --- a/plugins/qpa/eglplatformcontext.cpp +++ b/plugins/qpa/eglplatformcontext.cpp @@ -33,7 +33,7 @@ namespace QPA EGLPlatformContext::EGLPlatformContext(QOpenGLContext *context, EGLDisplay display) : m_eglDisplay(display) { - create(context->format(), kwinApp()->platform()->sceneEglContext()); + create(context->format(), kwinApp()->platform()->sceneEglGlobalShareContext()); } EGLPlatformContext::~EGLPlatformContext() diff --git a/plugins/qpa/integration.cpp b/plugins/qpa/integration.cpp index 9d982a7f29..ace782a620 100644 --- a/plugins/qpa/integration.cpp +++ b/plugins/qpa/integration.cpp @@ -10,6 +10,7 @@ #include "integration.h" #include "backingstore.h" #include "eglplatformcontext.h" +#include "logging.h" #include "offscreensurface.h" #include "screen.h" #include "window.h" @@ -122,6 +123,10 @@ QStringList Integration::themeNames() const QPlatformOpenGLContext *Integration::createPlatformOpenGLContext(QOpenGLContext *context) const { + if (kwinApp()->platform()->sceneEglGlobalShareContext() == EGL_NO_CONTEXT) { + qCWarning(KWIN_QPA) << "Attempting to create a QOpenGLContext before the scene is initialized"; + return nullptr; + } const EGLDisplay eglDisplay = kwinApp()->platform()->sceneEglDisplay(); if (eglDisplay != EGL_NO_DISPLAY) { EGLPlatformContext *platformContext = new EGLPlatformContext(context, eglDisplay);