Introduce render layers

This is the first tiny step towards the layer-based compositing in kwin.
The RenderLayer represents a layer with some contents. The actual
contents is represented by the RenderLayerDelegate class.

Currently, the RenderLayer is just a simple class responsible for
geometry, and repaints, but it will grow in the future. For example,
render layers need to form a tree.

The next (missing) biggest component in the layer-based compositing are
output layers. When output layers are added, each render layer would
have an output layer assigned to it or have its output layer inherited
from the parent.

The render layer tree wouldn't be affected by changes to the output
layer tree so transition between software and hardware cursors can be
seamless.

The next big milestone will be to try to port some of existing kwin
functionality to the RenderLayer, e.g. software cursor or screen edges.
This commit is contained in:
Vlad Zahorodnii 2022-02-04 20:38:37 +02:00
parent 65ccfd336f
commit 5933a21641
12 changed files with 692 additions and 108 deletions

View file

@ -93,6 +93,8 @@ target_sources(kwin PRIVATE
popup_input_filter.cpp popup_input_filter.cpp
renderbackend.cpp renderbackend.cpp
renderjournal.cpp renderjournal.cpp
renderlayer.cpp
renderlayerdelegate.cpp
renderloop.cpp renderloop.cpp
rootinfo_filter.cpp rootinfo_filter.cpp
rulebooksettings.cpp rulebooksettings.cpp

View file

@ -95,7 +95,11 @@ public:
~AbstractOutput() override; ~AbstractOutput() override;
/** /**
* Returns the primary layer. TODO: remove it * Returns a dummy OutputLayer corresponding to the primary plane.
*
* TODO: remove this. The Compositor should allocate and deallocate hardware planes
* after the pre paint pass. Planes must be allocated based on the bounding rect, transform,
* and visibility (for the cursor plane).
*/ */
OutputLayer *layer() const; OutputLayer *layer() const;

View file

@ -16,9 +16,11 @@
#include "ftrace.h" #include "ftrace.h"
#include "internal_client.h" #include "internal_client.h"
#include "openglbackend.h" #include "openglbackend.h"
#include "outputlayer.h"
#include "overlaywindow.h" #include "overlaywindow.h"
#include "platform.h" #include "platform.h"
#include "qpainterbackend.h" #include "qpainterbackend.h"
#include "renderlayer.h"
#include "renderloop.h" #include "renderloop.h"
#include "scene.h" #include "scene.h"
#include "scenes/opengl/scene_opengl.h" #include "scenes/opengl/scene_opengl.h"
@ -352,16 +354,20 @@ void Compositor::startupWithWorkspace()
m_scene->initialize(); m_scene->initialize();
const QVector<AbstractOutput *> outputs = kwinApp()->platform()->enabledOutputs(); const QVector<AbstractOutput *> outputs = kwinApp()->platform()->enabledOutputs();
if (kwinApp()->platform()->isPerScreenRenderingEnabled()) { if (kwinApp()->operationMode() == Application::OperationModeX11) {
for (AbstractOutput *output : outputs) { auto workspaceLayer = new RenderLayer(outputs.constFirst()->renderLoop());
registerRenderLoop(output->renderLoop(), output); workspaceLayer->setDelegate(new SceneDelegate(m_scene));
} workspaceLayer->setGeometry(workspace()->geometry());
connect(kwinApp()->platform(), &Platform::outputEnabled, connect(workspace(), &Workspace::geometryChanged, this, [workspaceLayer]() {
this, &Compositor::handleOutputEnabled); workspaceLayer->setGeometry(workspace()->geometry());
connect(kwinApp()->platform(), &Platform::outputDisabled, });
this, &Compositor::handleOutputDisabled); addSuperLayer(workspaceLayer);
} else { } else {
registerRenderLoop(outputs.constFirst()->renderLoop(), nullptr); for (AbstractOutput *output : outputs) {
addOutput(output);
}
connect(kwinApp()->platform(), &Platform::outputEnabled, this, &Compositor::addOutput);
connect(kwinApp()->platform(), &Platform::outputDisabled, this, &Compositor::removeOutput);
} }
m_state = State::On; m_state = State::On;
@ -391,9 +397,6 @@ void Compositor::startupWithWorkspace()
if (m_releaseSelectionTimer.isActive()) { if (m_releaseSelectionTimer.isActive()) {
m_releaseSelectionTimer.stop(); m_releaseSelectionTimer.stop();
} }
// Render at least once.
m_scene->addRepaintFull();
} }
AbstractOutput *Compositor::findOutput(RenderLoop *loop) const AbstractOutput *Compositor::findOutput(RenderLoop *loop) const
@ -407,33 +410,38 @@ AbstractOutput *Compositor::findOutput(RenderLoop *loop) const
return nullptr; return nullptr;
} }
void Compositor::registerRenderLoop(RenderLoop *renderLoop, AbstractOutput *output) void Compositor::addOutput(AbstractOutput *output)
{ {
Q_ASSERT(!m_renderLoops.contains(renderLoop)); auto workspaceLayer = new RenderLayer(output->renderLoop());
m_renderLoops.insert(renderLoop, output); workspaceLayer->setDelegate(new SceneDelegate(m_scene, output));
connect(renderLoop, &RenderLoop::frameRequested, this, &Compositor::handleFrameRequested); workspaceLayer->setGeometry(output->geometry());
connect(output, &AbstractOutput::geometryChanged, this, [output, workspaceLayer]() {
workspaceLayer->setGeometry(output->geometry());
});
addSuperLayer(workspaceLayer);
} }
void Compositor::unregisterRenderLoop(RenderLoop *renderLoop) void Compositor::removeOutput(AbstractOutput *output)
{ {
Q_ASSERT(m_renderLoops.contains(renderLoop)); removeSuperLayer(m_superlayers[output->renderLoop()]);
m_renderLoops.remove(renderLoop);
disconnect(renderLoop, &RenderLoop::frameRequested, this, &Compositor::handleFrameRequested);
} }
void Compositor::handleOutputEnabled(AbstractOutput *output) void Compositor::addSuperLayer(RenderLayer *layer)
{ {
registerRenderLoop(output->renderLoop(), output); m_superlayers.insert(layer->loop(), layer);
connect(layer->loop(), &RenderLoop::frameRequested, this, &Compositor::handleFrameRequested);
} }
void Compositor::handleOutputDisabled(AbstractOutput *output) void Compositor::removeSuperLayer(RenderLayer *layer)
{ {
unregisterRenderLoop(output->renderLoop()); m_superlayers.remove(layer->loop());
disconnect(layer->loop(), &RenderLoop::frameRequested, this, &Compositor::handleFrameRequested);
delete layer;
} }
void Compositor::scheduleRepaint() void Compositor::scheduleRepaint()
{ {
for (auto it = m_renderLoops.constBegin(); it != m_renderLoops.constEnd(); ++it) { for (auto it = m_superlayers.constBegin(); it != m_superlayers.constEnd(); ++it) {
it.key()->scheduleRepaint(); it.key()->scheduleRepaint();
} }
} }
@ -493,14 +501,13 @@ void Compositor::stop()
} }
} }
while (!m_renderLoops.isEmpty()) { const auto superlayers = m_superlayers;
unregisterRenderLoop(m_renderLoops.firstKey()); for (auto it = superlayers.begin(); it != superlayers.end(); ++it) {
removeSuperLayer(*it);
} }
disconnect(kwinApp()->platform(), &Platform::outputEnabled, disconnect(kwinApp()->platform(), &Platform::outputEnabled, this, &Compositor::addOutput);
this, &Compositor::handleOutputEnabled); disconnect(kwinApp()->platform(), &Platform::outputDisabled, this, &Compositor::removeOutput);
disconnect(kwinApp()->platform(), &Platform::outputDisabled,
this, &Compositor::handleOutputDisabled);
delete m_scene; delete m_scene;
m_scene = nullptr; m_scene = nullptr;
@ -601,33 +608,92 @@ void Compositor::composite(RenderLoop *renderLoop)
} }
AbstractOutput *output = findOutput(renderLoop); AbstractOutput *output = findOutput(renderLoop);
OutputLayer *outputLayer = output->layer();
fTraceDuration("Paint (", output->name(), ")"); fTraceDuration("Paint (", output->name(), ")");
const QRegion damage = m_scene->repaints(output); RenderLayer *superLayer = m_superlayers[renderLoop];
m_scene->resetRepaints(output); prePaintPass(superLayer);
m_scene->prePaint(output); superLayer->setOutputLayer(outputLayer);
SurfaceItem *scanoutCandidate = m_scene->scanoutCandidate(); SurfaceItem *scanoutCandidate = superLayer->delegate()->scanoutCandidate();
renderLoop->setFullscreenSurface(scanoutCandidate); renderLoop->setFullscreenSurface(scanoutCandidate);
renderLoop->beginFrame(); renderLoop->beginFrame();
bool directScanout = false; bool directScanout = false;
if (scanoutCandidate) { if (scanoutCandidate) {
if (!output->usesSoftwareCursor() && !output->directScanoutInhibited()) { 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 = m_backend->scanout(output, scanoutCandidate); directScanout = m_backend->scanout(output, scanoutCandidate);
} }
} }
if (directScanout) { if (directScanout) {
renderLoop->endFrame(); renderLoop->endFrame();
} else { } else {
QRegion update, valid; QRegion repaint = outputLayer->repaints();
const QRegion repaint = m_backend->beginFrame(output); outputLayer->resetRepaints();
m_scene->paint(damage, repaint, update, valid); preparePaintPass(superLayer, &repaint);
QRegion surfaceDamage;
QRegion bufferDamage;
const QRegion repair = m_backend->beginFrame(output);
paintPass(superLayer, repaint, repair, &surfaceDamage, &bufferDamage);
renderLoop->endFrame(); renderLoop->endFrame();
m_backend->endFrame(output, valid, update); m_backend->endFrame(output, bufferDamage, surfaceDamage);
} }
m_scene->postPaint(); postPaintPass(superLayer);
}
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.
*repaint += layer->mapToGlobal(layer->repaints());
layer->resetRepaints();
const auto sublayers = layer->sublayers();
for (RenderLayer *sublayer : sublayers) {
if (sublayer->isVisible()) {
preparePaintPass(sublayer, repaint);
}
}
}
void Compositor::paintPass(RenderLayer *layer, const QRegion &repaint, const QRegion &repair, QRegion *surfaceDamage, QRegion *bufferDamage)
{
QRegion localSurfaceDamage;
QRegion localBufferDamage;
layer->delegate()->paint(repaint, repair, localSurfaceDamage, localBufferDamage);
*surfaceDamage += localSurfaceDamage;
*bufferDamage += localBufferDamage;
const auto sublayers = layer->sublayers();
for (RenderLayer *sublayer : sublayers) {
if (sublayer->isVisible()) {
paintPass(sublayer, repaint, repair, surfaceDamage, bufferDamage);
}
}
} }
bool Compositor::isActive() bool Compositor::isActive()

