Commit 5933a216 authored by Vlad Zahorodnii's avatar Vlad Zahorodnii
Browse files

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.
parent 65ccfd33
......@@ -93,6 +93,8 @@ target_sources(kwin PRIVATE
popup_input_filter.cpp
renderbackend.cpp
renderjournal.cpp
renderlayer.cpp
renderlayerdelegate.cpp
renderloop.cpp
rootinfo_filter.cpp
rulebooksettings.cpp
......
......@@ -95,7 +95,11 @@ public:
~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;
......
......@@ -16,9 +16,11 @@
#include "ftrace.h"
#include "internal_client.h"
#include "openglbackend.h"
#include "outputlayer.h"
#include "overlaywindow.h"
#include "platform.h"
#include "qpainterbackend.h"
#include "renderlayer.h"
#include "renderloop.h"
#include "scene.h"
#include "scenes/opengl/scene_opengl.h"
......@@ -352,16 +354,20 @@ void Compositor::startupWithWorkspace()
m_scene->initialize();
const QVector<AbstractOutput *> outputs = kwinApp()->platform()->enabledOutputs();
if (kwinApp()->platform()->isPerScreenRenderingEnabled()) {
if (kwinApp()->operationMode() == Application::OperationModeX11) {
auto workspaceLayer = new RenderLayer(outputs.constFirst()->renderLoop());
workspaceLayer->setDelegate(new SceneDelegate(m_scene));
workspaceLayer->setGeometry(workspace()->geometry());
connect(workspace(), &Workspace::geometryChanged, this, [workspaceLayer]() {
workspaceLayer->setGeometry(workspace()->geometry());
});
addSuperLayer(workspaceLayer);
} else {
for (AbstractOutput *output : outputs) {
registerRenderLoop(output->renderLoop(), output);
addOutput(output);
}
connect(kwinApp()->platform(), &Platform::outputEnabled,
this, &Compositor::handleOutputEnabled);
connect(kwinApp()->platform(), &Platform::outputDisabled,
this, &Compositor::handleOutputDisabled);
} else {
registerRenderLoop(outputs.constFirst()->renderLoop(), nullptr);
connect(kwinApp()->platform(), &Platform::outputEnabled, this, &Compositor::addOutput);
connect(kwinApp()->platform(), &Platform::outputDisabled, this, &Compositor::removeOutput);
}
m_state = State::On;
......@@ -391,9 +397,6 @@ void Compositor::startupWithWorkspace()
if (m_releaseSelectionTimer.isActive()) {
m_releaseSelectionTimer.stop();
}
// Render at least once.
m_scene->addRepaintFull();
}
AbstractOutput *Compositor::findOutput(RenderLoop *loop) const
......@@ -407,33 +410,38 @@ AbstractOutput *Compositor::findOutput(RenderLoop *loop) const
return nullptr;
}
void Compositor::registerRenderLoop(RenderLoop *renderLoop, AbstractOutput *output)
void Compositor::addOutput(AbstractOutput *output)
{
Q_ASSERT(!m_renderLoops.contains(renderLoop));
m_renderLoops.insert(renderLoop, output);
connect(renderLoop, &RenderLoop::frameRequested, this, &Compositor::handleFrameRequested);
auto workspaceLayer = new RenderLayer(output->renderLoop());
workspaceLayer->setDelegate(new SceneDelegate(m_scene, output));
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));
m_renderLoops.remove(renderLoop);
disconnect(renderLoop, &RenderLoop::frameRequested, this, &Compositor::handleFrameRequested);
removeSuperLayer(m_superlayers[output->renderLoop()]);
}
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()
{
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();
}
}
......@@ -493,14 +501,13 @@ void Compositor::stop()
}
}
while (!m_renderLoops.isEmpty()) {
unregisterRenderLoop(m_renderLoops.firstKey());
const auto superlayers = m_superlayers;
for (auto it = superlayers.begin(); it != superlayers.end(); ++it) {
removeSuperLayer(*it);
}
disconnect(kwinApp()->platform(), &Platform::outputEnabled,
this, &Compositor::handleOutputEnabled);
disconnect(kwinApp()->platform(), &Platform::outputDisabled,
this, &Compositor::handleOutputDisabled);
disconnect(kwinApp()->platform(), &Platform::outputEnabled, this, &Compositor::addOutput);
disconnect(kwinApp()->platform(), &Platform::outputDisabled, this, &Compositor::removeOutput);
delete m_scene;
m_scene = nullptr;
......@@ -601,33 +608,92 @@ void Compositor::composite(RenderLoop *renderLoop)
}
AbstractOutput *output = findOutput(renderLoop);
OutputLayer *outputLayer = output->layer();
fTraceDuration("Paint (", output->name(), ")");
const QRegion damage = m_scene->repaints(output);
m_scene->resetRepaints(output);
m_scene->prePaint(output);
RenderLayer *superLayer = m_superlayers[renderLoop];
prePaintPass(superLayer);
superLayer->setOutputLayer(outputLayer);
SurfaceItem *scanoutCandidate = m_scene->scanoutCandidate();
SurfaceItem *scanoutCandidate = superLayer->delegate()->scanoutCandidate();
renderLoop->setFullscreenSurface(scanoutCandidate);
renderLoop->beginFrame();
bool directScanout = false;
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);
}
}
if (directScanout) {
renderLoop->endFrame();
} else {
QRegion update, valid;
const QRegion repaint = m_backend->beginFrame(output);
m_scene->paint(damage, repaint, update, valid);
QRegion repaint = outputLayer->repaints();
outputLayer->resetRepaints();
preparePaintPass(superLayer, &repaint);
QRegion surfaceDamage;
QRegion bufferDamage;
const QRegion repair = m_backend->beginFrame(output);
paintPass(superLayer, repaint, repair, &surfaceDamage, &bufferDamage);
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()
......
......@@ -21,6 +21,7 @@ namespace KWin
class AbstractOutput;
class CompositorSelectionOwner;
class RenderBackend;
class RenderLayer;
class RenderLoop;
class Scene;
class Toplevel;
......@@ -118,8 +119,6 @@ protected Q_SLOTS:
private Q_SLOTS:
void handleFrameRequested(RenderLoop *renderLoop);
void handleOutputEnabled(AbstractOutput *output);
void handleOutputDisabled(AbstractOutput *output);
private:
void initializeX11();
......@@ -128,13 +127,20 @@ private:
void releaseCompositorSelection();
void deleteUnusedSupportProperties();
void registerRenderLoop(RenderLoop *renderLoop, AbstractOutput *output);
void unregisterRenderLoop(RenderLoop *renderLoop);
bool attemptOpenGLCompositing();
bool attemptQPainterCompositing();
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;
CompositorSelectionOwner *m_selectionOwner = nullptr;
......@@ -143,7 +149,7 @@ private:
QTimer m_unusedSupportPropertyTimer;
Scene *m_scene = nullptr;
RenderBackend *m_backend = nullptr;
QMap<RenderLoop *, AbstractOutput *> m_renderLoops;
QHash<RenderLoop *, RenderLayer *> m_superlayers;
};
class KWIN_EXPORT WaylandCompositor final : public Compositor
......
......@@ -1776,7 +1776,10 @@ void EffectsHandlerImpl::slotOutputDisabled(AbstractOutput *output)
void EffectsHandlerImpl::renderScreen(EffectScreen *screen)
{
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
......
/*
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
/*
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