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;