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:
parent
d4c44220b4
commit
aac0609bb9
17 changed files with 237 additions and 374 deletions
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -24,6 +24,11 @@ void RenderLayerDelegate::setLayer(RenderLayer *layer)
|
|||
m_layer = layer;
|
||||
}
|
||||
|
||||
QRegion RenderLayerDelegate::repaints() const
|
||||
{
|
||||
return QRegion();
|
||||
}
|
||||
|
||||
void RenderLayerDelegate::prePaint()
|
||||
{
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
|
445
src/scene.cpp
445
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<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 ®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<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 ®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<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 ®ion)
|
||||
void Scene::paintSimpleScreen(int, const QRegion ®ion)
|
||||
{
|
||||
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 ®ion, 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 ®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;
|
||||
|
|
39
src/scene.h
39
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> 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
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
|
Loading…
Reference in a new issue