diff --git a/abstract_egl_backend.h b/abstract_egl_backend.h --- a/abstract_egl_backend.h +++ b/abstract_egl_backend.h @@ -40,17 +40,17 @@ return m_context; } + static void unbindWaylandDisplay(); + protected: AbstractEglBackend(); EGLSurface surface() const { return m_surface; } EGLConfig config() const { return m_config; } - void setEglDisplay(const EGLDisplay &display) { - m_display = display; - } + void setEglDisplay(const EGLDisplay &display); void setSurface(const EGLSurface &surface) { m_surface = surface; } diff --git a/abstract_egl_backend.cpp b/abstract_egl_backend.cpp --- a/abstract_egl_backend.cpp +++ b/abstract_egl_backend.cpp @@ -19,6 +19,7 @@ *********************************************************************/ #include "abstract_egl_backend.h" #include "options.h" +#include "platform.h" #include "wayland_server.h" #include #include @@ -56,16 +57,20 @@ AbstractEglBackend::~AbstractEglBackend() = default; -void AbstractEglBackend::cleanup() +void AbstractEglBackend::unbindWaylandDisplay() { - if (eglUnbindWaylandDisplayWL && eglDisplay() != EGL_NO_DISPLAY) { - eglUnbindWaylandDisplayWL(eglDisplay(), *(WaylandServer::self()->display())); + auto display = kwinApp()->platform()->sceneEglDisplay(); + if (eglUnbindWaylandDisplayWL && display != EGL_NO_DISPLAY) { + eglUnbindWaylandDisplayWL(display, *(WaylandServer::self()->display())); } +} + +void AbstractEglBackend::cleanup() +{ cleanupGL(); doneCurrent(); eglDestroyContext(m_display, m_context); cleanupSurfaces(); - eglTerminate(m_display); eglReleaseThread(); } @@ -137,11 +142,14 @@ eglBindWaylandDisplayWL = (eglBindWaylandDisplayWL_func)eglGetProcAddress("eglBindWaylandDisplayWL"); eglUnbindWaylandDisplayWL = (eglUnbindWaylandDisplayWL_func)eglGetProcAddress("eglUnbindWaylandDisplayWL"); eglQueryWaylandBufferWL = (eglQueryWaylandBufferWL_func)eglGetProcAddress("eglQueryWaylandBufferWL"); - if (!eglBindWaylandDisplayWL(eglDisplay(), *(WaylandServer::self()->display()))) { - eglUnbindWaylandDisplayWL = nullptr; - eglQueryWaylandBufferWL = nullptr; - } else { - waylandServer()->display()->setEglDisplay(eglDisplay()); + // only bind if not already done + if (waylandServer()->display()->eglDisplay() != eglDisplay()) { + if (!eglBindWaylandDisplayWL(eglDisplay(), *(WaylandServer::self()->display()))) { + eglUnbindWaylandDisplayWL = nullptr; + eglQueryWaylandBufferWL = nullptr; + } else { + waylandServer()->display()->setEglDisplay(eglDisplay()); + } } } } @@ -263,6 +271,11 @@ return true; } +void AbstractEglBackend::setEglDisplay(const EGLDisplay &display) { + m_display = display; + kwinApp()->platform()->setSceneEglDisplay(display); +} + AbstractEglTexture::AbstractEglTexture(SceneOpenGL::Texture *texture, AbstractEglBackend *backend) : SceneOpenGL::TexturePrivate() , q(texture) diff --git a/autotests/integration/CMakeLists.txt b/autotests/integration/CMakeLists.txt --- a/autotests/integration/CMakeLists.txt +++ b/autotests/integration/CMakeLists.txt @@ -34,6 +34,7 @@ integrationTest(NAME testShellClient SRCS shell_client_test.cpp) integrationTest(NAME testDontCrashNoBorder SRCS dont_crash_no_border.cpp) integrationTest(NAME testXClipboardSync SRCS xclipboardsync_test.cpp) +integrationTest(NAME testSceneOpenGL SRCS scene_opengl_test.cpp) integrationTest(NAME testSceneQPainter SRCS scene_qpainter_test.cpp) integrationTest(NAME testNoXdgRuntimeDir SRCS no_xdg_runtime_dir_test.cpp) integrationTest(NAME testScreenChanges SRCS screen_changes_test.cpp) diff --git a/autotests/integration/scene_opengl_test.cpp b/autotests/integration/scene_opengl_test.cpp new file mode 100644 --- /dev/null +++ b/autotests/integration/scene_opengl_test.cpp @@ -0,0 +1,103 @@ +/******************************************************************** +KWin - the KDE window manager +This file is part of the KDE project. + +Copyright (C) 2016 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 "kwin_wayland_test.h" +#include "composite.h" +#include "effectloader.h" +#include "cursor.h" +#include "platform.h" +#include "scene_opengl.h" +#include "shell_client.h" +#include "wayland_server.h" +#include "effect_builtins.h" + +#include + +using namespace KWin; +static const QString s_socketName = QStringLiteral("wayland_test_kwin_scene_opengl-0"); + +class SceneOpenGLTest : public QObject +{ +Q_OBJECT +private Q_SLOTS: + void initTestCase(); + void cleanup(); + void testRestart(); +}; + +void SceneOpenGLTest::cleanup() +{ + Test::destroyWaylandConnection(); +} + +void SceneOpenGLTest::initTestCase() +{ + if (!QFile::exists(QStringLiteral("/dev/dri/card0"))) { + QSKIP("Needs a dri device"); + } + qRegisterMetaType(); + qRegisterMetaType(); + QSignalSpy workspaceCreatedSpy(kwinApp(), &Application::workspaceCreated); + QVERIFY(workspaceCreatedSpy.isValid()); + kwinApp()->platform()->setInitialWindowSize(QSize(1280, 1024)); + QVERIFY(waylandServer()->init(s_socketName.toLocal8Bit())); + + // disable all effects - we don't want to have it interact with the rendering + auto config = KSharedConfig::openConfig(QString(), KConfig::SimpleConfig); + KConfigGroup plugins(config, QStringLiteral("Plugins")); + ScriptedEffectLoader loader; + const auto builtinNames = BuiltInEffects::availableEffectNames() << loader.listOfKnownEffects(); + for (QString name : builtinNames) { + plugins.writeEntry(name + QStringLiteral("Enabled"), false); + } + + config->sync(); + kwinApp()->setConfig(config); + + qputenv("XCURSOR_THEME", QByteArrayLiteral("DMZ-White")); + qputenv("XCURSOR_SIZE", QByteArrayLiteral("24")); + qputenv("KWIN_COMPOSE", QByteArrayLiteral("O2")); + + kwinApp()->start(); + QVERIFY(workspaceCreatedSpy.wait()); + QVERIFY(Compositor::self()); +} + +void SceneOpenGLTest::testRestart() +{ + // simple restart of the OpenGL compositor without any windows being shown + QSignalSpy sceneCreatedSpy(KWin::Compositor::self(), &Compositor::sceneCreated); + QVERIFY(sceneCreatedSpy.isValid()); + KWin::Compositor::self()->slotReinitialize(); + if (sceneCreatedSpy.isEmpty()) { + QVERIFY(sceneCreatedSpy.wait()); + } + QCOMPARE(sceneCreatedSpy.count(), 1); + auto scene = qobject_cast(KWin::Compositor::self()->scene()); + QVERIFY(scene); + + // trigger a repaint + KWin::Compositor::self()->addRepaintFull(); + // and wait 100 msec to ensure it's rendered + // TODO: introduce frameRendered signal in SceneOpenGL + QTest::qWait(100); +} + +WAYLANDTEST_MAIN(SceneOpenGLTest) +#include "scene_opengl_test.moc" diff --git a/composite.cpp b/composite.cpp --- a/composite.cpp +++ b/composite.cpp @@ -19,6 +19,7 @@ *********************************************************************/ #include "composite.h" +#include "abstract_egl_backend.h" #include "dbusinterface.h" #include "utils.h" #include @@ -145,6 +146,7 @@ Compositor::~Compositor() { emit aboutToDestroy(); + AbstractEglBackend::unbindWaylandDisplay(); finish(); deleteUnusedSupportProperties(); delete cm_selection; diff --git a/platform.h b/platform.h --- a/platform.h +++ b/platform.h @@ -65,7 +65,8 @@ /** * The EGLDisplay used by the compositing scene. **/ - virtual EGLDisplay sceneEglDisplay() const; + EGLDisplay sceneEglDisplay() const; + void setSceneEglDisplay(EGLDisplay display); /** * The EGLContext used by the compositing scene. **/ @@ -231,6 +232,7 @@ bool m_pointerWarping = false; bool m_outputsEnabled = true; int m_initialOutputCount = 1; + EGLDisplay m_eglDisplay; }; } diff --git a/platform.cpp b/platform.cpp --- a/platform.cpp +++ b/platform.cpp @@ -33,11 +33,15 @@ Platform::Platform(QObject *parent) : QObject(parent) + , m_eglDisplay(EGL_NO_DISPLAY) { } Platform::~Platform() { + if (m_eglDisplay != EGL_NO_DISPLAY) { + eglTerminate(m_eglDisplay); + } } QImage Platform::softwareCursor() const @@ -250,14 +254,14 @@ return hasGLExtension(QByteArrayLiteral("EGL_KHR_surfaceless_context")); } -EGLDisplay Platform::sceneEglDisplay() const +EGLDisplay KWin::Platform::sceneEglDisplay() const { - if (Compositor *c = Compositor::self()) { - if (SceneOpenGL *s = dynamic_cast(c->scene())) { - return static_cast(s->backend())->eglDisplay(); - } - } - return EGL_NO_DISPLAY; + return m_eglDisplay; +} + +void Platform::setSceneEglDisplay(EGLDisplay display) +{ + m_eglDisplay = display; } EGLContext Platform::sceneEglContext() const diff --git a/plugins/platforms/drm/egl_gbm_backend.cpp b/plugins/platforms/drm/egl_gbm_backend.cpp --- a/plugins/platforms/drm/egl_gbm_backend.cpp +++ b/plugins/platforms/drm/egl_gbm_backend.cpp @@ -90,23 +90,25 @@ bool EglGbmBackend::initializeEgl() { initClientExtensions(); - EGLDisplay display = EGL_NO_DISPLAY; + EGLDisplay display = m_backend->sceneEglDisplay(); // Use eglGetPlatformDisplayEXT() to get the display pointer // if the implementation supports it. - if (!hasClientExtension(QByteArrayLiteral("EGL_EXT_platform_base")) || - !hasClientExtension(QByteArrayLiteral("EGL_MESA_platform_gbm"))) { - setFailed("EGL_EXT_platform_base and/or EGL_MESA_platform_gbm missing"); - return false; - } + if (display == EGL_NO_DISPLAY) { + if (!hasClientExtension(QByteArrayLiteral("EGL_EXT_platform_base")) || + !hasClientExtension(QByteArrayLiteral("EGL_MESA_platform_gbm"))) { + setFailed("EGL_EXT_platform_base and/or EGL_MESA_platform_gbm missing"); + return false; + } - m_device = gbm_create_device(m_backend->fd()); - if (!m_device) { - setFailed("Could not create gbm device"); - return false; - } + m_device = gbm_create_device(m_backend->fd()); + if (!m_device) { + setFailed("Could not create gbm device"); + return false; + } - display = eglGetPlatformDisplayEXT(EGL_PLATFORM_GBM_MESA, m_device, nullptr); + display = eglGetPlatformDisplayEXT(EGL_PLATFORM_GBM_MESA, m_device, nullptr); + } if (display == EGL_NO_DISPLAY) return false; diff --git a/plugins/platforms/hwcomposer/egl_hwcomposer_backend.cpp b/plugins/platforms/hwcomposer/egl_hwcomposer_backend.cpp --- a/plugins/platforms/hwcomposer/egl_hwcomposer_backend.cpp +++ b/plugins/platforms/hwcomposer/egl_hwcomposer_backend.cpp @@ -43,9 +43,11 @@ { // cannot use initClientExtensions as that crashes in libhybris qputenv("EGL_PLATFORM", QByteArrayLiteral("hwcomposer")); - EGLDisplay display = EGL_NO_DISPLAY; + EGLDisplay display = m_backend->sceneEglDisplay(); - display = eglGetDisplay(nullptr); + if (display == EGL_NO_DISPLAY) { + display = eglGetDisplay(nullptr); + } if (display == EGL_NO_DISPLAY) { return false; } diff --git a/plugins/platforms/virtual/egl_gbm_backend.cpp b/plugins/platforms/virtual/egl_gbm_backend.cpp --- a/plugins/platforms/virtual/egl_gbm_backend.cpp +++ b/plugins/platforms/virtual/egl_gbm_backend.cpp @@ -53,17 +53,19 @@ bool EglGbmBackend::initializeEgl() { initClientExtensions(); - EGLDisplay display = EGL_NO_DISPLAY; + EGLDisplay display = m_backend->sceneEglDisplay(); // Use eglGetPlatformDisplayEXT() to get the display pointer // if the implementation supports it. - if (!hasClientExtension(QByteArrayLiteral("EGL_EXT_platform_base")) || - !hasClientExtension(QByteArrayLiteral("EGL_MESA_platform_gbm"))) { - setFailed("EGL_EXT_platform_base and/or EGL_MESA_platform_gbm missing"); - return false; - } + if (display == EGL_NO_DISPLAY) { + if (!hasClientExtension(QByteArrayLiteral("EGL_EXT_platform_base")) || + !hasClientExtension(QByteArrayLiteral("EGL_MESA_platform_gbm"))) { + setFailed("EGL_EXT_platform_base and/or EGL_MESA_platform_gbm missing"); + return false; + } - display = eglGetPlatformDisplayEXT(EGL_PLATFORM_GBM_MESA, EGL_DEFAULT_DISPLAY, nullptr); + display = eglGetPlatformDisplayEXT(EGL_PLATFORM_GBM_MESA, EGL_DEFAULT_DISPLAY, nullptr); + } if (display == EGL_NO_DISPLAY) return false; diff --git a/plugins/platforms/wayland/egl_wayland_backend.cpp b/plugins/platforms/wayland/egl_wayland_backend.cpp --- a/plugins/platforms/wayland/egl_wayland_backend.cpp +++ b/plugins/platforms/wayland/egl_wayland_backend.cpp @@ -70,19 +70,21 @@ bool EglWaylandBackend::initializeEgl() { initClientExtensions(); - EGLDisplay display = EGL_NO_DISPLAY; + EGLDisplay display = m_wayland->sceneEglDisplay(); // Use eglGetPlatformDisplayEXT() to get the display pointer // if the implementation supports it. - m_havePlatformBase = hasClientExtension(QByteArrayLiteral("EGL_EXT_platform_base")); - if (m_havePlatformBase) { - // Make sure that the wayland platform is supported - if (!hasClientExtension(QByteArrayLiteral("EGL_EXT_platform_wayland"))) - return false; - - display = eglGetPlatformDisplayEXT(EGL_PLATFORM_WAYLAND_EXT, m_wayland->display(), nullptr); - } else { - display = eglGetDisplay(m_wayland->display()); + if (display == EGL_NO_DISPLAY) { + m_havePlatformBase = hasClientExtension(QByteArrayLiteral("EGL_EXT_platform_base")); + if (m_havePlatformBase) { + // Make sure that the wayland platform is supported + if (!hasClientExtension(QByteArrayLiteral("EGL_EXT_platform_wayland"))) + return false; + + display = eglGetPlatformDisplayEXT(EGL_PLATFORM_WAYLAND_EXT, m_wayland->display(), nullptr); + } else { + display = eglGetDisplay(m_wayland->display()); + } } if (display == EGL_NO_DISPLAY) diff --git a/plugins/platforms/x11/common/eglonxbackend.cpp b/plugins/platforms/x11/common/eglonxbackend.cpp --- a/plugins/platforms/x11/common/eglonxbackend.cpp +++ b/plugins/platforms/x11/common/eglonxbackend.cpp @@ -22,6 +22,7 @@ #include "main.h" #include "options.h" #include "overlaywindow.h" +#include "platform.h" #include "screens.h" #include "xcbutils.h" // kwin libs @@ -171,27 +172,29 @@ bool EglOnXBackend::initRenderingContext() { initClientExtensions(); - EGLDisplay dpy; + EGLDisplay dpy = kwinApp()->platform()->sceneEglDisplay(); // Use eglGetPlatformDisplayEXT() to get the display pointer // if the implementation supports it. - const bool havePlatformBase = hasClientExtension(QByteArrayLiteral("EGL_EXT_platform_base")); - setHavePlatformBase(havePlatformBase); - if (havePlatformBase) { - // Make sure that the X11 platform is supported - if (!hasClientExtension(QByteArrayLiteral("EGL_EXT_platform_x11"))) { - qCWarning(KWIN_CORE) << "EGL_EXT_platform_base is supported, but EGL_EXT_platform_x11 is not. Cannot create EGLDisplay on X11"; - return false; - } + if (display == EGL_NO_DISPLAY) { + const bool havePlatformBase = hasClientExtension(QByteArrayLiteral("EGL_EXT_platform_base")); + setHavePlatformBase(havePlatformBase); + if (havePlatformBase) { + // Make sure that the X11 platform is supported + if (!hasClientExtension(QByteArrayLiteral("EGL_EXT_platform_x11"))) { + qCWarning(KWIN_CORE) << "EGL_EXT_platform_base is supported, but EGL_EXT_platform_x11 is not. Cannot create EGLDisplay on X11"; + return false; + } - const int attribs[] = { - EGL_PLATFORM_X11_SCREEN_EXT, m_x11ScreenNumber, - EGL_NONE - }; + const int attribs[] = { + EGL_PLATFORM_X11_SCREEN_EXT, m_x11ScreenNumber, + EGL_NONE + }; - dpy = eglGetPlatformDisplayEXT(EGL_PLATFORM_X11_EXT, m_x11Display, attribs); - } else { - dpy = eglGetDisplay(m_x11Display); + dpy = eglGetPlatformDisplayEXT(EGL_PLATFORM_X11_EXT, m_x11Display, attribs); + } else { + dpy = eglGetDisplay(m_x11Display); + } } if (dpy == EGL_NO_DISPLAY) {