From 9b89a3d967795d55439d96d1686ce5db0464b5cb Mon Sep 17 00:00:00 2001 From: Vlad Zahorodnii Date: Mon, 12 Oct 2020 09:27:27 +0300 Subject: [PATCH] qpa: Merge OpenGL platform context classes This makes our QPlatformOpenGLContext private subclass simpler. As a slightly unrelated change, this patch also fixes a bug where our platform opengl context may return a wrong surface format if surfaceless contexts are unsupported. --- plugins/qpa/CMakeLists.txt | 3 +- plugins/qpa/abstractplatformcontext.h | 56 ------- plugins/qpa/backingstore.h | 4 + ...formcontext.cpp => eglplatformcontext.cpp} | 147 ++++++++++++++---- plugins/qpa/eglplatformcontext.h | 51 ++++++ plugins/qpa/integration.cpp | 15 +- plugins/qpa/offscreensurface.cpp | 2 +- plugins/qpa/offscreensurface.h | 2 +- plugins/qpa/sharingplatformcontext.cpp | 115 -------------- plugins/qpa/sharingplatformcontext.h | 42 ----- plugins/qpa/window.cpp | 9 ++ plugins/qpa/window.h | 5 + 12 files changed, 193 insertions(+), 258 deletions(-) delete mode 100644 plugins/qpa/abstractplatformcontext.h rename plugins/qpa/{abstractplatformcontext.cpp => eglplatformcontext.cpp} (64%) create mode 100644 plugins/qpa/eglplatformcontext.h delete mode 100644 plugins/qpa/sharingplatformcontext.cpp delete mode 100644 plugins/qpa/sharingplatformcontext.h diff --git a/plugins/qpa/CMakeLists.txt b/plugins/qpa/CMakeLists.txt index e2f72b0b94..1eca68dbc0 100644 --- a/plugins/qpa/CMakeLists.txt +++ b/plugins/qpa/CMakeLists.txt @@ -2,15 +2,14 @@ include_directories(${Qt5Core_PRIVATE_INCLUDE_DIRS}) include_directories(${Qt5Gui_PRIVATE_INCLUDE_DIRS}) set(QPA_SOURCES - abstractplatformcontext.cpp backingstore.cpp eglhelpers.cpp + eglplatformcontext.cpp integration.cpp main.cpp offscreensurface.cpp platformcursor.cpp screen.cpp - sharingplatformcontext.cpp window.cpp ) diff --git a/plugins/qpa/abstractplatformcontext.h b/plugins/qpa/abstractplatformcontext.h deleted file mode 100644 index ce807ec8dd..0000000000 --- a/plugins/qpa/abstractplatformcontext.h +++ /dev/null @@ -1,56 +0,0 @@ -/* - KWin - the KDE window manager - This file is part of the KDE project. - - SPDX-FileCopyrightText: 2015 Martin Gräßlin - - SPDX-License-Identifier: GPL-2.0-or-later -*/ -#ifndef KWIN_QPA_ABSTRACTPLATFORMCONTEXT_H -#define KWIN_QPA_ABSTRACTPLATFORMCONTEXT_H - -#include -#include "fixqopengl.h" -#include -#include - -namespace KWin -{ -namespace QPA -{ - -class AbstractPlatformContext : public QPlatformOpenGLContext -{ -public: - AbstractPlatformContext(QOpenGLContext *context, EGLDisplay display, EGLConfig config = nullptr); - ~AbstractPlatformContext() override; - - void doneCurrent() override; - QSurfaceFormat format() const override; - bool isValid() const override; - QFunctionPointer getProcAddress(const char *procName) override; - -protected: - EGLDisplay eglDisplay() const { - return m_eglDisplay; - } - EGLConfig config() const { - return m_config; - } - bool bindApi(); - EGLContext eglContext() const { - return m_context; - } - void createContext(EGLContext shareContext = EGL_NO_CONTEXT); - -private: - EGLDisplay m_eglDisplay; - EGLConfig m_config; - EGLContext m_context = EGL_NO_CONTEXT; - QSurfaceFormat m_format; -}; - -} -} - -#endif diff --git a/plugins/qpa/backingstore.h b/plugins/qpa/backingstore.h index 5b881443a2..51741a8800 100644 --- a/plugins/qpa/backingstore.h +++ b/plugins/qpa/backingstore.h @@ -10,6 +10,10 @@ #ifndef KWIN_QPA_BACKINGSTORE_H #define KWIN_QPA_BACKINGSTORE_H +#include +#include "fixqopengl.h" +#include + #include namespace KWin diff --git a/plugins/qpa/abstractplatformcontext.cpp b/plugins/qpa/eglplatformcontext.cpp similarity index 64% rename from plugins/qpa/abstractplatformcontext.cpp rename to plugins/qpa/eglplatformcontext.cpp index daa5b855bf..40551be42f 100644 --- a/plugins/qpa/abstractplatformcontext.cpp +++ b/plugins/qpa/eglplatformcontext.cpp @@ -3,71 +3,156 @@ This file is part of the KDE project. SPDX-FileCopyrightText: 2015 Martin Gräßlin + SPDX-FileCopyrightText: 2020 Vlad Zahorodnii SPDX-License-Identifier: GPL-2.0-or-later */ -#include "abstractplatformcontext.h" + +#include "eglplatformcontext.h" #include "egl_context_attribute_builder.h" #include "eglhelpers.h" +#include "internal_client.h" +#include "offscreensurface.h" +#include "platform.h" +#include "window.h" -#include +#include "logging.h" #include +#include + +#include #include namespace KWin { - namespace QPA { -AbstractPlatformContext::AbstractPlatformContext(QOpenGLContext *context, EGLDisplay display, EGLConfig config) - : QPlatformOpenGLContext() - , m_eglDisplay(display) - , m_config(config ? config : configFromFormat(m_eglDisplay, context->format())) - , m_format(formatFromConfig(m_eglDisplay, m_config)) +EGLPlatformContext::EGLPlatformContext(QOpenGLContext *context, EGLDisplay display) + : m_eglDisplay(display) { + create(context->format(), kwinApp()->platform()->sceneEglContext()); } -AbstractPlatformContext::~AbstractPlatformContext() +EGLPlatformContext::~EGLPlatformContext() { if (m_context != EGL_NO_CONTEXT) { eglDestroyContext(m_eglDisplay, m_context); } } -void AbstractPlatformContext::doneCurrent() +EGLDisplay EGLPlatformContext::eglDisplay() const +{ + return m_eglDisplay; +} + +EGLContext EGLPlatformContext::eglContext() const +{ + return m_context; +} + +static EGLSurface eglSurfaceForPlatformSurface(QPlatformSurface *surface) +{ + if (surface->surface()->surfaceClass() == QSurface::Window) { + return static_cast(surface)->eglSurface(); + } else { + return static_cast(surface)->eglSurface(); + } +} + +bool EGLPlatformContext::makeCurrent(QPlatformSurface *surface) +{ + const EGLSurface eglSurface = eglSurfaceForPlatformSurface(surface); + + const bool ok = eglMakeCurrent(eglDisplay(), eglSurface, eglSurface, eglContext()); + if (!ok) { + qCWarning(KWIN_QPA, "eglMakeCurrent failed: %x", eglGetError()); + return false; + } + + if (surface->surface()->surfaceClass() == QSurface::Window) { + // QOpenGLContextPrivate::setCurrentContext will be called after this + // method returns, but that's too late, as we need a current context in + // order to bind the content framebuffer object. + QOpenGLContextPrivate::setCurrentContext(context()); + + Window *window = static_cast(surface); + window->bindContentFBO(); + } + + return true; +} + +void EGLPlatformContext::doneCurrent() { eglMakeCurrent(m_eglDisplay, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT); } -QSurfaceFormat AbstractPlatformContext::format() const -{ - return m_format; -} - -QFunctionPointer AbstractPlatformContext::getProcAddress(const char *procName) -{ - return eglGetProcAddress(procName); -} - -bool AbstractPlatformContext::isValid() const +bool EGLPlatformContext::isValid() const { return m_context != EGL_NO_CONTEXT; } -bool AbstractPlatformContext::bindApi() +bool EGLPlatformContext::isSharing() const { - if (eglBindAPI(isOpenGLES() ? EGL_OPENGL_ES_API : EGL_OPENGL_API) == EGL_FALSE) { - qCWarning(KWIN_QPA) << "eglBindAPI failed"; - return false; - } - return true; + return false; } -void AbstractPlatformContext::createContext(EGLContext shareContext) +QSurfaceFormat EGLPlatformContext::format() const { + return m_format; +} + +QFunctionPointer EGLPlatformContext::getProcAddress(const char *procName) +{ + return eglGetProcAddress(procName); +} + +void EGLPlatformContext::swapBuffers(QPlatformSurface *surface) +{ + if (surface->surface()->surfaceClass() == QSurface::Window) { + Window *window = static_cast(surface); + InternalClient *client = window->client(); + if (!client) { + return; + } + context()->makeCurrent(surface->surface()); + glFlush(); + client->present(window->swapFBO()); + window->bindContentFBO(); + } +} + +GLuint EGLPlatformContext::defaultFramebufferObject(QPlatformSurface *surface) const +{ + if (Window *window = dynamic_cast(surface)) { + const auto &fbo = window->contentFBO(); + if (!fbo.isNull()) { + return fbo->handle(); + } + qCDebug(KWIN_QPA) << "No default framebuffer object for internal window"; + } + + return 0; +} + +void EGLPlatformContext::create(const QSurfaceFormat &format, EGLContext shareContext) +{ + if (!eglBindAPI(isOpenGLES() ? EGL_OPENGL_ES_API : EGL_OPENGL_API)) { + qCWarning(KWIN_QPA, "eglBindAPI failed: 0x%x", eglGetError()); + return; + } + + m_config = configFromFormat(m_eglDisplay, format); + if (m_config == EGL_NO_CONFIG_KHR) { + qCWarning(KWIN_QPA) << "Could not find suitable EGLConfig for" << format; + return; + } + + m_format = formatFromConfig(m_eglDisplay, m_config); + const QByteArray eglExtensions = eglQueryString(eglDisplay(), EGL_EXTENSIONS); const QList extensions = eglExtensions.split(' '); const bool haveRobustness = extensions.contains(QByteArrayLiteral("EGL_EXT_create_context_robustness")); @@ -165,7 +250,7 @@ void AbstractPlatformContext::createContext(EGLContext shareContext) EGLContext context = EGL_NO_CONTEXT; for (auto it = candidates.begin(); it != candidates.end(); it++) { const auto attribs = (*it)->build(); - context = eglCreateContext(eglDisplay(), config(), shareContext, attribs.data()); + context = eglCreateContext(eglDisplay(), m_config, shareContext, attribs.data()); if (context != EGL_NO_CONTEXT) { qCDebug(KWIN_QPA) << "Created EGL context with attributes:" << (*it).get(); break; @@ -179,5 +264,5 @@ void AbstractPlatformContext::createContext(EGLContext shareContext) m_context = context; } -} -} +} // namespace QPA +} // namespace KWin diff --git a/plugins/qpa/eglplatformcontext.h b/plugins/qpa/eglplatformcontext.h new file mode 100644 index 0000000000..b582d84866 --- /dev/null +++ b/plugins/qpa/eglplatformcontext.h @@ -0,0 +1,51 @@ +/* + KWin - the KDE window manager + This file is part of the KDE project. + + SPDX-FileCopyrightText: 2015 Martin Gräßlin + SPDX-FileCopyrightText: 2020 Vlad Zahorodnii + + SPDX-License-Identifier: GPL-2.0-or-later +*/ + +#pragma once + +#include +#include "fixqopengl.h" +#include +#include + +namespace KWin +{ +namespace QPA +{ + +class EGLPlatformContext : public QPlatformOpenGLContext +{ +public: + EGLPlatformContext(QOpenGLContext *context, EGLDisplay display); + ~EGLPlatformContext() override; + + bool makeCurrent(QPlatformSurface *surface) override; + void doneCurrent() override; + QSurfaceFormat format() const override; + bool isValid() const override; + bool isSharing() const override; + GLuint defaultFramebufferObject(QPlatformSurface *surface) const override; + QFunctionPointer getProcAddress(const char *procName) override; + void swapBuffers(QPlatformSurface *surface) override; + + EGLDisplay eglDisplay() const; + EGLContext eglContext() const; + +private: + void create(const QSurfaceFormat &format, EGLContext shareContext); + + EGLDisplay m_eglDisplay; + EGLConfig m_config = EGL_NO_CONFIG_KHR; + EGLContext m_context = EGL_NO_CONTEXT; + QSurfaceFormat m_format; +}; + +} // namespace QPA +} // namespace KWin diff --git a/plugins/qpa/integration.cpp b/plugins/qpa/integration.cpp index 7ed16567f9..9d982a7f29 100644 --- a/plugins/qpa/integration.cpp +++ b/plugins/qpa/integration.cpp @@ -9,9 +9,9 @@ */ #include "integration.h" #include "backingstore.h" +#include "eglplatformcontext.h" #include "offscreensurface.h" #include "screen.h" -#include "sharingplatformcontext.h" #include "window.h" #include "../../main.h" #include "../../platform.h" @@ -122,15 +122,10 @@ QStringList Integration::themeNames() const QPlatformOpenGLContext *Integration::createPlatformOpenGLContext(QOpenGLContext *context) const { - if (kwinApp()->platform()->supportsQpaContext()) { - return new SharingPlatformContext(context); - } - if (kwinApp()->platform()->sceneEglDisplay() != EGL_NO_DISPLAY) { - auto s = kwinApp()->platform()->sceneEglSurface(); - if (s != EGL_NO_SURFACE) { - // try a SharingPlatformContext with a created surface - return new SharingPlatformContext(context, s, kwinApp()->platform()->sceneEglConfig()); - } + const EGLDisplay eglDisplay = kwinApp()->platform()->sceneEglDisplay(); + if (eglDisplay != EGL_NO_DISPLAY) { + EGLPlatformContext *platformContext = new EGLPlatformContext(context, eglDisplay); + return platformContext; } return nullptr; } diff --git a/plugins/qpa/offscreensurface.cpp b/plugins/qpa/offscreensurface.cpp index 6bdb60705d..a44bda56dc 100644 --- a/plugins/qpa/offscreensurface.cpp +++ b/plugins/qpa/offscreensurface.cpp @@ -62,7 +62,7 @@ bool OffscreenSurface::isValid() const return m_surface != EGL_NO_SURFACE; } -EGLSurface OffscreenSurface::nativeHandle() const +EGLSurface OffscreenSurface::eglSurface() const { return m_surface; } diff --git a/plugins/qpa/offscreensurface.h b/plugins/qpa/offscreensurface.h index ab46c47e9a..9a17e0e2e5 100644 --- a/plugins/qpa/offscreensurface.h +++ b/plugins/qpa/offscreensurface.h @@ -29,7 +29,7 @@ public: QSurfaceFormat format() const override; bool isValid() const override; - EGLSurface nativeHandle() const; + EGLSurface eglSurface() const; private: QSurfaceFormat m_format; diff --git a/plugins/qpa/sharingplatformcontext.cpp b/plugins/qpa/sharingplatformcontext.cpp deleted file mode 100644 index 6ac3b9d557..0000000000 --- a/plugins/qpa/sharingplatformcontext.cpp +++ /dev/null @@ -1,115 +0,0 @@ -/* - KWin - the KDE window manager - This file is part of the KDE project. - - SPDX-FileCopyrightText: 2015 Martin Gräßlin - - SPDX-License-Identifier: GPL-2.0-or-later -*/ -#include "sharingplatformcontext.h" -#include "offscreensurface.h" -#include "window.h" - -#include "../../internal_client.h" -#include "../../main.h" -#include "../../platform.h" - -#include - -#include -#include - -namespace KWin -{ - -namespace QPA -{ - -SharingPlatformContext::SharingPlatformContext(QOpenGLContext *context) - : SharingPlatformContext(context, EGL_NO_SURFACE) -{ -} - -SharingPlatformContext::SharingPlatformContext(QOpenGLContext *context, const EGLSurface &surface, EGLConfig config) - : AbstractPlatformContext(context, kwinApp()->platform()->sceneEglDisplay(), config) - , m_surface(surface) -{ - create(); -} - -bool SharingPlatformContext::makeCurrent(QPlatformSurface *surface) -{ - EGLSurface eglSurface; - if (surface->surface()->surfaceClass() == QSurface::Window) { - eglSurface = m_surface; - } else { - eglSurface = static_cast(surface)->nativeHandle(); - } - - const bool ok = eglMakeCurrent(eglDisplay(), eglSurface, eglSurface, eglContext()); - if (!ok) { - qCWarning(KWIN_QPA, "eglMakeCurrent failed: %x", eglGetError()); - return false; - } - - if (surface->surface()->surfaceClass() == QSurface::Window) { - // QOpenGLContextPrivate::setCurrentContext will be called after this - // method returns, but that's too late, as we need a current context in - // order to bind the content framebuffer object. - QOpenGLContextPrivate::setCurrentContext(context()); - - Window *window = static_cast(surface); - window->bindContentFBO(); - } - - return true; -} - -bool SharingPlatformContext::isSharing() const -{ - return false; -} - -void SharingPlatformContext::swapBuffers(QPlatformSurface *surface) -{ - if (surface->surface()->surfaceClass() == QSurface::Window) { - Window *window = static_cast(surface); - InternalClient *client = window->client(); - if (!client) { - return; - } - context()->makeCurrent(surface->surface()); - glFlush(); - client->present(window->swapFBO()); - window->bindContentFBO(); - } -} - -GLuint SharingPlatformContext::defaultFramebufferObject(QPlatformSurface *surface) const -{ - if (Window *window = dynamic_cast(surface)) { - const auto &fbo = window->contentFBO(); - if (!fbo.isNull()) { - return fbo->handle(); - } - qCDebug(KWIN_QPA) << "No default framebuffer object for internal window"; - } - - return 0; -} - -void SharingPlatformContext::create() -{ - if (!config()) { - qCWarning(KWIN_QPA) << "Did not get an EGL config"; - return; - } - if (!bindApi()) { - qCWarning(KWIN_QPA) << "Could not bind API."; - return; - } - createContext(kwinApp()->platform()->sceneEglContext()); -} - -} -} diff --git a/plugins/qpa/sharingplatformcontext.h b/plugins/qpa/sharingplatformcontext.h deleted file mode 100644 index 84b8680e0b..0000000000 --- a/plugins/qpa/sharingplatformcontext.h +++ /dev/null @@ -1,42 +0,0 @@ -/* - KWin - the KDE window manager - This file is part of the KDE project. - - SPDX-FileCopyrightText: 2015 Martin Gräßlin - - SPDX-License-Identifier: GPL-2.0-or-later -*/ -#ifndef KWIN_QPA_SHARINGPLATFORMCONTEXT_H -#define KWIN_QPA_SHARINGPLATFORMCONTEXT_H - -#include "abstractplatformcontext.h" - -namespace KWin -{ -namespace QPA -{ - -class SharingPlatformContext : public AbstractPlatformContext -{ -public: - explicit SharingPlatformContext(QOpenGLContext *context); - SharingPlatformContext(QOpenGLContext *context, const EGLSurface &surface, EGLConfig config = nullptr); - - void swapBuffers(QPlatformSurface *surface) override; - - GLuint defaultFramebufferObject(QPlatformSurface *surface) const override; - - bool makeCurrent(QPlatformSurface *surface) override; - - bool isSharing() const override; - -private: - void create(); - - EGLSurface m_surface; -}; - -} -} - -#endif diff --git a/plugins/qpa/window.cpp b/plugins/qpa/window.cpp index 2bf5b97e57..50bd5a756e 100644 --- a/plugins/qpa/window.cpp +++ b/plugins/qpa/window.cpp @@ -8,6 +8,7 @@ SPDX-License-Identifier: GPL-2.0-or-later */ #include "window.h" +#include "platform.h" #include "screens.h" #include "internal_client.h" @@ -143,5 +144,13 @@ void Window::unmap() m_contentFBO = nullptr; } +EGLSurface Window::eglSurface() const +{ + if (kwinApp()->platform()->supportsQpaContext()) { + return EGL_NO_SURFACE; + } + return kwinApp()->platform()->sceneEglSurface(); +} + } } diff --git a/plugins/qpa/window.h b/plugins/qpa/window.h index 705246933a..e221451926 100644 --- a/plugins/qpa/window.h +++ b/plugins/qpa/window.h @@ -10,6 +10,10 @@ #ifndef KWIN_QPA_WINDOW_H #define KWIN_QPA_WINDOW_H +#include +#include "fixqopengl.h" +#include + #include #include @@ -39,6 +43,7 @@ public: QSharedPointer swapFBO(); InternalClient *client() const; + EGLSurface eglSurface() const; private: void createFBO();