View file

@ -21,6 +21,7 @@ namespace KWin
class AbstractOutput; class AbstractOutput;
class CompositorSelectionOwner; class CompositorSelectionOwner;
class RenderBackend; class RenderBackend;
class RenderLayer;
class RenderLoop; class RenderLoop;
class Scene; class Scene;
class Toplevel; class Toplevel;
@ -118,8 +119,6 @@ protected Q_SLOTS:
private Q_SLOTS: private Q_SLOTS:
void handleFrameRequested(RenderLoop *renderLoop); void handleFrameRequested(RenderLoop *renderLoop);
void handleOutputEnabled(AbstractOutput *output);
void handleOutputDisabled(AbstractOutput *output);
private: private:
void initializeX11(); void initializeX11();
@ -128,13 +127,20 @@ private:
void releaseCompositorSelection(); void releaseCompositorSelection();
void deleteUnusedSupportProperties(); void deleteUnusedSupportProperties();
void registerRenderLoop(RenderLoop *renderLoop, AbstractOutput *output);
void unregisterRenderLoop(RenderLoop *renderLoop);
bool attemptOpenGLCompositing(); bool attemptOpenGLCompositing();
bool attemptQPainterCompositing(); bool attemptQPainterCompositing();
AbstractOutput *findOutput(RenderLoop *loop) const; AbstractOutput *findOutput(RenderLoop *loop) const;
void addOutput(AbstractOutput *output);
void removeOutput(AbstractOutput *output);
void addSuperLayer(RenderLayer *layer);
void removeSuperLayer(RenderLayer *layer);
void prePaintPass(RenderLayer *layer);
void postPaintPass(RenderLayer *layer);
void preparePaintPass(RenderLayer *layer, QRegion *repaint);
void paintPass(RenderLayer *layer, const QRegion &repaint, const QRegion &repair, QRegion *surfaceDamage, QRegion *bufferDamage);
State m_state = State::Off; State m_state = State::Off;
CompositorSelectionOwner *m_selectionOwner = nullptr; CompositorSelectionOwner *m_selectionOwner = nullptr;
@ -143,7 +149,7 @@ private:
QTimer m_unusedSupportPropertyTimer; QTimer m_unusedSupportPropertyTimer;
Scene *m_scene = nullptr; Scene *m_scene = nullptr;
RenderBackend *m_backend = nullptr; RenderBackend *m_backend = nullptr;
QMap<RenderLoop *, AbstractOutput *> m_renderLoops; QHash<RenderLoop *, RenderLayer *> m_superlayers;
}; };
class KWIN_EXPORT WaylandCompositor final : public Compositor class KWIN_EXPORT WaylandCompositor final : public Compositor

