Avoid painting unchanged scene layers
Currently when we move the mouse the one render loop triggers a repaint. When the cursor layer needs a new update we end up in the compositor repainting the main content. Even though painting should mostly no-op it still goes through all existing items and effects to collect damage, still potentially making the GL context current which could stall. A waste when we know we haven't got anything to do. It's enough to cause noticable mouse lag on some hardware. Co-authored-by: Vlad Zahorodnii <vlad.zahorodnii@kde.org>
This commit is contained in:
parent
fe1d4ffbc5
commit
10ed34bc9d
14 changed files with 142 additions and 56 deletions
|
@ -404,7 +404,8 @@ void DrmTest::testModeset()
|
|||
const auto output = gpu->drmOutputs().front();
|
||||
const auto layer = renderBackend->primaryLayer(output);
|
||||
layer->beginFrame();
|
||||
output->renderLoop()->beginFrame();
|
||||
output->renderLoop()->prepareNewFrame();
|
||||
output->renderLoop()->beginPaint();
|
||||
layer->endFrame(infiniteRegion(), infiniteRegion());
|
||||
QVERIFY(gpu->drmOutputs().front()->present());
|
||||
|
||||
|
|
|
@ -31,11 +31,9 @@
|
|||
#include "scene/surfaceitem_x11.h"
|
||||
#include "scene/workspacescene_opengl.h"
|
||||
#include "scene/workspacescene_qpainter.h"
|
||||
#include "shadow.h"
|
||||
#include "useractions.h"
|
||||
#include "utils/common.h"
|
||||
#include "utils/xcbutils.h"
|
||||
#include "wayland/surface_interface.h"
|
||||
#include "wayland_server.h"
|
||||
#include "workspace.h"
|
||||
#include "x11syncmanager.h"
|
||||
|
@ -685,44 +683,51 @@ void Compositor::composite(RenderLoop *renderLoop)
|
|||
fTraceDuration("Paint (", output->name(), ")");
|
||||
|
||||
RenderLayer *superLayer = m_superlayers[renderLoop];
|
||||
prePaintPass(superLayer);
|
||||
superLayer->setOutputLayer(primaryLayer);
|
||||
|
||||
SurfaceItem *scanoutCandidate = superLayer->delegate()->scanoutCandidate();
|
||||
renderLoop->setFullscreenSurface(scanoutCandidate);
|
||||
output->setContentType(scanoutCandidate ? scanoutCandidate->contentType() : ContentType::None);
|
||||
renderLoop->prepareNewFrame();
|
||||
|
||||
renderLoop->beginFrame();
|
||||
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 (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 (!directScanout) {
|
||||
QRegion surfaceDamage = primaryLayer->repaints();
|
||||
primaryLayer->resetRepaints();
|
||||
preparePaintPass(superLayer, &surfaceDamage);
|
||||
|
||||
if (auto beginInfo = primaryLayer->beginFrame()) {
|
||||
auto &[renderTarget, repaint] = beginInfo.value();
|
||||
if (auto beginInfo = primaryLayer->beginFrame()) {
|
||||
auto &[renderTarget, repaint] = beginInfo.value();
|
||||
|
||||
const QRegion bufferDamage = surfaceDamage.united(repaint).intersected(superLayer->rect().toAlignedRect());
|
||||
const QRegion bufferDamage = surfaceDamage.united(repaint).intersected(superLayer->rect().toAlignedRect());
|
||||
|
||||
paintPass(superLayer, renderTarget, bufferDamage);
|
||||
primaryLayer->endFrame(bufferDamage, surfaceDamage);
|
||||
paintPass(superLayer, renderTarget, bufferDamage);
|
||||
primaryLayer->endFrame(bufferDamage, surfaceDamage);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
postPaintPass(superLayer);
|
||||
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 =
|
||||
|
@ -737,6 +742,15 @@ void Compositor::composite(RenderLoop *renderLoop)
|
|||
}
|
||||
}
|
||||
|
||||
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();
|
||||
|
|
|
@ -203,6 +203,7 @@ private:
|
|||
void postPaintPass(RenderLayer *layer);
|
||||
void preparePaintPass(RenderLayer *layer, QRegion *repaint);
|
||||
void paintPass(RenderLayer *layer, const RenderTarget &renderTarget, const QRegion ®ion);
|
||||
void framePass(RenderLayer *layer);
|
||||
|
||||
State m_state = State::Off;
|
||||
std::unique_ptr<CompositorSelectionOwner> m_selectionOwner;
|
||||
|
|
|
@ -134,6 +134,25 @@ void RenderLayer::setGeometry(const QRectF &geometry)
|
|||
}
|
||||
}
|
||||
|
||||
void RenderLayer::scheduleRepaint(Item *item)
|
||||
{
|
||||
m_repaintScheduled = true;
|
||||
m_loop->scheduleRepaint(item);
|
||||
}
|
||||
|
||||
bool RenderLayer::needsRepaint() const
|
||||
{
|
||||
if (m_repaintScheduled) {
|
||||
return true;
|
||||
}
|
||||
if (!m_repaints.isEmpty()) {
|
||||
return true;
|
||||
}
|
||||
return std::any_of(m_sublayers.constBegin(), m_sublayers.constEnd(), [](RenderLayer *layer) {
|
||||
return layer->needsRepaint();
|
||||
});
|
||||
}
|
||||
|
||||
void RenderLayer::updateBoundingRect()
|
||||
{
|
||||
QRectF boundingRect = rect();
|
||||
|
@ -183,6 +202,7 @@ QRegion RenderLayer::repaints() const
|
|||
void RenderLayer::resetRepaints()
|
||||
{
|
||||
m_repaints = QRegion();
|
||||
m_repaintScheduled = false;
|
||||
}
|
||||
|
||||
bool RenderLayer::isVisible() const
|
||||
|
|
|
@ -21,6 +21,7 @@ namespace KWin
|
|||
class OutputLayer;
|
||||
class RenderLayerDelegate;
|
||||
class RenderLoop;
|
||||
class Item;
|
||||
|
||||
/**
|
||||
* The RenderLayer class represents a composited layer.
|
||||
|
@ -72,6 +73,16 @@ public:
|
|||
QRectF geometry() const;
|
||||
void setGeometry(const QRectF &rect);
|
||||
|
||||
/**
|
||||
* Mark this layer as dirty and trigger a repaint
|
||||
* on the render loop
|
||||
*/
|
||||
void scheduleRepaint(Item *item);
|
||||
/**
|
||||
* Returns true if this or a sublayer has requested an update
|
||||
*/
|
||||
bool needsRepaint() const;
|
||||
|
||||
void addRepaint(const QRegion ®ion);
|
||||
void addRepaint(const QRect &rect);
|
||||
void addRepaint(int x, int y, int width, int height);
|
||||
|
@ -87,6 +98,7 @@ private:
|
|||
bool computeEffectiveVisibility() const;
|
||||
|
||||
RenderLoop *m_loop;
|
||||
bool m_repaintScheduled = false;
|
||||
std::unique_ptr<RenderLayerDelegate> m_delegate;
|
||||
QRegion m_repaints;
|
||||
QRectF m_boundingRect;
|
||||
|
|
|
@ -24,6 +24,10 @@ QRegion RenderLayerDelegate::repaints() const
|
|||
return QRegion();
|
||||
}
|
||||
|
||||
void RenderLayerDelegate::frame()
|
||||
{
|
||||
}
|
||||
|
||||
void RenderLayerDelegate::prePaint()
|
||||
{
|
||||
}
|
||||
|
|
|
@ -35,13 +35,18 @@ public:
|
|||
virtual QRegion repaints() const;
|
||||
|
||||
/**
|
||||
* This function is called by the compositor before starting compositing. Reimplement
|
||||
* This function is called by the compositor after compositing the frame.
|
||||
*/
|
||||
virtual void frame();
|
||||
|
||||
/**
|
||||
* This function is called by the compositor before starting painting. Reimplement
|
||||
* this function to do frame initialization.
|
||||
*/
|
||||
virtual void prePaint();
|
||||
|
||||
/**
|
||||
* This function is called by the compositor after finishing compositing. Reimplement
|
||||
* This function is called by the compositor after finishing painting. Reimplement
|
||||
* this function to do post frame cleanup.
|
||||
*/
|
||||
virtual void postPaint();
|
||||
|
|
|
@ -198,10 +198,14 @@ void RenderLoop::uninhibit()
|
|||
}
|
||||
}
|
||||
|
||||
void RenderLoop::beginFrame()
|
||||
void RenderLoop::prepareNewFrame()
|
||||
{
|
||||
d->pendingFrameCount++;
|
||||
}
|
||||
|
||||
void RenderLoop::beginPaint()
|
||||
{
|
||||
d->pendingRepaint = false;
|
||||
d->pendingFrameCount++;
|
||||
}
|
||||
|
||||
int RenderLoop::refreshRate() const
|
||||
|
|
|
@ -46,11 +46,17 @@ public:
|
|||
*/
|
||||
void uninhibit();
|
||||
|
||||
/**
|
||||
* This function must be called before the Compositor sumbits the next
|
||||
* frame.
|
||||
*/
|
||||
void prepareNewFrame();
|
||||
|
||||
/**
|
||||
* This function must be called before the Compositor starts rendering the next
|
||||
* frame.
|
||||
*/
|
||||
void beginFrame();
|
||||
void beginPaint();
|
||||
|
||||
/**
|
||||
* Returns the refresh rate at which the output is being updated, in millihertz.
|
||||
|
|
|
@ -6,7 +6,6 @@
|
|||
|
||||
#include "scene/item.h"
|
||||
#include "core/renderlayer.h"
|
||||
#include "core/renderloop.h"
|
||||
#include "scene/scene.h"
|
||||
#include "utils/common.h"
|
||||
|
||||
|
@ -310,7 +309,7 @@ void Item::scheduleRepaintInternal(const QRegion ®ion)
|
|||
const QRegion dirtyRegion = globalRegion & delegate->viewport();
|
||||
if (!dirtyRegion.isEmpty()) {
|
||||
m_repaints[delegate] += dirtyRegion;
|
||||
delegate->layer()->loop()->scheduleRepaint(this);
|
||||
delegate->layer()->scheduleRepaint(this);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -321,7 +320,7 @@ void Item::scheduleRepaintInternal(SceneDelegate *delegate, const QRegion ®io
|
|||
const QRegion dirtyRegion = globalRegion & delegate->viewport();
|
||||
if (!dirtyRegion.isEmpty()) {
|
||||
m_repaints[delegate] += dirtyRegion;
|
||||
delegate->layer()->loop()->scheduleRepaint(this);
|
||||
delegate->layer()->scheduleRepaint(this);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -334,7 +333,7 @@ void Item::scheduleFrame()
|
|||
const QList<SceneDelegate *> delegates = m_scene->delegates();
|
||||
for (SceneDelegate *delegate : delegates) {
|
||||
if (delegate->viewport().intersects(geometry)) {
|
||||
delegate->layer()->loop()->scheduleRepaint(this);
|
||||
delegate->layer()->scheduleRepaint(this);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -49,6 +49,11 @@ void SceneDelegate::paint(const RenderTarget &renderTarget, const QRegion ®io
|
|||
m_scene->paint(renderTarget, region == infiniteRegion() ? infiniteRegion() : region.translated(viewport().topLeft()));
|
||||
}
|
||||
|
||||
void SceneDelegate::frame()
|
||||
{
|
||||
m_scene->frame(this);
|
||||
}
|
||||
|
||||
Output *SceneDelegate::output() const
|
||||
{
|
||||
return m_output;
|
||||
|
@ -139,6 +144,10 @@ SurfaceItem *Scene::scanoutCandidate() const
|
|||
return nullptr;
|
||||
}
|
||||
|
||||
void Scene::frame(SceneDelegate *delegate)
|
||||
{
|
||||
}
|
||||
|
||||
} // namespace KWin
|
||||
|
||||
#include "moc_scene.cpp"
|
||||
|
|
|
@ -31,6 +31,7 @@ public:
|
|||
|
||||
QRegion repaints() const override;
|
||||
SurfaceItem *scanoutCandidate() const override;
|
||||
void frame() override;
|
||||
void prePaint() override;
|
||||
void postPaint() override;
|
||||
void paint(const RenderTarget &renderTarget, const QRegion ®ion) override;
|
||||
|
@ -85,6 +86,7 @@ public:
|
|||
virtual void prePaint(SceneDelegate *delegate) = 0;
|
||||
virtual void postPaint() = 0;
|
||||
virtual void paint(const RenderTarget &renderTarget, const QRegion ®ion) = 0;
|
||||
virtual void frame(SceneDelegate *delegate);
|
||||
|
||||
Q_SIGNALS:
|
||||
void delegateRemoved(SceneDelegate *delegate);
|
||||
|
|
|
@ -184,6 +184,33 @@ SurfaceItem *WorkspaceScene::scanoutCandidate() const
|
|||
return candidate;
|
||||
}
|
||||
|
||||
void WorkspaceScene::frame(SceneDelegate *delegate)
|
||||
{
|
||||
if (waylandServer()) {
|
||||
Output *output = delegate->output();
|
||||
const std::chrono::milliseconds frameTime =
|
||||
std::chrono::duration_cast<std::chrono::milliseconds>(output->renderLoop()->lastPresentationTimestamp());
|
||||
|
||||
const QList<Item *> items = m_containerItem->sortedChildItems();
|
||||
for (Item *item : items) {
|
||||
if (!item->isVisible()) {
|
||||
continue;
|
||||
}
|
||||
Window *window = static_cast<WindowItem *>(item)->window();
|
||||
if (!window->isOnOutput(output)) {
|
||||
continue;
|
||||
}
|
||||
if (auto surface = window->surface()) {
|
||||
surface->frameRendered(frameTime.count());
|
||||
}
|
||||
}
|
||||
|
||||
if (m_dndIcon) {
|
||||
m_dndIcon->frameRendered(frameTime.count());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void WorkspaceScene::prePaint(SceneDelegate *delegate)
|
||||
{
|
||||
createStackingOrder();
|
||||
|
@ -320,25 +347,6 @@ void WorkspaceScene::preparePaintSimpleScreen()
|
|||
|
||||
void WorkspaceScene::postPaint()
|
||||
{
|
||||
if (waylandServer()) {
|
||||
const std::chrono::milliseconds frameTime =
|
||||
std::chrono::duration_cast<std::chrono::milliseconds>(painted_screen->renderLoop()->lastPresentationTimestamp());
|
||||
|
||||
for (WindowItem *windowItem : std::as_const(stacking_order)) {
|
||||
Window *window = windowItem->window();
|
||||
if (!window->isOnOutput(painted_screen)) {
|
||||
continue;
|
||||
}
|
||||
if (auto surface = window->surface()) {
|
||||
surface->frameRendered(frameTime.count());
|
||||
}
|
||||
}
|
||||
|
||||
if (m_dndIcon) {
|
||||
m_dndIcon->frameRendered(frameTime.count());
|
||||
}
|
||||
}
|
||||
|
||||
for (WindowItem *w : std::as_const(stacking_order)) {
|
||||
effects->postPaintWindow(w->window()->effectWindow());
|
||||
}
|
||||
|
|
|
@ -60,6 +60,7 @@ public:
|
|||
void prePaint(SceneDelegate *delegate) override;
|
||||
void postPaint() override;
|
||||
void paint(const RenderTarget &renderTarget, const QRegion ®ion) override;
|
||||
void frame(SceneDelegate *delegate) override;
|
||||
|
||||
virtual bool makeOpenGLContextCurrent();
|
||||
virtual void doneOpenGLContextCurrent();
|
||||
|
|
Loading…
Reference in a new issue