Move X11Compositor and WaylandCompositor in their own files

master
Vlad Zahorodnii 1 year ago
parent 14ab38b596
commit 6dd6e176e3

@ -9,7 +9,7 @@
#include "kwin_wayland_test.h"
#include "backends/virtual/virtual_backend.h"
#include "compositor.h"
#include "compositor_wayland.h"
#include "core/session.h"
#include "effects.h"
#include "inputmethod.h"

@ -43,6 +43,8 @@ target_sources(kwin PRIVATE
colors/colordevice.cpp
colors/colormanager.cpp
compositor.cpp
compositor_wayland.cpp
compositor_x11.cpp
core/colorlut.cpp
core/colorpipelinestage.cpp
core/colortransformation.cpp

@ -13,14 +13,11 @@
#include "core/output.h"
#include "core/outputbackend.h"
#include "core/outputlayer.h"
#include "core/overlaywindow.h"
#include "core/renderlayer.h"
#include "core/renderloop.h"
#include "dbusinterface.h"
#include "decorations/decoratedclient.h"
#include "effects.h"
#include "ftrace.h"
#include "internalwindow.h"
#include "platformsupport/scenes/opengl/openglbackend.h"
#include "platformsupport/scenes/qpainter/qpainterbackend.h"
#include "scene/cursordelegate_opengl.h"
@ -28,43 +25,24 @@
#include "scene/cursorscene.h"
#include "scene/itemrenderer_opengl.h"
#include "scene/itemrenderer_qpainter.h"
#include "scene/surfaceitem_x11.h"
#include "scene/surfaceitem.h"
#include "scene/workspacescene_opengl.h"
#include "scene/workspacescene_qpainter.h"
#include "useractions.h"
#include "utils/common.h"
#include "utils/xcbutils.h"
#include "wayland_server.h"
#include "workspace.h"
#include "x11syncmanager.h"
#include "x11window.h"
#include "libkwineffects/glplatform.h"
#include "libkwineffects/gltexture.h"
#include <KCrash>
#include <KGlobalAccel>
#include <KLocalizedString>
#if KWIN_BUILD_NOTIFICATIONS
#include <KNotification>
#endif
#include <KSelectionOwner>
#include <QDateTime>
#include <QFutureWatcher>
#include <QMenu>
#include <QOpenGLContext>
#include <QQuickWindow>
#include <QTextStream>
#include <QTimerEvent>
#include <QtConcurrentRun>
#include <xcb/composite.h>
#include <xcb/damage.h>
#include <cstdio>
Q_DECLARE_METATYPE(KWin::X11Compositor::SuspendReason)
namespace KWin
{
@ -75,21 +53,6 @@ Compositor *Compositor::self()
return s_compositor;
}
WaylandCompositor *WaylandCompositor::create(QObject *parent)
{
Q_ASSERT(!s_compositor);
auto *compositor = new WaylandCompositor(parent);
s_compositor = compositor;
return compositor;
}
X11Compositor *X11Compositor::create(QObject *parent)
{
Q_ASSERT(!s_compositor);
auto *compositor = new X11Compositor(parent);
s_compositor = compositor;
return compositor;
}
class CompositorSelectionOwner : public KSelectionOwner
{
Q_OBJECT
@ -808,392 +771,7 @@ void Compositor::createOpenGLSafePoint(OpenGLSafePoint safePoint)
{
}
WaylandCompositor::WaylandCompositor(QObject *parent)
: Compositor(parent)
{
connect(kwinApp(), &Application::x11ConnectionAboutToBeDestroyed,
this, &WaylandCompositor::destroyCompositorSelection);
}
WaylandCompositor::~WaylandCompositor()
{
Q_EMIT aboutToDestroy();
stop(); // this can't be called in the destructor of Compositor
}
void WaylandCompositor::toggleCompositing()
{
// For the shortcut. Not possible on Wayland because we always composite.
}
void WaylandCompositor::start()
{
if (!Compositor::setupStart()) {
// Internal setup failed, abort.
return;
}
if (Workspace::self()) {
startupWithWorkspace();
} else {
connect(kwinApp(), &Application::workspaceCreated,
this, &WaylandCompositor::startupWithWorkspace);
}
}
X11Compositor::X11Compositor(QObject *parent)
: Compositor(parent)
, m_suspended(options->isUseCompositing() ? NoReasonSuspend : UserSuspend)
{
if (qEnvironmentVariableIsSet("KWIN_MAX_FRAMES_TESTED")) {
m_framesToTestForSafety = qEnvironmentVariableIntValue("KWIN_MAX_FRAMES_TESTED");
}
}
X11Compositor::~X11Compositor()
{
Q_EMIT aboutToDestroy();
if (m_openGLFreezeProtectionThread) {
m_openGLFreezeProtectionThread->quit();
m_openGLFreezeProtectionThread->wait();
}
stop(); // this can't be called in the destructor of Compositor
}
X11SyncManager *X11Compositor::syncManager() const
{
return m_syncManager.get();
}
void X11Compositor::toggleCompositing()
{
if (m_suspended) {
// Direct user call; clear all bits.
resume(AllReasonSuspend);
} else {
// But only set the user one (sufficient to suspend).
suspend(UserSuspend);
}
}
void X11Compositor::reinitialize()
{
// Resume compositing if suspended.
m_suspended = NoReasonSuspend;
Compositor::reinitialize();
}
void X11Compositor::configChanged()
{
if (m_suspended) {
stop();
return;
}
Compositor::configChanged();
}
void X11Compositor::suspend(X11Compositor::SuspendReason reason)
{
Q_ASSERT(reason != NoReasonSuspend);
m_suspended |= reason;
if (reason & ScriptSuspend) {
// When disabled show a shortcut how the user can get back compositing.
const auto shortcuts = KGlobalAccel::self()->shortcut(
workspace()->findChild<QAction *>(QStringLiteral("Suspend Compositing")));
if (!shortcuts.isEmpty()) {
// Display notification only if there is the shortcut.
const QString message =
i18n("Desktop effects have been suspended by another application.<br/>"
"You can resume using the '%1' shortcut.",
shortcuts.first().toString(QKeySequence::NativeText));
#if KWIN_BUILD_NOTIFICATIONS
KNotification::event(QStringLiteral("compositingsuspendeddbus"), message);
#endif
}
}
stop();
}
void X11Compositor::resume(X11Compositor::SuspendReason reason)
{
Q_ASSERT(reason != NoReasonSuspend);
m_suspended &= ~reason;
start();
}
void X11Compositor::start()
{
if (m_suspended) {
QStringList reasons;
if (m_suspended & UserSuspend) {
reasons << QStringLiteral("Disabled by User");
}
if (m_suspended & BlockRuleSuspend) {
reasons << QStringLiteral("Disabled by Window");
}
if (m_suspended & ScriptSuspend) {
reasons << QStringLiteral("Disabled by Script");
}
qCInfo(KWIN_CORE) << "Compositing is suspended, reason:" << reasons;
return;
} else if (!compositingPossible()) {
qCWarning(KWIN_CORE) << "Compositing is not possible";
return;
}
if (!Compositor::setupStart()) {
// Internal setup failed, abort.
return;
}
startupWithWorkspace();
m_syncManager.reset(X11SyncManager::create());
}
void X11Compositor::stop()
{
m_syncManager.reset();
Compositor::stop();
}
void X11Compositor::composite(RenderLoop *renderLoop)
{
if (backend()->overlayWindow() && !isOverlayWindowVisible()) {
// Return since nothing is visible.
return;
}
QList<Window *> windows = workspace()->stackingOrder();
QList<SurfaceItemX11 *> dirtyItems;
// Reset the damage state of each window and fetch the damage region
// without waiting for a reply
for (Window *window : std::as_const(windows)) {
SurfaceItemX11 *surfaceItem = static_cast<SurfaceItemX11 *>(window->surfaceItem());
if (surfaceItem->fetchDamage()) {
dirtyItems.append(surfaceItem);
}
}
if (dirtyItems.count() > 0) {
if (m_syncManager) {
m_syncManager->triggerFence();
}
xcb_flush(kwinApp()->x11Connection());
}
// Get the replies
for (SurfaceItemX11 *item : std::as_const(dirtyItems)) {
item->waitForDamage();
}
if (m_framesToTestForSafety > 0 && (backend()->compositingType() & OpenGLCompositing)) {
createOpenGLSafePoint(OpenGLSafePoint::PreFrame);
}
Compositor::composite(renderLoop);
if (m_syncManager) {
if (!m_syncManager->endFrame()) {
qCDebug(KWIN_CORE) << "Aborting explicit synchronization with the X command stream.";
qCDebug(KWIN_CORE) << "Future frames will be rendered unsynchronized.";
m_syncManager.reset();
}
}
if (m_framesToTestForSafety > 0) {
if (backend()->compositingType() & OpenGLCompositing) {
createOpenGLSafePoint(OpenGLSafePoint::PostFrame);
}
m_framesToTestForSafety--;
if (m_framesToTestForSafety == 0 && (backend()->compositingType() & OpenGLCompositing)) {
createOpenGLSafePoint(OpenGLSafePoint::PostLastGuardedFrame);
}
}
}
bool X11Compositor::checkForOverlayWindow(WId w) const
{
if (!backend()) {
// No backend, so it cannot be the overlay window.
return false;
}
if (!backend()->overlayWindow()) {
// No overlay window, it cannot be the overlay.
return false;
}
// Compare the window ID's.
return w == backend()->overlayWindow()->window();
}
bool X11Compositor::isOverlayWindowVisible() const
{
if (!backend()) {
return false;
}
if (!backend()->overlayWindow()) {
return false;
}
return backend()->overlayWindow()->isVisible();
}
void X11Compositor::updateClientCompositeBlocking(X11Window *c)
{
if (c) {
if (c->isBlockingCompositing()) {
// Do NOT attempt to call suspend(true) from within the eventchain!
if (!(m_suspended & BlockRuleSuspend)) {
QMetaObject::invokeMethod(
this, [this]() {
suspend(BlockRuleSuspend);
},
Qt::QueuedConnection);
}
}
} else if (m_suspended & BlockRuleSuspend) {
// If !c we just check if we can resume in case a blocking client was lost.
bool shouldResume = true;
const auto windows = workspace()->windows();
for (Window *window : windows) {
X11Window *x11Window = qobject_cast<X11Window *>(window);
if (x11Window && x11Window->isBlockingCompositing()) {
shouldResume = false;
break;
}
}
if (shouldResume) {
// Do NOT attempt to call suspend(false) from within the eventchain!
QMetaObject::invokeMethod(
this, [this]() {
resume(BlockRuleSuspend);
},
Qt::QueuedConnection);
}
}
}
X11Compositor *X11Compositor::self()
{
return qobject_cast<X11Compositor *>(Compositor::self());
}
bool X11Compositor::openGLCompositingIsBroken() const
{
auto timestamp = KConfigGroup(kwinApp()->config(), "Compositing").readEntry(QLatin1String("LastFailureTimestamp"), 0);
if (timestamp > 0) {
if (QDateTime::currentSecsSinceEpoch() - timestamp < 60) {
return true;
}
}
return false;
}
QString X11Compositor::compositingNotPossibleReason() const
{
// first off, check whether we figured that we'll crash on detection because of a buggy driver
KConfigGroup gl_workaround_group(kwinApp()->config(), "Compositing");
if (gl_workaround_group.readEntry("Backend", "OpenGL") == QLatin1String("OpenGL") && openGLCompositingIsBroken()) {
return i18n("<b>OpenGL compositing (the default) has crashed KWin in the past.</b><br>"
"This was most likely due to a driver bug."
"<p>If you think that you have meanwhile upgraded to a stable driver,<br>"
"you can reset this protection but <b>be aware that this might result in an immediate crash!</b></p>");
}
if (!Xcb::Extensions::self()->isCompositeAvailable() || !Xcb::Extensions::self()->isDamageAvailable()) {
return i18n("Required X extensions (XComposite and XDamage) are not available.");
}
if (!Xcb::Extensions::self()->hasGlx()) {
return i18n("GLX/OpenGL is not available.");
}
return QString();
}
bool X11Compositor::compositingPossible() const
{
// first off, check whether we figured that we'll crash on detection because of a buggy driver
KConfigGroup gl_workaround_group(kwinApp()->config(), "Compositing");
if (gl_workaround_group.readEntry("Backend", "OpenGL") == QLatin1String("OpenGL") && openGLCompositingIsBroken()) {
qCWarning(KWIN_CORE) << "Compositing disabled: video driver seems unstable. If you think it's a false positive, please try again in a few minutes.";
return false;
}
if (!Xcb::Extensions::self()->isCompositeAvailable()) {
qCWarning(KWIN_CORE) << "Compositing disabled: no composite extension available";
return false;
}
if (!Xcb::Extensions::self()->isDamageAvailable()) {
qCWarning(KWIN_CORE) << "Compositing disabled: no damage extension available";
return false;
}
if (Xcb::Extensions::self()->hasGlx()) {
return true;
}
if (QOpenGLContext::openGLModuleType() == QOpenGLContext::LibGLES) {
return true;
} else if (qstrcmp(qgetenv("KWIN_COMPOSE"), "O2ES") == 0) {
return true;
}
qCWarning(KWIN_CORE) << "Compositing disabled: no OpenGL support";
return false;
}
void X11Compositor::createOpenGLSafePoint(OpenGLSafePoint safePoint)
{
auto group = KConfigGroup(kwinApp()->config(), "Compositing");
switch (safePoint) {
case OpenGLSafePoint::PreInit:
// Explicitly write the failure timestamp so that if we crash during
// OpenGL init, we know we should not try again.
group.writeEntry(QLatin1String("LastFailureTimestamp"), QDateTime::currentSecsSinceEpoch());
group.sync();
// Deliberately continue with PreFrame
Q_FALLTHROUGH();
case OpenGLSafePoint::PreFrame:
if (m_openGLFreezeProtectionThread == nullptr) {
Q_ASSERT(m_openGLFreezeProtection == nullptr);
m_openGLFreezeProtectionThread = std::make_unique<QThread>();
m_openGLFreezeProtectionThread->setObjectName("FreezeDetector");
m_openGLFreezeProtectionThread->start();
m_openGLFreezeProtection = std::make_unique<QTimer>();
m_openGLFreezeProtection->setInterval(15000);
m_openGLFreezeProtection->setSingleShot(true);
m_openGLFreezeProtection->start();
const QString configName = kwinApp()->config()->name();
m_openGLFreezeProtection->moveToThread(m_openGLFreezeProtectionThread.get());
connect(
m_openGLFreezeProtection.get(), &QTimer::timeout, m_openGLFreezeProtection.get(),
[configName] {
auto group = KConfigGroup(KSharedConfig::openConfig(configName), "Compositing");
group.writeEntry(QLatin1String("LastFailureTimestamp"), QDateTime::currentSecsSinceEpoch());
group.sync();
KCrash::setDrKonqiEnabled(false);
qFatal("Freeze in OpenGL initialization detected");
},
Qt::DirectConnection);
} else {
Q_ASSERT(m_openGLFreezeProtection);
QMetaObject::invokeMethod(m_openGLFreezeProtection.get(), QOverload<>::of(&QTimer::start), Qt::QueuedConnection);
}
break;
case OpenGLSafePoint::PostInit:
group.deleteEntry(QLatin1String("LastFailureTimestamp"));
group.sync();
// Deliberately continue with PostFrame
Q_FALLTHROUGH();
case OpenGLSafePoint::PostFrame:
QMetaObject::invokeMethod(m_openGLFreezeProtection.get(), &QTimer::stop, Qt::QueuedConnection);
break;
case OpenGLSafePoint::PostLastGuardedFrame:
m_openGLFreezeProtectionThread->quit();
m_openGLFreezeProtectionThread->wait();
m_openGLFreezeProtectionThread.reset();
m_openGLFreezeProtection.reset();
break;
}
}
}
} // namespace KWin
// included for CompositorSelectionOwner
#include "compositor.moc"