View file

@ -1776,7 +1776,10 @@ void EffectsHandlerImpl::slotOutputDisabled(AbstractOutput *output)
void EffectsHandlerImpl::renderScreen(EffectScreen *screen) void EffectsHandlerImpl::renderScreen(EffectScreen *screen)
{ {
auto output = static_cast<EffectScreenImpl *>(screen)->platformOutput(); auto output = static_cast<EffectScreenImpl *>(screen)->platformOutput();
scene()->paintScreen(output); m_scene->prePaint(output);
QRegion update, valid;
m_scene->paint(output->geometry(), QRect(), update, valid);
m_scene->postPaint();
} }
bool EffectsHandlerImpl::isCursorHidden() const bool EffectsHandlerImpl::isCursorHidden() const

276
src/renderlayer.cpp Normal file
View file

@ -0,0 +1,276 @@
/*
SPDX-FileCopyrightText: 2021 Vlad Zahorodnii <vlad.zahorodnii@kde.org>
SPDX-License-Identifier: GPL-2.0-or-later
*/
#include "renderlayer.h"
#include "outputlayer.h"
#include "renderlayerdelegate.h"
#include "renderloop.h"
namespace KWin
{
RenderLayer::RenderLayer(RenderLoop *loop, RenderLayer *superlayer)
: m_loop(loop)
{
setSuperlayer(superlayer);
}
RenderLayer::~RenderLayer()
{
const auto sublayers = m_sublayers;
for (RenderLayer *sublayer : sublayers) {
sublayer->setSuperlayer(superlayer());
}
setSuperlayer(nullptr);
}
OutputLayer *RenderLayer::outputLayer() const
{
return m_outputLayer;
}
void RenderLayer::setOutputLayer(OutputLayer *layer)
{
if (m_outputLayer == layer) {
return;
}
if (m_outputLayer) {
m_outputLayer->addRepaint(mapToGlobal(boundingRect()));
}
m_outputLayer = layer;
for (RenderLayer *sublayer : std::as_const(m_sublayers)) {
sublayer->setOutputLayer(layer);
}
}
RenderLayer *RenderLayer::superlayer() const
{
return m_superlayer;
}
void RenderLayer::setSuperlayer(RenderLayer *layer)
{
if (m_superlayer == layer) {
return;
}
if (m_superlayer) {
m_superlayer->removeSublayer(this);
}
m_superlayer = layer;
if (m_superlayer) {
m_superlayer->addSublayer(this);
}
updateEffectiveVisibility();
}
QList<RenderLayer *> RenderLayer::sublayers() const
{
return m_sublayers;
}
void RenderLayer::addSublayer(RenderLayer *sublayer)
{
m_sublayers.append(sublayer);
sublayer->setOutputLayer(m_outputLayer);
updateBoundingRect();
}
void RenderLayer::removeSublayer(RenderLayer *sublayer)
{
m_sublayers.removeOne(sublayer);
sublayer->setOutputLayer(nullptr);
updateBoundingRect();
}
RenderLoop *RenderLayer::loop() const
{
return m_loop;
}
RenderLayerDelegate *RenderLayer::delegate() const
{
return m_delegate.data();
}
void RenderLayer::setDelegate(RenderLayerDelegate *delegate)
{
m_delegate.reset(delegate);
m_delegate->setLayer(this);
}
QRect RenderLayer::rect() const
{
return QRect(0, 0, m_geometry.width(), m_geometry.height());
}
QRect RenderLayer::boundingRect() const
{
return m_boundingRect;
}
QRect RenderLayer::geometry() const
{
return m_geometry;
}
void RenderLayer::setGeometry(const QRect &geometry)
{
if (m_geometry == geometry) {
return;
}
if (m_effectiveVisible && m_outputLayer) {
m_outputLayer->addRepaint(mapToGlobal(boundingRect()));
}
m_geometry = geometry;
addRepaintFull();
updateBoundingRect();
if (m_superlayer) {
m_superlayer->updateBoundingRect();
}
}
void RenderLayer::updateBoundingRect()
{
QRect boundingRect = rect();
for (const RenderLayer *sublayer : std::as_const(m_sublayers)) {
boundingRect |= sublayer->boundingRect().translated(sublayer->geometry().topLeft());
}
if (m_boundingRect != boundingRect) {
m_boundingRect = boundingRect;
if (m_superlayer) {
m_superlayer->updateBoundingRect();
}
}
}
void RenderLayer::addRepaintFull()
{
addRepaint(rect());
}
void RenderLayer::addRepaint(int x, int y, int width, int height)
{
addRepaint(QRegion(x, y, width, height));
}
void RenderLayer::addRepaint(const QRect &rect)
{
addRepaint(QRegion(rect));
}
void RenderLayer::addRepaint(const QRegion &region)
{
if (!m_effectiveVisible) {
return;
}
if (!region.isEmpty()) {
m_repaints += region;
m_loop->scheduleRepaint();
}
}
QRegion RenderLayer::repaints() const
{
return m_repaints;
}
void RenderLayer::resetRepaints()
{
m_repaints = QRegion();
}
bool RenderLayer::isVisible() const
{
return m_effectiveVisible;
}
void RenderLayer::setVisible(bool visible)
{
if (m_explicitVisible != visible) {
m_explicitVisible = visible;
updateEffectiveVisibility();
}
}
bool RenderLayer::computeEffectiveVisibility() const
{
return m_explicitVisible && (!m_superlayer || m_superlayer->isVisible());
}
void RenderLayer::updateEffectiveVisibility()
{
const bool effectiveVisible = computeEffectiveVisibility();
if (m_effectiveVisible == effectiveVisible) {
return;
}
m_effectiveVisible = effectiveVisible;
if (effectiveVisible) {
addRepaintFull();
} else {
if (m_outputLayer) {
m_outputLayer->addRepaint(mapToGlobal(boundingRect()));
}
}
for (RenderLayer *sublayer : std::as_const(m_sublayers)) {
sublayer->updateEffectiveVisibility();
}
}
QPoint RenderLayer::mapToGlobal(const QPoint &point) const
{
QPoint result = point;
const RenderLayer *layer = this;
while (layer) {
result += layer->geometry().topLeft();
layer = layer->superlayer();
}
return result;
}
QRect RenderLayer::mapToGlobal(const QRect &rect) const
{
return rect.translated(mapToGlobal(QPoint(0, 0)));
}
QRegion RenderLayer::mapToGlobal(const QRegion &region) const
{
if (region.isEmpty()) {
return QRegion();
}
return region.translated(mapToGlobal(QPoint(0, 0)));
}
QPoint RenderLayer::mapFromGlobal(const QPoint &point) const
{
QPoint result = point;
const RenderLayer *layer = this;
while (layer) {
result -= layer->geometry().topLeft();
layer = layer->superlayer();
}
return result;
}
QRect RenderLayer::mapFromGlobal(const QRect &rect) const
{
return rect.translated(mapFromGlobal(QPoint(0, 0)));
}
QRegion RenderLayer::mapFromGlobal(const QRegion &region) const
{
if (region.isEmpty()) {
return QRegion();
}
return region.translated(mapFromGlobal(QPoint(0, 0)));
}
} // namespace KWin

