scene: Rework surface damage tracking

It's not possible to get the surface damage before calling
Scene::paint(), which is a big problem because it blocks proper surface
damage and buffer damage calculation when walking render layer tree.

This change reworks the scene compositing stages to allow getting the
next surface damage before calling Scene::paint().

The main challenge is that the effects can expand the surface damage. We
have to call prePaintWindow() and prePaintScreen() before actually
starting painting. However, prePaintWindow() is called after starting
rendering.

This change makes Scene call prePaintWindow() and prePaintScreen() so
it's possible to know the surface damage beforehand. Unfortunately, it's
also a breaking change. Some fullscreen effects will have to adapt to
the new Scene paint order. Paint hooks will be invoked in the following
order:

* prePaintScreen() once per frame
* prePaintWindow() once per frame
* paintScreen() can be called multiple times
* paintWindow() can be called as many times as paintScreen()
* postPaintWindow() once per frame
* postPaintScreen() once per frame

After walking the render layer tree, the Compositor will poke the render
backend for the back buffer repair region and combine it with the
surface damage to get the buffer damage, which can be passed to the
render backend (in order to optimize performance with tiled gpus) and
Scene::paint(), which will determine what parts of the scene have to
repainted based on the buffer damage.
This commit is contained in:
Vlad Zahorodnii 2022-02-16 19:13:57 +02:00
parent d4c44220b4
commit aac0609bb9
17 changed files with 237 additions and 374 deletions

View file

@ -666,14 +666,15 @@ void Compositor::composite(RenderLoop *renderLoop)
if (directScanout) {
renderLoop->endFrame();
} else {
QRegion repaint = outputLayer->repaints();
QRegion surfaceDamage = outputLayer->repaints();
outputLayer->resetRepaints();
preparePaintPass(superLayer, &repaint);
preparePaintPass(superLayer, &surfaceDamage);
QRegion surfaceDamage;
QRegion bufferDamage;
const QRegion repair = m_backend->beginFrame(output);
paintPass(superLayer, repaint, repair, &surfaceDamage, &bufferDamage);
const QRegion bufferDamage = surfaceDamage.united(repair);
m_backend->aboutToStartPainting(output, bufferDamage);
paintPass(superLayer, bufferDamage);
renderLoop->endFrame();
m_backend->endFrame(output, bufferDamage, surfaceDamage);
}
@ -715,7 +716,7 @@ void Compositor::postPaintPass(RenderLayer *layer)
void Compositor::preparePaintPass(RenderLayer *layer, QRegion *repaint)
{
// TODO: Cull opaque region.
*repaint += layer->mapToGlobal(layer->repaints());
*repaint += layer->mapToGlobal(layer->repaints() + layer->delegate()->repaints());
layer->resetRepaints();
const auto sublayers = layer->sublayers();
for (RenderLayer *sublayer : sublayers) {
@ -725,19 +726,14 @@ void Compositor::preparePaintPass(RenderLayer *layer, QRegion *repaint)
}
}
void Compositor::paintPass(RenderLayer *layer, const QRegion &repaint, const QRegion &repair, QRegion *surfaceDamage, QRegion *bufferDamage)
void Compositor::paintPass(RenderLayer *layer, const QRegion &region)
{
QRegion localSurfaceDamage;
QRegion localBufferDamage;
layer->delegate()->paint(repaint, repair, localSurfaceDamage, localBufferDamage);
*surfaceDamage += localSurfaceDamage;
*bufferDamage += localBufferDamage;
layer->delegate()->paint(region);
const auto sublayers = layer->sublayers();
for (RenderLayer *sublayer : sublayers) {
if (sublayer->isVisible()) {
paintPass(sublayer, repaint, repair, surfaceDamage, bufferDamage);
paintPass(sublayer, region);
}
}
}

View file

@ -141,7 +141,7 @@ private:
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);
void paintPass(RenderLayer *layer, const QRegion &region);
State m_state = State::Off;
CompositorSelectionOwner *m_selectionOwner = nullptr;

View file

