diff --git a/src/composite.cpp b/src/composite.cpp index ac0629a3b0..f84095d095 100644 --- a/src/composite.cpp +++ b/src/composite.cpp @@ -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 ®ion) { - 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); } } } diff --git a/src/composite.h b/src/composite.h index 9c23ddd928..80ea6124c8 100644 --- a/src/composite.h +++ b/src/composite.h @@ -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 ®ion); State m_state = State::Off; CompositorSelectionOwner *m_selectionOwner = nullptr; diff --git a/src/cursorview.cpp b/src/cursorview.cpp index f6cddb855a..7dc77cbe07 100644 --- a/src/cursorview.cpp +++ b/src/cursorview.cpp @@ -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 ®ion) { 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); } } diff --git a/src/cursorview.h b/src/cursorview.h index 4d995643db..2d5016ed6c 100644 --- a/src/cursorview.h +++ b/src/cursorview.h @@ -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 ®ion) override; private: CursorView *m_view; diff --git a/src/effects.cpp b/src/effects.cpp index 75255bca24..de979bd64f 100644 --- a/src/effects.cpp +++ b/src/effects.cpp @@ -1797,8 +1797,7 @@ void EffectsHandlerImpl::renderScreen(EffectScreen *screen) { auto output = static_cast(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(); } diff --git a/src/platformsupport/scenes/opengl/openglbackend.cpp b/src/platformsupport/scenes/opengl/openglbackend.cpp index 9c4fe5ea17..0f064fd1b8 100644 --- a/src/platformsupport/scenes/opengl/openglbackend.cpp +++ b/src/platformsupport/scenes/opengl/openglbackend.cpp @@ -62,12 +62,6 @@ QSharedPointer 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) diff --git a/src/platformsupport/scenes/opengl/openglbackend.h b/src/platformsupport/scenes/opengl/openglbackend.h index def1d9fddf..bbccee9ffc 100644 --- a/src/platformsupport/scenes/opengl/openglbackend.h +++ b/src/platformsupport/scenes/opengl/openglbackend.h @@ -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; diff --git a/src/renderbackend.cpp b/src/renderbackend.cpp index 318c63ad3d..6067616942 100644 --- a/src/renderbackend.cpp +++ b/src/renderbackend.cpp @@ -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) diff --git a/src/renderbackend.h b/src/renderbackend.h index 111ea7c6d5..15030c487d 100644 --- a/src/renderbackend.h +++ b/src/renderbackend.h @@ -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; diff --git a/src/renderlayerdelegate.cpp b/src/renderlayerdelegate.cpp index afbb1b3d0b..c3a1d2d0a8 100644 --- a/src/renderlayerdelegate.cpp +++ b/src/renderlayerdelegate.cpp @@ -24,6 +24,11 @@ void RenderLayerDelegate::setLayer(RenderLayer *layer) m_layer = layer; } +QRegion RenderLayerDelegate::repaints() const +{ + return QRegion(); +} + void RenderLayerDelegate::prePaint() { } diff --git a/src/renderlayerdelegate.h b/src/renderlayerdelegate.h index bd8812324f..25c39d758b 100644 --- a/src/renderlayerdelegate.h +++ b/src/renderlayerdelegate.h @@ -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 ®ion) = 0; private: RenderLayer *m_layer = nullptr; diff --git a/src/scene.cpp b/src/scene.cpp index 108af23d9f..6e5cd3c70e 100644 --- a/src/scene.cpp +++ b/src/scene.cpp @@ -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 ®ion) { - m_scene->paint(damage, repaint, update, valid); + m_scene->paint(region); } //**************************************** @@ -175,6 +180,11 @@ void Scene::addRepaint(const QRegion ®ion) } } +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(renderLoop->nextPresentationTimestamp()); + + if (Q_UNLIKELY(presentTime < m_expectedPresentTimestamp)) { + qCDebug(KWIN_CORE, + "Provided presentation timestamp is invalid: %lld (current: %lld)", + static_cast(presentTime.count()), + static_cast(m_expectedPresentTimestamp.count())); + } else { + m_expectedPresentTimestamp = presentTime; + } + + // preparation step + auto effectsImpl = static_cast(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(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(painted_screen->renderLoop()->lastPresentationTimestamp()); @@ -353,296 +504,64 @@ QRegion Scene::mapToRenderTarget(const QRegion ®ion) 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 ®ion) { - const RenderLoop *renderLoop = painted_screen->renderLoop(); - const std::chrono::milliseconds presentTime = - std::chrono::duration_cast(renderLoop->nextPresentationTimestamp()); - - if (Q_UNLIKELY(presentTime < m_expectedPresentTimestamp)) { - qCDebug(KWIN_CORE, - "Provided presentation timestamp is invalid: %lld (current: %lld)", - static_cast(presentTime.count()), - static_cast(m_expectedPresentTimestamp.count())); - } else { - m_expectedPresentTimestamp = presentTime; - } - - // preparation step - auto effectsImpl = static_cast(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 ®ion, 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 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 ®ion) +void Scene::paintSimpleScreen(int, const QRegion ®ion) { - Q_ASSERT((orig_mask & (PAINT_SCREEN_TRANSFORMED - | PAINT_SCREEN_WITH_TRANSFORMED_WINDOWS)) == 0); - QVector 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(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 ®ion, ScreenPai static_cast(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 ®ion, WindowPaintData& data) { @@ -754,12 +667,6 @@ void Scene::finalDrawWindow(EffectWindowImpl* w, int mask, const QRegion ®ion w->sceneWindow()->performPaint(mask, region, data); } -void Scene::extendPaintRegion(QRegion ®ion, bool opaqueFullscreen) -{ - Q_UNUSED(region); - Q_UNUSED(opaqueFullscreen); -} - bool Scene::makeOpenGLContextCurrent() { return false; diff --git a/src/scene.h b/src/scene.h index 8d4506d082..17e0c018e7 100644 --- a/src/scene.h +++ b/src/scene.h @@ -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 ®ion) 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 ®ion) = 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 ®ion); friend class EffectsHandlerImpl; // called after all effects had their paintScreen() called void finalPaintScreen(int mask, const QRegion ®ion, 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 ®ion); // paint the background (not the desktop background - the whole background) virtual void paintBackground(const QRegion ®ion) = 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 ®ion, WindowPaintData& data); // shared implementation, starts painting the window virtual void paintWindow(Window* w, int mask, const QRegion ®ion); // called after all effects had their drawWindow() called virtual void finalDrawWindow(EffectWindowImpl* w, int mask, const QRegion ®ion, 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 ®ion, 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; + }; + // 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 diff --git a/src/scenes/opengl/scene_opengl.cpp b/src/scenes/opengl/scene_opengl.cpp index 716066e00a..5ef7623b03 100644 --- a/src/scenes/opengl/scene_opengl.cpp +++ b/src/scenes/opengl/scene_opengl.cpp @@ -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 ®ion) { GLVertexBuffer::streamingBuffer()->beginFrame(); - paintScreen(damage, repaint, &update, &valid); + paintScreen(region); GLVertexBuffer::streamingBuffer()->endOfFrame(); } @@ -154,34 +149,6 @@ void SceneOpenGL::paintBackground(const QRegion ®ion) } } -void SceneOpenGL::extendPaintRegion(QRegion ®ion, 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 ®ion, ScreenPaintData &data) { const QRect r = region.boundingRect(); diff --git a/src/scenes/opengl/scene_opengl.h b/src/scenes/opengl/scene_opengl.h index cca3af1e76..736d699a78 100644 --- a/src/scenes/opengl/scene_opengl.h +++ b/src/scenes/opengl/scene_opengl.h @@ -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 ®ion) 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 ®ion) override; - void aboutToStartPainting(AbstractOutput *output, const QRegion &damage) override; - void extendPaintRegion(QRegion ®ion, bool opaqueFullscreen) override; QMatrix4x4 transformation(int mask, const ScreenPaintData &data) const; void paintDesktop(int desktop, int mask, const QRegion ®ion, ScreenPaintData &data) override; void paintOffscreenQuickView(OffscreenQuickView *w) override; diff --git a/src/scenes/qpainter/scene_qpainter.cpp b/src/scenes/qpainter/scene_qpainter.cpp index 74dd361191..cc27081514 100644 --- a/src/scenes/qpainter/scene_qpainter.cpp +++ b/src/scenes/qpainter/scene_qpainter.cpp @@ -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 ®ion) { 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(); } } diff --git a/src/scenes/qpainter/scene_qpainter.h b/src/scenes/qpainter/scene_qpainter.h index 2ee10e1d44..997c79201d 100644 --- a/src/scenes/qpainter/scene_qpainter.h +++ b/src/scenes/qpainter/scene_qpainter.h @@ -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 ®ion) override; void paintGenericScreen(int mask, const ScreenPaintData &data) override; bool initFailed() const override; EffectFrame *createEffectFrame(EffectFrameImpl *frame) override;