94
src/renderlayer.h Normal file
View file

@ -0,0 +1,94 @@
/*
SPDX-FileCopyrightText: 2021 Vlad Zahorodnii <vlad.zahorodnii@kde.org>
SPDX-License-Identifier: GPL-2.0-or-later
*/
#pragma once
#include "kwin_export.h"
#include <QMap>
#include <QObject>
#include <QRegion>
namespace KWin
{
class OutputLayer;
class RenderLayerDelegate;
class RenderLoop;
/**
* The RenderLayer class represents a composited layer.
*
* The contents of the layer is represented by the RenderLayerDelegate. The render layer
* takes the ownership of the delegate.
*
* Each render layer has a hardware layer assigned to it, represented by OutputLayer. If
* the output layer is not set explicitly, it's inherited from the parent render layer.
* Output layers can be freely assigned or unassigned to render layers.
*/
class KWIN_EXPORT RenderLayer : public QObject
{
Q_OBJECT
public:
explicit RenderLayer(RenderLoop *loop, RenderLayer *superlayer = nullptr);
~RenderLayer() override;
OutputLayer *outputLayer() const;
void setOutputLayer(OutputLayer *layer);
RenderLoop *loop() const;
RenderLayerDelegate *delegate() const;
void setDelegate(RenderLayerDelegate *delegate);
QList<RenderLayer *> sublayers() const;
RenderLayer *superlayer() const;
void setSuperlayer(RenderLayer *layer);
bool isVisible() const;
void setVisible(bool visible);
QPoint mapToGlobal(const QPoint &point) const;
QRegion mapToGlobal(const QRegion &region) const;
QRect mapToGlobal(const QRect &rect) const;
QPoint mapFromGlobal(const QPoint &point) const;
QRegion mapFromGlobal(const QRegion &region) const;
QRect mapFromGlobal(const QRect &rect) const;
QRect rect() const;
QRect boundingRect() const;
QRect geometry() const;
void setGeometry(const QRect &rect);
void addRepaint(const QRegion &region);
void addRepaint(const QRect &rect);
void addRepaint(int x, int y, int width, int height);
void addRepaintFull();
QRegion repaints() const;
void resetRepaints();
private:
void addSublayer(RenderLayer *sublayer);
void removeSublayer(RenderLayer *sublayer);
void updateBoundingRect();
void updateEffectiveVisibility();
bool computeEffectiveVisibility() const;
RenderLoop *m_loop;
QScopedPointer<RenderLayerDelegate> m_delegate;
QRegion m_repaints;
QRect m_boundingRect;
QRect m_geometry;
OutputLayer *m_outputLayer = nullptr;
RenderLayer *m_superlayer = nullptr;
QList<RenderLayer *> m_sublayers;
bool m_effectiveVisible = true;
bool m_explicitVisible = true;
};
} // namespace KWin