@ -23,17 +23,11 @@ CursorDelegate::CursorDelegate(AbstractOutput *output, CursorView *view)
{
}
void CursorDelegate::paint(const QRegion &damage, const QRegion &repaint, QRegion &update, QRegion &valid)
void CursorDelegate::paint(const QRegion &region)
{
const Cursor *cursor = Cursors::self()->currentCursor();
const QRegion cursorDamage = damage.intersected(cursor->geometry());
const QRegion cursorRepaint = repaint.intersected(cursor->geometry());
update = cursorDamage;
valid = cursorDamage.united(cursorRepaint);
if (!valid.isEmpty()) {
m_view->paint(m_output, valid);
if (region.intersects(cursor->geometry())) {
m_view->paint(m_output, region);
}
}

View file

@ -30,7 +30,7 @@ class KWIN_EXPORT CursorDelegate : public RenderLayerDelegate
public:
CursorDelegate(AbstractOutput *output, CursorView *view);
void paint(const QRegion &damage, const QRegion &repaint, QRegion &update, QRegion &valid) override;
void paint(const QRegion &region) override;
private:
CursorView *m_view;

View file

@ -1797,8 +1797,7 @@ void EffectsHandlerImpl::renderScreen(EffectScreen *screen)
{
auto output = static_cast<EffectScreenImpl *>(screen)->platformOutput();
m_scene->prePaint(output);
QRegion update, valid;
m_scene->paint(output->geometry(), QRect(), update, valid);
m_scene->paint(output->geometry());
m_scene->postPaint();
}

View file

@ -62,12 +62,6 @@ QSharedPointer<KWin::GLTexture> OpenGLBackend::textureForOutput(AbstractOutput*
return {};
}
void OpenGLBackend::aboutToStartPainting(AbstractOutput *output, const QRegion &damage)
{
Q_UNUSED(output)
Q_UNUSED(damage)
}
SurfaceTexture *OpenGLBackend::createSurfaceTextureInternal(SurfacePixmapInternal *pixmap)
{
Q_UNUSED(pixmap)

View file

@ -55,12 +55,6 @@ public:
virtual SurfaceTexture *createSurfaceTextureX11(SurfacePixmapX11 *pixmap);
virtual SurfaceTexture *createSurfaceTextureWayland(SurfacePixmapWayland *pixmap);
/**
* Notifies about starting to paint.
*
* @p damage contains the reported damage as suggested by windows and effects on prepaint calls.
*/
virtual void aboutToStartPainting(AbstractOutput *output, const QRegion &damage);
virtual bool makeCurrent() = 0;
virtual void doneCurrent() = 0;

View file

@ -24,6 +24,12 @@ bool RenderBackend::checkGraphicsReset()
return false;
}
void RenderBackend::aboutToStartPainting(AbstractOutput *output, const QRegion &damage)
{
Q_UNUSED(output)
Q_UNUSED(damage)
}
bool RenderBackend::scanout(AbstractOutput *output, SurfaceItem *surfaceItem)
{
Q_UNUSED(output)

View file

@ -32,6 +32,13 @@ public:
virtual bool checkGraphicsReset();
/**
* Notifies about starting to paint.
*
* @p damage contains the reported damage as suggested by windows and effects on prepaint calls.
*/
virtual void aboutToStartPainting(AbstractOutput *output, const QRegion &damage);
virtual QRegion beginFrame(AbstractOutput *output) = 0;
virtual void endFrame(AbstractOutput *output, const QRegion &renderedRegion, const QRegion &damagedRegion) = 0;

View file

@ -24,6 +24,11 @@ void RenderLayerDelegate::setLayer(RenderLayer *layer)
m_layer = layer;
}
QRegion RenderLayerDelegate::repaints() const
{
return QRegion();
}
void RenderLayerDelegate::prePaint()
{
}

View file

@ -30,6 +30,11 @@ public:
RenderLayer *layer() const;
void setLayer(RenderLayer *layer);
/**
* Returns the repaints schduled for the next frame.
*/
virtual QRegion repaints() const;
/**
* This function is called by the compositor before starting compositing. Reimplement
* this function to do frame initialization.
@ -52,7 +57,7 @@ public:
* 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;
virtual void paint(const QRegion &region) = 0;
private:
RenderLayer *m_layer = nullptr;

View file

@ -102,6 +102,11 @@ SceneDelegate::~SceneDelegate()
m_scene->removeDelegate(this);
}
QRegion SceneDelegate::repaints() const
{
return m_scene->damage();
}
SurfaceItem *SceneDelegate::scanoutCandidate() const
{
return m_scene->scanoutCandidate();
@ -117,9 +122,9 @@ void SceneDelegate::postPaint()
m_scene->postPaint();
}
void SceneDelegate::paint(const QRegion &damage, const QRegion &repaint, QRegion &update, QRegion &valid)
void SceneDelegate::paint(const QRegion &region)
{
m_scene->paint(damage, repaint, update, valid);
m_scene->paint(region);
}
//****************************************
@ -175,6 +180,11 @@ void Scene::addRepaint(const QRegion &region)
}
}
QRegion Scene::damage() const
{
return m_paintContext.damage;
}
QRect Scene::geometry() const
{
return m_geometry;
@ -266,10 +276,151 @@ void Scene::prePaint(AbstractOutput *output)
setRenderTargetRect(painted_screen->geometry());
setRenderTargetScale(painted_screen->scale());
}
const RenderLoop *renderLoop = painted_screen->renderLoop();
const std::chrono::milliseconds presentTime =
std::chrono::duration_cast<std::chrono::milliseconds>(renderLoop->nextPresentationTimestamp());
if (Q_UNLIKELY(presentTime < m_expectedPresentTimestamp)) {
qCDebug(KWIN_CORE,
"Provided presentation timestamp is invalid: %lld (current: %lld)",
static_cast<long long>(presentTime.count()),
static_cast<long long>(m_expectedPresentTimestamp.count()));
} else {
m_expectedPresentTimestamp = presentTime;
}
// preparation step
auto effectsImpl = static_cast<EffectsHandlerImpl *>(effects);
effectsImpl->startPaint();
ScreenPrePaintData prePaintData;
prePaintData.mask = 0;
prePaintData.screen = EffectScreenImpl::get(painted_screen);
effects->prePaintScreen(prePaintData, m_expectedPresentTimestamp);
m_paintContext.damage = prePaintData.paint;
m_paintContext.mask = prePaintData.mask;
m_paintContext.phase2Data.clear();
if (m_paintContext.mask & (PAINT_SCREEN_TRANSFORMED | PAINT_SCREEN_WITH_TRANSFORMED_WINDOWS)) {
preparePaintGenericScreen();
} else {
preparePaintSimpleScreen();
}
}
static void resetRepaintsHelper(Item *item, AbstractOutput *output)
{
item->resetRepaints(output);
const auto childItems = item->childItems();
for (Item *childItem : childItems) {
resetRepaintsHelper(childItem, output);
}
}
static void accumulateRepaints(Item *item, AbstractOutput *output, QRegion *repaints)
{
*repaints += item->repaints(output);
item->resetRepaints(output);
const auto childItems = item->childItems();
for (Item *childItem : childItems) {
accumulateRepaints(childItem, output, repaints);
}
}
void Scene::preparePaintGenericScreen()
{
for (Window *sceneWindow : std::as_const(stacking_order)) {
resetRepaintsHelper(sceneWindow->windowItem(), painted_screen);
WindowPrePaintData data;
data.mask = m_paintContext.mask | (sceneWindow->isOpaque() ? PAINT_WINDOW_OPAQUE : PAINT_WINDOW_TRANSLUCENT);
data.paint = infiniteRegion(); // no clipping, so doesn't really matter
sceneWindow->resetPaintingEnabled();
effects->prePaintWindow(effectWindow(sceneWindow), data, m_expectedPresentTimestamp);
if (sceneWindow->isPaintingEnabled()) {
m_paintContext.phase2Data.append(Phase2Data{
.window = sceneWindow,
.region = infiniteRegion(),
.clip = data.clip,
.mask = data.mask,
});
}
}
m_paintContext.damage = QRect(QPoint(0, 0), renderTargetRect().size());
}
void Scene::preparePaintSimpleScreen()
{
for (Window *sceneWindow : std::as_const(stacking_order)) {
const Toplevel *toplevel = sceneWindow->window();
WindowPrePaintData data;
data.mask = m_paintContext.mask | (sceneWindow->isOpaque() ? PAINT_WINDOW_OPAQUE : PAINT_WINDOW_TRANSLUCENT);
accumulateRepaints(sceneWindow->windowItem(), painted_screen, &data.paint);
// Clip out the decoration for opaque windows; the decoration is drawn in the second pass.
if (sceneWindow->isOpaque()) {
const SurfaceItem *surfaceItem = sceneWindow->surfaceItem();
if (surfaceItem) {
data.clip |= surfaceItem->mapToGlobal(surfaceItem->shape());
}
} else if (toplevel->hasAlpha() && toplevel->opacity() == 1.0) {
const SurfaceItem *surfaceItem = sceneWindow->surfaceItem();
if (surfaceItem) {
const QRegion shape = surfaceItem->shape();
const QRegion opaque = surfaceItem->opaque();
data.clip = surfaceItem->mapToGlobal(shape & opaque);
if (opaque == shape) {
data.mask = m_paintContext.mask | PAINT_WINDOW_OPAQUE;
}
}
}
const AbstractClient *client = dynamic_cast<const AbstractClient *>(toplevel);
if (client && !client->decorationHasAlpha() && toplevel->opacity() == 1.0) {
data.clip |= sceneWindow->decorationShape().translated(sceneWindow->pos());
}
sceneWindow->resetPaintingEnabled();
effects->prePaintWindow(effectWindow(sceneWindow), data, m_expectedPresentTimestamp);
if (sceneWindow->isPaintingEnabled()) {
m_paintContext.phase2Data.append(Phase2Data{
.window = sceneWindow,
.region = data.paint,
.clip = data.clip,
.mask = data.mask,
});
}
}
// Perform an occlusion cull pass, remove surface damage occluded by opaque windows.
QRegion surfaceDamage;
QRegion opaque;
for (int i = m_paintContext.phase2Data.size() - 1; i >= 0; --i) {
const auto &paintData = m_paintContext.phase2Data.at(i);
surfaceDamage += paintData.region - opaque;
if (!(paintData.mask & PAINT_WINDOW_TRANSLUCENT)) {
opaque += paintData.clip;
}
}
m_paintContext.damage += surfaceDamage & renderTargetRect();
m_paintContext.damage.translate(-renderTargetRect().topLeft());
}
void Scene::postPaint()
{
for (Window *w : std::as_const(stacking_order)) {
effects->postPaintWindow(effectWindow(w));
}
effects->postPaintScreen();
if (waylandServer()) {
const std::chrono::milliseconds frameTime =
std::chrono::duration_cast<std::chrono::milliseconds>(painted_screen->renderLoop()->lastPresentationTimestamp());
@ -353,296 +504,64 @@ QRegion Scene::mapToRenderTarget(const QRegion &region) const
return result;
}
// returns mask and possibly modified region
void Scene::paintScreen(const QRegion &damage, const QRegion &repaint,
QRegion *updateRegion, QRegion *validRegion)
void Scene::paintScreen(const QRegion &region)
{
const RenderLoop *renderLoop = painted_screen->renderLoop();
const std::chrono::milliseconds presentTime =
std::chrono::duration_cast<std::chrono::milliseconds>(renderLoop->nextPresentationTimestamp());
if (Q_UNLIKELY(presentTime < m_expectedPresentTimestamp)) {
qCDebug(KWIN_CORE,
"Provided presentation timestamp is invalid: %lld (current: %lld)",
static_cast<long long>(presentTime.count()),
static_cast<long long>(m_expectedPresentTimestamp.count()));
} else {
m_expectedPresentTimestamp = presentTime;
}
// preparation step
auto effectsImpl = static_cast<EffectsHandlerImpl *>(effects);
effectsImpl->startPaint();
const QRegion displayRegion(renderTargetRect());
QRegion region = damage;
auto screen = EffectScreenImpl::get(painted_screen);
ScreenPrePaintData pdata;
pdata.mask = (damage == displayRegion) ? 0 : PAINT_SCREEN_REGION;
pdata.paint = region;
pdata.screen = screen;
effects->prePaintScreen(pdata, m_expectedPresentTimestamp);
region = pdata.paint;
int mask = pdata.mask;
if (mask & (PAINT_SCREEN_TRANSFORMED | PAINT_SCREEN_WITH_TRANSFORMED_WINDOWS)) {
// Region painting is not possible with transformations,
// because screen damage doesn't match transformed positions.
mask &= ~PAINT_SCREEN_REGION;
region = infiniteRegion();
} else if (mask & PAINT_SCREEN_REGION) {
// make sure not to go outside visible screen
region &= displayRegion;
} else {
// whole screen, not transformed, force region to be full
region = displayRegion;
}
painted_region = region;
repaint_region = repaint;
ScreenPaintData data(m_renderTargetProjectionMatrix, screen);
effects->paintScreen(mask, region, data);
Q_EMIT frameRendered();
for (Window *w : qAsConst(stacking_order)) {
effects->postPaintWindow(effectWindow(w));
}
effects->postPaintScreen();
// make sure not to go outside of the screen area
*updateRegion = damaged_region;
*validRegion = (region | painted_region) & displayRegion;
repaint_region = QRegion();
damaged_region = QRegion();
ScreenPaintData data(m_renderTargetProjectionMatrix, EffectScreenImpl::get(painted_screen));
effects->paintScreen(m_paintContext.mask, region, data);
m_paintScreenCount = 0;
Q_EMIT frameRendered();
}
// the function that'll be eventually called by paintScreen() above
void Scene::finalPaintScreen(int mask, const QRegion &region, ScreenPaintData& data)
{
m_paintScreenCount++;
if (mask & (PAINT_SCREEN_TRANSFORMED | PAINT_SCREEN_WITH_TRANSFORMED_WINDOWS))
if (mask & (PAINT_SCREEN_TRANSFORMED | PAINT_SCREEN_WITH_TRANSFORMED_WINDOWS)) {
paintGenericScreen(mask, data);
else
} else {
paintSimpleScreen(mask, region);
}
static void resetRepaintsHelper(Item *item, AbstractOutput *output)
{
item->resetRepaints(output);
const auto childItems = item->childItems();
for (Item *childItem : childItems) {
resetRepaintsHelper(childItem, output);
}
}
// The generic painting code that can handle even transformations.
// It simply paints bottom-to-top.
void Scene::paintGenericScreen(int orig_mask, const ScreenPaintData &)
void Scene::paintGenericScreen(int, const ScreenPaintData &)
{
QVector<Phase2Data> phase2;
phase2.reserve(stacking_order.size());
for (Window * w : qAsConst(stacking_order)) { // bottom to top
// Reset the repaint_region.
// This has to be done here because many effects schedule a repaint for
// the next frame within Effects::prePaintWindow.
resetRepaintsHelper(w->windowItem(), painted_screen);
WindowPrePaintData data;
data.mask = orig_mask | (w->isOpaque() ? PAINT_WINDOW_OPAQUE : PAINT_WINDOW_TRANSLUCENT);
w->resetPaintingEnabled();
data.paint = infiniteRegion(); // no clipping, so doesn't really matter
data.clip = QRegion();
// preparation step
effects->prePaintWindow(effectWindow(w), data, m_expectedPresentTimestamp);
if (!w->isPaintingEnabled()) {
continue;
}
phase2.append({w, infiniteRegion(), data.clip, data.mask,});
}
damaged_region = renderTargetRect();
if (m_paintScreenCount == 1) {
aboutToStartPainting(painted_screen, damaged_region);
if (orig_mask & PAINT_SCREEN_BACKGROUND_FIRST) {
if (m_paintContext.mask & PAINT_SCREEN_BACKGROUND_FIRST) {
if (m_paintScreenCount == 1) {
paintBackground(infiniteRegion());
}
}
if (!(orig_mask & PAINT_SCREEN_BACKGROUND_FIRST)) {
} else {
paintBackground(infiniteRegion());
}
for (const Phase2Data &d : qAsConst(phase2)) {
paintWindow(d.window, d.mask, d.region);
}
}
static void accumulateRepaints(Item *item, AbstractOutput *output, QRegion *repaints)
{
*repaints += item->repaints(output);
item->resetRepaints(output);
const auto childItems = item->childItems();
for (Item *childItem : childItems) {
accumulateRepaints(childItem, output, repaints);
for (const Phase2Data &paintData : std::as_const(m_paintContext.phase2Data)) {
paintWindow(paintData.window, paintData.mask, paintData.region);
}
}
// The optimized case without any transformations at all.
// It can paint only the requested region and can use clipping
// to reduce painting and improve performance.
void Scene::paintSimpleScreen(int orig_mask, const QRegion &region)
void Scene::paintSimpleScreen(int, const QRegion &region)
{
Q_ASSERT((orig_mask & (PAINT_SCREEN_TRANSFORMED
| PAINT_SCREEN_WITH_TRANSFORMED_WINDOWS)) == 0);
QVector<Phase2Data> phase2data;
phase2data.reserve(stacking_order.size());
QRegion dirtyArea = region;
bool opaqueFullscreen = false;
// Traverse the scene windows from bottom to top.
for (int i = 0; i < stacking_order.count(); ++i) {
Window *window = stacking_order[i];
Toplevel *toplevel = window->window();
WindowPrePaintData data;
data.mask = orig_mask | (window->isOpaque() ? PAINT_WINDOW_OPAQUE : PAINT_WINDOW_TRANSLUCENT);
window->resetPaintingEnabled();
data.paint = region;
accumulateRepaints(window->windowItem(), painted_screen, &data.paint);
// 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?)
AbstractClient *client = dynamic_cast<AbstractClient *>(toplevel);
if (window->isOpaque()) {
if (client) {
opaqueFullscreen = client->isFullScreen();
}
const SurfaceItem *surfaceItem = window->surfaceItem();
if (surfaceItem) {
data.clip |= surfaceItem->mapToGlobal(surfaceItem->shape());
}
} else if (toplevel->hasAlpha() && toplevel->opacity() == 1.0) {
const SurfaceItem *surfaceItem = window->surfaceItem();
if (surfaceItem) {
const QRegion shape = surfaceItem->shape();
const QRegion opaque = surfaceItem->opaque();
data.clip = surfaceItem->mapToGlobal(shape & opaque);
if (opaque == shape) {
data.mask = orig_mask | PAINT_WINDOW_OPAQUE;
}
}
} else {
data.clip = QRegion();
}
if (client && !client->decorationHasAlpha() && toplevel->opacity() == 1.0) {
data.clip |= window->decorationShape().translated(window->pos());
}
// preparation step
effects->prePaintWindow(effectWindow(window), data, m_expectedPresentTimestamp);
if (!window->isPaintingEnabled()) {
continue;
}
dirtyArea |= data.paint;
// Schedule the window for painting
phase2data.append({ window, data.paint, data.clip, data.mask, });
}
// Save the part of the repaint region that's exclusively rendered to
// bring a reused back buffer up to date. Then union the dirty region
// with the repaint region.
const QRegion repaintClip = repaint_region - dirtyArea;
dirtyArea |= repaint_region;
const QRegion displayRegion(renderTargetRect());
bool fullRepaint(dirtyArea == displayRegion); // spare some expensive region operations
if (!fullRepaint) {
extendPaintRegion(dirtyArea, opaqueFullscreen);
fullRepaint = (dirtyArea == displayRegion);
}
QRegion allclips, upperTranslucentDamage;
upperTranslucentDamage = repaint_region;
// This is the occlusion culling pass
for (int i = phase2data.count() - 1; i >= 0; --i) {
Phase2Data *data = &phase2data[i];
QRegion visible = region;
for (int i = m_paintContext.phase2Data.size() - 1; i >= 0; --i) {
Phase2Data *data = &m_paintContext.phase2Data[i];
const Item *item = data->window->windowItem();
if (fullRepaint) {
data->region = displayRegion;
} else {
data->region |= upperTranslucentDamage;
}
// subtract the parts which will possibly been drawn as part of
// a higher opaque window
data->region -= allclips;
// Here we rely on WindowPrePaintData::setTranslucent() to remove
// the clip if needed.
if (!data->clip.isEmpty() && !(data->mask & PAINT_WINDOW_TRANSLUCENT)) {
// clip away the opaque regions for all windows below this one
allclips |= data->clip;
// extend the translucent damage for windows below this by remaining (translucent) regions
if (!fullRepaint) {
upperTranslucentDamage |= data->region - data->clip;
}
} else if (!fullRepaint) {
upperTranslucentDamage |= data->region;
data->region = visible & item->mapToGlobal(item->boundingRect());
if (!(data->mask & PAINT_WINDOW_TRANSLUCENT)) {
visible -= data->clip;
}
}
QRegion paintedArea;
// Fill any areas of the root window not covered by opaque windows
if (m_paintScreenCount == 1) {
aboutToStartPainting(painted_screen, dirtyArea);
paintBackground(visible);
if (orig_mask & PAINT_SCREEN_BACKGROUND_FIRST) {
paintBackground(infiniteRegion());
}
}
if (!(orig_mask & PAINT_SCREEN_BACKGROUND_FIRST)) {
paintedArea = dirtyArea - allclips;
paintBackground(paintedArea);
}
// Now walk the list bottom to top and draw the windows.
for (int i = 0; i < phase2data.count(); ++i) {
Phase2Data *data = &phase2data[i];
// add all regions which have been drawn so far
paintedArea |= data->region;
data->region = paintedArea;
paintWindow(data->window, data->mask, data->region);
}
if (fullRepaint) {
painted_region = displayRegion;
damaged_region = displayRegion - repaintClip;
} else {
painted_region |= paintedArea;
// Clip the repainted region from the damaged region.
// It's important that we don't add the union of the damaged region
// and the repainted region to the damage history. Otherwise the
// repaint region will grow with every frame until it eventually
// covers the whole back buffer, at which point we're always doing
// full repaints.
damaged_region = paintedArea - repaintClip;
for (const Phase2Data &paintData : std::as_const(m_paintContext.phase2Data)) {
paintWindow(paintData.window, paintData.mask, paintData.region);
}
}
@ -733,12 +652,6 @@ void Scene::paintDesktop(int desktop, int mask, const QRegion &region, ScreenPai
static_cast<EffectsHandlerImpl*>(effects)->paintDesktop(desktop, mask, region, data);
}
void Scene::aboutToStartPainting(AbstractOutput *output, const QRegion &damage)
{
Q_UNUSED(output)
Q_UNUSED(damage)
}
// the function that'll be eventually called by paintWindow() above
void Scene::finalPaintWindow(EffectWindowImpl* w, int mask, const QRegion &region, WindowPaintData& data)
{
@ -754,12 +667,6 @@ void Scene::finalDrawWindow(EffectWindowImpl* w, int mask, const QRegion &region
w->sceneWindow()->performPaint(mask, region, data);
}
void Scene::extendPaintRegion(QRegion &region, bool opaqueFullscreen)
{
Q_UNUSED(region);
Q_UNUSED(opaqueFullscreen);
}
bool Scene::makeOpenGLContextCurrent()
{
return false;

View file

@ -53,10 +53,11 @@ public:
explicit SceneDelegate(Scene *scene, AbstractOutput *output, QObject *parent = nullptr);
~SceneDelegate() override;
QRegion repaints() const override;
SurfaceItem *scanoutCandidate() const override;
void prePaint() override;
void postPaint() override;
void paint(const QRegion &damage, const QRegion &repaint, QRegion &update, QRegion &valid) override;
void paint(const QRegion &region) override;
private:
Scene *m_scene;
@ -82,6 +83,7 @@ public:
void addRepaint(const QRect &rect);
void addRepaint(int x, int y, int width, int height);
void addRepaintFull();
QRegion damage() const;
QRect geometry() const;
void setGeometry(const QRect &rect);
@ -96,7 +98,7 @@ public:
SurfaceItem *scanoutCandidate() const;
void prePaint(AbstractOutput *output);
void postPaint();
virtual void paint(const QRegion &damage, const QRegion &repaint, QRegion &update, QRegion &valid) = 0;
virtual void paint(const QRegion &region) = 0;
/**
* Adds the Toplevel to the Scene.
@ -222,34 +224,25 @@ protected:
void createStackingOrder();
void clearStackingOrder();
// shared implementation, starts painting the screen
void paintScreen(const QRegion &damage, const QRegion &repaint,
QRegion *updateRegion, QRegion *validRegion);
void paintScreen(const QRegion &region);
friend class EffectsHandlerImpl;
// called after all effects had their paintScreen() called
void finalPaintScreen(int mask, const QRegion &region, ScreenPaintData& data);
// shared implementation of painting the screen in the generic
// (unoptimized) way
void preparePaintGenericScreen();
virtual void paintGenericScreen(int mask, const ScreenPaintData &data);
// shared implementation of painting the screen in an optimized way
void preparePaintSimpleScreen();
virtual void paintSimpleScreen(int mask, const QRegion &region);
// paint the background (not the desktop background - the whole background)
virtual void paintBackground(const QRegion &region) = 0;
/**
* Notifies about starting to paint.
*
* @p damage contains the reported damage as suggested by windows and effects on prepaint calls.
*/
virtual void aboutToStartPainting(AbstractOutput *output, const QRegion &damage);
// called after all effects had their paintWindow() called
void finalPaintWindow(EffectWindowImpl* w, int mask, const QRegion &region, WindowPaintData& data);
// shared implementation, starts painting the window
virtual void paintWindow(Window* w, int mask, const QRegion &region);
// called after all effects had their drawWindow() called
virtual void finalDrawWindow(EffectWindowImpl* w, int mask, const QRegion &region, WindowPaintData& data);
// let the scene decide whether it's better to paint more of the screen, eg. in order to allow a buffer swap
// the default is NOOP
virtual void extendPaintRegion(QRegion &region, bool opaqueFullscreen);
virtual void paintOffscreenQuickView(OffscreenQuickView *w) = 0;
@ -260,16 +253,13 @@ protected:
QRegion clip;
int mask = 0;
};
// The region which actually has been painted by paintScreen() and should be
// copied from the buffer to the screen. I.e. the region returned from Scene::paintScreen().
// Since prePaintWindow() can extend areas to paint, these changes would have to propagate
// up all the way from paintSimpleScreen() up to paintScreen(), so save them here rather
// than propagate them up in arguments.
QRegion painted_region;
// Additional damage that needs to be repaired to bring a reused back buffer up to date
QRegion repaint_region;
// The dirty region before it was unioned with repaint_region
QRegion damaged_region;
struct PaintContext {
QRegion damage;
int mask = 0;
QVector<Phase2Data> phase2Data;
};
// The screen that is being currently painted
AbstractOutput *painted_screen = nullptr;
@ -285,6 +275,7 @@ private:
qreal m_renderTargetScale = 1;
// how many times finalPaintScreen() has been called
int m_paintScreenCount = 0;
PaintContext m_paintContext;
};
// The base class for windows representations in composite backends

