Store repaint regions per individual screen

AnimationEffect schedules repaints in postPaintWindow() and performs
cleanup in preScreenPaint(). With the X11-style rendering, this doesn't
have any issues, scheduled repaints will be reset during the next
compositing cycle.

But with per screen rendering, we might hit the following case

    - Paint screen 0
    - Reset scheduled repaints
    - AnimationEffect::prePaintScreen(): update the timeline
    - AnimationEffect::postPaintScreen(): schedule a repaint

    - Paint screen 1
    - Reset scheduled repaints
    - AnimationEffect::prePaintScreen(): destroy the animation
    - AnimationEffect::postPaintScreen(): no repaint is scheduled

    - Return to the event loop

In this scenario, the repaint region scheduled by AnimationEffect will
be lost when compositing is performed on screen 1.

There is no any other way to fix this issue but maintain repaint regions
per each individual screen if per screen rendering is enabled.

BUG: 428439
This commit is contained in:
Vlad Zahorodnii 2020-10-29 20:25:39 +02:00
parent 3d828d891c
commit 74391e250e
12 changed files with 149 additions and 73 deletions

View file

@ -729,7 +729,7 @@ template <class T>
static bool repaintsPending(const QList<T*> &windows)
{
return std::any_of(windows.begin(), windows.end(),
[](T *t) { return !t->repaints().isEmpty(); });
[](const T *t) { return t->wantsRepaint(); });
}
bool Compositor::windowRepaintsPending() const
@ -745,16 +745,16 @@ bool Compositor::windowRepaintsPending() const
}
if (auto *server = waylandServer()) {
const auto &clients = server->clients();
auto test = [](AbstractClient *c) {
return c->readyForPainting() && !c->repaints().isEmpty();
auto test = [](const AbstractClient *c) {
return c->readyForPainting() && c->wantsRepaint();
};
if (std::any_of(clients.begin(), clients.end(), test)) {
return true;
}
}
const auto &internalClients = workspace()->internalClients();
auto internalTest = [] (InternalClient *client) {
return client->isShown(true) && !client->repaints().isEmpty();
auto internalTest = [] (const InternalClient *client) {
return client->isShown(true) && client->wantsRepaint();
};
if (std::any_of(internalClients.begin(), internalClients.end(), internalTest)) {
return true;

View file

@ -47,11 +47,6 @@ Deleted::Deleted()
Deleted::~Deleted()
{
const QRegion dirty = repaints();
if (!dirty.isEmpty()) {
addWorkspaceRepaint(dirty);
}
if (delete_refcount != 0)
qCCritical(KWIN_CORE) << "Deleted client has non-zero reference count (" << delete_refcount << ")";
Q_ASSERT(delete_refcount == 0);

View file

@ -350,6 +350,8 @@ SceneOpenGL::SceneOpenGL(OpenGLBackend *backend, QObject *parent)
qCDebug(KWIN_OPENGL) << "Explicit synchronization with the X command stream disabled by environment variable";
}
}
setPerScreenRenderingEnabled(m_backend->perScreenRendering());
}
SceneOpenGL::~SceneOpenGL()
@ -633,6 +635,7 @@ qint64 SceneOpenGL::paint(const QRegion &damage, const QList<Toplevel *> &toplev
// trigger start render timer
m_backend->prepareRenderingFrame();
for (int i = 0; i < screens()->count(); ++i) {
painted_screen = i;
const QRect &geo = screens()->geometry(i);
const qreal scaling = screens()->scale(i);
QRegion update;
@ -663,6 +666,7 @@ qint64 SceneOpenGL::paint(const QRegion &damage, const QList<Toplevel *> &toplev
GLVertexBuffer::streamingBuffer()->framePosted();
}
} else {
painted_screen = -1;
m_backend->makeCurrent();
QRegion repaint = m_backend->prepareRenderingFrame();

View file

@ -55,6 +55,7 @@ SceneQPainter::SceneQPainter(QPainterBackend *backend, QObject *parent)
, m_backend(backend)
, m_painter(new QPainter())
{
setPerScreenRenderingEnabled(m_backend->perScreenRendering());
}
SceneQPainter::~SceneQPainter()
@ -98,6 +99,7 @@ qint64 SceneQPainter::paint(const QRegion &_damage, const QList<Toplevel *> &top
}
QRegion overallUpdate;
for (int i = 0; i < screens()->count(); ++i) {
painted_screen = i;
const QRect geometry = screens()->geometry(i);
QImage *buffer = m_backend->bufferForScreen(i);
if (!buffer || buffer->isNull()) {
@ -118,6 +120,7 @@ qint64 SceneQPainter::paint(const QRegion &_damage, const QList<Toplevel *> &top
m_backend->showOverlay();
m_backend->present(mask, overallUpdate);
} else {
painted_screen = -1;
m_painter->begin(m_backend->buffer());
m_painter->setClipping(true);
m_painter->setClipRegion(damage);

View file

@ -240,6 +240,7 @@ bool SceneXrender::initFailed() const
// the entry point for painting
qint64 SceneXrender::paint(const QRegion &damage, const QList<Toplevel *> &toplevels)
{
painted_screen = -1;
QElapsedTimer renderTimer;
renderTimer.start();

View file

@ -68,8 +68,8 @@
#include "shadow.h"
#include "subsurfacemonitor.h"
#include "wayland_server.h"
#include "thumbnailitem.h"
#include "composite.h"
#include <KWaylandServer/buffer_interface.h>
#include <KWaylandServer/subcompositor_interface.h>
@ -93,6 +93,16 @@ Scene::~Scene()
Q_ASSERT(m_windows.isEmpty());
}
bool Scene::isPerScreenRenderingEnabled() const
{
return m_isPerScreenRenderingEnabled;
}
void Scene::setPerScreenRenderingEnabled(bool enabled)
{
m_isPerScreenRenderingEnabled = enabled;
}
// returns mask and possibly modified region
void Scene::paintScreen(int* mask, const QRegion &damage, const QRegion &repaint,
QRegion *updateRegion, QRegion *validRegion, const QMatrix4x4 &projection, const QRect &outputGeometry, const qreal screenScale)
@ -197,15 +207,13 @@ void Scene::paintGenericScreen(int orig_mask, const ScreenPaintData &)
QVector<Phase2Data> phase2;
phase2.reserve(stacking_order.size());
foreach (Window * w, stacking_order) { // bottom to top
Toplevel* topw = w->window();
// Let the scene window update the window pixmap tree.
w->preprocess();
// Reset the repaint_region.
// This has to be done here because many effects schedule a repaint for
// the next frame within Effects::prePaintWindow.
topw->resetRepaints();
w->resetRepaints(painted_screen);
WindowPrePaintData data;
data.mask = orig_mask | (w->isOpaque() ? PAINT_WINDOW_OPAQUE : PAINT_WINDOW_TRANSLUCENT);
@ -264,7 +272,7 @@ void Scene::paintSimpleScreen(int orig_mask, const QRegion &region)
data.mask = orig_mask | (window->isOpaque() ? PAINT_WINDOW_OPAQUE : PAINT_WINDOW_TRANSLUCENT);
window->resetPaintingEnabled();
data.paint = region;
data.paint |= toplevel->repaints();
data.paint |= window->repaints(painted_screen);
// Let the scene window update the window pixmap tree.
window->preprocess();
@ -272,7 +280,7 @@ void Scene::paintSimpleScreen(int orig_mask, const QRegion &region)
// Reset the repaint_region.
// This has to be done here because many effects schedule a repaint for
// the next frame within Effects::prePaintWindow.
toplevel->resetRepaints();
window->resetRepaints(painted_screen);
// Clip out the decoration for opaque windows; the decoration is drawn in the second pass
opaqueFullscreen = false; // TODO: do we care about unmanged windows here (maybe input windows?)
@ -735,10 +743,22 @@ Scene::Window::Window(Toplevel *client, QObject *parent)
, disable_painting(0)
, cached_quad_list(nullptr)
{
const Scene *scene = Compositor::self()->scene();
if (scene->isPerScreenRenderingEnabled()) {
connect(screens(), &Screens::countChanged, this, &Window::reallocRepaints);
}
reallocRepaints();
}
Scene::Window::~Window()
{
for (int i = 0; i < m_repaints.count(); ++i) {
const QRegion dirty = repaints(i);
if (!dirty.isEmpty()) {
Compositor::self()->addRepaint(dirty);
}
}
delete m_shadow;
}
@ -1109,6 +1129,64 @@ void Scene::Window::preprocess()
}
}
void Scene::Window::addRepaint(const QRegion &region)
{
for (int screen = 0; screen < m_repaints.count(); ++screen) {
m_repaints[screen] += region;
}
}
void Scene::Window::addLayerRepaint(const QRegion &region)
{
for (int screen = 0; screen < m_layerRepaints.count(); ++screen) {
m_layerRepaints[screen] += region;
}
}
QRegion Scene::Window::repaints(int screen) const
{
Q_ASSERT(!m_repaints.isEmpty() && !m_layerRepaints.isEmpty());
const int index = screen != -1 ? screen : 0;
if (m_repaints[index] == infiniteRegion() || m_layerRepaints[index] == infiniteRegion()) {
return QRect(QPoint(0, 0), screens()->size());
}
return m_repaints[index].translated(pos()) + m_layerRepaints[index];
}
void Scene::Window::resetRepaints(int screen)
{
Q_ASSERT(!m_repaints.isEmpty() && !m_layerRepaints.isEmpty());
const int index = screen != -1 ? screen : 0;
m_repaints[index] = QRegion();
m_layerRepaints[index] = QRegion();
}
void Scene::Window::reallocRepaints()
{
const Scene *scene = Compositor::self()->scene();
if (scene->isPerScreenRenderingEnabled()) {
m_repaints.resize(screens()->count());
m_layerRepaints.resize(screens()->count());
} else {
m_repaints.resize(1);
m_layerRepaints.resize(1);
}
m_repaints.fill(infiniteRegion());
m_layerRepaints.fill(infiniteRegion());
}
static bool wantsRepaint_test(const QRegion &region)
{
return !region.isEmpty();
}
bool Scene::Window::wantsRepaint() const
{
return std::any_of(m_repaints.begin(), m_repaints.end(), wantsRepaint_test) ||
std::any_of(m_layerRepaints.begin(), m_layerRepaints.end(), wantsRepaint_test);
}
//****************************************
// WindowPixmap
//****************************************

14
scene.h
View file

@ -136,6 +136,7 @@ public:
virtual bool blocksForRetrace() const;
virtual bool syncsToVBlank() const;
virtual OverlayWindow* overlayWindow() const = 0;
bool isPerScreenRenderingEnabled() const;
virtual bool makeOpenGLContextCurrent();
virtual void doneOpenGLContextCurrent();
@ -202,6 +203,7 @@ public Q_SLOTS:
void windowClosed(KWin::Toplevel* c, KWin::Deleted* deleted);
protected:
virtual Window *createWindow(Toplevel *toplevel) = 0;
void setPerScreenRenderingEnabled(bool enabled);
void createStackingOrder(const QList<Toplevel *> &toplevels);
void clearStackingOrder();
// shared implementation, starts painting the screen
@ -262,6 +264,8 @@ protected:
// time since last repaint
int time_diff;
QElapsedTimer last_time;
// The screen that is being currently painted
int painted_screen = -1;
private:
void paintWindowThumbnails(Scene::Window *w, const QRegion &region, qreal opacity, qreal brightness, qreal saturation);
void paintDesktopThumbnails(Scene::Window *w);
@ -270,6 +274,7 @@ private:
QVector< Window* > stacking_order;
// how many times finalPaintScreen() has been called
int m_paintScreenCount = 0;
bool m_isPerScreenRenderingEnabled = false;
};
/**
@ -354,6 +359,11 @@ public:
void unreferencePreviousPixmap();
void discardQuads();
void preprocess();
void addRepaint(const QRegion &region);
void addLayerRepaint(const QRegion &region);
QRegion repaints(int screen) const;
void resetRepaints(int screen);
bool wantsRepaint() const;
virtual QSharedPointer<GLTexture> windowTexture() {
return {};
@ -391,8 +401,12 @@ protected:
ImageFilterType filter;
Shadow *m_shadow;
private:
void reallocRepaints();
QScopedPointer<WindowPixmap> m_currentPixmap;
QScopedPointer<WindowPixmap> m_previousPixmap;
QVector<QRegion> m_repaints;
QVector<QRegion> m_layerRepaints;
int m_referencePixmapCounter;
int disable_painting;
mutable QRegion m_bufferShape;

View file

@ -113,8 +113,6 @@ void Toplevel::copyToDeleted(Toplevel* c)
ready_for_painting = c->ready_for_painting;
damage_handle = XCB_NONE;
damage_region = c->damage_region;
repaints_region = c->repaints_region;
layer_repaints_region = c->layer_repaints_region;
is_shape = c->is_shape;
effect_window = c->effect_window;
if (effect_window != nullptr)
@ -311,7 +309,6 @@ void Toplevel::finishCompositing(ReleaseReason releaseReason)
damage_handle = XCB_NONE;
damage_region = QRegion();
repaints_region = QRegion();
effect_window = nullptr;
}
@ -404,7 +401,7 @@ void Toplevel::getDamageRegionReply()
const QRect frameRect = frameGeometry();
damage_region += region;
repaints_region += region.translated(bufferRect.topLeft() - frameRect.topLeft());
addRepaint(region.translated(bufferRect.topLeft() - frameRect.topLeft()));
free(reply);
}
@ -423,7 +420,7 @@ void Toplevel::addDamageFull()
const QRect damagedRect(0, 0, bufferRect.width(), bufferRect.height());
damage_region = damagedRect;
repaints_region |= damagedRect.translated(offsetX, offsetY);
addRepaint(damagedRect.translated(offsetX, offsetY));
emit damaged(this, damage_region);
}
@ -433,63 +430,47 @@ void Toplevel::resetDamage()
damage_region = QRegion();
}
void Toplevel::addRepaint(const QRect& r)
void Toplevel::addRepaint(const QRect &rect)
{
if (!compositing()) {
addRepaint(QRegion(rect));
}
void Toplevel::addRepaint(int x, int y, int width, int height)
{
addRepaint(QRegion(x, y, width, height));
}
void Toplevel::addRepaint(const QRegion &region)
{
if (!effectWindow() || !effectWindow()->sceneWindow()) {
return;
}
repaints_region += r;
effectWindow()->sceneWindow()->addRepaint(region);
emit needsRepaint();
}
void Toplevel::addRepaint(int x, int y, int w, int h)
void Toplevel::addLayerRepaint(const QRect &rect)
{
QRect r(x, y, w, h);
addRepaint(r);
addLayerRepaint(QRegion(rect));
}
void Toplevel::addRepaint(const QRegion& r)
void Toplevel::addLayerRepaint(int x, int y, int width, int height)
{
if (!compositing()) {
addLayerRepaint(QRegion(x, y, width, height));
}
void Toplevel::addLayerRepaint(const QRegion &region)
{
if (!effectWindow() || !effectWindow()->sceneWindow()) {
return;
}
repaints_region += r;
emit needsRepaint();
}
void Toplevel::addLayerRepaint(const QRect& r)
{
if (!compositing()) {
return;
}
layer_repaints_region += r;
emit needsRepaint();
}
void Toplevel::addLayerRepaint(int x, int y, int w, int h)
{
QRect r(x, y, w, h);
addLayerRepaint(r);
}
void Toplevel::addLayerRepaint(const QRegion& r)
{
if (!compositing())
return;
layer_repaints_region += r;
effectWindow()->sceneWindow()->addLayerRepaint(region);
emit needsRepaint();
}
void Toplevel::addRepaintFull()
{
repaints_region = visibleRect().translated(-pos());
emit needsRepaint();
}
void Toplevel::resetRepaints()
{
repaints_region = QRegion();
layer_repaints_region = QRegion();
addRepaint(visibleRect().translated(-pos()));
}
void Toplevel::addWorkspaceRepaint(int x, int y, int w, int h)
@ -511,6 +492,14 @@ void Toplevel::addWorkspaceRepaint(const QRegion &region)
}
}
bool Toplevel::wantsRepaint() const
{
if (!effectWindow() || !effectWindow()->sceneWindow()) {
return false;
}
return effectWindow()->sceneWindow()->wantsRepaint();
}
void Toplevel::setReadyForPainting()
{
if (!ready_for_painting) {

View file

@ -467,8 +467,7 @@ public:
void addWorkspaceRepaint(const QRect& r);
void addWorkspaceRepaint(int x, int y, int w, int h);
void addWorkspaceRepaint(const QRegion &region);
QRegion repaints() const;
void resetRepaints();
bool wantsRepaint() const;
QRegion damage() const;
void resetDamage();
EffectWindowImpl* effectWindow();
@ -734,8 +733,6 @@ protected:
int bit_depth;
NETWinInfo* info;
bool ready_for_painting;
QRegion repaints_region; // updating, repaint just requires repaint of that area
QRegion layer_repaints_region;
/**
* An FBO object KWin internal windows might render to.
*/
@ -939,11 +936,6 @@ inline QRegion Toplevel::damage() const
return damage_region;
}
inline QRegion Toplevel::repaints() const
{
return repaints_region.translated(pos()) | layer_repaints_region;
}
inline bool Toplevel::shape() const
{
return is_shape;

View file

@ -165,7 +165,7 @@ bool Unmanaged::isOutline() const
void Unmanaged::addDamage(const QRegion &damage)
{
repaints_region += damage;
addRepaint(damage);
Toplevel::addDamage(damage);
}

View file

@ -2792,7 +2792,7 @@ void X11Client::addDamage(const QRegion &damage)
setupWindowManagementInterface();
}
}
repaints_region += damage.translated(bufferGeometry().topLeft() - frameGeometry().topLeft());
addRepaint(damage.translated(bufferGeometry().topLeft() - frameGeometry().topLeft()));
Toplevel::addDamage(damage);
}

View file

@ -322,7 +322,7 @@ void XdgSurfaceClient::addDamage(const QRegion &damage)
{
const int offsetX = bufferGeometry().x() - frameGeometry().x();
const int offsetY = bufferGeometry().y() - frameGeometry().y();
repaints_region += damage.translated(offsetX, offsetY);
addRepaint(damage.translated(offsetX, offsetY));
Toplevel::addDamage(damage);
}