@ -23,16 +23,11 @@ namespace KWin
class Output;
class CompositorSelectionOwner;
class CursorScene;
class CursorView;
class RenderBackend;
class RenderLayer;
class RenderLoop;
class RenderTarget;
class WorkspaceScene;
class Window;
class X11Window;
class X11SyncManager;
class RenderViewport;
class KWIN_EXPORT Compositor : public QObject
{
@ -212,111 +207,4 @@ private:
CompositingType m_selectedCompositor = NoCompositing;
};
class KWIN_EXPORT WaylandCompositor final : public Compositor
{
Q_OBJECT
public:
static WaylandCompositor *create(QObject *parent = nullptr);
~WaylandCompositor() override;
void toggleCompositing() override;
protected:
void start() override;
private:
explicit WaylandCompositor(QObject *parent);
};
class KWIN_EXPORT X11Compositor final : public Compositor
{
Q_OBJECT
public:
enum SuspendReason {
NoReasonSuspend = 0,
UserSuspend = 1 << 0,
BlockRuleSuspend = 1 << 1,
ScriptSuspend = 1 << 2,
AllReasonSuspend = 0xff
};
Q_DECLARE_FLAGS(SuspendReasons, SuspendReason)
Q_ENUM(SuspendReason)
Q_FLAG(SuspendReasons)
static X11Compositor *create(QObject *parent = nullptr);
~X11Compositor() override;
X11SyncManager *syncManager() const;
/**
* @brief Suspends the Compositor if it is currently active.
*
* Note: it is possible that the Compositor is not able to suspend. Use isActive to check
* whether the Compositor has been suspended.
*
* @return void
* @see resume
* @see isActive
*/
void suspend(SuspendReason reason);
/**
* @brief Resumes the Compositor if it is currently suspended.
*
* Note: it is possible that the Compositor cannot be resumed, that is there might be Clients
* blocking the usage of Compositing or the Scene might be broken. Use isActive to check
* whether the Compositor has been resumed. Also check isCompositingPossible and
* isOpenGLBroken.
*
* Note: The starting of the Compositor can require some time and is partially done threaded.
* After this method returns the setup may not have been completed.
*
* @return void
* @see suspend
* @see isActive
* @see isCompositingPossible
* @see isOpenGLBroken
*/
void resume(SuspendReason reason);
void toggleCompositing() override;
void reinitialize() override;
void configChanged() override;
bool compositingPossible() const override;
QString compositingNotPossibleReason() const override;
bool openGLCompositingIsBroken() const override;
void createOpenGLSafePoint(OpenGLSafePoint safePoint) override;
/**
* Checks whether @p w is the Scene's overlay window.
*/
bool checkForOverlayWindow(WId w) const;
/**
* @returns Whether the Scene's Overlay X Window is visible.
*/
bool isOverlayWindowVisible() const;
void updateClientCompositeBlocking(X11Window *client = nullptr);
static X11Compositor *self();
protected:
void start() override;
void stop() override;
void composite(RenderLoop *renderLoop) override;
private:
explicit X11Compositor(QObject *parent);
std::unique_ptr<QThread> m_openGLFreezeProtectionThread;
std::unique_ptr<QTimer> m_openGLFreezeProtection;
std::unique_ptr<X11SyncManager> m_syncManager;
/**
* Whether the Compositor is currently suspended, 8 bits encoding the reason
*/
SuspendReasons m_suspended;
int m_framesToTestForSafety = 3;
};
}
} // namespace KWin

