diff --git a/plugins/qpa/CMakeLists.txt b/plugins/qpa/CMakeLists.txt --- a/plugins/qpa/CMakeLists.txt +++ b/plugins/qpa/CMakeLists.txt @@ -4,9 +4,11 @@ set(QPA_SOURCES abstractplatformcontext.cpp backingstore.cpp + eglhelpers.cpp integration.cpp main.cpp nativeinterface.cpp + offscreensurface.cpp platformcursor.cpp screen.cpp sharingplatformcontext.cpp diff --git a/plugins/qpa/abstractplatformcontext.cpp b/plugins/qpa/abstractplatformcontext.cpp --- a/plugins/qpa/abstractplatformcontext.cpp +++ b/plugins/qpa/abstractplatformcontext.cpp @@ -18,88 +18,25 @@ along with this program. If not, see . *********************************************************************/ #include "abstractplatformcontext.h" -#include "integration.h" #include "egl_context_attribute_builder.h" +#include "eglhelpers.h" + #include +#include + #include namespace KWin { namespace QPA { -static bool isOpenGLES() -{ - if (qstrcmp(qgetenv("KWIN_COMPOSE"), "O2ES") == 0) { - return true; - } - return QOpenGLContext::openGLModuleType() == QOpenGLContext::LibGLES; -} - -static EGLConfig configFromGLFormat(EGLDisplay dpy, const QSurfaceFormat &format) -{ -#define SIZE( __buffer__ ) format.__buffer__##BufferSize() > 0 ? format.__buffer__##BufferSize() : 0 - // not setting samples as QtQuick doesn't need it - const EGLint config_attribs[] = { - EGL_SURFACE_TYPE, 0, - EGL_RED_SIZE, SIZE(red), - EGL_GREEN_SIZE, SIZE(green), - EGL_BLUE_SIZE, SIZE(blue), - EGL_ALPHA_SIZE, SIZE(alpha), - EGL_DEPTH_SIZE, SIZE(depth), - EGL_STENCIL_SIZE, SIZE(stencil), - EGL_RENDERABLE_TYPE, isOpenGLES() ? EGL_OPENGL_ES2_BIT : EGL_OPENGL_BIT, - EGL_NONE, - }; - qCDebug(KWIN_QPA) << "Trying to find a format with: rgba/depth/stencil" << (SIZE(red)) << (SIZE(green)) <<( SIZE(blue)) << (SIZE(alpha)) << (SIZE(depth)) << (SIZE(stencil)); -#undef SIZE - - EGLint count; - EGLConfig configs[1024]; - if (eglChooseConfig(dpy, config_attribs, configs, 1, &count) == EGL_FALSE) { - qCWarning(KWIN_QPA) << "eglChooseConfig failed"; - return 0; - } - if (count != 1) { - qCWarning(KWIN_QPA) << "eglChooseConfig did not return any configs"; - return 0; - } - return configs[0]; -} - -static QSurfaceFormat formatFromConfig(EGLDisplay dpy, EGLConfig config) -{ - QSurfaceFormat format; - EGLint value = 0; -#define HELPER(__egl__, __qt__) \ - eglGetConfigAttrib(dpy, config, EGL_##__egl__, &value); \ - format.set##__qt__(value); \ - value = 0; - -#define BUFFER_HELPER(__eglColor__, __color__) \ - HELPER(__eglColor__##_SIZE, __color__##BufferSize) - - BUFFER_HELPER(RED, Red) - BUFFER_HELPER(GREEN, Green) - BUFFER_HELPER(BLUE, Blue) - BUFFER_HELPER(ALPHA, Alpha) - BUFFER_HELPER(STENCIL, Stencil) - BUFFER_HELPER(DEPTH, Depth) -#undef BUFFER_HELPER - HELPER(SAMPLES, Samples) -#undef HELPER - format.setRenderableType(isOpenGLES() ? QSurfaceFormat::OpenGLES : QSurfaceFormat::OpenGL); - format.setStereo(false); - - return format; -} - AbstractPlatformContext::AbstractPlatformContext(QOpenGLContext *context, EGLDisplay display, EGLConfig config) : QPlatformOpenGLContext() , m_eglDisplay(display) - , m_config(config ? config :configFromGLFormat(m_eglDisplay, context->format())) + , m_config(config ? config : configFromFormat(m_eglDisplay, context->format())) , m_format(formatFromConfig(m_eglDisplay, m_config)) { } diff --git a/plugins/qpa/eglhelpers.h b/plugins/qpa/eglhelpers.h new file mode 100644 --- /dev/null +++ b/plugins/qpa/eglhelpers.h @@ -0,0 +1,40 @@ +/******************************************************************** + KWin - the KDE window manager + This file is part of the KDE project. + +Copyright (C) 2019 Vlad Zagorodniy + +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . +*********************************************************************/ + +#pragma once + +#define MESA_EGL_NO_X11_HEADERS +#include +#include "fixqopengl.h" + +#include + +namespace KWin +{ +namespace QPA +{ + +bool isOpenGLES(); + +EGLConfig configFromFormat(EGLDisplay display, const QSurfaceFormat &surfaceFormat, EGLint surfaceType = 0); +QSurfaceFormat formatFromConfig(EGLDisplay display, EGLConfig config); + +} // namespace QPA +} // namespace KWin diff --git a/plugins/qpa/eglhelpers.cpp b/plugins/qpa/eglhelpers.cpp new file mode 100644 --- /dev/null +++ b/plugins/qpa/eglhelpers.cpp @@ -0,0 +1,116 @@ +/******************************************************************** + KWin - the KDE window manager + This file is part of the KDE project. + +Copyright (C) 2015 Martin Flöser +Copyright (C) 2019 Vlad Zagorodniy + +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . +*********************************************************************/ + +#include "eglhelpers.h" + +#include + +#include + +namespace KWin +{ +namespace QPA +{ + +bool isOpenGLES() +{ + if (qstrcmp(qgetenv("KWIN_COMPOSE"), "O2ES") == 0) { + return true; + } + + return QOpenGLContext::openGLModuleType() == QOpenGLContext::LibGLES; +} + +EGLConfig configFromFormat(EGLDisplay display, const QSurfaceFormat &surfaceFormat, EGLint surfaceType) +{ + // qMax as these values are initialized to -1 by default. + const EGLint redSize = qMax(surfaceFormat.redBufferSize(), 0); + const EGLint greenSize = qMax(surfaceFormat.greenBufferSize(), 0); + const EGLint blueSize = qMax(surfaceFormat.blueBufferSize(), 0); + const EGLint alphaSize = qMax(surfaceFormat.alphaBufferSize(), 0); + const EGLint depthSize = qMax(surfaceFormat.depthBufferSize(), 0); + const EGLint stencilSize = qMax(surfaceFormat.stencilBufferSize(), 0); + + const EGLint renderableType = isOpenGLES() ? EGL_OPENGL_ES2_BIT : EGL_OPENGL_BIT; + + // Not setting samples as QtQuick doesn't need it. + const QVector attributes { + EGL_SURFACE_TYPE, surfaceType, + EGL_RED_SIZE, redSize, + EGL_GREEN_SIZE, greenSize, + EGL_BLUE_SIZE, blueSize, + EGL_ALPHA_SIZE, alphaSize, + EGL_DEPTH_SIZE, depthSize, + EGL_STENCIL_SIZE, stencilSize, + EGL_RENDERABLE_TYPE, renderableType, + EGL_NONE + }; + + EGLint configCount; + EGLConfig configs[1024]; + if (!eglChooseConfig(display, attributes.data(), configs, 1, &configCount)) { + // FIXME: Don't bail out yet, we should try to find the most suitable config. + qCWarning(KWIN_QPA, "eglChooseConfig failed: %x", eglGetError()); + return EGL_NO_CONFIG_KHR; + } + + if (configCount != 1) { + qCWarning(KWIN_QPA) << "eglChooseConfig did not return any configs"; + return EGL_NO_CONFIG_KHR; + } + + return configs[0]; +} + +QSurfaceFormat formatFromConfig(EGLDisplay display, EGLConfig config) +{ + int redSize = 0; + int blueSize = 0; + int greenSize = 0; + int alphaSize = 0; + int stencilSize = 0; + int depthSize = 0; + int sampleCount = 0; + + eglGetConfigAttrib(display, config, EGL_RED_SIZE, &redSize); + eglGetConfigAttrib(display, config, EGL_GREEN_SIZE, &greenSize); + eglGetConfigAttrib(display, config, EGL_BLUE_SIZE, &blueSize); + eglGetConfigAttrib(display, config, EGL_ALPHA_SIZE, &alphaSize); + eglGetConfigAttrib(display, config, EGL_STENCIL_SIZE, &stencilSize); + eglGetConfigAttrib(display, config, EGL_DEPTH_SIZE, &depthSize); + eglGetConfigAttrib(display, config, EGL_SAMPLES, &sampleCount); + + QSurfaceFormat format; + format.setRedBufferSize(redSize); + format.setGreenBufferSize(greenSize); + format.setBlueBufferSize(blueSize); + format.setAlphaBufferSize(alphaSize); + format.setStencilBufferSize(stencilSize); + format.setDepthBufferSize(depthSize); + format.setSamples(sampleCount); + format.setRenderableType(isOpenGLES() ? QSurfaceFormat::OpenGLES : QSurfaceFormat::OpenGL); + format.setStereo(false); + + return format; +} + +} // namespace QPA +} // namespace KWin diff --git a/plugins/qpa/integration.h b/plugins/qpa/integration.h --- a/plugins/qpa/integration.h +++ b/plugins/qpa/integration.h @@ -53,6 +53,7 @@ bool hasCapability(Capability cap) const override; QPlatformWindow *createPlatformWindow(QWindow *window) const override; + QPlatformOffscreenSurface *createPlatformOffscreenSurface(QOffscreenSurface *surface) const override; QPlatformBackingStore *createPlatformBackingStore(QWindow *window) const override; QAbstractEventDispatcher *createEventDispatcher() const override; QPlatformFontDatabase *fontDatabase() const override; diff --git a/plugins/qpa/integration.cpp b/plugins/qpa/integration.cpp --- a/plugins/qpa/integration.cpp +++ b/plugins/qpa/integration.cpp @@ -22,6 +22,7 @@ #include "platform.h" #include "backingstore.h" #include "nativeinterface.h" +#include "offscreensurface.h" #include "screen.h" #include "sharingplatformcontext.h" #include "window.h" @@ -160,6 +161,11 @@ } } +QPlatformOffscreenSurface *Integration::createPlatformOffscreenSurface(QOffscreenSurface *surface) const +{ + return new OffscreenSurface(surface); +} + QPlatformFontDatabase *Integration::fontDatabase() const { return m_fontDb; diff --git a/plugins/qpa/offscreensurface.h b/plugins/qpa/offscreensurface.h new file mode 100644 --- /dev/null +++ b/plugins/qpa/offscreensurface.h @@ -0,0 +1,53 @@ +/******************************************************************** + KWin - the KDE window manager + This file is part of the KDE project. + +Copyright (C) 2019 Vlad Zagorodniy + +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . +*********************************************************************/ + +#pragma once + +#define MESA_EGL_NO_X11_HEADERS +#include +#include "fixqopengl.h" + +#include + +namespace KWin +{ +namespace QPA +{ + +class OffscreenSurface : public QPlatformOffscreenSurface +{ +public: + explicit OffscreenSurface(QOffscreenSurface *surface); + ~OffscreenSurface() override; + + QSurfaceFormat format() const override; + bool isValid() const override; + + EGLSurface nativeHandle() const; + +private: + QSurfaceFormat m_format; + + EGLDisplay m_eglDisplay = EGL_NO_DISPLAY; + EGLSurface m_surface = EGL_NO_SURFACE; +}; + +} // namespace QPA +} // namespace KWin diff --git a/plugins/qpa/offscreensurface.cpp b/plugins/qpa/offscreensurface.cpp new file mode 100644 --- /dev/null +++ b/plugins/qpa/offscreensurface.cpp @@ -0,0 +1,82 @@ +/******************************************************************** + KWin - the KDE window manager + This file is part of the KDE project. + +Copyright (C) 2019 Vlad Zagorodniy + +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . +*********************************************************************/ + +#include "offscreensurface.h" +#include "eglhelpers.h" +#include "main.h" +#include "platform.h" + +#include + +namespace KWin +{ +namespace QPA +{ + +OffscreenSurface::OffscreenSurface(QOffscreenSurface *surface) + : QPlatformOffscreenSurface(surface) + , m_eglDisplay(kwinApp()->platform()->sceneEglDisplay()) +{ + const QSize size = surface->size(); + + EGLConfig config = configFromFormat(m_eglDisplay, surface->requestedFormat(), EGL_PBUFFER_BIT); + if (config == EGL_NO_CONFIG_KHR) { + return; + } + + const EGLint attributes[] = { + EGL_WIDTH, size.width(), + EGL_HEIGHT, size.height(), + EGL_NONE + }; + + m_surface = eglCreatePbufferSurface(m_eglDisplay, config, attributes); + if (m_surface == EGL_NO_SURFACE) { + return; + } + + // Requested and actual surface format might be different. + m_format = formatFromConfig(m_eglDisplay, config); +} + +OffscreenSurface::~OffscreenSurface() +{ + if (m_surface != EGL_NO_SURFACE) { + eglDestroySurface(m_eglDisplay, m_surface); + } +} + +QSurfaceFormat OffscreenSurface::format() const +{ + return m_format; +} + +bool OffscreenSurface::isValid() const +{ + return m_surface != EGL_NO_SURFACE; +} + +EGLSurface OffscreenSurface::nativeHandle() const +{ + return m_surface; +} + +} // namespace QPA +} // namespace KWin diff --git a/plugins/qpa/sharingplatformcontext.cpp b/plugins/qpa/sharingplatformcontext.cpp --- a/plugins/qpa/sharingplatformcontext.cpp +++ b/plugins/qpa/sharingplatformcontext.cpp @@ -19,6 +19,7 @@ *********************************************************************/ #include "sharingplatformcontext.h" #include "integration.h" +#include "offscreensurface.h" #include "window.h" #include "../../platform.h" #include "../../wayland_server.h" @@ -48,22 +49,30 @@ bool SharingPlatformContext::makeCurrent(QPlatformSurface *surface) { - Window *window = static_cast(surface); + EGLSurface eglSurface; + if (surface->surface()->surfaceClass() == QSurface::Window) { + eglSurface = m_surface; + } else { + eglSurface = static_cast(surface)->nativeHandle(); + } - // QOpenGLContext::makeCurrent in Qt5.12 calls platfrom->setContext before setCurrentContext - // but binding the content FBO looks up the format from the current context, so we need // to make sure sure Qt knows what the correct one is already - QOpenGLContextPrivate::setCurrentContext(context()); - if (eglMakeCurrent(eglDisplay(), m_surface, m_surface, eglContext())) { - window->bindContentFBO(); - return true; + const bool ok = eglMakeCurrent(eglDisplay(), eglSurface, eglSurface, eglContext()); + if (!ok) { + qCWarning(KWIN_QPA, "eglMakeCurrent failed: %x", eglGetError()); + return false; } - qCWarning(KWIN_QPA) << "Failed to make context current"; - EGLint error = eglGetError(); - if (error != EGL_SUCCESS) { - qCWarning(KWIN_QPA) << "EGL error code: " << error; + + 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 false; + return true; } bool SharingPlatformContext::isSharing() const @@ -73,16 +82,18 @@ void SharingPlatformContext::swapBuffers(QPlatformSurface *surface) { - Window *window = static_cast(surface); - auto c = window->shellClient(); - if (!c) { - qCDebug(KWIN_QPA) << "SwapBuffers called but there is no ShellClient"; - return; + if (surface->surface()->surfaceClass() == QSurface::Window) { + Window *window = static_cast(surface); + auto c = window->shellClient(); + if (!c) { + qCDebug(KWIN_QPA) << "SwapBuffers called but there is no ShellClient"; + return; + } + context()->makeCurrent(surface->surface()); + glFlush(); + c->setInternalFramebufferObject(window->swapFBO()); + window->bindContentFBO(); } - context()->makeCurrent(surface->surface()); - glFlush(); - c->setInternalFramebufferObject(window->swapFBO()); - window->bindContentFBO(); } GLuint SharingPlatformContext::defaultFramebufferObject(QPlatformSurface *surface) const @@ -93,7 +104,7 @@ return fbo->handle(); } } - qCDebug(KWIN_QPA) << "No default framebuffer object for internal window"; + return 0; }