diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index dd634ffde3..260474acfc 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -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 diff --git a/src/abstract_output.h b/src/abstract_output.h index c9e32e86b7..ee83852228 100644 --- a/src/abstract_output.h +++ b/src/abstract_output.h @@ -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; diff --git a/src/composite.cpp b/src/composite.cpp index a7b7399d39..aec916cf3a 100644 --- a/src/composite.cpp +++ b/src/composite.cpp @@ -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 outputs = kwinApp()->platform()->enabledOutputs(); - if (kwinApp()->platform()->isPerScreenRenderingEnabled()) { - for (AbstractOutput *output : outputs) { - registerRenderLoop(output->renderLoop(), output); - } - connect(kwinApp()->platform(), &Platform::outputEnabled, - this, &Compositor::handleOutputEnabled); - connect(kwinApp()->platform(), &Platform::outputDisabled, - this, &Compositor::handleOutputDisabled); + 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 { - 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; @@ -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() diff --git a/src/composite.h b/src/composite.h index a6fc21b48a..3d4aad2a51 100644 --- a/src/composite.h +++ b/src/composite.h @@ -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 m_renderLoops; + QHash m_superlayers; }; class KWIN_EXPORT WaylandCompositor final : public Compositor diff --git a/src/effects.cpp b/src/effects.cpp index a812cc8454..2c75e1cba6 100644 --- a/src/effects.cpp +++ b/src/effects.cpp @@ -1776,7 +1776,10 @@ void EffectsHandlerImpl::slotOutputDisabled(AbstractOutput *output) void EffectsHandlerImpl::renderScreen(EffectScreen *screen) { auto output = static_cast(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 diff --git a/src/renderlayer.cpp b/src/renderlayer.cpp new file mode 100644 index 0000000000..08a9598b20 --- /dev/null +++ b/src/renderlayer.cpp @@ -0,0 +1,276 @@ +/* + SPDX-FileCopyrightText: 2021 Vlad Zahorodnii + + 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::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 ®ion) +{ + 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 ®ion) 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 ®ion) const +{ + if (region.isEmpty()) { + return QRegion(); + } + return region.translated(mapFromGlobal(QPoint(0, 0))); +} + +} // namespace KWin diff --git a/src/renderlayer.h b/src/renderlayer.h new file mode 100644 index 0000000000..db0a3ebcda --- /dev/null +++ b/src/renderlayer.h @@ -0,0 +1,94 @@ +/* + SPDX-FileCopyrightText: 2021 Vlad Zahorodnii + + SPDX-License-Identifier: GPL-2.0-or-later +*/ + +#pragma once + +#include "kwin_export.h" + +#include +#include +#include + +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 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 ®ion) const; + QRect mapToGlobal(const QRect &rect) const; + + QPoint mapFromGlobal(const QPoint &point) const; + QRegion mapFromGlobal(const QRegion ®ion) 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 ®ion); + 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 m_delegate; + QRegion m_repaints; + QRect m_boundingRect; + QRect m_geometry; + OutputLayer *m_outputLayer = nullptr; + RenderLayer *m_superlayer = nullptr; + QList m_sublayers; + bool m_effectiveVisible = true; + bool m_explicitVisible = true; +}; + +} // namespace KWin diff --git a/src/renderlayerdelegate.cpp b/src/renderlayerdelegate.cpp new file mode 100644 index 0000000000..afbb1b3d0b --- /dev/null +++ b/src/renderlayerdelegate.cpp @@ -0,0 +1,40 @@ +/* + SPDX-FileCopyrightText: 2021 Vlad Zahorodnii + + 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 diff --git a/src/renderlayerdelegate.h b/src/renderlayerdelegate.h new file mode 100644 index 0000000000..bd8812324f --- /dev/null +++ b/src/renderlayerdelegate.h @@ -0,0 +1,61 @@ +/* + SPDX-FileCopyrightText: 2021 Vlad Zahorodnii + + SPDX-License-Identifier: GPL-2.0-or-later +*/ + +#pragma once + +#include "kwin_export.h" + +#include +#include + +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 diff --git a/src/scene.cpp b/src/scene.cpp index c65364a583..5259c382bb 100644 --- a/src/scene.cpp +++ b/src/scene.cpp @@ -56,6 +56,7 @@ #include "abstract_output.h" #include "internal_client.h" #include "platform.h" +#include "renderlayer.h" #include "shadowitem.h" #include "surfaceitem.h" #include "unmanaged.h" @@ -81,6 +82,46 @@ 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 //**************************************** @@ -97,8 +138,6 @@ Scene::~Scene() void Scene::initialize() { - connect(kwinApp()->platform(), &Platform::outputDisabled, this, &Scene::removeRepaints); - connect(workspace(), &Workspace::deletedRemoved, this, &Scene::removeToplevel); connect(workspace(), &Workspace::currentActivityChanged, this, &Scene::addRepaintFull); @@ -145,18 +184,11 @@ void Scene::addRepaint(const QRect &rect) void Scene::addRepaint(const QRegion ®ion) { - const QVector outputs = kwinApp()->platform()->enabledOutputs(); - if (kwinApp()->platform()->isPerScreenRenderingEnabled()) { - for (const auto &output : outputs) { - const QRegion dirtyRegion = region & output->geometry(); - if (!dirtyRegion.isEmpty()) { - m_repaints[output] += dirtyRegion; - output->renderLoop()->scheduleRepaint(); - } + for (const auto &delegate : std::as_const(m_delegates)) { + const QRegion dirtyRegion = region & delegate->layer()->geometry(); + if (!dirtyRegion.isEmpty()) { + delegate->layer()->addRepaint(delegate->layer()->mapFromGlobal(dirtyRegion)); } - } 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 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) @@ -238,7 +270,16 @@ SurfaceItem *Scene::scanoutCandidate() const void Scene::prePaint(AbstractOutput *output) { 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() @@ -332,16 +373,6 @@ QRegion Scene::mapToRenderTarget(const QRegion ®ion) const 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 void Scene::paintScreen(const QRegion &damage, const QRegion &repaint, QRegion *updateRegion, QRegion *validRegion) @@ -363,14 +394,6 @@ void Scene::paintScreen(const QRegion &damage, const QRegion &repaint, auto effectsImpl = static_cast(effects); effectsImpl->startPaint(); - if (kwinApp()->platform()->isPerScreenRenderingEnabled()) { - setRenderTargetRect(painted_screen->geometry()); - setRenderTargetScale(painted_screen->scale()); - } else { - setRenderTargetRect(geometry()); - setRenderTargetScale(1); - } - const QRegion displayRegion(renderTargetRect()); QRegion region = damage; diff --git a/src/scene.h b/src/scene.h index 11254fb8f3..af023aeb58 100644 --- a/src/scene.h +++ b/src/scene.h @@ -10,6 +10,7 @@ #ifndef KWIN_SCENE_H #define KWIN_SCENE_H +#include "renderlayerdelegate.h" #include "toplevel.h" #include "utils/common.h" #include "kwineffects.h" @@ -33,6 +34,7 @@ class EffectWindowImpl; class GLTexture; class Item; class RenderLoop; +class Scene; class Shadow; class ShadowItem; class SurfaceItem; @@ -42,13 +44,32 @@ class SurfacePixmapX11; class SurfaceTexture; 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 { Q_OBJECT + public: explicit Scene(QObject *parent = nullptr); - ~Scene() override = 0; + ~Scene() override; class EffectFrame; class Window; @@ -65,23 +86,18 @@ public: QRect geometry() const; void setGeometry(const QRect &rect); - /** - * Returns the repaints region for output with the specified @a output. - */ - QRegion repaints(AbstractOutput *output) const; - void resetRepaints(AbstractOutput *output); + QList delegates() const; + void addDelegate(SceneDelegate *delegate); + void removeDelegate(SceneDelegate *delegate); // Returns true if the ctor failed to properly initialize. virtual bool initFailed() const = 0; SurfaceItem *scanoutCandidate() const; - void prePaint(AbstractOutput *output); void postPaint(); virtual void paint(const QRegion &damage, const QRegion &repaint, QRegion &update, QRegion &valid) = 0; - void paintScreen(AbstractOutput *output); - /** * Adds the Toplevel to the Scene. * @@ -262,12 +278,11 @@ protected: // windows in their stacking order QVector< Window* > stacking_order; private: - void removeRepaints(AbstractOutput *output); void addCursorRepaints(); std::chrono::milliseconds m_expectedPresentTimestamp = std::chrono::milliseconds::zero(); + QList m_delegates; QHash< Toplevel*, Window* > m_windows; - QMap m_repaints; QRect m_geometry; QMatrix4x4 m_renderTargetProjectionMatrix; QRect m_renderTargetRect; diff --git a/src/scenes/qpainter/scene_qpainter.cpp b/src/scenes/qpainter/scene_qpainter.cpp index e2843842b6..a082aed883 100644 --- a/src/scenes/qpainter/scene_qpainter.cpp +++ b/src/scenes/qpainter/scene_qpainter.cpp @@ -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) { - Q_ASSERT(kwinApp()->platform()->isPerScreenRenderingEnabled()); - QImage *buffer = m_backend->bufferForScreen(painted_screen); if (buffer && !buffer->isNull()) { - const QRect geometry = painted_screen->geometry(); m_painter->begin(buffer); - m_painter->setWindow(geometry); - - QRegion updateRegion, validRegion; - paintScreen(damage, repaint, &updateRegion, &validRegion); - paintCursor(painted_screen, updateRegion); - + m_painter->setWindow(painted_screen->geometry()); + paintScreen(damage, repaint, &update, &valid); + paintCursor(painted_screen, update); m_painter->end(); } }