View file

@ -97,15 +97,10 @@ bool SceneOpenGL::initFailed() const
return !init_ok;
}
void SceneOpenGL::aboutToStartPainting(AbstractOutput *output, const QRegion &damage)
{
m_backend->aboutToStartPainting(output, damage);
}
void SceneOpenGL::paint(const QRegion &damage, const QRegion &repaint, QRegion &update, QRegion &valid)
void SceneOpenGL::paint(const QRegion &region)
{
GLVertexBuffer::streamingBuffer()->beginFrame();
paintScreen(damage, repaint, &update, &valid);
paintScreen(region);
GLVertexBuffer::streamingBuffer()->endOfFrame();
}
@ -154,34 +149,6 @@ void SceneOpenGL::paintBackground(const QRegion &region)
}
}
void SceneOpenGL::extendPaintRegion(QRegion &region, bool opaqueFullscreen)
{
if (m_backend->supportsBufferAge())
return;
if (options->glPreferBufferSwap() == Options::ExtendDamage) { // only Extend "large" repaints
const QRegion displayRegion(geometry());
uint damagedPixels = 0;
const QSize &sceneSize = geometry().size();
const uint fullRepaintLimit = (opaqueFullscreen?0.49f:0.748f)*sceneSize.width()*sceneSize.height();
// 16:9 is 75% of 4:3 and 2.55:1 is 49.01% of 5:4
// (5:4 is the most square format and 2.55:1 is Cinemascope55 - the widest ever shot
// movie aspect - two times ;-) It's a Fox format, though, so maybe we want to restrict
// to 2.20:1 - Panavision - which has actually been used for interesting movies ...)
// would be 57% of 5/4
for (const QRect &r : region) {
// damagedPixels += r.width() * r.height(); // combined window damage test
damagedPixels = r.width() * r.height(); // experimental single window damage testing
if (damagedPixels > fullRepaintLimit) {
region = displayRegion;
return;
}
}
} else if (options->glPreferBufferSwap() == Options::PaintFullScreen) { // forced full rePaint
region = QRegion(geometry());
}
}
void SceneOpenGL::paintDesktop(int desktop, int mask, const QRegion &region, ScreenPaintData &data)
{
const QRect r = region.boundingRect();

View file

@ -33,7 +33,7 @@ public:
explicit SceneOpenGL(OpenGLBackend *backend, QObject *parent = nullptr);
~SceneOpenGL() override;
bool initFailed() const override;
void paint(const QRegion &damage, const QRegion &repaint, QRegion &update, QRegion &valid) override;
void paint(const QRegion &region) override;
Scene::EffectFrame *createEffectFrame(EffectFrameImpl *frame) override;
Shadow *createShadow(Toplevel *toplevel) override;
bool makeOpenGLContextCurrent() override;
@ -59,8 +59,6 @@ public:
protected:
void paintBackground(const QRegion &region) override;
void aboutToStartPainting(AbstractOutput *output, const QRegion &damage) override;
void extendPaintRegion(QRegion &region, bool opaqueFullscreen) override;
QMatrix4x4 transformation(int mask, const ScreenPaintData &data) const;
void paintDesktop(int desktop, int mask, const QRegion &region, ScreenPaintData &data) override;
void paintOffscreenQuickView(OffscreenQuickView *w) override;

View file

@ -68,13 +68,13 @@ void SceneQPainter::paintGenericScreen(int mask, const ScreenPaintData &data)
m_painter->restore();
}
void SceneQPainter::paint(const QRegion &damage, const QRegion &repaint, QRegion &update, QRegion &valid)
void SceneQPainter::paint(const QRegion &region)
{
QImage *buffer = m_backend->bufferForScreen(painted_screen);
if (buffer && !buffer->isNull()) {
m_painter->begin(buffer);
m_painter->setWindow(painted_screen->geometry());
paintScreen(damage, repaint, &update, &valid);
paintScreen(region);
m_painter->end();
}
}

View file

@ -23,7 +23,7 @@ class KWIN_EXPORT SceneQPainter : public Scene
public:
~SceneQPainter() override;
void paint(const QRegion &damage, const QRegion &repaint, QRegion &update, QRegion &valid) override;
void paint(const QRegion &region) override;
void paintGenericScreen(int mask, const ScreenPaintData &data) override;
bool initFailed() const override;
EffectFrame *createEffectFrame(EffectFrameImpl *frame) override;