@ -0,0 +1,60 @@
/*
KWin - the KDE window manager
This file is part of the KDE project.
SPDX-FileCopyrightText: 2006 Lubos Lunak <l.lunak@kde.org>
SPDX-License-Identifier: GPL-2.0-or-later
*/
#include "compositor_wayland.h"
#include "main.h"
#include "workspace.h"
namespace KWin
{
WaylandCompositor *WaylandCompositor::create(QObject *parent)
{
Q_ASSERT(!s_compositor);
auto *compositor = new WaylandCompositor(parent);
s_compositor = compositor;
return compositor;
}
WaylandCompositor::WaylandCompositor(QObject *parent)
: Compositor(parent)
{
connect(kwinApp(), &Application::x11ConnectionAboutToBeDestroyed,
this, &WaylandCompositor::destroyCompositorSelection);
}
WaylandCompositor::~WaylandCompositor()
{
Q_EMIT aboutToDestroy();
stop(); // this can't be called in the destructor of Compositor
}
void WaylandCompositor::toggleCompositing()
{
// For the shortcut. Not possible on Wayland because we always composite.
}
void WaylandCompositor::start()
{
if (!Compositor::setupStart()) {
// Internal setup failed, abort.
return;
}
if (Workspace::self()) {
startupWithWorkspace();
} else {
connect(kwinApp(), &Application::workspaceCreated,
this, &WaylandCompositor::startupWithWorkspace);
}
}
} // namespace KWin
#include "moc_compositor_wayland.cpp"

