diff --git a/abstract_opengl_context_attribute_builder.h b/abstract_opengl_context_attribute_builder.h --- a/abstract_opengl_context_attribute_builder.h +++ b/abstract_opengl_context_attribute_builder.h @@ -19,11 +19,12 @@ *********************************************************************/ #pragma once #include +#include namespace KWin { -class AbstractOpenGLContextAttributeBuilder +class KWIN_EXPORT AbstractOpenGLContextAttributeBuilder { public: virtual ~AbstractOpenGLContextAttributeBuilder() { @@ -55,6 +56,36 @@ return m_robust; } + void setForwardCompatible(bool forward) { + m_forwardCompatible = forward; + } + + bool isForwardCompatible() const { + return m_forwardCompatible; + } + + void setCoreProfile(bool core) { + m_coreProfile = core; + if (m_coreProfile) { + setCompatibilityProfile(false); + } + } + + bool isCoreProfile() const { + return m_coreProfile; + } + + void setCompatibilityProfile(bool compatibility) { + m_compatibilityProfile = compatibility; + if (m_compatibilityProfile) { + setCoreProfile(false); + } + } + + bool isCompatibilityProfile() const { + return m_compatibilityProfile; + } + virtual std::vector build() const = 0; QDebug operator<<(QDebug dbg) const; @@ -64,6 +95,9 @@ int m_majorVersion = 0; int m_minorVersion = 0; bool m_robust = false; + bool m_forwardCompatible = false; + bool m_coreProfile = false; + bool m_compatibilityProfile = false; }; inline QDebug operator<<(QDebug dbg, const AbstractOpenGLContextAttributeBuilder *attribs) diff --git a/abstract_opengl_context_attribute_builder.cpp b/abstract_opengl_context_attribute_builder.cpp --- a/abstract_opengl_context_attribute_builder.cpp +++ b/abstract_opengl_context_attribute_builder.cpp @@ -29,7 +29,10 @@ if (isVersionRequested()) { dbg.nospace() << "Version:\t" << majorVersion() << "." << minorVersion() << "\n"; } - dbg.nospace() << "Robust:\t" << isRobust(); + dbg.nospace() << "Robust:\t" << isRobust() << "\n"; + dbg.nospace() << "Forward compatible:\t" << isForwardCompatible() << "\n"; + dbg.nospace() << "Core profile:\t" << isCoreProfile() << "\n"; + dbg.nospace() << "Compatibility profile:\t" << isCompatibilityProfile(); return dbg; } diff --git a/autotests/opengl_context_attribute_builder_test.cpp b/autotests/opengl_context_attribute_builder_test.cpp --- a/autotests/opengl_context_attribute_builder_test.cpp +++ b/autotests/opengl_context_attribute_builder_test.cpp @@ -30,6 +30,8 @@ private Q_SLOTS: void testCtor(); void testRobust(); + void testForwardCompatible(); + void testProfile(); void testVersionMajor(); void testVersionMajorAndMinor(); void testEgl_data(); @@ -56,6 +58,9 @@ QCOMPARE(builder.majorVersion(), 0); QCOMPARE(builder.minorVersion(), 0); QCOMPARE(builder.isRobust(), false); + QCOMPARE(builder.isForwardCompatible(), false); + QCOMPARE(builder.isCoreProfile(), false); + QCOMPARE(builder.isCompatibilityProfile(), false); } void OpenGLContextAttributeBuilderTest::testRobust() @@ -68,6 +73,32 @@ QCOMPARE(builder.isRobust(), false); } +void OpenGLContextAttributeBuilderTest::testForwardCompatible() +{ + MockOpenGLContextAttributeBuilder builder; + QCOMPARE(builder.isForwardCompatible(), false); + builder.setForwardCompatible(true); + QCOMPARE(builder.isForwardCompatible(), true); + builder.setForwardCompatible(false); + QCOMPARE(builder.isForwardCompatible(), false); +} + +void OpenGLContextAttributeBuilderTest::testProfile() +{ + MockOpenGLContextAttributeBuilder builder; + QCOMPARE(builder.isCoreProfile(), false); + QCOMPARE(builder.isCompatibilityProfile(), false); + builder.setCoreProfile(true); + QCOMPARE(builder.isCoreProfile(), true); + QCOMPARE(builder.isCompatibilityProfile(), false); + builder.setCompatibilityProfile(true); + QCOMPARE(builder.isCoreProfile(), false); + QCOMPARE(builder.isCompatibilityProfile(), true); + builder.setCoreProfile(true); + QCOMPARE(builder.isCoreProfile(), true); + QCOMPARE(builder.isCompatibilityProfile(), false); +} + void OpenGLContextAttributeBuilderTest::testVersionMajor() { MockOpenGLContextAttributeBuilder builder; @@ -100,40 +131,92 @@ QTest::addColumn("major"); QTest::addColumn("minor"); QTest::addColumn("robust"); + QTest::addColumn("forwardCompatible"); + QTest::addColumn("coreProfile"); + QTest::addColumn("compatibilityProfile"); QTest::addColumn>("expectedAttribs"); - QTest::newRow("fallback") << false << 0 << 0 << false << std::vector{EGL_NONE}; - QTest::newRow("legacy/robust") << false << 0 << 0 << true << + QTest::newRow("fallback") << false << 0 << 0 << false << false << false << false << std::vector{EGL_NONE}; + QTest::newRow("legacy/robust") << false << 0 << 0 << true << false << false << false << std::vector{ EGL_CONTEXT_OPENGL_RESET_NOTIFICATION_STRATEGY_KHR, EGL_LOSE_CONTEXT_ON_RESET_KHR, EGL_CONTEXT_FLAGS_KHR, EGL_CONTEXT_OPENGL_ROBUST_ACCESS_BIT_KHR, EGL_NONE}; - QTest::newRow("core") << true << 3 << 1 << false << + QTest::newRow("core") << true << 3 << 1 << false << false << false << false << std::vector{ EGL_CONTEXT_MAJOR_VERSION_KHR, 3, EGL_CONTEXT_MINOR_VERSION_KHR, 1, EGL_NONE}; - QTest::newRow("core/robust") << true << 3 << 1 << true << + QTest::newRow("core/robust") << true << 3 << 1 << true << false << false << false << std::vector{ EGL_CONTEXT_MAJOR_VERSION_KHR, 3, EGL_CONTEXT_MINOR_VERSION_KHR, 1, EGL_CONTEXT_OPENGL_RESET_NOTIFICATION_STRATEGY_KHR, EGL_LOSE_CONTEXT_ON_RESET_KHR, EGL_CONTEXT_FLAGS_KHR, EGL_CONTEXT_OPENGL_ROBUST_ACCESS_BIT_KHR, EGL_NONE}; + QTest::newRow("core/robust/forward compatible") << true << 3 << 1 << true << true << false << false << + std::vector{ + EGL_CONTEXT_MAJOR_VERSION_KHR, 3, + EGL_CONTEXT_MINOR_VERSION_KHR, 1, + EGL_CONTEXT_OPENGL_RESET_NOTIFICATION_STRATEGY_KHR, EGL_LOSE_CONTEXT_ON_RESET_KHR, + EGL_CONTEXT_FLAGS_KHR, EGL_CONTEXT_OPENGL_ROBUST_ACCESS_BIT_KHR | EGL_CONTEXT_OPENGL_FORWARD_COMPATIBLE_BIT_KHR, + EGL_NONE}; + QTest::newRow("core/forward compatible") << true << 3 << 1 << false << true << false << false << + std::vector{ + EGL_CONTEXT_MAJOR_VERSION_KHR, 3, + EGL_CONTEXT_MINOR_VERSION_KHR, 1, + EGL_CONTEXT_FLAGS_KHR, EGL_CONTEXT_OPENGL_FORWARD_COMPATIBLE_BIT_KHR, + EGL_NONE}; + QTest::newRow("core profile/forward compatible") << true << 3 << 2 << false << true << true << false << + std::vector{ + EGL_CONTEXT_MAJOR_VERSION_KHR, 3, + EGL_CONTEXT_MINOR_VERSION_KHR, 2, + EGL_CONTEXT_FLAGS_KHR, EGL_CONTEXT_OPENGL_FORWARD_COMPATIBLE_BIT_KHR, + EGL_CONTEXT_OPENGL_PROFILE_MASK_KHR, EGL_CONTEXT_OPENGL_CORE_PROFILE_BIT_KHR, + EGL_NONE}; + QTest::newRow("compatibility profile/forward compatible") << true << 3 << 2 << false << true << false << true << + std::vector{ + EGL_CONTEXT_MAJOR_VERSION_KHR, 3, + EGL_CONTEXT_MINOR_VERSION_KHR, 2, + EGL_CONTEXT_FLAGS_KHR, EGL_CONTEXT_OPENGL_FORWARD_COMPATIBLE_BIT_KHR, + EGL_CONTEXT_OPENGL_PROFILE_MASK_KHR, EGL_CONTEXT_OPENGL_COMPATIBILITY_PROFILE_BIT_KHR, + EGL_NONE}; + QTest::newRow("core profile/robust/forward compatible") << true << 3 << 2 << true << true << true << false << + std::vector{ + EGL_CONTEXT_MAJOR_VERSION_KHR, 3, + EGL_CONTEXT_MINOR_VERSION_KHR, 2, + EGL_CONTEXT_OPENGL_RESET_NOTIFICATION_STRATEGY_KHR, EGL_LOSE_CONTEXT_ON_RESET_KHR, + EGL_CONTEXT_FLAGS_KHR, EGL_CONTEXT_OPENGL_ROBUST_ACCESS_BIT_KHR | EGL_CONTEXT_OPENGL_FORWARD_COMPATIBLE_BIT_KHR, + EGL_CONTEXT_OPENGL_PROFILE_MASK_KHR, EGL_CONTEXT_OPENGL_CORE_PROFILE_BIT_KHR, + EGL_NONE}; + QTest::newRow("compatibility profile/robust/forward compatible") << true << 3 << 2 << true << true << false << true << + std::vector{ + EGL_CONTEXT_MAJOR_VERSION_KHR, 3, + EGL_CONTEXT_MINOR_VERSION_KHR, 2, + EGL_CONTEXT_OPENGL_RESET_NOTIFICATION_STRATEGY_KHR, EGL_LOSE_CONTEXT_ON_RESET_KHR, + EGL_CONTEXT_FLAGS_KHR, EGL_CONTEXT_OPENGL_ROBUST_ACCESS_BIT_KHR | EGL_CONTEXT_OPENGL_FORWARD_COMPATIBLE_BIT_KHR, + EGL_CONTEXT_OPENGL_PROFILE_MASK_KHR, EGL_CONTEXT_OPENGL_COMPATIBILITY_PROFILE_BIT_KHR, + EGL_NONE}; } void OpenGLContextAttributeBuilderTest::testEgl() { QFETCH(bool, requestVersion); QFETCH(int, major); QFETCH(int, minor); QFETCH(bool, robust); + QFETCH(bool, forwardCompatible); + QFETCH(bool, coreProfile); + QFETCH(bool, compatibilityProfile); EglContextAttributeBuilder builder; if (requestVersion) { builder.setVersion(major, minor); } builder.setRobust(robust); + builder.setForwardCompatible(forwardCompatible); + builder.setCoreProfile(coreProfile); + builder.setCompatibilityProfile(compatibilityProfile); auto attribs = builder.build(); QTEST(attribs, "expectedAttribs"); diff --git a/egl_context_attribute_builder.h b/egl_context_attribute_builder.h --- a/egl_context_attribute_builder.h +++ b/egl_context_attribute_builder.h @@ -19,17 +19,18 @@ *********************************************************************/ #pragma once #include "abstract_opengl_context_attribute_builder.h" +#include namespace KWin { -class EglContextAttributeBuilder : public AbstractOpenGLContextAttributeBuilder +class KWIN_EXPORT EglContextAttributeBuilder : public AbstractOpenGLContextAttributeBuilder { public: std::vector build() const override; }; -class EglOpenGLESContextAttributeBuilder : public AbstractOpenGLContextAttributeBuilder +class KWIN_EXPORT EglOpenGLESContextAttributeBuilder : public AbstractOpenGLContextAttributeBuilder { public: std::vector build() const override; diff --git a/egl_context_attribute_builder.cpp b/egl_context_attribute_builder.cpp --- a/egl_context_attribute_builder.cpp +++ b/egl_context_attribute_builder.cpp @@ -31,11 +31,26 @@ attribs.emplace_back(EGL_CONTEXT_MINOR_VERSION_KHR); attribs.emplace_back(minorVersion()); } + int contextFlags = 0; if (isRobust()) { attribs.emplace_back(EGL_CONTEXT_OPENGL_RESET_NOTIFICATION_STRATEGY_KHR); attribs.emplace_back(EGL_LOSE_CONTEXT_ON_RESET_KHR); + contextFlags |= EGL_CONTEXT_OPENGL_ROBUST_ACCESS_BIT_KHR; + } + if (isForwardCompatible()) { + contextFlags |= EGL_CONTEXT_OPENGL_FORWARD_COMPATIBLE_BIT_KHR; + } + if (contextFlags != 0) { attribs.emplace_back(EGL_CONTEXT_FLAGS_KHR); - attribs.emplace_back(EGL_CONTEXT_OPENGL_ROBUST_ACCESS_BIT_KHR); + attribs.emplace_back(contextFlags); + } + if (isCoreProfile() || isCompatibilityProfile()) { + attribs.emplace_back(EGL_CONTEXT_OPENGL_PROFILE_MASK_KHR); + if (isCoreProfile()) { + attribs.emplace_back(EGL_CONTEXT_OPENGL_CORE_PROFILE_BIT_KHR); + } else if (isCompatibilityProfile()) { + attribs.emplace_back(EGL_CONTEXT_OPENGL_COMPATIBILITY_PROFILE_BIT_KHR); + } } attribs.emplace_back(EGL_NONE); return attribs; diff --git a/plugins/qpa/abstractplatformcontext.cpp b/plugins/qpa/abstractplatformcontext.cpp --- a/plugins/qpa/abstractplatformcontext.cpp +++ b/plugins/qpa/abstractplatformcontext.cpp @@ -19,8 +19,11 @@ *********************************************************************/ #include "abstractplatformcontext.h" #include "integration.h" +#include "egl_context_attribute_builder.h" #include +#include + namespace KWin { @@ -145,66 +148,58 @@ const bool haveRobustness = extensions.contains(QByteArrayLiteral("EGL_EXT_create_context_robustness")); const bool haveCreateContext = extensions.contains(QByteArrayLiteral("EGL_KHR_create_context")); - EGLContext context = EGL_NO_CONTEXT; + std::vector> candidates; if (isOpenGLES()) { if (haveCreateContext && haveRobustness) { - const EGLint context_attribs[] = { - EGL_CONTEXT_CLIENT_VERSION, 2, - EGL_CONTEXT_OPENGL_ROBUST_ACCESS_EXT, EGL_TRUE, - EGL_CONTEXT_OPENGL_RESET_NOTIFICATION_STRATEGY_EXT, EGL_LOSE_CONTEXT_ON_RESET_EXT, - EGL_NONE - }; - context = eglCreateContext(eglDisplay(), config(), shareContext, context_attribs); - } - if (context == EGL_NO_CONTEXT) { - const EGLint context_attribs[] = { - EGL_CONTEXT_CLIENT_VERSION, 2, - EGL_NONE - }; - - context = eglCreateContext(eglDisplay(), config(), shareContext, context_attribs); + auto glesRobust = std::unique_ptr(new EglOpenGLESContextAttributeBuilder); + glesRobust->setVersion(2); + glesRobust->setRobust(true); + candidates.push_back(std::move(glesRobust)); } + 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) { - const int attribs[] = { - EGL_CONTEXT_MAJOR_VERSION_KHR, m_format.majorVersion(), - EGL_CONTEXT_MINOR_VERSION_KHR, m_format.minorVersion(), - EGL_CONTEXT_OPENGL_RESET_NOTIFICATION_STRATEGY_KHR, EGL_LOSE_CONTEXT_ON_RESET_KHR, - EGL_CONTEXT_FLAGS_KHR, EGL_CONTEXT_OPENGL_ROBUST_ACCESS_BIT_KHR | EGL_CONTEXT_OPENGL_FORWARD_COMPATIBLE_BIT_KHR, - EGL_CONTEXT_OPENGL_PROFILE_MASK_KHR, m_format.profile() == QSurfaceFormat::CoreProfile ? EGL_CONTEXT_OPENGL_CORE_PROFILE_BIT_KHR : EGL_CONTEXT_OPENGL_COMPATIBILITY_PROFILE_BIT_KHR, - EGL_NONE - }; - context = eglCreateContext(eglDisplay(), config(), shareContext, attribs); + 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 (context == EGL_NO_CONTEXT) { - // try without robustness - const EGLint attribs[] = { - EGL_CONTEXT_MAJOR_VERSION_KHR, m_format.majorVersion(), - EGL_CONTEXT_MINOR_VERSION_KHR, m_format.minorVersion(), - EGL_CONTEXT_FLAGS_KHR, EGL_CONTEXT_OPENGL_FORWARD_COMPATIBLE_BIT_KHR, - EGL_CONTEXT_OPENGL_PROFILE_MASK_KHR, m_format.profile() == QSurfaceFormat::CoreProfile ? EGL_CONTEXT_OPENGL_CORE_PROFILE_BIT_KHR : EGL_CONTEXT_OPENGL_COMPATIBILITY_PROFILE_BIT_KHR, - EGL_NONE - }; - context = eglCreateContext(eglDisplay(), config(), shareContext, attribs); + 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 (context == EGL_NO_CONTEXT && haveRobustness && haveCreateContext) { - const int attribs[] = { - EGL_CONTEXT_FLAGS_KHR, EGL_CONTEXT_OPENGL_ROBUST_ACCESS_BIT_KHR, - EGL_CONTEXT_OPENGL_RESET_NOTIFICATION_STRATEGY_KHR, EGL_LOSE_CONTEXT_ON_RESET_KHR, - EGL_NONE - }; - context = eglCreateContext(eglDisplay(), config(), shareContext, attribs); + if (haveRobustness && haveCreateContext) { + auto robust = std::unique_ptr(new EglContextAttributeBuilder); + robust->setRobust(true); + candidates.push_back(std::move(robust)); } - if (context == EGL_NO_CONTEXT) { - // and last but not least: try without robustness - const EGLint attribs[] = { - EGL_NONE - }; - context = eglCreateContext(eglDisplay(), config(), shareContext, attribs); + 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; } }