View file

@ -0,0 +1,40 @@
/*
SPDX-FileCopyrightText: 2021 Vlad Zahorodnii <vlad.zahorodnii@kde.org>
SPDX-License-Identifier: GPL-2.0-or-later
*/
#include "renderlayerdelegate.h"
namespace KWin
{
RenderLayerDelegate::RenderLayerDelegate(QObject *parent)
: QObject(parent)
{
}
RenderLayer *RenderLayerDelegate::layer() const
{
return m_layer;
}
void RenderLayerDelegate::setLayer(RenderLayer *layer)
{
m_layer = layer;
}
void RenderLayerDelegate::prePaint()
{
}
void RenderLayerDelegate::postPaint()
{
}
SurfaceItem *RenderLayerDelegate::scanoutCandidate() const
{
return nullptr;
}
} // namespace KWin

61
src/renderlayerdelegate.h Normal file
View file

@ -0,0 +1,61 @@
/*
SPDX-FileCopyrightText: 2021 Vlad Zahorodnii <vlad.zahorodnii@kde.org>
SPDX-License-Identifier: GPL-2.0-or-later
*/
#pragma once
#include "kwin_export.h"
#include <QObject>
#include <QRegion>
namespace KWin
{
class RenderLayer;
class SurfaceItem;
/**
* The RenderLayerDelegate class represents a render layer's contents.
*/
class KWIN_EXPORT RenderLayerDelegate : public QObject
{
Q_OBJECT
public:
explicit RenderLayerDelegate(QObject *parent = nullptr);
RenderLayer *layer() const;
void setLayer(RenderLayer *layer);
/**
* This function is called by the compositor before starting compositing. Reimplement
* this function to do frame initialization.
*/
virtual void prePaint();
/**
* This function is called by the compositor after finishing compositing. Reimplement
* this function to do post frame cleanup.
*/
virtual void postPaint();
/**
* Returns the direct scanout candidate hint. It can be used to avoid compositing the
* render layer.
*/
virtual SurfaceItem *scanoutCandidate() const;
/**
* This function is called when the compositor wants the render layer delegate
* to repaint its contents.
*/
virtual void paint(const QRegion &damage, const QRegion &repaint, QRegion &update, QRegion &valid) = 0;
private:
RenderLayer *m_layer = nullptr;
};
} // namespace KWin