@ -0,0 +1,34 @@
/*
KWin - the KDE window manager
This file is part of the KDE project.
SPDX-FileCopyrightText: 2011 Arthur Arlt <a.arlt@stud.uni-heidelberg.de>
SPDX-FileCopyrightText: 2012 Martin Gräßlin <mgraesslin@kde.org>
SPDX-License-Identifier: GPL-2.0-or-later
*/
#pragma once
#include "compositor.h"
namespace KWin
{
class KWIN_EXPORT WaylandCompositor final : public Compositor
{
Q_OBJECT
public:
static WaylandCompositor *create(QObject *parent = nullptr);
~WaylandCompositor() override;
void toggleCompositing() override;
protected:
void start() override;
private:
explicit WaylandCompositor(QObject *parent);
};
} // namespace KWin

@ -0,0 +1,398 @@
/*
KWin - the KDE window manager
This file is part of the KDE project.
SPDX-FileCopyrightText: 2006 Lubos Lunak <l.lunak@kde.org>
SPDX-License-Identifier: GPL-2.0-or-later
*/
#include "compositor_x11.h"
#include "core/overlaywindow.h"
#include "core/renderbackend.h"
#include "options.h"
#include "scene/surfaceitem_x11.h"
#include "utils/common.h"
#include "utils/xcbutils.h"
#include "workspace.h"
#include "x11syncmanager.h"
#include "x11window.h"
#include <KCrash>
#include <KGlobalAccel>
#include <KLocalizedString>
#if KWIN_BUILD_NOTIFICATIONS
#include <KNotification>
#endif
#include <QAction>
#include <QOpenGLContext>
#include <QThread>
Q_DECLARE_METATYPE(KWin::X11Compositor::SuspendReason)
namespace KWin
{
X11Compositor *X11Compositor::create(QObject *parent)
{
Q_ASSERT(!s_compositor);
auto *compositor = new X11Compositor(parent);
s_compositor = compositor;
return compositor;
}
X11Compositor::X11Compositor(QObject *parent)
: Compositor(parent)
, m_suspended(options->isUseCompositing() ? NoReasonSuspend : UserSuspend)
{
if (qEnvironmentVariableIsSet("KWIN_MAX_FRAMES_TESTED")) {
m_framesToTestForSafety = qEnvironmentVariableIntValue("KWIN_MAX_FRAMES_TESTED");
}
}
X11Compositor::~X11Compositor()
{
Q_EMIT aboutToDestroy();
if (m_openGLFreezeProtectionThread) {
m_openGLFreezeProtectionThread->quit();
m_openGLFreezeProtectionThread->wait();
}
stop(); // this can't be called in the destructor of Compositor
}
X11SyncManager *X11Compositor::syncManager() const
{
return m_syncManager.get();
}
void X11Compositor::toggleCompositing()
{
if (m_suspended) {
// Direct user call; clear all bits.
resume(AllReasonSuspend);
} else {
// But only set the user one (sufficient to suspend).
suspend(UserSuspend);
}
}
void X11Compositor::reinitialize()
{
// Resume compositing if suspended.
m_suspended = NoReasonSuspend;
Compositor::reinitialize();
}
void X11Compositor::configChanged()
{
if (m_suspended) {
stop();
return;
}
Compositor::configChanged();
}
void X11Compositor::suspend(X11Compositor::SuspendReason reason)
{
Q_ASSERT(reason != NoReasonSuspend);
m_suspended |= reason;
if (reason & ScriptSuspend) {
// When disabled show a shortcut how the user can get back compositing.
const auto shortcuts = KGlobalAccel::self()->shortcut(workspace()->findChild<QAction *>(QStringLiteral("Suspend Compositing")));
if (!shortcuts.isEmpty()) {
// Display notification only if there is the shortcut.
const QString message =
i18n("Desktop effects have been suspended by another application.<br/>"
"You can resume using the '%1' shortcut.",
shortcuts.first().toString(QKeySequence::NativeText));
#if KWIN_BUILD_NOTIFICATIONS
KNotification::event(QStringLiteral("compositingsuspendeddbus"), message);
#endif
}
}
stop();
}
void X11Compositor::resume(X11Compositor::SuspendReason reason)
{
Q_ASSERT(reason != NoReasonSuspend);
m_suspended &= ~reason;
start();
}
void X11Compositor::start()
{
if (m_suspended) {
QStringList reasons;
if (m_suspended & UserSuspend) {
reasons << QStringLiteral("Disabled by User");
}
if (m_suspended & BlockRuleSuspend) {
reasons << QStringLiteral("Disabled by Window");
}
if (m_suspended & ScriptSuspend) {
reasons << QStringLiteral("Disabled by Script");
}
qCInfo(KWIN_CORE) << "Compositing is suspended, reason:" << reasons;
return;
} else if (!compositingPossible()) {
qCWarning(KWIN_CORE) << "Compositing is not possible";
return;
}
if (!Compositor::setupStart()) {
// Internal setup failed, abort.
return;
}
startupWithWorkspace();
m_syncManager.reset(X11SyncManager::create());
}
void X11Compositor::stop()
{
m_syncManager.reset();
Compositor::stop();
}
void X11Compositor::composite(RenderLoop *renderLoop)
{
if (backend()->overlayWindow() && !isOverlayWindowVisible()) {
// Return since nothing is visible.
return;
}
QList<Window *> windows = workspace()->stackingOrder();
QList<SurfaceItemX11 *> dirtyItems;
// Reset the damage state of each window and fetch the damage region
// without waiting for a reply
for (Window *window : std::as_const(windows)) {
SurfaceItemX11 *surfaceItem = static_cast<SurfaceItemX11 *>(window->surfaceItem());
if (surfaceItem->fetchDamage()) {
dirtyItems.append(surfaceItem);
}
}
if (dirtyItems.count() > 0) {
if (m_syncManager) {
m_syncManager->triggerFence();
}
xcb_flush(kwinApp()->x11Connection());
}
// Get the replies
for (SurfaceItemX11 *item : std::as_const(dirtyItems)) {
item->waitForDamage();
}
if (m_framesToTestForSafety > 0 && (backend()->compositingType() & OpenGLCompositing)) {
createOpenGLSafePoint(OpenGLSafePoint::PreFrame);
}
Compositor::composite(renderLoop);
if (m_syncManager) {
if (!m_syncManager->endFrame()) {
qCDebug(KWIN_CORE) << "Aborting explicit synchronization with the X command stream.";
qCDebug(KWIN_CORE) << "Future frames will be rendered unsynchronized.";
m_syncManager.reset();
}
}
if (m_framesToTestForSafety > 0) {
if (backend()->compositingType() & OpenGLCompositing) {
createOpenGLSafePoint(OpenGLSafePoint::PostFrame);
}
m_framesToTestForSafety--;
if (m_framesToTestForSafety == 0 && (backend()->compositingType() & OpenGLCompositing)) {
createOpenGLSafePoint(OpenGLSafePoint::PostLastGuardedFrame);
}
}
}
bool X11Compositor::checkForOverlayWindow(WId w) const
{
if (!backend()) {
// No backend, so it cannot be the overlay window.
return false;
}
if (!backend()->overlayWindow()) {
// No overlay window, it cannot be the overlay.
return false;
}
// Compare the window ID's.
return w == backend()->overlayWindow()->window();
}
bool X11Compositor::isOverlayWindowVisible() const
{
if (!backend()) {
return false;
}
if (!backend()->overlayWindow()) {
return false;
}
return backend()->overlayWindow()->isVisible();
}
void X11Compositor::updateClientCompositeBlocking(X11Window *c)
{
if (c) {
if (c->isBlockingCompositing()) {
// Do NOT attempt to call suspend(true) from within the eventchain!
if (!(m_suspended & BlockRuleSuspend)) {
QMetaObject::invokeMethod(
this, [this]() {
suspend(BlockRuleSuspend);
},
Qt::QueuedConnection);
}
}
} else if (m_suspended & BlockRuleSuspend) {
// If !c we just check if we can resume in case a blocking client was lost.
bool shouldResume = true;
const auto windows = workspace()->windows();
for (Window *window : windows) {
X11Window *x11Window = qobject_cast<X11Window *>(window);
if (x11Window && x11Window->isBlockingCompositing()) {
shouldResume = false;
break;
}
}
if (shouldResume) {
// Do NOT attempt to call suspend(false) from within the eventchain!
QMetaObject::invokeMethod(
this, [this]() {
resume(BlockRuleSuspend);
},
Qt::QueuedConnection);
}
}
}
X11Compositor *X11Compositor::self()
{
return qobject_cast<X11Compositor *>(Compositor::self());
}
bool X11Compositor::openGLCompositingIsBroken() const
{
auto timestamp = KConfigGroup(kwinApp()->config(), "Compositing").readEntry(QLatin1String("LastFailureTimestamp"), 0);
if (timestamp > 0) {
if (QDateTime::currentSecsSinceEpoch() - timestamp < 60) {
return true;
}
}
return false;
}
QString X11Compositor::compositingNotPossibleReason() const
{
// first off, check whether we figured that we'll crash on detection because of a buggy driver
KConfigGroup gl_workaround_group(kwinApp()->config(), "Compositing");
if (gl_workaround_group.readEntry("Backend", "OpenGL") == QLatin1String("OpenGL") && openGLCompositingIsBroken()) {
return i18n("<b>OpenGL compositing (the default) has crashed KWin in the past.</b><br>"
"This was most likely due to a driver bug."
"<p>If you think that you have meanwhile upgraded to a stable driver,<br>"
"you can reset this protection but <b>be aware that this might result in an immediate crash!</b></p>");
}
if (!Xcb::Extensions::self()->isCompositeAvailable() || !Xcb::Extensions::self()->isDamageAvailable()) {
return i18n("Required X extensions (XComposite and XDamage) are not available.");
}
if (!Xcb::Extensions::self()->hasGlx()) {
return i18n("GLX/OpenGL is not available.");
}
return QString();
}
bool X11Compositor::compositingPossible() const
{
// first off, check whether we figured that we'll crash on detection because of a buggy driver
KConfigGroup gl_workaround_group(kwinApp()->config(), "Compositing");
if (gl_workaround_group.readEntry("Backend", "OpenGL") == QLatin1String("OpenGL") && openGLCompositingIsBroken()) {
qCWarning(KWIN_CORE) << "Compositing disabled: video driver seems unstable. If you think it's a false positive, please try again in a few minutes.";
return false;
}
if (!Xcb::Extensions::self()->isCompositeAvailable()) {
qCWarning(KWIN_CORE) << "Compositing disabled: no composite extension available";
return false;
}
if (!Xcb::Extensions::self()->isDamageAvailable()) {
qCWarning(KWIN_CORE) << "Compositing disabled: no damage extension available";
return false;
}
if (Xcb::Extensions::self()->hasGlx()) {
return true;
}
if (QOpenGLContext::openGLModuleType() == QOpenGLContext::LibGLES) {
return true;
} else if (qstrcmp(qgetenv("KWIN_COMPOSE"), "O2ES") == 0) {
return true;
}
qCWarning(KWIN_CORE) << "Compositing disabled: no OpenGL support";
return false;
}
void X11Compositor::createOpenGLSafePoint(OpenGLSafePoint safePoint)
{
auto group = KConfigGroup(kwinApp()->config(), "Compositing");
switch (safePoint) {
case OpenGLSafePoint::PreInit:
// Explicitly write the failure timestamp so that if we crash during
// OpenGL init, we know we should not try again.
group.writeEntry(QLatin1String("LastFailureTimestamp"), QDateTime::currentSecsSinceEpoch());
group.sync();
// Deliberately continue with PreFrame
Q_FALLTHROUGH();
case OpenGLSafePoint::PreFrame:
if (m_openGLFreezeProtectionThread == nullptr) {
Q_ASSERT(m_openGLFreezeProtection == nullptr);
m_openGLFreezeProtectionThread = std::make_unique<QThread>();
m_openGLFreezeProtectionThread->setObjectName("FreezeDetector");
m_openGLFreezeProtectionThread->start();
m_openGLFreezeProtection = std::make_unique<QTimer>();
m_openGLFreezeProtection->setInterval(15000);
m_openGLFreezeProtection->setSingleShot(true);
m_openGLFreezeProtection->start();
const QString configName = kwinApp()->config()->name();
m_openGLFreezeProtection->moveToThread(m_openGLFreezeProtectionThread.get());
connect(
m_openGLFreezeProtection.get(), &QTimer::timeout, m_openGLFreezeProtection.get(),
[configName] {
auto group = KConfigGroup(KSharedConfig::openConfig(configName), "Compositing");
group.writeEntry(QLatin1String("LastFailureTimestamp"), QDateTime::currentSecsSinceEpoch());
group.sync();
KCrash::setDrKonqiEnabled(false);
qFatal("Freeze in OpenGL initialization detected");
},
Qt::DirectConnection);
} else {
Q_ASSERT(m_openGLFreezeProtection);
QMetaObject::invokeMethod(m_openGLFreezeProtection.get(), QOverload<>::of(&QTimer::start), Qt::QueuedConnection);
}
break;
case OpenGLSafePoint::PostInit:
group.deleteEntry(QLatin1String("LastFailureTimestamp"));
group.sync();
// Deliberately continue with PostFrame
Q_FALLTHROUGH();
case OpenGLSafePoint::PostFrame:
QMetaObject::invokeMethod(m_openGLFreezeProtection.get(), &QTimer::stop, Qt::QueuedConnection);
break;
case OpenGLSafePoint::PostLastGuardedFrame:
m_openGLFreezeProtectionThread->quit();
m_openGLFreezeProtectionThread->wait();
m_openGLFreezeProtectionThread.reset();
m_openGLFreezeProtection.reset();
break;
}
}
} // namespace KWin
#include "moc_compositor_x11.cpp"

