From 292335beacfc07cca2d1db99e6ab01599024f664 Mon Sep 17 00:00:00 2001 From: Vlad Zahorodnii Date: Fri, 16 Oct 2020 17:57:35 +0300 Subject: [PATCH] Introduce persistent global share context On Wayland, internal windows that use OpenGL are rendered into fbos, which are later handed over to kwin. In order to achieve that, our QPA creates OpenGL contexts that share resources with the scene's context. The problems start when compositing has been restarted. If user changes any compositing settings, the underlying render backend will be reinitialized and with it, the scene's context will be destroyed. Thus, we no longer can accept framebuffer objects from internal windows. This change addresses the framebuffer object sharing problem by adding a so called global share context. It persists throughout the lifetime of kwin. It can never be made current. The scene context and all contexts created in our QPA share resources with it. Therefore we can destroy the scene OpenGL context without affecting OpenGL contexts owned by internal windows, e.g. the outline visual or tabbox. It's worth noting that Qt provides a way to create a global share context. But for our purposes it's not suitable since the share context must be known when QGuiApplication attempts to instantiate a QOpenGLContext object. At that moment, the backend is not initialized and thus the EGLDisplay is not available yet. BUG: 415798 --- platform.cpp | 10 +++ platform.h | 14 ++++ .../scenes/opengl/abstract_egl_backend.cpp | 66 +++++++++++++++++-- .../scenes/opengl/abstract_egl_backend.h | 2 +- plugins/qpa/eglplatformcontext.cpp | 2 +- plugins/qpa/integration.cpp | 5 ++ 6 files changed, 90 insertions(+), 9 deletions(-) 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);