View file

@ -56,6 +56,7 @@
#include "abstract_output.h" #include "abstract_output.h"
#include "internal_client.h" #include "internal_client.h"
#include "platform.h" #include "platform.h"
#include "renderlayer.h"
#include "shadowitem.h" #include "shadowitem.h"
#include "surfaceitem.h" #include "surfaceitem.h"
#include "unmanaged.h" #include "unmanaged.h"
@ -81,6 +82,46 @@
namespace KWin namespace KWin
{ {
SceneDelegate::SceneDelegate(Scene *scene, QObject *parent)
: RenderLayerDelegate(parent)
, m_scene(scene)
{
m_scene->addDelegate(this);
}
SceneDelegate::SceneDelegate(Scene *scene, AbstractOutput *output, QObject *parent)
: RenderLayerDelegate(parent)
, m_scene(scene)
, m_output(output)
{
m_scene->addDelegate(this);
}
SceneDelegate::~SceneDelegate()
{
m_scene->removeDelegate(this);
}
SurfaceItem *SceneDelegate::scanoutCandidate() const
{
return m_scene->scanoutCandidate();
}
void SceneDelegate::prePaint()
{
m_scene->prePaint(m_output);
}
void SceneDelegate::postPaint()
{
m_scene->postPaint();
}
void SceneDelegate::paint(const QRegion &damage, const QRegion &repaint, QRegion &update, QRegion &valid)
{
m_scene->paint(damage, repaint, update, valid);
}
//**************************************** //****************************************
// Scene // Scene
//**************************************** //****************************************
@ -97,8 +138,6 @@ Scene::~Scene()
void Scene::initialize() void Scene::initialize()
{ {
connect(kwinApp()->platform(), &Platform::outputDisabled, this, &Scene::removeRepaints);
connect(workspace(), &Workspace::deletedRemoved, this, &Scene::removeToplevel); connect(workspace(), &Workspace::deletedRemoved, this, &Scene::removeToplevel);
connect(workspace(), &Workspace::currentActivityChanged, this, &Scene::addRepaintFull); connect(workspace(), &Workspace::currentActivityChanged, this, &Scene::addRepaintFull);
@ -145,18 +184,11 @@ void Scene::addRepaint(const QRect &rect)
void Scene::addRepaint(const QRegion &region) void Scene::addRepaint(const QRegion &region)
{ {
const QVector<AbstractOutput *> outputs = kwinApp()->platform()->enabledOutputs(); for (const auto &delegate : std::as_const(m_delegates)) {
if (kwinApp()->platform()->isPerScreenRenderingEnabled()) { const QRegion dirtyRegion = region & delegate->layer()->geometry();
for (const auto &output : outputs) { if (!dirtyRegion.isEmpty()) {
const QRegion dirtyRegion = region & output->geometry(); delegate->layer()->addRepaint(delegate->layer()->mapFromGlobal(dirtyRegion));
if (!dirtyRegion.isEmpty()) {
m_repaints[output] += dirtyRegion;
output->renderLoop()->scheduleRepaint();
}
} }
} else {
m_repaints[outputs.constFirst()] += region;
outputs.constFirst()->renderLoop()->scheduleRepaint();
} }
} }
@ -173,19 +205,19 @@ void Scene::setGeometry(const QRect &rect)
} }
} }
QRegion Scene::repaints(AbstractOutput *output) const QList<SceneDelegate *> Scene::delegates() const
{ {
return m_repaints.value(output, infiniteRegion()); return m_delegates;
} }
void Scene::resetRepaints(AbstractOutput *output) void Scene::addDelegate(SceneDelegate *delegate)
{ {
m_repaints.insert(output, QRegion()); m_delegates.append(delegate);
} }
void Scene::removeRepaints(AbstractOutput *output) void Scene::removeDelegate(SceneDelegate *delegate)
{ {
m_repaints.remove(output); m_delegates.removeOne(delegate);
} }
static SurfaceItem *findTopMostSurface(SurfaceItem *item) static SurfaceItem *findTopMostSurface(SurfaceItem *item)
@ -238,7 +270,16 @@ SurfaceItem *Scene::scanoutCandidate() const
void Scene::prePaint(AbstractOutput *output) void Scene::prePaint(AbstractOutput *output)
{ {
createStackingOrder(); createStackingOrder();
painted_screen = output;
if (kwinApp()->operationMode() == Application::OperationModeX11) {
painted_screen = kwinApp()->platform()->enabledOutputs().constFirst();
setRenderTargetRect(geometry());
setRenderTargetScale(1);
} else {
painted_screen = output;
setRenderTargetRect(painted_screen->geometry());
setRenderTargetScale(painted_screen->scale());
}
} }
void Scene::postPaint() void Scene::postPaint()
@ -332,16 +373,6 @@ QRegion Scene::mapToRenderTarget(const QRegion &region) const
return result; return result;
} }
void Scene::paintScreen(AbstractOutput *output)
{
createStackingOrder();
painted_screen = output;
QRegion update, valid;
paintScreen(output->geometry(), QRect(), &update, &valid);
clearStackingOrder();
}
// returns mask and possibly modified region // returns mask and possibly modified region
void Scene::paintScreen(const QRegion &damage, const QRegion &repaint, void Scene::paintScreen(const QRegion &damage, const QRegion &repaint,
QRegion *updateRegion, QRegion *validRegion) QRegion *updateRegion, QRegion *validRegion)
@ -363,14 +394,6 @@ void Scene::paintScreen(const QRegion &damage, const QRegion &repaint,
auto effectsImpl = static_cast<EffectsHandlerImpl *>(effects); auto effectsImpl = static_cast<EffectsHandlerImpl *>(effects);
effectsImpl->startPaint(); effectsImpl->startPaint();
if (kwinApp()->platform()->isPerScreenRenderingEnabled()) {
setRenderTargetRect(painted_screen->geometry());
setRenderTargetScale(painted_screen->scale());
} else {
setRenderTargetRect(geometry());
setRenderTargetScale(1);
}
const QRegion displayRegion(renderTargetRect()); const QRegion displayRegion(renderTargetRect());
QRegion region = damage; QRegion region = damage;

View file

@ -10,6 +10,7 @@
#ifndef KWIN_SCENE_H #ifndef KWIN_SCENE_H
#define KWIN_SCENE_H #define KWIN_SCENE_H
#include "renderlayerdelegate.h"
#include "toplevel.h" #include "toplevel.h"
#include "utils/common.h" #include "utils/common.h"
#include "kwineffects.h" #include "kwineffects.h"
@ -33,6 +34,7 @@ class EffectWindowImpl;
class GLTexture; class GLTexture;
class Item; class Item;
class RenderLoop; class RenderLoop;
class Scene;
class Shadow; class Shadow;
class ShadowItem; class ShadowItem;
class SurfaceItem; class SurfaceItem;
@ -42,13 +44,32 @@ class SurfacePixmapX11;
class SurfaceTexture; class SurfaceTexture;
class WindowItem; class WindowItem;
// The base class for compositing backends. class SceneDelegate : public RenderLayerDelegate
{
Q_OBJECT
public:
explicit SceneDelegate(Scene *scene, QObject *parent = nullptr);
explicit SceneDelegate(Scene *scene, AbstractOutput *output, QObject *parent = nullptr);
~SceneDelegate() override;
SurfaceItem *scanoutCandidate() const override;
void prePaint() override;
void postPaint() override;
void paint(const QRegion &damage, const QRegion &repaint, QRegion &update, QRegion &valid) override;
private:
Scene *m_scene;
AbstractOutput *m_output = nullptr;
};
class KWIN_EXPORT Scene : public QObject class KWIN_EXPORT Scene : public QObject
{ {
Q_OBJECT Q_OBJECT
public: public:
explicit Scene(QObject *parent = nullptr); explicit Scene(QObject *parent = nullptr);
~Scene() override = 0; ~Scene() override;
class EffectFrame; class EffectFrame;
class Window; class Window;
@ -65,23 +86,18 @@ public:
QRect geometry() const; QRect geometry() const;
void setGeometry(const QRect &rect); void setGeometry(const QRect &rect);
/** QList<SceneDelegate *> delegates() const;
* Returns the repaints region for output with the specified @a output. void addDelegate(SceneDelegate *delegate);
*/ void removeDelegate(SceneDelegate *delegate);
QRegion repaints(AbstractOutput *output) const;
void resetRepaints(AbstractOutput *output);
// Returns true if the ctor failed to properly initialize. // Returns true if the ctor failed to properly initialize.
virtual bool initFailed() const = 0; virtual bool initFailed() const = 0;
SurfaceItem *scanoutCandidate() const; SurfaceItem *scanoutCandidate() const;
void prePaint(AbstractOutput *output); void prePaint(AbstractOutput *output);
void postPaint(); void postPaint();
virtual void paint(const QRegion &damage, const QRegion &repaint, QRegion &update, QRegion &valid) = 0; virtual void paint(const QRegion &damage, const QRegion &repaint, QRegion &update, QRegion &valid) = 0;
void paintScreen(AbstractOutput *output);
/** /**
* Adds the Toplevel to the Scene. * Adds the Toplevel to the Scene.
* *
@ -262,12 +278,11 @@ protected:
// windows in their stacking order // windows in their stacking order
QVector< Window* > stacking_order; QVector< Window* > stacking_order;
private: private:
void removeRepaints(AbstractOutput *output);
void addCursorRepaints(); void addCursorRepaints();
std::chrono::milliseconds m_expectedPresentTimestamp = std::chrono::milliseconds::zero(); std::chrono::milliseconds m_expectedPresentTimestamp = std::chrono::milliseconds::zero();
QList<SceneDelegate *> m_delegates;
QHash< Toplevel*, Window* > m_windows; QHash< Toplevel*, Window* > m_windows;
QMap<AbstractOutput *, QRegion> m_repaints;
QRect m_geometry; QRect m_geometry;
QMatrix4x4 m_renderTargetProjectionMatrix; QMatrix4x4 m_renderTargetProjectionMatrix;
QRect m_renderTargetRect; QRect m_renderTargetRect;

View file

@ -70,18 +70,12 @@ void SceneQPainter::paintGenericScreen(int mask, const ScreenPaintData &data)
void SceneQPainter::paint(const QRegion &damage, const QRegion &repaint, QRegion &update, QRegion &valid) void SceneQPainter::paint(const QRegion &damage, const QRegion &repaint, QRegion &update, QRegion &valid)
{ {
Q_ASSERT(kwinApp()->platform()->isPerScreenRenderingEnabled());
QImage *buffer = m_backend->bufferForScreen(painted_screen); QImage *buffer = m_backend->bufferForScreen(painted_screen);
if (buffer && !buffer->isNull()) { if (buffer && !buffer->isNull()) {
const QRect geometry = painted_screen->geometry();
m_painter->begin(buffer); m_painter->begin(buffer);
m_painter->setWindow(geometry); m_painter->setWindow(painted_screen->geometry());
paintScreen(damage, repaint, &update, &valid);
QRegion updateRegion, validRegion; paintCursor(painted_screen, update);
paintScreen(damage, repaint, &updateRegion, &validRegion);
paintCursor(painted_screen, updateRegion);
m_painter->end(); m_painter->end();
} }
} }