diff --git a/CMakeLists.txt b/CMakeLists.txt --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -440,6 +440,8 @@ xkb.cpp gestures.cpp popup_input_filter.cpp + abstract_opengl_context_attribute_builder.cpp + egl_context_attribute_builder.cpp ) if(KWIN_BUILD_TABBOX) diff --git a/abstract_egl_backend.cpp b/abstract_egl_backend.cpp --- a/abstract_egl_backend.cpp +++ b/abstract_egl_backend.cpp @@ -18,6 +18,7 @@ along with this program. If not, see . *********************************************************************/ #include "abstract_egl_backend.h" +#include "egl_context_attribute_builder.h" #include "options.h" #include "platform.h" #include "wayland_server.h" @@ -30,6 +31,8 @@ #include #include +#include + namespace KWin { @@ -206,63 +209,44 @@ const bool haveRobustness = hasExtension(QByteArrayLiteral("EGL_EXT_create_context_robustness")); const bool haveCreateContext = hasExtension(QByteArrayLiteral("EGL_KHR_create_context")); - EGLContext ctx = 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 - }; - ctx = eglCreateContext(m_display, config(), EGL_NO_CONTEXT, context_attribs); - } - if (ctx == EGL_NO_CONTEXT) { - const EGLint context_attribs[] = { - EGL_CONTEXT_CLIENT_VERSION, 2, - EGL_NONE - }; - - ctx = eglCreateContext(m_display, config(), EGL_NO_CONTEXT, 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 (options->glCoreProfile() && haveCreateContext) { if (haveRobustness) { - const int attribs[] = { - 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 - }; - ctx = eglCreateContext(m_display, config(), EGL_NO_CONTEXT, attribs); - } - if (ctx == EGL_NO_CONTEXT) { - // try without robustness - const EGLint attribs[] = { - EGL_CONTEXT_MAJOR_VERSION_KHR, 3, - EGL_CONTEXT_MINOR_VERSION_KHR, 1, - EGL_NONE - }; - ctx = eglCreateContext(m_display, config(), EGL_NO_CONTEXT, attribs); + auto robustCore = std::unique_ptr(new EglContextAttributeBuilder); + robustCore->setVersion(3, 1); + robustCore->setRobust(true); + candidates.push_back(std::move(robustCore)); } + auto core = std::unique_ptr(new EglContextAttributeBuilder); + core->setVersion(3, 1); + candidates.push_back(std::move(core)); } - - if (ctx == 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 - }; - ctx = eglCreateContext(m_display, config(), EGL_NO_CONTEXT, attribs); + if (haveRobustness && haveCreateContext) { + auto robust = std::unique_ptr(new EglContextAttributeBuilder); + robust->setRobust(true); + candidates.push_back(std::move(robust)); } - if (ctx == EGL_NO_CONTEXT) { - // and last but not least: try without robustness - const EGLint attribs[] = { - EGL_NONE - }; - ctx = eglCreateContext(m_display, config(), EGL_NO_CONTEXT, attribs); + candidates.emplace_back(new EglContextAttributeBuilder); + } + + 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()); + if (ctx != EGL_NO_CONTEXT) { + qCDebug(KWIN_CORE) << "Created EGL context with attributes:" << (*it).get(); + break; } } diff --git a/abstract_opengl_context_attribute_builder.h b/abstract_opengl_context_attribute_builder.h new file mode 100644 --- /dev/null +++ b/abstract_opengl_context_attribute_builder.h @@ -0,0 +1,74 @@ +/******************************************************************** + KWin - the KDE window manager + This file is part of the KDE project. + +Copyright (C) 2017 Martin Flöser + +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 +#include + +namespace KWin +{ + +class AbstractOpenGLContextAttributeBuilder +{ +public: + virtual ~AbstractOpenGLContextAttributeBuilder() { + } + + void setVersion(int major, int minor = 0) { + m_versionRequested = true; + m_majorVersion = major; + m_minorVersion = minor; + } + + bool isVersionRequested() const { + return m_versionRequested; + } + + int majorVersion() const { + return m_majorVersion; + } + + int minorVersion() const { + return m_minorVersion; + } + + void setRobust(bool robust) { + m_robust = robust; + } + + bool isRobust() const { + return m_robust; + } + + virtual std::vector build() const = 0; + + QDebug operator<<(QDebug dbg) const; + +private: + bool m_versionRequested = false; + int m_majorVersion = 0; + int m_minorVersion = 0; + bool m_robust = false; +}; + +inline QDebug operator<<(QDebug dbg, const AbstractOpenGLContextAttributeBuilder *attribs) +{ + return attribs->operator<<(dbg); +} + +} diff --git a/abstract_opengl_context_attribute_builder.cpp b/abstract_opengl_context_attribute_builder.cpp new file mode 100644 --- /dev/null +++ b/abstract_opengl_context_attribute_builder.cpp @@ -0,0 +1,36 @@ +/******************************************************************** + KWin - the KDE window manager + This file is part of the KDE project. + +Copyright (C) 2017 Martin Flöser + +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 "abstract_opengl_context_attribute_builder.h" + +namespace KWin +{ + +QDebug AbstractOpenGLContextAttributeBuilder::operator<<(QDebug dbg) const +{ + QDebugStateSaver saver(dbg); + dbg.nospace() << "\nVersion requested:\t" << isVersionRequested() << "\n"; + if (isVersionRequested()) { + dbg.nospace() << "Version:\t" << majorVersion() << "." << minorVersion() << "\n"; + } + dbg.nospace() << "Robust:\t" << isRobust(); + return dbg; +} + +} diff --git a/autotests/CMakeLists.txt b/autotests/CMakeLists.txt --- a/autotests/CMakeLists.txt +++ b/autotests/CMakeLists.txt @@ -361,3 +361,8 @@ ) add_test(kwin-testX11TimestampUpdate testX11TimestampUpdate) ecm_mark_as_test(testX11TimestampUpdate) + +add_executable(testOpenGLContextAttributeBuilder opengl_context_attribute_builder_test.cpp ../abstract_opengl_context_attribute_builder.cpp ../egl_context_attribute_builder.cpp) +target_link_libraries(testOpenGLContextAttributeBuilder Qt5::Test) +add_test(kwin-testOpenGLContextAttributeBuilder testOpenGLContextAttributeBuilder) +ecm_mark_as_test(testOpenGLContextAttributeBuilder) diff --git a/autotests/opengl_context_attribute_builder_test.cpp b/autotests/opengl_context_attribute_builder_test.cpp new file mode 100644 --- /dev/null +++ b/autotests/opengl_context_attribute_builder_test.cpp @@ -0,0 +1,170 @@ +/******************************************************************** + KWin - the KDE window manager + This file is part of the KDE project. + +Copyright (C) 2017 Martin Flöser + +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 "../abstract_opengl_context_attribute_builder.h" +#include "../egl_context_attribute_builder.h" +#include +#include + +using namespace KWin; + +class OpenGLContextAttributeBuilderTest : public QObject +{ + Q_OBJECT +private Q_SLOTS: + void testCtor(); + void testRobust(); + void testVersionMajor(); + void testVersionMajorAndMinor(); + void testEgl_data(); + void testEgl(); + void testGles_data(); + void testGles(); +}; + +class MockOpenGLContextAttributeBuilder : public AbstractOpenGLContextAttributeBuilder +{ +public: + std::vector build() const override; +}; + +std::vector MockOpenGLContextAttributeBuilder::build() const +{ + return std::vector(); +} + +void OpenGLContextAttributeBuilderTest::testCtor() +{ + MockOpenGLContextAttributeBuilder builder; + QCOMPARE(builder.isVersionRequested(), false); + QCOMPARE(builder.majorVersion(), 0); + QCOMPARE(builder.minorVersion(), 0); + QCOMPARE(builder.isRobust(), false); +} + +void OpenGLContextAttributeBuilderTest::testRobust() +{ + MockOpenGLContextAttributeBuilder builder; + QCOMPARE(builder.isRobust(), false); + builder.setRobust(true); + QCOMPARE(builder.isRobust(), true); + builder.setRobust(false); + QCOMPARE(builder.isRobust(), false); +} + +void OpenGLContextAttributeBuilderTest::testVersionMajor() +{ + MockOpenGLContextAttributeBuilder builder; + builder.setVersion(2); + QCOMPARE(builder.isVersionRequested(), true); + QCOMPARE(builder.majorVersion(), 2); + QCOMPARE(builder.minorVersion(), 0); + builder.setVersion(3); + QCOMPARE(builder.isVersionRequested(), true); + QCOMPARE(builder.majorVersion(), 3); + QCOMPARE(builder.minorVersion(), 0); +} + +void OpenGLContextAttributeBuilderTest::testVersionMajorAndMinor() +{ + MockOpenGLContextAttributeBuilder builder; + builder.setVersion(2, 1); + QCOMPARE(builder.isVersionRequested(), true); + QCOMPARE(builder.majorVersion(), 2); + QCOMPARE(builder.minorVersion(), 1); + builder.setVersion(3, 2); + QCOMPARE(builder.isVersionRequested(), true); + QCOMPARE(builder.majorVersion(), 3); + QCOMPARE(builder.minorVersion(), 2); +} + +void OpenGLContextAttributeBuilderTest::testEgl_data() +{ + QTest::addColumn("requestVersion"); + QTest::addColumn("major"); + QTest::addColumn("minor"); + QTest::addColumn("robust"); + QTest::addColumn>("expectedAttribs"); + + QTest::newRow("fallback") << false << 0 << 0 << false << std::vector{EGL_NONE}; + QTest::newRow("legacy/robust") << false << 0 << 0 << true << + 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 << + std::vector{ + EGL_CONTEXT_MAJOR_VERSION_KHR, 3, + EGL_CONTEXT_MINOR_VERSION_KHR, 1, + EGL_NONE}; + QTest::newRow("core/robust") << true << 3 << 1 << true << + 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}; +} + +void OpenGLContextAttributeBuilderTest::testEgl() +{ + QFETCH(bool, requestVersion); + QFETCH(int, major); + QFETCH(int, minor); + QFETCH(bool, robust); + + EglContextAttributeBuilder builder; + if (requestVersion) { + builder.setVersion(major, minor); + } + builder.setRobust(robust); + + auto attribs = builder.build(); + QTEST(attribs, "expectedAttribs"); +} + +void OpenGLContextAttributeBuilderTest::testGles_data() +{ + QTest::addColumn("robust"); + QTest::addColumn>("expectedAttribs"); + + QTest::newRow("robust") << true << std::vector{ + 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}; + QTest::newRow("normal") << false << std::vector{ + EGL_CONTEXT_CLIENT_VERSION, 2, + EGL_NONE}; +} + +void OpenGLContextAttributeBuilderTest::testGles() +{ + QFETCH(bool, robust); + + EglOpenGLESContextAttributeBuilder builder; + builder.setVersion(2); + builder.setRobust(robust); + + auto attribs = builder.build(); + QTEST(attribs, "expectedAttribs"); +} + +QTEST_GUILESS_MAIN(OpenGLContextAttributeBuilderTest) +#include "opengl_context_attribute_builder_test.moc" diff --git a/egl_context_attribute_builder.h b/egl_context_attribute_builder.h new file mode 100644 --- /dev/null +++ b/egl_context_attribute_builder.h @@ -0,0 +1,38 @@ +/******************************************************************** + KWin - the KDE window manager + This file is part of the KDE project. + +Copyright (C) 2017 Martin Flöser + +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 +#include "abstract_opengl_context_attribute_builder.h" + +namespace KWin +{ + +class EglContextAttributeBuilder : public AbstractOpenGLContextAttributeBuilder +{ +public: + std::vector build() const override; +}; + +class EglOpenGLESContextAttributeBuilder : public AbstractOpenGLContextAttributeBuilder +{ +public: + std::vector build() const override; +}; + +} diff --git a/egl_context_attribute_builder.cpp b/egl_context_attribute_builder.cpp new file mode 100644 --- /dev/null +++ b/egl_context_attribute_builder.cpp @@ -0,0 +1,59 @@ +/******************************************************************** + KWin - the KDE window manager + This file is part of the KDE project. + +Copyright (C) 2017 Martin Flöser + +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 "egl_context_attribute_builder.h" +#include + +namespace KWin +{ +std::vector EglContextAttributeBuilder::build() const +{ + std::vector attribs; + if (isVersionRequested()) { + attribs.emplace_back(EGL_CONTEXT_MAJOR_VERSION_KHR); + attribs.emplace_back(majorVersion()); + attribs.emplace_back(EGL_CONTEXT_MINOR_VERSION_KHR); + attribs.emplace_back(minorVersion()); + } + if (isRobust()) { + attribs.emplace_back(EGL_CONTEXT_OPENGL_RESET_NOTIFICATION_STRATEGY_KHR); + attribs.emplace_back(EGL_LOSE_CONTEXT_ON_RESET_KHR); + attribs.emplace_back(EGL_CONTEXT_FLAGS_KHR); + attribs.emplace_back(EGL_CONTEXT_OPENGL_ROBUST_ACCESS_BIT_KHR); + } + attribs.emplace_back(EGL_NONE); + return attribs; +} + +std::vector EglOpenGLESContextAttributeBuilder::build() const +{ + std::vector attribs; + attribs.emplace_back(EGL_CONTEXT_CLIENT_VERSION); + attribs.emplace_back(majorVersion()); + if (isRobust()) { + attribs.emplace_back(EGL_CONTEXT_OPENGL_ROBUST_ACCESS_EXT); + attribs.emplace_back(EGL_TRUE); + attribs.emplace_back(EGL_CONTEXT_OPENGL_RESET_NOTIFICATION_STRATEGY_EXT); + attribs.emplace_back(EGL_LOSE_CONTEXT_ON_RESET_EXT); + } + attribs.emplace_back(EGL_NONE); + return attribs; +} + +}