@ -0,0 +1,112 @@
/*
KWin - the KDE window manager
This file is part of the KDE project.
SPDX-FileCopyrightText: 2011 Arthur Arlt <a.arlt@stud.uni-heidelberg.de>
SPDX-FileCopyrightText: 2012 Martin Gräßlin <mgraesslin@kde.org>
SPDX-License-Identifier: GPL-2.0-or-later
*/
#pragma once
#include "compositor.h"
namespace KWin
{
class X11SyncManager;
class X11Window;
class KWIN_EXPORT X11Compositor final : public Compositor
{
Q_OBJECT
public:
enum SuspendReason {
NoReasonSuspend = 0,
UserSuspend = 1 << 0,
BlockRuleSuspend = 1 << 1,
ScriptSuspend = 1 << 2,
AllReasonSuspend = 0xff
};
Q_DECLARE_FLAGS(SuspendReasons, SuspendReason)
Q_ENUM(SuspendReason)
Q_FLAG(SuspendReasons)
static X11Compositor *create(QObject *parent = nullptr);
~X11Compositor() override;
X11SyncManager *syncManager() const;
/**
* @brief Suspends the Compositor if it is currently active.
*
* Note: it is possible that the Compositor is not able to suspend. Use isActive to check
* whether the Compositor has been suspended.
*
* @return void
* @see resume
* @see isActive
*/
void suspend(SuspendReason reason);
/**
* @brief Resumes the Compositor if it is currently suspended.
*
* Note: it is possible that the Compositor cannot be resumed, that is there might be Clients
* blocking the usage of Compositing or the Scene might be broken. Use isActive to check
* whether the Compositor has been resumed. Also check isCompositingPossible and
* isOpenGLBroken.
*
* Note: The starting of the Compositor can require some time and is partially done threaded.
* After this method returns the setup may not have been completed.
*
* @return void
* @see suspend
* @see isActive
* @see isCompositingPossible
* @see isOpenGLBroken
*/
void resume(SuspendReason reason);
void toggleCompositing() override;
void reinitialize() override;
void configChanged() override;
bool compositingPossible() const override;
QString compositingNotPossibleReason() const override;
bool openGLCompositingIsBroken() const override;
void createOpenGLSafePoint(OpenGLSafePoint safePoint) override;
/**
* Checks whether @p w is the Scene's overlay window.
*/
bool checkForOverlayWindow(WId w) const;
/**
* @returns Whether the Scene's Overlay X Window is visible.
*/
bool isOverlayWindowVisible() const;
void updateClientCompositeBlocking(X11Window *client = nullptr);
static X11Compositor *self();
protected:
void start() override;
void stop() override;
void composite(RenderLoop *renderLoop) override;
private:
explicit X11Compositor(QObject *parent);
std::unique_ptr<QThread> m_openGLFreezeProtectionThread;
std::unique_ptr<QTimer> m_openGLFreezeProtection;
std::unique_ptr<X11SyncManager> m_syncManager;
/**
* Whether the Compositor is currently suspended, 8 bits encoding the reason
*/
SuspendReasons m_suspended;
int m_framesToTestForSafety = 3;
};
} // namespace KWin

@ -15,7 +15,7 @@
// kwin
#include "atoms.h"
#include "compositor.h"
#include "compositor_x11.h"
#include "core/output.h"
#include "core/renderbackend.h"
#include "debug_console.h"

@ -14,7 +14,7 @@
#include "backends/virtual/virtual_backend.h"
#include "backends/wayland/wayland_backend.h"
#include "backends/x11/windowed/x11_windowed_backend.h"
#include "compositor.h"
#include "compositor_wayland.h"
#include "core/outputbackend.h"
#include "core/session.h"
#include "effects.h"

@ -5,7 +5,7 @@
*/
#include "scene/surfaceitem_x11.h"
#include "compositor.h"
#include "compositor_x11.h"
#include "core/renderbackend.h"
#include "x11syncmanager.h"
#include "x11window.h"

@ -20,7 +20,7 @@
#endif
#include "appmenu.h"
#include "atoms.h"
#include "compositor.h"
#include "compositor_x11.h"
#include "core/outputbackend.h"
#include "core/outputconfiguration.h"
#include "cursor.h"

Loading…
Cancel
Save