diff --git a/composite.h b/composite.h --- a/composite.h +++ b/composite.h @@ -239,6 +239,7 @@ Scene *m_scene; bool m_bufferSwapPending; bool m_composeAtSwapCompletion; + int m_framesToTestForSafety = 3; KWIN_SINGLETON_VARIABLE(Compositor, s_compositor) }; diff --git a/composite.cpp b/composite.cpp --- a/composite.cpp +++ b/composite.cpp @@ -135,6 +135,9 @@ } ); + if (qEnvironmentVariableIsSet("KWIN_MAX_FRAMES_TESTED")) + m_framesToTestForSafety = qEnvironmentVariableIntValue("KWIN_MAX_FRAMES_TESTED"); + // register DBus new CompositorDBusInterface(this); } @@ -207,7 +210,6 @@ m_scene = SceneOpenGL::createScene(this); - // TODO: Add 30 second delay to protect against screen freezes as well kwinApp()->platform()->createOpenGLSafePoint(Platform::OpenGLSafePoint::PostInit); if (m_scene && !m_scene->initFailed()) { @@ -734,7 +736,19 @@ // clear all repaints, so that post-pass can add repaints for the next repaint repaints_region = QRegion(); + if (m_framesToTestForSafety > 0 && (m_scene->compositingType() & OpenGLCompositing)) { + kwinApp()->platform()->createOpenGLSafePoint(Platform::OpenGLSafePoint::PreFrame); + } m_timeSinceLastVBlank = m_scene->paint(repaints, windows); + if (m_framesToTestForSafety > 0) { + if (m_scene->compositingType() & OpenGLCompositing) { + kwinApp()->platform()->createOpenGLSafePoint(Platform::OpenGLSafePoint::PostFrame); + } + m_framesToTestForSafety--; + if (m_framesToTestForSafety == 0 && (m_scene->compositingType() & OpenGLCompositing)) { + kwinApp()->platform()->createOpenGLSafePoint(Platform::OpenGLSafePoint::PostLastGuardedFrame); + } + } m_timeSinceStart += m_timeSinceLastVBlank; if (waylandServer()) { diff --git a/platform.h b/platform.h --- a/platform.h +++ b/platform.h @@ -142,7 +142,10 @@ virtual bool openGLCompositingIsBroken() const; enum class OpenGLSafePoint { PreInit, - PostInit + PostInit, + PreFrame, + PostFrame, + PostLastGuardedFrame }; /** * This method is invoked before and after creating the OpenGL rendering Scene. diff --git a/plugins/platforms/x11/standalone/x11_platform.h b/plugins/platforms/x11/standalone/x11_platform.h --- a/plugins/platforms/x11/standalone/x11_platform.h +++ b/plugins/platforms/x11/standalone/x11_platform.h @@ -62,6 +62,8 @@ static bool hasGlx(); XInputIntegration *m_xinputIntegration = nullptr; + QThread *m_openGLFreezeProtectionThread = nullptr; + QTimer *m_openGLFreezeProtection = nullptr; }; diff --git a/plugins/platforms/x11/standalone/x11_platform.cpp b/plugins/platforms/x11/standalone/x11_platform.cpp --- a/plugins/platforms/x11/standalone/x11_platform.cpp +++ b/plugins/platforms/x11/standalone/x11_platform.cpp @@ -37,6 +37,7 @@ #include #include +#include #include #include @@ -200,12 +201,48 @@ switch (safePoint) { case OpenGLSafePoint::PreInit: group.writeEntry(unsafeKey, true); + group.sync(); + // Deliberately continue with PreFrame + case OpenGLSafePoint::PreFrame: + if (m_openGLFreezeProtectionThread == nullptr) { + Q_ASSERT(m_openGLFreezeProtection == nullptr); + m_openGLFreezeProtectionThread = new QThread(this); + m_openGLFreezeProtectionThread->setObjectName("FreezeDetector"); + m_openGLFreezeProtectionThread->start(); + m_openGLFreezeProtection = new QTimer; + m_openGLFreezeProtection->setInterval(15000); + m_openGLFreezeProtection->setSingleShot(true); + m_openGLFreezeProtection->start(); + m_openGLFreezeProtection->moveToThread(m_openGLFreezeProtectionThread); + connect(m_openGLFreezeProtection, &QTimer::timeout, m_openGLFreezeProtection, + [] { + const QString unsafeKey(QLatin1String("OpenGLIsUnsafe") + (kwinApp()->isX11MultiHead() ? QString::number(kwinApp()->x11ScreenNumber()) : QString())); + auto group = KConfigGroup(kwinApp()->config(), "Compositing"); + group.writeEntry(unsafeKey, true); + group.sync(); + qFatal("Freeze in OpenGL initialization detected"); + }, Qt::DirectConnection); + } else { + Q_ASSERT(m_openGLFreezeProtection); + QMetaObject::invokeMethod(m_openGLFreezeProtection, "start", Qt::QueuedConnection); + } break; case OpenGLSafePoint::PostInit: group.writeEntry(unsafeKey, false); + group.sync(); + // Deliberately continue with PostFrame + case OpenGLSafePoint::PostFrame: + QMetaObject::invokeMethod(m_openGLFreezeProtection, "stop", Qt::QueuedConnection); + break; + case OpenGLSafePoint::PostLastGuardedFrame: + m_openGLFreezeProtection->deleteLater(); + m_openGLFreezeProtection = nullptr; + m_openGLFreezeProtectionThread->quit(); + m_openGLFreezeProtectionThread->wait(); + delete m_openGLFreezeProtectionThread; + m_openGLFreezeProtectionThread = nullptr; break; } - group.sync(); } }