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
This commit is contained in:
parent
da12d3804f
commit
292335beac
6 changed files with 90 additions and 9 deletions
10
platform.cpp
10
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;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
14
platform.h
14
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;
|
||||
|
|
|
@ -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<int> 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;
|
||||
|
|
|
@ -69,7 +69,7 @@ protected:
|
|||
bool createContext();
|
||||
|
||||
private:
|
||||
void unbindWaylandDisplay();
|
||||
void teardown();
|
||||
|
||||
EGLDisplay m_display = EGL_NO_DISPLAY;
|
||||
EGLSurface m_surface = EGL_NO_SURFACE;
|
||||
|
|
|
@ -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()
|
||||
|
|
|
@ -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);
|
||||
|
|
Loading…
Reference in a new issue