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:
parent
3d828d891c
commit
74391e250e
12 changed files with 149 additions and 73 deletions
|
@ -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;
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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();
|
||||
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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();
|
||||
|
||||
|
|
90
scene.cpp
90
scene.cpp
|
@ -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 ®ion)
|
|||
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 ®ion)
|
|||
// 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 ®ion)
|
||||
{
|
||||
for (int screen = 0; screen < m_repaints.count(); ++screen) {
|
||||
m_repaints[screen] += region;
|
||||
}
|
||||
}
|
||||
|
||||
void Scene::Window::addLayerRepaint(const QRegion ®ion)
|
||||
{
|
||||
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 ®ion)
|
||||
{
|
||||
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
14
scene.h
|
@ -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 ®ion, 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 ®ion);
|
||||
void addLayerRepaint(const QRegion ®ion);
|
||||
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;
|
||||
|
|
79
toplevel.cpp
79
toplevel.cpp
|
@ -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 ®ion)
|
||||
{
|
||||
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 ®ion)
|
||||
{
|
||||
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 ®ion)
|
|||
}
|
||||
}
|
||||
|
||||
bool Toplevel::wantsRepaint() const
|
||||
{
|
||||
if (!effectWindow() || !effectWindow()->sceneWindow()) {
|
||||
return false;
|
||||
}
|
||||
return effectWindow()->sceneWindow()->wantsRepaint();
|
||||
}
|
||||
|
||||
void Toplevel::setReadyForPainting()
|
||||
{
|
||||
if (!ready_for_painting) {
|
||||
|
|
10
toplevel.h
10
toplevel.h
|
@ -467,8 +467,7 @@ public:
|
|||
void addWorkspaceRepaint(const QRect& r);
|
||||
void addWorkspaceRepaint(int x, int y, int w, int h);
|
||||
void addWorkspaceRepaint(const QRegion ®ion);
|
||||
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;
|
||||
|
|
|
@ -165,7 +165,7 @@ bool Unmanaged::isOutline() const
|
|||
|
||||
void Unmanaged::addDamage(const QRegion &damage)
|
||||
{
|
||||
repaints_region += damage;
|
||||
addRepaint(damage);
|
||||
Toplevel::addDamage(damage);
|
||||
}
|
||||
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in a new issue