From 203d7b3b8af7c4ec60769c62d37e862de7bff719 Mon Sep 17 00:00:00 2001 From: Vlad Zahorodnii Date: Fri, 4 Feb 2022 19:38:58 +0200 Subject: [PATCH] Move direct scanout management to Compositor The responsibilities of the Scene must be reduced to painting only so we can move forward with the layer-based compositing. This change moves direct scanout logic from the opengl scene to the base scene class and the compositor. It makes the opengl scene less overloaded and allows to share direct scanout logic. --- src/backends/drm/egl_gbm_backend.cpp | 5 - src/backends/drm/egl_gbm_backend.h | 2 - src/composite.cpp | 80 +++--------- src/composite.h | 1 - src/effects.cpp | 2 +- .../scenes/opengl/openglbackend.cpp | 14 --- .../scenes/opengl/openglbackend.h | 6 - src/renderbackend.cpp | 7 ++ src/renderbackend.h | 7 ++ src/scene.cpp | 116 +++++++++++++++++- src/scene.h | 14 +-- src/scenes/opengl/scene_opengl.cpp | 81 +----------- src/scenes/opengl/scene_opengl.h | 3 +- src/scenes/qpainter/scene_qpainter.cpp | 20 +-- src/scenes/qpainter/scene_qpainter.h | 3 +- 15 files changed, 164 insertions(+), 197 deletions(-) diff --git a/src/backends/drm/egl_gbm_backend.cpp b/src/backends/drm/egl_gbm_backend.cpp index f2b07ca425..78e3b12d44 100644 --- a/src/backends/drm/egl_gbm_backend.cpp +++ b/src/backends/drm/egl_gbm_backend.cpp @@ -306,11 +306,6 @@ QSharedPointer EglGbmBackend::textureForOutput(AbstractOutput *output return m_surfaces[output]->texture(); } -bool EglGbmBackend::directScanoutAllowed(AbstractOutput *output) const -{ - return !output->usesSoftwareCursor() && !output->directScanoutInhibited(); -} - GbmFormat EglGbmBackend::gbmFormatForDrmFormat(uint32_t format) const { // TODO use a hardcoded lookup table where needed instead? diff --git a/src/backends/drm/egl_gbm_backend.h b/src/backends/drm/egl_gbm_backend.h index 25d8c96342..39870a384f 100644 --- a/src/backends/drm/egl_gbm_backend.h +++ b/src/backends/drm/egl_gbm_backend.h @@ -69,8 +69,6 @@ public: QSharedPointer textureForOutput(AbstractOutput *requestedOutput) const override; - bool directScanoutAllowed(AbstractOutput *output) const override; - QSharedPointer testBuffer(DrmAbstractOutput *output); EGLConfig config(uint32_t format) const; GbmFormat gbmFormatForDrmFormat(uint32_t format) const; diff --git a/src/composite.cpp b/src/composite.cpp index ed30452c0c..a7b7399d39 100644 --- a/src/composite.cpp +++ b/src/composite.cpp @@ -591,38 +591,6 @@ void Compositor::handleFrameRequested(RenderLoop *renderLoop) composite(renderLoop); } -QList Compositor::windowsToRender() const -{ - // Create a list of all windows in the stacking order - QList windows = Workspace::self()->xStackingOrder(); - - // Move elevated windows to the top of the stacking order - const QList elevatedList = static_cast(effects)->elevatedWindows(); - for (EffectWindow *c : elevatedList) { - Toplevel *t = static_cast(c)->window(); - windows.removeAll(t); - windows.append(t); - } - - // Skip windows that are not yet ready for being painted and if screen is locked skip windows - // that are neither lockscreen nor inputmethod windows. - // - // TODO? This cannot be used so carelessly - needs protections against broken clients, the - // window should not get focus before it's displayed, handle unredirected windows properly and - // so on. - for (Toplevel *win : windows) { - if (!win->readyForPainting()) { - windows.removeAll(win); - } - if (waylandServer() && waylandServer()->isScreenLocked()) { - if(!win->isLockScreen() && !win->isInputMethod()) { - windows.removeAll(win); - } - } - } - return windows; -} - void Compositor::composite(RenderLoop *renderLoop) { if (m_backend->checkGraphicsReset()) { @@ -635,39 +603,31 @@ void Compositor::composite(RenderLoop *renderLoop) AbstractOutput *output = findOutput(renderLoop); fTraceDuration("Paint (", output->name(), ")"); - const auto windows = windowsToRender(); - - const QRegion repaints = m_scene->repaints(output); + const QRegion damage = m_scene->repaints(output); m_scene->resetRepaints(output); + m_scene->prePaint(output); - m_scene->paint(output, repaints, windows, renderLoop); + SurfaceItem *scanoutCandidate = m_scene->scanoutCandidate(); + renderLoop->setFullscreenSurface(scanoutCandidate); - if (waylandServer()) { - const std::chrono::milliseconds frameTime = - std::chrono::duration_cast(renderLoop->lastPresentationTimestamp()); - - for (Toplevel *window : windows) { - if (!window->readyForPainting()) { - continue; - } - if (waylandServer()->isScreenLocked() && - !(window->isLockScreen() || window->isInputMethod())) { - continue; - } - if (!window->isOnOutput(output)) { - continue; - } - if (auto surface = window->surface()) { - surface->frameRendered(frameTime.count()); - } - } - if (!Cursors::self()->isCursorHidden()) { - Cursor *cursor = Cursors::self()->currentCursor(); - if (cursor->geometry().intersects(output->geometry())) { - cursor->markAsRendered(frameTime); - } + renderLoop->beginFrame(); + bool directScanout = false; + if (scanoutCandidate) { + if (!output->usesSoftwareCursor() && !output->directScanoutInhibited()) { + directScanout = m_backend->scanout(output, scanoutCandidate); } } + if (directScanout) { + renderLoop->endFrame(); + } else { + QRegion update, valid; + const QRegion repaint = m_backend->beginFrame(output); + m_scene->paint(damage, repaint, update, valid); + renderLoop->endFrame(); + m_backend->endFrame(output, valid, update); + } + + m_scene->postPaint(); } bool Compositor::isActive() diff --git a/src/composite.h b/src/composite.h index c61a996f44..a6fc21b48a 100644 --- a/src/composite.h +++ b/src/composite.h @@ -84,7 +84,6 @@ public: // for delayed supportproperty management of effects void keepSupportProperty(xcb_atom_t atom); void removeSupportProperty(xcb_atom_t atom); - QList windowsToRender() const; Q_SIGNALS: void compositingToggled(bool active); diff --git a/src/effects.cpp b/src/effects.cpp index 29e5c6dde4..a812cc8454 100644 --- a/src/effects.cpp +++ b/src/effects.cpp @@ -1776,7 +1776,7 @@ void EffectsHandlerImpl::slotOutputDisabled(AbstractOutput *output) void EffectsHandlerImpl::renderScreen(EffectScreen *screen) { auto output = static_cast(screen)->platformOutput(); - scene()->paintScreen(output, Compositor::self()->windowsToRender()); + scene()->paintScreen(output); } bool EffectsHandlerImpl::isCursorHidden() const diff --git a/src/platformsupport/scenes/opengl/openglbackend.cpp b/src/platformsupport/scenes/opengl/openglbackend.cpp index c801ce663a..9c4fe5ea17 100644 --- a/src/platformsupport/scenes/opengl/openglbackend.cpp +++ b/src/platformsupport/scenes/opengl/openglbackend.cpp @@ -43,13 +43,6 @@ void OpenGLBackend::setFailed(const QString &reason) m_failed = true; } -bool OpenGLBackend::scanout(AbstractOutput *output, SurfaceItem *surfaceItem) -{ - Q_UNUSED(output) - Q_UNUSED(surfaceItem) - return false; -} - void OpenGLBackend::copyPixels(const QRegion ®ion) { const int height = screens()->size().height(); @@ -75,13 +68,6 @@ void OpenGLBackend::aboutToStartPainting(AbstractOutput *output, const QRegion & Q_UNUSED(damage) } - -bool OpenGLBackend::directScanoutAllowed(AbstractOutput *output) const -{ - Q_UNUSED(output); - return false; -} - 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 00388f9681..def1d9fddf 100644 --- a/src/platformsupport/scenes/opengl/openglbackend.h +++ b/src/platformsupport/scenes/opengl/openglbackend.h @@ -63,11 +63,6 @@ public: virtual void aboutToStartPainting(AbstractOutput *output, const QRegion &damage); virtual bool makeCurrent() = 0; virtual void doneCurrent() = 0; - /** - * Tries to directly scan out a surface to the screen) - * @return if the scanout fails (or is not supported on the specified screen) - */ - virtual bool scanout(AbstractOutput *output, SurfaceItem *surfaceItem); /** * @brief Whether the creation of the Backend failed. @@ -109,7 +104,6 @@ public: { return m_haveNativeFence; } - virtual bool directScanoutAllowed(AbstractOutput *output) const; /** * The backend specific extensions (e.g. EGL/GLX extensions). diff --git a/src/renderbackend.cpp b/src/renderbackend.cpp index 54c0ceb8bd..318c63ad3d 100644 --- a/src/renderbackend.cpp +++ b/src/renderbackend.cpp @@ -24,4 +24,11 @@ bool RenderBackend::checkGraphicsReset() return false; } +bool RenderBackend::scanout(AbstractOutput *output, SurfaceItem *surfaceItem) +{ + Q_UNUSED(output) + Q_UNUSED(surfaceItem) + return false; +} + } // namespace KWin diff --git a/src/renderbackend.h b/src/renderbackend.h index 096a91c286..111ea7c6d5 100644 --- a/src/renderbackend.h +++ b/src/renderbackend.h @@ -15,6 +15,7 @@ namespace KWin class AbstractOutput; class OverlayWindow; +class SurfaceItem; /** * The RenderBackend class is the base class for all rendering backends. @@ -33,6 +34,12 @@ public: virtual QRegion beginFrame(AbstractOutput *output) = 0; virtual void endFrame(AbstractOutput *output, const QRegion &renderedRegion, const QRegion &damagedRegion) = 0; + + /** + * Tries to directly scan out a surface to the screen + * Returns @c true if scanout succeeds, @c false if rendering is necessary + */ + virtual bool scanout(AbstractOutput *output, SurfaceItem *surfaceItem); }; } // namespace KWin diff --git a/src/scene.cpp b/src/scene.cpp index 934013c389..c65364a583 100644 --- a/src/scene.cpp +++ b/src/scene.cpp @@ -76,6 +76,8 @@ #include "composite.h" #include +#include + namespace KWin { @@ -186,6 +188,84 @@ void Scene::removeRepaints(AbstractOutput *output) m_repaints.remove(output); } +static SurfaceItem *findTopMostSurface(SurfaceItem *item) +{ + const QList children = item->childItems(); + if (children.isEmpty()) { + return item; + } else { + return findTopMostSurface(static_cast(children.constLast())); + } +} + +SurfaceItem *Scene::scanoutCandidate() const +{ + SurfaceItem *candidate = nullptr; + if (!static_cast(effects)->blocksDirectScanout()) { + for (int i = stacking_order.count() - 1; i >=0; i--) { + Window *window = stacking_order[i]; + Toplevel *toplevel = window->window(); + if (painted_screen && toplevel->isOnOutput(painted_screen) && window->isVisible() && toplevel->opacity() > 0) { + AbstractClient *c = dynamic_cast(toplevel); + if (!c || !c->isFullScreen()) { + break; + } + if (!window->surfaceItem()) { + break; + } + SurfaceItem *topMost = findTopMostSurface(window->surfaceItem()); + auto pixmap = topMost->pixmap(); + if (!pixmap) { + break; + } + pixmap->update(); + // the subsurface has to be able to cover the whole window + if (topMost->position() != QPoint(0, 0)) { + break; + } + // and it has to be completely opaque + if (!window->isOpaque() && !topMost->opaque().contains(QRect(0, 0, window->width(), window->height()))) { + break; + } + candidate = topMost; + break; + } + } + } + return candidate; +} + +void Scene::prePaint(AbstractOutput *output) +{ + createStackingOrder(); + painted_screen = output; +} + +void Scene::postPaint() +{ + if (waylandServer()) { + const std::chrono::milliseconds frameTime = + std::chrono::duration_cast(painted_screen->renderLoop()->lastPresentationTimestamp()); + + for (Window *window : std::as_const(m_windows)) { + Toplevel *toplevel = window->window(); + if (!toplevel->isOnOutput(painted_screen)) { + continue; + } + if (auto surface = toplevel->surface()) { + surface->frameRendered(frameTime.count()); + } + } + if (!Cursors::self()->isCursorHidden()) { + Cursor *cursor = Cursors::self()->currentCursor(); + if (cursor->geometry().intersects(painted_screen->geometry())) { + cursor->markAsRendered(frameTime); + } + } + } + + clearStackingOrder(); +} static QMatrix4x4 createProjectionMatrix(const QRect &rect) { @@ -252,9 +332,9 @@ QRegion Scene::mapToRenderTarget(const QRegion ®ion) const return result; } -void Scene::paintScreen(AbstractOutput *output, const QList &toplevels) +void Scene::paintScreen(AbstractOutput *output) { - createStackingOrder(toplevels); + createStackingOrder(); painted_screen = output; QRegion update, valid; @@ -594,10 +674,38 @@ void Scene::windowClosed(Toplevel *toplevel, Deleted *deleted) m_windows[deleted] = window; } -void Scene::createStackingOrder(const QList &toplevels) +void Scene::createStackingOrder() { + // Create a list of all windows in the stacking order + QList windows = Workspace::self()->xStackingOrder(); + + // Move elevated windows to the top of the stacking order + const QList elevatedList = static_cast(effects)->elevatedWindows(); + for (EffectWindow *c : elevatedList) { + Toplevel *t = static_cast(c)->window(); + windows.removeAll(t); + windows.append(t); + } + + // Skip windows that are not yet ready for being painted and if screen is locked skip windows + // that are neither lockscreen nor inputmethod windows. + // + // TODO? This cannot be used so carelessly - needs protections against broken clients, the + // window should not get focus before it's displayed, handle unredirected windows properly and + // so on. + for (Toplevel *win : windows) { + if (!win->readyForPainting()) { + windows.removeAll(win); + } + if (waylandServer() && waylandServer()->isScreenLocked()) { + if(!win->isLockScreen() && !win->isInputMethod()) { + windows.removeAll(win); + } + } + } + // TODO: cache the stacking_order in case it has not changed - for (Toplevel *c : toplevels) { + for (Toplevel *c : std::as_const(windows)) { Q_ASSERT(m_windows.contains(c)); stacking_order.append(m_windows[ c ]); } diff --git a/src/scene.h b/src/scene.h index 27a31176d2..11254fb8f3 100644 --- a/src/scene.h +++ b/src/scene.h @@ -74,15 +74,13 @@ public: // Returns true if the ctor failed to properly initialize. virtual bool initFailed() const = 0; - // Repaints the given screen areas, windows provides the stacking order. - // The entry point for the main part of the painting pass. - // returns the time since the last vblank signal - if there's one - // ie. "what of this frame is lost to painting" - virtual void paint(AbstractOutput *output, const QRegion &damage, const QList &windows, - RenderLoop *renderLoop) = 0; + SurfaceItem *scanoutCandidate() const; + void prePaint(AbstractOutput *output); + void postPaint(); + virtual void paint(const QRegion &damage, const QRegion &repaint, QRegion &update, QRegion &valid) = 0; - void paintScreen(AbstractOutput *output, const QList &toplevels); + void paintScreen(AbstractOutput *output); /** * Adds the Toplevel to the Scene. @@ -205,7 +203,7 @@ public Q_SLOTS: void windowClosed(KWin::Toplevel* c, KWin::Deleted* deleted); protected: virtual Window *createWindow(Toplevel *toplevel) = 0; - void createStackingOrder(const QList &toplevels); + void createStackingOrder(); void clearStackingOrder(); // shared implementation, starts painting the screen void paintScreen(const QRegion &damage, const QRegion &repaint, diff --git a/src/scenes/opengl/scene_opengl.cpp b/src/scenes/opengl/scene_opengl.cpp index 2e6f394179..c873c80263 100644 --- a/src/scenes/opengl/scene_opengl.cpp +++ b/src/scenes/opengl/scene_opengl.cpp @@ -174,83 +174,12 @@ void SceneOpenGL::aboutToStartPainting(AbstractOutput *output, const QRegion &da m_backend->aboutToStartPainting(output, damage); } -static SurfaceItem *findTopMostSurface(SurfaceItem *item) +void SceneOpenGL::paint(const QRegion &damage, const QRegion &repaint, QRegion &update, QRegion &valid) { - const QList children = item->childItems(); - if (children.isEmpty()) { - return item; - } else { - return findTopMostSurface(static_cast(children.constLast())); - } -} - -void SceneOpenGL::paint(AbstractOutput *output, const QRegion &damage, const QList &toplevels, - RenderLoop *renderLoop) -{ - painted_screen = output; - // actually paint the frame, flushed with the NEXT frame - createStackingOrder(toplevels); - - QRegion update; - QRegion valid; - QRegion repaint; - - renderLoop->beginFrame(); - - SurfaceItem *fullscreenSurface = nullptr; - for (int i = stacking_order.count() - 1; i >=0; i--) { - Window *window = stacking_order[i]; - Toplevel *toplevel = window->window(); - if (output && toplevel->isOnOutput(output) && window->isVisible() && toplevel->opacity() > 0) { - AbstractClient *c = dynamic_cast(toplevel); - if (!c || !c->isFullScreen()) { - break; - } - if (!window->surfaceItem()) { - break; - } - SurfaceItem *topMost = findTopMostSurface(window->surfaceItem()); - auto pixmap = topMost->pixmap(); - if (!pixmap) { - break; - } - pixmap->update(); - // the subsurface has to be able to cover the whole window - if (topMost->position() != QPoint(0, 0)) { - break; - } - // and it has to be completely opaque - if (!window->isOpaque() && !topMost->opaque().contains(QRect(0, 0, window->width(), window->height()))) { - break; - } - fullscreenSurface = topMost; - break; - } - } - renderLoop->setFullscreenSurface(fullscreenSurface); - - bool directScanout = false; - if (m_backend->directScanoutAllowed(output) && !static_cast(effects)->blocksDirectScanout()) { - directScanout = m_backend->scanout(output, fullscreenSurface); - } - if (directScanout) { - renderLoop->endFrame(); - } else { - // prepare rendering makescontext current on the output - repaint = m_backend->beginFrame(output); - GLVertexBuffer::streamingBuffer()->beginFrame(); - - paintScreen(damage, repaint, &update, &valid); - paintCursor(output, valid); - - renderLoop->endFrame(); - - GLVertexBuffer::streamingBuffer()->endOfFrame(); - m_backend->endFrame(output, valid, update); - } - - // do cleanup - clearStackingOrder(); + GLVertexBuffer::streamingBuffer()->beginFrame(); + paintScreen(damage, repaint, &update, &valid); + paintCursor(painted_screen, valid); + GLVertexBuffer::streamingBuffer()->endOfFrame(); } QMatrix4x4 SceneOpenGL::transformation(int mask, const ScreenPaintData &data) const diff --git a/src/scenes/opengl/scene_opengl.h b/src/scenes/opengl/scene_opengl.h index 9085ae8b64..86085eef6f 100644 --- a/src/scenes/opengl/scene_opengl.h +++ b/src/scenes/opengl/scene_opengl.h @@ -33,8 +33,7 @@ public: explicit SceneOpenGL(OpenGLBackend *backend, QObject *parent = nullptr); ~SceneOpenGL() override; bool initFailed() const override; - void paint(AbstractOutput *output, const QRegion &damage, const QList &windows, - RenderLoop *renderLoop) override; + void paint(const QRegion &damage, const QRegion &repaint, QRegion &update, QRegion &valid) override; Scene::EffectFrame *createEffectFrame(EffectFrameImpl *frame) override; Shadow *createShadow(Toplevel *toplevel) override; bool makeOpenGLContextCurrent() override; diff --git a/src/scenes/qpainter/scene_qpainter.cpp b/src/scenes/qpainter/scene_qpainter.cpp index 847305f2e4..e2843842b6 100644 --- a/src/scenes/qpainter/scene_qpainter.cpp +++ b/src/scenes/qpainter/scene_qpainter.cpp @@ -68,34 +68,22 @@ void SceneQPainter::paintGenericScreen(int mask, const ScreenPaintData &data) m_painter->restore(); } -void SceneQPainter::paint(AbstractOutput *output, const QRegion &damage, const QList &toplevels, - RenderLoop *renderLoop) +void SceneQPainter::paint(const QRegion &damage, const QRegion &repaint, QRegion &update, QRegion &valid) { Q_ASSERT(kwinApp()->platform()->isPerScreenRenderingEnabled()); - painted_screen = output; - createStackingOrder(toplevels); - - const QRegion repaint = m_backend->beginFrame(output); - const QRect geometry = output->geometry(); - - QImage *buffer = m_backend->bufferForScreen(output); + QImage *buffer = m_backend->bufferForScreen(painted_screen); if (buffer && !buffer->isNull()) { - renderLoop->beginFrame(); + const QRect geometry = painted_screen->geometry(); m_painter->begin(buffer); m_painter->setWindow(geometry); QRegion updateRegion, validRegion; paintScreen(damage, repaint, &updateRegion, &validRegion); - paintCursor(output, updateRegion); + paintCursor(painted_screen, updateRegion); m_painter->end(); - renderLoop->endFrame(); - m_backend->endFrame(output, validRegion, updateRegion); } - - // do cleanup - clearStackingOrder(); } void SceneQPainter::paintBackground(const QRegion ®ion) diff --git a/src/scenes/qpainter/scene_qpainter.h b/src/scenes/qpainter/scene_qpainter.h index 03f1d28c04..ab23ec211e 100644 --- a/src/scenes/qpainter/scene_qpainter.h +++ b/src/scenes/qpainter/scene_qpainter.h @@ -23,8 +23,7 @@ class KWIN_EXPORT SceneQPainter : public Scene public: ~SceneQPainter() override; - void paint(AbstractOutput *output, const QRegion &damage, const QList &windows, - RenderLoop *renderLoop) override; + void paint(const QRegion &damage, const QRegion &repaint, QRegion &update, QRegion &valid) override; void paintGenericScreen(int mask, const ScreenPaintData &data) override; bool initFailed() const override; EffectFrame *createEffectFrame(EffectFrameImpl *frame) override;