kwin/src/compositor.cpp

763 lines
25 KiB
C++
Raw Normal View History

2020-08-02 22:22:19 +00:00
/*
KWin - the KDE window manager
This file is part of the KDE project.
2020-08-02 22:22:19 +00:00
SPDX-FileCopyrightText: 2006 Lubos Lunak <l.lunak@kde.org>
2020-08-02 22:22:19 +00:00
SPDX-License-Identifier: GPL-2.0-or-later
*/
2023-08-31 12:23:42 +00:00
#include "compositor.h"
#include <config-kwin.h>
#include "core/output.h"
2022-11-05 10:43:41 +00:00
#include "core/outputbackend.h"
#include "core/outputlayer.h"
#include "core/renderlayer.h"
#include "core/renderloop.h"
#include "dbusinterface.h"
#include "effects.h"
#include "ftrace.h"
#include "platformsupport/scenes/opengl/openglbackend.h"
#include "platformsupport/scenes/qpainter/qpainterbackend.h"
#include "scene/cursordelegate_opengl.h"
#include "scene/cursordelegate_qpainter.h"
#include "scene/cursorscene.h"
#include "scene/itemrenderer_opengl.h"
#include "scene/itemrenderer_qpainter.h"
#include "scene/surfaceitem.h"
#include "scene/workspacescene_opengl.h"
#include "scene/workspacescene_qpainter.h"
2022-01-18 08:35:52 +00:00
#include "utils/common.h"
#include "wayland_server.h"
#include "workspace.h"
#include "libkwineffects/glplatform.h"
#include <KLocalizedString>
#if KWIN_BUILD_NOTIFICATIONS
#include <KNotification>
#endif
#include <KSelectionOwner>
#include <QQuickWindow>
2012-12-11 20:21:35 +00:00
#include <xcb/composite.h>
namespace KWin
{
Compositor *Compositor::s_compositor = nullptr;
Compositor *Compositor::self()
{
return s_compositor;
}
class CompositorSelectionOwner : public KSelectionOwner
{
Q_OBJECT
public:
CompositorSelectionOwner(const char *selection)
: KSelectionOwner(selection, kwinApp()->x11Connection(), kwinApp()->x11RootWindow())
, m_owning(false)
{
connect(this, &CompositorSelectionOwner::lostOwnership,
this, [this]() {
m_owning = false;
});
}
bool owning() const
{
return m_owning;
}
void setOwning(bool own)
{
m_owning = own;
}
private:
bool m_owning;
};
Compositor::Compositor(QObject *workspace)
: QObject(workspace)
{
connect(options, &Options::configChanged, this, &Compositor::configChanged);
connect(options, &Options::animationSpeedChanged, this, &Compositor::configChanged);
// 2 sec which should be enough to restart the compositor.
static const int compositorLostMessageDelay = 2000;
m_releaseSelectionTimer.setSingleShot(true);
m_releaseSelectionTimer.setInterval(compositorLostMessageDelay);
connect(&m_releaseSelectionTimer, &QTimer::timeout,
this, &Compositor::releaseCompositorSelection);
m_unusedSupportPropertyTimer.setInterval(compositorLostMessageDelay);
m_unusedSupportPropertyTimer.setSingleShot(true);
connect(&m_unusedSupportPropertyTimer, &QTimer::timeout,
this, &Compositor::deleteUnusedSupportProperties);
// Delay the call to start by one event cycle.
// The ctor of this class is invoked from the Workspace ctor, that means before
// Workspace is completely constructed, so calling Workspace::self() would result
// in undefined behavior. This is fixed by using a delayed invocation.
QTimer::singleShot(0, this, &Compositor::start);
// register DBus
new CompositorDBusInterface(this);
FTraceLogger::create();
}
Compositor::~Compositor()
{
deleteUnusedSupportProperties();
destroyCompositorSelection();
s_compositor = nullptr;
}
bool Compositor::attemptOpenGLCompositing()
{
// Some broken drivers crash on glXQuery() so to prevent constant KWin crashes:
if (openGLCompositingIsBroken()) {
qCWarning(KWIN_CORE) << "KWin has detected that your OpenGL library is unsafe to use";
return false;
}
createOpenGLSafePoint(OpenGLSafePoint::PreInit);
auto safePointScope = qScopeGuard([this]() {
createOpenGLSafePoint(OpenGLSafePoint::PostInit);
});
2022-11-05 10:43:41 +00:00
std::unique_ptr<OpenGLBackend> backend = kwinApp()->outputBackend()->createOpenGLBackend();
if (!backend) {
return false;
}
if (!backend->isFailed()) {
backend->init();
}
if (backend->isFailed()) {
return false;
}
const QByteArray forceEnv = qgetenv("KWIN_COMPOSE");
if (!forceEnv.isEmpty()) {
if (qstrcmp(forceEnv, "O2") == 0 || qstrcmp(forceEnv, "O2ES") == 0) {
qCDebug(KWIN_CORE) << "OpenGL 2 compositing enforced by environment variable";
} else {
// OpenGL 2 disabled by environment variable
return false;
}
} else {
if (GLPlatform::instance()->recommendedCompositor() < OpenGLCompositing) {
qCDebug(KWIN_CORE) << "Driver does not recommend OpenGL compositing";
return false;
}
}
// We only support the OpenGL 2+ shader API, not GL_ARB_shader_objects
if (!hasGLVersion(2, 0)) {
qCDebug(KWIN_CORE) << "OpenGL 2.0 is not supported";
return false;
}
m_scene = std::make_unique<WorkspaceSceneOpenGL>(backend.get());
m_cursorScene = std::make_unique<CursorScene>(std::make_unique<ItemRendererOpenGL>());
m_backend = std::move(backend);
// set strict binding
if (options->isGlStrictBindingFollowsDriver()) {
options->setGlStrictBinding(!GLPlatform::instance()->supports(GLFeature::LooseBinding));
}
qCDebug(KWIN_CORE) << "OpenGL compositing has been successfully initialized";
return true;
}
bool Compositor::attemptQPainterCompositing()
{
2022-11-05 10:43:41 +00:00
std::unique_ptr<QPainterBackend> backend(kwinApp()->outputBackend()->createQPainterBackend());
if (!backend || backend->isFailed()) {
return false;
}
m_scene = std::make_unique<WorkspaceSceneQPainter>(backend.get());
m_cursorScene = std::make_unique<CursorScene>(std::make_unique<ItemRendererQPainter>());
m_backend = std::move(backend);
qCDebug(KWIN_CORE) << "QPainter compositing has been successfully initialized";
return true;
}
bool Compositor::setupStart()
2011-01-30 14:34:42 +00:00
{
if (kwinApp()->isTerminating()) {
// Don't start while KWin is terminating. An event to restart might be lingering
// in the event queue due to graphics reset.
return false;
}
if (m_state != State::Off) {
return false;
2011-01-30 14:34:42 +00:00
}
m_state = State::Starting;
if (kwinApp()->operationMode() == Application::OperationModeX11) {
if (!m_selectionOwner) {
m_selectionOwner = std::make_unique<CompositorSelectionOwner>("_NET_WM_CM_S0");
connect(m_selectionOwner.get(), &CompositorSelectionOwner::lostOwnership, this, &Compositor::stop);
}
if (!m_selectionOwner->owning()) {
// Force claim ownership.
m_selectionOwner->claim(true);
m_selectionOwner->setOwning(true);
}
xcb_composite_redirect_subwindows(kwinApp()->x11Connection(),
kwinApp()->x11RootWindow(),
XCB_COMPOSITE_REDIRECT_MANUAL);
}
Q_EMIT aboutToToggleCompositing();
2022-11-05 10:43:41 +00:00
const QVector<CompositingType> availableCompositors = kwinApp()->outputBackend()->supportedCompositors();
QVector<CompositingType> candidateCompositors;
// If compositing has been restarted, try to use the last used compositing type.
if (m_selectedCompositor != NoCompositing) {
candidateCompositors.append(m_selectedCompositor);
} else {
candidateCompositors = availableCompositors;
const auto userConfigIt = std::find(candidateCompositors.begin(), candidateCompositors.end(), options->compositingMode());
if (userConfigIt != candidateCompositors.end()) {
candidateCompositors.erase(userConfigIt);
candidateCompositors.prepend(options->compositingMode());
} else {
qCWarning(KWIN_CORE) << "Configured compositor not supported by Platform. Falling back to defaults";
}
}
for (auto type : std::as_const(candidateCompositors)) {
bool stop = false;
switch (type) {
case OpenGLCompositing:
qCDebug(KWIN_CORE) << "Attempting to load the OpenGL scene";
stop = attemptOpenGLCompositing();
break;
case QPainterCompositing:
qCDebug(KWIN_CORE) << "Attempting to load the QPainter scene";
stop = attemptQPainterCompositing();
break;
case NoCompositing:
qCDebug(KWIN_CORE) << "Starting without compositing...";
stop = true;
break;
}
if (stop) {
break;
} else if (qEnvironmentVariableIsSet("KWIN_COMPOSE")) {
qCCritical(KWIN_CORE) << "Could not fulfill the requested compositing mode in KWIN_COMPOSE:" << type << ". Exiting.";
qApp->quit();
Move SceneXRender into a plugin Summary: First step for loading the compositor Scenes through plugins. The general idea is that we currently needlessly pull in all the Scenes although only one will be used. E.g. on X11 we pull in QPainter, although they are not compatible. On Wayland we pull in XRender although they are not compatible. Furthermore our current Scene creation strategy is not really fault tolerant and can create situations where we don't get a compositor. E.g on fbdev backend the default settings won't work as it does not support OpenGL. Long term I want to tackle those conceptional problems together: we try to load all plugins supported by the current platform till we have a scene which works. Thus on Wayland we don't end up in a situation where we don't have a working compositor because the configuration is bad. To make this possible the switch statement in the Scene needs to go and needs to be replaced by a for loop iterating over all the available scenes on the platform. If we go there it makes sense to replace it directly with a plugin based approach. So this is a change which tackles the problem by first introducing the plugin loading. The xrender based scene (as it's the most simple one) is moved into a plugin. It is first tried to find a scene plugin and only if there is none the existing code is used. Test Plan: Tested all scenes Reviewers: #kwin, #plasma Subscribers: plasma-devel, kwin Tags: #kwin Differential Revision: https://phabricator.kde.org/D7232
2017-08-10 16:13:42 +00:00
}
}
if (!m_backend) {
m_state = State::Off;
if (kwinApp()->operationMode() == Application::OperationModeX11) {
xcb_composite_unredirect_subwindows(kwinApp()->x11Connection(),
kwinApp()->x11RootWindow(),
XCB_COMPOSITE_REDIRECT_MANUAL);
if (m_selectionOwner) {
m_selectionOwner->setOwning(false);
m_selectionOwner->release();
}
}
if (!availableCompositors.contains(NoCompositing)) {
qCCritical(KWIN_CORE) << "The used windowing system requires compositing";
qCCritical(KWIN_CORE) << "We are going to quit KWin now as it is broken";
qApp->quit();
}
return false;
2011-01-30 14:34:42 +00:00
}
m_selectedCompositor = m_backend->compositingType();
if (!Workspace::self() && m_backend && m_backend->compositingType() == QPainterCompositing) {
// Force Software QtQuick on first startup with QPainter.
QQuickWindow::setGraphicsApi(QSGRendererInterface::Software);
}
Q_EMIT sceneCreated();
return true;
}
void Compositor::startupWithWorkspace()
{
Q_ASSERT(m_scene);
m_scene->initialize();
m_cursorScene->initialize();
const QList<Output *> outputs = workspace()->outputs();
if (kwinApp()->operationMode() == Application::OperationModeX11) {
auto workspaceLayer = new RenderLayer(outputs.constFirst()->renderLoop());
workspaceLayer->setDelegate(std::make_unique<SceneDelegate>(m_scene.get(), nullptr));
workspaceLayer->setGeometry(workspace()->geometry());
connect(workspace(), &Workspace::geometryChanged, workspaceLayer, [workspaceLayer]() {
workspaceLayer->setGeometry(workspace()->geometry());
});
addSuperLayer(workspaceLayer);
} else {
for (Output *output : outputs) {
addOutput(output);
2020-11-19 08:52:29 +00:00
}
connect(workspace(), &Workspace::outputAdded, this, &Compositor::addOutput);
connect(workspace(), &Workspace::outputRemoved, this, &Compositor::removeOutput);
}
m_state = State::On;
const auto windows = workspace()->windows();
for (Window *window : windows) {
window->setupCompositing();
}
// Sets also the 'effects' pointer.
kwinApp()->createEffectsHandler(this, m_scene.get());
Q_EMIT compositingToggled(true);
if (m_releaseSelectionTimer.isActive()) {
m_releaseSelectionTimer.stop();
}
2020-11-19 08:52:29 +00:00
}
Output *Compositor::findOutput(RenderLoop *loop) const
{
const auto outputs = workspace()->outputs();
for (Output *output : outputs) {
if (output->renderLoop() == loop) {
return output;
}
}
return nullptr;
}
void Compositor::addOutput(Output *output)
2020-11-19 08:52:29 +00:00
{
Q_ASSERT(kwinApp()->operationMode() != Application::OperationModeX11);
auto workspaceLayer = new RenderLayer(output->renderLoop());
workspaceLayer->setDelegate(std::make_unique<SceneDelegate>(m_scene.get(), output));
workspaceLayer->setGeometry(output->rect());
connect(output, &Output::geometryChanged, workspaceLayer, [output, workspaceLayer]() {
workspaceLayer->setGeometry(output->rect());
});
auto cursorLayer = new RenderLayer(output->renderLoop());
cursorLayer->setVisible(false);
if (m_backend->compositingType() == OpenGLCompositing) {
cursorLayer->setDelegate(std::make_unique<CursorDelegateOpenGL>(output));
} else {
cursorLayer->setDelegate(std::make_unique<CursorDelegateQPainter>(output));
}
cursorLayer->setParent(workspaceLayer);
cursorLayer->setSuperlayer(workspaceLayer);
static bool valid;
static const bool forceSoftwareCursor = qEnvironmentVariableIntValue("KWIN_FORCE_SW_CURSOR", &valid) == 1 && valid;
auto updateCursorLayer = [this, output, cursorLayer]() {
const Cursor *cursor = Cursors::self()->currentCursor();
const QRectF outputLocalRect = output->mapFromGlobal(cursor->geometry());
const auto outputLayer = m_backend->cursorLayer(output);
if (!cursor->isOnOutput(output)) {
if (outputLayer && outputLayer->isEnabled()) {
outputLayer->setEnabled(false);
output->updateCursorLayer();
}
cursorLayer->setVisible(false);
return true;
}
const auto renderHardwareCursor = [&]() {
if (!outputLayer || forceSoftwareCursor) {
return false;
}
const QMatrix4x4 monitorMatrix = Output::logicalToNativeMatrix(output->rect(), output->scale(), output->transform());
QRectF nativeCursorRect = monitorMatrix.mapRect(outputLocalRect);
QSize bufferSize(std::ceil(nativeCursorRect.width()), std::ceil(nativeCursorRect.height()));
if (const auto fixedSize = outputLayer->fixedSize()) {
if (fixedSize->width() < bufferSize.width() || fixedSize->height() < bufferSize.height()) {
return false;
}
bufferSize = *fixedSize;
nativeCursorRect = monitorMatrix.mapRect(QRectF(outputLocalRect.topLeft(), QSizeF(bufferSize) / output->scale()));
}
outputLayer->setPosition(nativeCursorRect.topLeft());
outputLayer->setHotspot(Output::logicalToNativeMatrix(QRectF(QPointF(), QSizeF(bufferSize) / output->scale()), output->scale(), output->transform()).map(cursor->hotspot()));
outputLayer->setSize(bufferSize);
if (auto beginInfo = outputLayer->beginFrame()) {
const RenderTarget &renderTarget = beginInfo->renderTarget;
RenderLayer renderLayer(output->renderLoop());
renderLayer.setDelegate(std::make_unique<SceneDelegate>(m_cursorScene.get(), output));
renderLayer.setOutputLayer(outputLayer);
renderLayer.delegate()->prePaint();
renderLayer.delegate()->paint(renderTarget, infiniteRegion());
renderLayer.delegate()->postPaint();
if (!outputLayer->endFrame(infiniteRegion(), infiniteRegion())) {
return false;
}
}
outputLayer->setEnabled(true);
return output->updateCursorLayer();
};
if (renderHardwareCursor()) {
cursorLayer->setVisible(false);
return true;
} else {
if (outputLayer && outputLayer->isEnabled()) {
outputLayer->setEnabled(false);
output->updateCursorLayer();
}
cursorLayer->setVisible(cursor->isOnOutput(output));
cursorLayer->setGeometry(outputLocalRect);
cursorLayer->addRepaintFull();
return false;
}
};
auto moveCursorLayer = [this, output, cursorLayer, updateCursorLayer]() {
const Cursor *cursor = Cursors::self()->currentCursor();
const QRectF outputLocalRect = output->mapFromGlobal(cursor->geometry());
const auto outputLayer = m_backend->cursorLayer(output);
bool hardwareCursor = false;
if (outputLayer) {
if (outputLayer->isEnabled()) {
const QMatrix4x4 monitorMatrix = Output::logicalToNativeMatrix(output->rect(), output->scale(), output->transform());
const QRectF nativeCursorRect = monitorMatrix.mapRect(QRectF(outputLocalRect.topLeft(), outputLayer->size() / output->scale()));
outputLayer->setPosition(nativeCursorRect.topLeft());
hardwareCursor = output->updateCursorLayer();
} else if (!cursorLayer->isVisible() && !forceSoftwareCursor) {
// this is for the case that the cursor wasn't visible because it was on a different output before
hardwareCursor = updateCursorLayer();
}
}
cursorLayer->setVisible(cursor->isOnOutput(output) && !hardwareCursor);
cursorLayer->setGeometry(outputLocalRect);
cursorLayer->addRepaintFull();
};
updateCursorLayer();
connect(output, &Output::geometryChanged, cursorLayer, updateCursorLayer);
connect(Cursors::self(), &Cursors::currentCursorChanged, cursorLayer, updateCursorLayer);
connect(Cursors::self(), &Cursors::hiddenChanged, cursorLayer, updateCursorLayer);
connect(Cursors::self(), &Cursors::positionChanged, cursorLayer, moveCursorLayer);
addSuperLayer(workspaceLayer);
2020-11-19 08:52:29 +00:00
}
void Compositor::removeOutput(Output *output)
2020-11-19 08:52:29 +00:00
{
removeSuperLayer(m_superlayers[output->renderLoop()]);
2020-11-19 08:52:29 +00:00
}
void Compositor::addSuperLayer(RenderLayer *layer)
2020-11-19 08:52:29 +00:00
{
m_superlayers.insert(layer->loop(), layer);
connect(layer->loop(), &RenderLoop::frameRequested, this, &Compositor::handleFrameRequested);
2020-11-19 08:52:29 +00:00
}
void Compositor::removeSuperLayer(RenderLayer *layer)
2020-11-19 08:52:29 +00:00
{
m_superlayers.remove(layer->loop());
disconnect(layer->loop(), &RenderLoop::frameRequested, this, &Compositor::handleFrameRequested);
delete layer;
2020-11-19 08:52:29 +00:00
}
void Compositor::stop()
2011-01-30 14:34:42 +00:00
{
if (m_state == State::Off || m_state == State::Stopping) {
return;
}
m_state = State::Stopping;
Q_EMIT aboutToToggleCompositing();
m_releaseSelectionTimer.start();
// Some effects might need access to effect windows when they are about to
// be destroyed, for example to unreference deleted windows, so we have to
// make sure that effect windows outlive effects.
delete effects;
effects = nullptr;
if (Workspace::self()) {
const auto windows = workspace()->windows();
for (Window *window : windows) {
window->finishCompositing();
}
if (kwinApp()->operationMode() == Application::OperationModeX11) {
xcb_composite_unredirect_subwindows(kwinApp()->x11Connection(),
kwinApp()->x11RootWindow(),
XCB_COMPOSITE_REDIRECT_MANUAL);
}
disconnect(workspace(), &Workspace::outputAdded, this, &Compositor::addOutput);
disconnect(workspace(), &Workspace::outputRemoved, this, &Compositor::removeOutput);
}
if (m_backend->compositingType() == OpenGLCompositing) {
// some layers need a context current for destruction
static_cast<OpenGLBackend *>(m_backend.get())->makeCurrent();
}
const auto superlayers = m_superlayers;
for (auto it = superlayers.begin(); it != superlayers.end(); ++it) {
removeSuperLayer(*it);
2020-11-19 08:52:29 +00:00
}
m_scene.reset();
m_cursorScene.reset();
m_backend.reset();
m_state = State::Off;
Q_EMIT compositingToggled(false);
2011-01-30 14:34:42 +00:00
}
void Compositor::destroyCompositorSelection()
{
m_selectionOwner.reset();
}
void Compositor::releaseCompositorSelection()
{
switch (m_state) {
case State::On:
// We are compositing at the moment. Don't release.
break;
case State::Off:
if (m_selectionOwner) {
qCDebug(KWIN_CORE) << "Releasing compositor selection";
m_selectionOwner->setOwning(false);
m_selectionOwner->release();
}
break;
case State::Starting:
case State::Stopping:
// Still starting or shutting down the compositor. Starting might fail
// or after stopping a restart might follow. So test again later on.
m_releaseSelectionTimer.start();
break;
}
}
void Compositor::keepSupportProperty(xcb_atom_t atom)
{
m_unusedSupportProperties.removeAll(atom);
}
void Compositor::removeSupportProperty(xcb_atom_t atom)
{
m_unusedSupportProperties << atom;
m_unusedSupportPropertyTimer.start();
}
void Compositor::deleteUnusedSupportProperties()
{
if (m_state == State::Starting || m_state == State::Stopping) {
// Currently still maybe restarting the compositor.
m_unusedSupportPropertyTimer.start();
return;
}
if (auto *con = kwinApp()->x11Connection()) {
for (const xcb_atom_t &atom : std::as_const(m_unusedSupportProperties)) {
// remove property from root window
xcb_delete_property(con, kwinApp()->x11RootWindow(), atom);
}
m_unusedSupportProperties.clear();
}
}
void Compositor::configChanged()
{
reinitialize();
}
void Compositor::reinitialize()
{
// Restart compositing
stop();
start();
}
2020-11-19 08:52:29 +00:00
void Compositor::handleFrameRequested(RenderLoop *renderLoop)
2011-01-30 14:34:42 +00:00
{
composite(renderLoop);
}
uint Compositor::outputFormat(Output *output)
{
OutputLayer *primaryLayer = m_backend->primaryLayer(output);
return primaryLayer->format();
}
void Compositor::composite(RenderLoop *renderLoop)
{
if (m_backend->checkGraphicsReset()) {
qCDebug(KWIN_CORE) << "Graphics reset occurred";
#if KWIN_BUILD_NOTIFICATIONS
KNotification::event(QStringLiteral("graphicsreset"), i18n("Desktop effects were restarted due to a graphics reset"));
#endif
reinitialize();
return;
}
Output *output = findOutput(renderLoop);
OutputLayer *primaryLayer = m_backend->primaryLayer(output);
fTraceDuration("Paint (", output->name(), ")");
RenderLayer *superLayer = m_superlayers[renderLoop];
superLayer->setOutputLayer(primaryLayer);
2020-11-19 08:52:29 +00:00
renderLoop->prepareNewFrame();
2020-11-19 08:52:29 +00:00
if (superLayer->needsRepaint()) {
renderLoop->beginPaint();
prePaintPass(superLayer);
SurfaceItem *scanoutCandidate = superLayer->delegate()->scanoutCandidate();
renderLoop->setFullscreenSurface(scanoutCandidate);
output->setContentType(scanoutCandidate ? scanoutCandidate->contentType() : ContentType::None);
bool directScanout = false;
if (scanoutCandidate) {
const auto sublayers = superLayer->sublayers();
const bool scanoutPossible = std::none_of(sublayers.begin(), sublayers.end(), [](RenderLayer *sublayer) {
return sublayer->isVisible();
});
if (scanoutPossible && !output->directScanoutInhibited()) {
directScanout = primaryLayer->scanout(scanoutCandidate);
}
}
if (!directScanout) {
QRegion surfaceDamage = primaryLayer->repaints();
primaryLayer->resetRepaints();
preparePaintPass(superLayer, &surfaceDamage);
if (auto beginInfo = primaryLayer->beginFrame()) {
auto &[renderTarget, repaint] = beginInfo.value();
const QRegion bufferDamage = surfaceDamage.united(repaint).intersected(superLayer->rect().toAlignedRect());
scene: Rework surface damage tracking It's not possible to get the surface damage before calling Scene::paint(), which is a big problem because it blocks proper surface damage and buffer damage calculation when walking render layer tree. This change reworks the scene compositing stages to allow getting the next surface damage before calling Scene::paint(). The main challenge is that the effects can expand the surface damage. We have to call prePaintWindow() and prePaintScreen() before actually starting painting. However, prePaintWindow() is called after starting rendering. This change makes Scene call prePaintWindow() and prePaintScreen() so it's possible to know the surface damage beforehand. Unfortunately, it's also a breaking change. Some fullscreen effects will have to adapt to the new Scene paint order. Paint hooks will be invoked in the following order: * prePaintScreen() once per frame * prePaintWindow() once per frame * paintScreen() can be called multiple times * paintWindow() can be called as many times as paintScreen() * postPaintWindow() once per frame * postPaintScreen() once per frame After walking the render layer tree, the Compositor will poke the render backend for the back buffer repair region and combine it with the surface damage to get the buffer damage, which can be passed to the render backend (in order to optimize performance with tiled gpus) and Scene::paint(), which will determine what parts of the scene have to repainted based on the buffer damage.
2022-02-16 17:13:57 +00:00
paintPass(superLayer, renderTarget, bufferDamage);
primaryLayer->endFrame(bufferDamage, surfaceDamage);
}
}
postPaintPass(superLayer);
}
m_backend->present(output);
framePass(superLayer);
// TODO: Put it inside the cursor layer once the cursor layer can be backed by a real output layer.
if (waylandServer()) {
const std::chrono::milliseconds frameTime =
std::chrono::duration_cast<std::chrono::milliseconds>(output->renderLoop()->lastPresentationTimestamp());
if (!Cursors::self()->isCursorHidden()) {
Cursor *cursor = Cursors::self()->currentCursor();
if (cursor->geometry().intersects(output->geometry())) {
cursor->markAsRendered(frameTime);
}
}
}
}
void Compositor::framePass(RenderLayer *layer)
{
layer->delegate()->frame();
const auto sublayers = layer->sublayers();
for (RenderLayer *sublayer : sublayers) {
framePass(sublayer);
}
}
void Compositor::prePaintPass(RenderLayer *layer)
{
layer->delegate()->prePaint();
const auto sublayers = layer->sublayers();
for (RenderLayer *sublayer : sublayers) {
prePaintPass(sublayer);
}
}
void Compositor::postPaintPass(RenderLayer *layer)
{
layer->delegate()->postPaint();
const auto sublayers = layer->sublayers();
for (RenderLayer *sublayer : sublayers) {
postPaintPass(sublayer);
}
}
void Compositor::preparePaintPass(RenderLayer *layer, QRegion *repaint)
{
// TODO: Cull opaque region.
scene: Rework surface damage tracking It's not possible to get the surface damage before calling Scene::paint(), which is a big problem because it blocks proper surface damage and buffer damage calculation when walking render layer tree. This change reworks the scene compositing stages to allow getting the next surface damage before calling Scene::paint(). The main challenge is that the effects can expand the surface damage. We have to call prePaintWindow() and prePaintScreen() before actually starting painting. However, prePaintWindow() is called after starting rendering. This change makes Scene call prePaintWindow() and prePaintScreen() so it's possible to know the surface damage beforehand. Unfortunately, it's also a breaking change. Some fullscreen effects will have to adapt to the new Scene paint order. Paint hooks will be invoked in the following order: * prePaintScreen() once per frame * prePaintWindow() once per frame * paintScreen() can be called multiple times * paintWindow() can be called as many times as paintScreen() * postPaintWindow() once per frame * postPaintScreen() once per frame After walking the render layer tree, the Compositor will poke the render backend for the back buffer repair region and combine it with the surface damage to get the buffer damage, which can be passed to the render backend (in order to optimize performance with tiled gpus) and Scene::paint(), which will determine what parts of the scene have to repainted based on the buffer damage.
2022-02-16 17:13:57 +00:00
*repaint += layer->mapToGlobal(layer->repaints() + layer->delegate()->repaints());
layer->resetRepaints();
const auto sublayers = layer->sublayers();
for (RenderLayer *sublayer : sublayers) {
if (sublayer->isVisible()) {
preparePaintPass(sublayer, repaint);
}
}
}
void Compositor::paintPass(RenderLayer *layer, const RenderTarget &renderTarget, const QRegion &region)
{
layer->delegate()->paint(renderTarget, region);
const auto sublayers = layer->sublayers();
for (RenderLayer *sublayer : sublayers) {
if (sublayer->isVisible()) {
paintPass(sublayer, renderTarget, region);
}
}
2011-01-30 14:34:42 +00:00
}
bool Compositor::isActive()
2011-01-30 14:34:42 +00:00
{
return m_state == State::On;
2011-01-30 14:34:42 +00:00
}
bool Compositor::compositingPossible() const
{
return true;
}
QString Compositor::compositingNotPossibleReason() const
{
return QString();
}
bool Compositor::openGLCompositingIsBroken() const
{
return false;
}
void Compositor::createOpenGLSafePoint(OpenGLSafePoint safePoint)
{
}
} // namespace KWin
// included for CompositorSelectionOwner
2023-08-31 12:23:42 +00:00
#include "compositor.moc"
2023-08-31 12:23:42 +00:00
#include "moc_compositor.cpp"