diff --git a/plugins/qpa/CMakeLists.txt b/plugins/qpa/CMakeLists.txt index 71077e50c..f3ecbab44 100644 --- a/plugins/qpa/CMakeLists.txt +++ b/plugins/qpa/CMakeLists.txt @@ -1,41 +1,42 @@ include_directories(${Qt5Core_PRIVATE_INCLUDE_DIRS}) include_directories(${Qt5Gui_PRIVATE_INCLUDE_DIRS}) set(QPA_SOURCES abstractplatformcontext.cpp backingstore.cpp + eglhelpers.cpp integration.cpp main.cpp nativeinterface.cpp platformcursor.cpp screen.cpp sharingplatformcontext.cpp window.cpp ) include(ECMQtDeclareLoggingCategory) ecm_qt_declare_logging_category(QPA_SOURCES HEADER logging.h IDENTIFIER KWIN_QPA CATEGORY_NAME kwin_qpa_plugin DEFAULT_SEVERITY Critical) add_library(KWinQpaPlugin MODULE ${QPA_SOURCES}) set_target_properties(KWinQpaPlugin PROPERTIES LIBRARY_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/bin/platforms/") set(QT5PLATFORMSUPPORT_LIBS Qt5FontDatabaseSupport::Qt5FontDatabaseSupport Qt5ThemeSupport::Qt5ThemeSupport Qt5EventDispatcherSupport::Qt5EventDispatcherSupport ) target_link_libraries(KWinQpaPlugin kwin KF5::WaylandClient ${QT5PLATFORMSUPPORT_LIBS} Fontconfig::Fontconfig ${FREETYPE_LIBRARIES} ) install( TARGETS KWinQpaPlugin DESTINATION ${PLUGIN_INSTALL_DIR}/platforms/ ) diff --git a/plugins/qpa/abstractplatformcontext.cpp b/plugins/qpa/abstractplatformcontext.cpp index beac25f79..40df3522e 100644 --- a/plugins/qpa/abstractplatformcontext.cpp +++ b/plugins/qpa/abstractplatformcontext.cpp @@ -1,257 +1,194 @@ /******************************************************************** KWin - the KDE window manager This file is part of the KDE project. Copyright (C) 2015 Martin Gräßlin 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 "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)) { } AbstractPlatformContext::~AbstractPlatformContext() { if (m_context != EGL_NO_CONTEXT) { eglDestroyContext(m_eglDisplay, m_context); } } void AbstractPlatformContext::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 { return m_context != EGL_NO_CONTEXT; } bool AbstractPlatformContext::bindApi() { if (eglBindAPI(isOpenGLES() ? EGL_OPENGL_ES_API : EGL_OPENGL_API) == EGL_FALSE) { qCWarning(KWIN_QPA) << "eglBindAPI failed"; return false; } return true; } void AbstractPlatformContext::createContext(EGLContext shareContext) { const QByteArray eglExtensions = eglQueryString(eglDisplay(), EGL_EXTENSIONS); const QList extensions = eglExtensions.split(' '); const bool haveRobustness = extensions.contains(QByteArrayLiteral("EGL_EXT_create_context_robustness")); const bool haveCreateContext = extensions.contains(QByteArrayLiteral("EGL_KHR_create_context")); const bool haveContextPriority = extensions.contains(QByteArrayLiteral("EGL_IMG_context_priority")); std::vector> candidates; if (isOpenGLES()) { if (haveCreateContext && haveRobustness && haveContextPriority) { auto glesRobustPriority = std::unique_ptr(new EglOpenGLESContextAttributeBuilder); glesRobustPriority->setVersion(2); glesRobustPriority->setRobust(true); glesRobustPriority->setHighPriority(true); candidates.push_back(std::move(glesRobustPriority)); } if (haveCreateContext && haveRobustness) { auto glesRobust = std::unique_ptr(new EglOpenGLESContextAttributeBuilder); glesRobust->setVersion(2); glesRobust->setRobust(true); candidates.push_back(std::move(glesRobust)); } if (haveContextPriority) { auto glesPriority = std::unique_ptr(new EglOpenGLESContextAttributeBuilder); glesPriority->setVersion(2); glesPriority->setHighPriority(true); candidates.push_back(std::move(glesPriority)); } auto gles = std::unique_ptr(new EglOpenGLESContextAttributeBuilder); gles->setVersion(2); candidates.push_back(std::move(gles)); } else { // Try to create a 3.1 core context if (m_format.majorVersion() >= 3 && haveCreateContext) { if (haveRobustness && haveContextPriority) { auto robustCorePriority = std::unique_ptr(new EglContextAttributeBuilder); robustCorePriority->setVersion(m_format.majorVersion(), m_format.minorVersion()); robustCorePriority->setRobust(true); robustCorePriority->setForwardCompatible(true); if (m_format.profile() == QSurfaceFormat::CoreProfile) { robustCorePriority->setCoreProfile(true); } else if (m_format.profile() == QSurfaceFormat::CompatibilityProfile) { robustCorePriority->setCompatibilityProfile(true); } robustCorePriority->setHighPriority(true); candidates.push_back(std::move(robustCorePriority)); } if (haveRobustness) { auto robustCore = std::unique_ptr(new EglContextAttributeBuilder); robustCore->setVersion(m_format.majorVersion(), m_format.minorVersion()); robustCore->setRobust(true); robustCore->setForwardCompatible(true); if (m_format.profile() == QSurfaceFormat::CoreProfile) { robustCore->setCoreProfile(true); } else if (m_format.profile() == QSurfaceFormat::CompatibilityProfile) { robustCore->setCompatibilityProfile(true); } candidates.push_back(std::move(robustCore)); } if (haveContextPriority) { auto corePriority = std::unique_ptr(new EglContextAttributeBuilder); corePriority->setVersion(m_format.majorVersion(), m_format.minorVersion()); corePriority->setForwardCompatible(true); if (m_format.profile() == QSurfaceFormat::CoreProfile) { corePriority->setCoreProfile(true); } else if (m_format.profile() == QSurfaceFormat::CompatibilityProfile) { corePriority->setCompatibilityProfile(true); } corePriority->setHighPriority(true); candidates.push_back(std::move(corePriority)); } auto core = std::unique_ptr(new EglContextAttributeBuilder); core->setVersion(m_format.majorVersion(), m_format.minorVersion()); core->setForwardCompatible(true); if (m_format.profile() == QSurfaceFormat::CoreProfile) { core->setCoreProfile(true); } else if (m_format.profile() == QSurfaceFormat::CompatibilityProfile) { core->setCompatibilityProfile(true); } candidates.push_back(std::move(core)); } if (haveRobustness && haveCreateContext && haveContextPriority) { auto robustPriority = std::unique_ptr(new EglContextAttributeBuilder); robustPriority->setRobust(true); robustPriority->setHighPriority(true); candidates.push_back(std::move(robustPriority)); } if (haveRobustness && haveCreateContext) { auto robust = std::unique_ptr(new EglContextAttributeBuilder); robust->setRobust(true); candidates.push_back(std::move(robust)); } candidates.emplace_back(new EglContextAttributeBuilder); } 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()); if (context != EGL_NO_CONTEXT) { qCDebug(KWIN_QPA) << "Created EGL context with attributes:" << (*it).get(); break; } } if (context == EGL_NO_CONTEXT) { qCWarning(KWIN_QPA) << "Failed to create EGL context"; return; } m_context = context; } } } diff --git a/plugins/qpa/eglhelpers.cpp b/plugins/qpa/eglhelpers.cpp new file mode 100644 index 000000000..3f4373237 --- /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/eglhelpers.h b/plugins/qpa/eglhelpers.h new file mode 100644 index 000000000..1eb762477 --- /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