From a99d09f0b3b0bb534680f7ea7fe545119aeb9e27 Mon Sep 17 00:00:00 2001 From: Vlad Zahorodnii Date: Thu, 2 Jan 2025 03:56:43 +0200 Subject: [PATCH] Client: Prune stale buffers in QWaylandShmBackingStore A backing store may sometimes allocate an extra buffer and keep it even though the buffer is never touched again. This change makes the QWaylandShmBackingStore clean up the old buffers more aggressively. Specifically, * if a buffer is referenced by the compositor and has a different size, there is no need to wait for the buffer to be released, it can be destroyed immediately * if a buffer has not been used for the past 50 frames, it probably won't be used any time soon so it can be disposed Depending on the compositor implementation details, the QWaylandShmBackingStore is likely to hold either one or two buffers now. Note that the backing store can still sometimes have more buffers because Qt widgets not always use QWindow::requestUpdate(). Change-Id: I57428d457d889dd07c4beb641cf380b1aceb88c2 Reviewed-by: David Edmundson --- .../wayland/qwaylandshmbackingstore.cpp | 45 ++++++++++++------- .../wayland/qwaylandshmbackingstore_p.h | 9 ++-- 2 files changed, 34 insertions(+), 20 deletions(-) diff --git a/src/plugins/platforms/wayland/qwaylandshmbackingstore.cpp b/src/plugins/platforms/wayland/qwaylandshmbackingstore.cpp index b1ae2e1ae4f..1abd6808bbc 100644 --- a/src/plugins/platforms/wayland/qwaylandshmbackingstore.cpp +++ b/src/plugins/platforms/wayland/qwaylandshmbackingstore.cpp @@ -255,23 +255,33 @@ void QWaylandShmBackingStore::resize(const QSize &size, const QRegion &) QWaylandShmBuffer *QWaylandShmBackingStore::getBuffer(const QSize &size, bool &bufferWasRecreated) { + static const int MAX_BUFFERS = 5; + static const int MAX_AGE = 10 * MAX_BUFFERS; bufferWasRecreated = false; - const auto copy = mBuffers; // remove when ported to vector + remove_if - for (QWaylandShmBuffer *b : copy) { - if (!b->busy()) { - if (b->size() == size) { - return b; - } else { - mBuffers.remove(b); - if (mBackBuffer == b) - mBackBuffer = nullptr; - delete b; - } + // Prune buffers that have not been used in a while or with different size. + for (auto i = mBuffers.size() - 1; i >= 0; --i) { + QWaylandShmBuffer *buffer = mBuffers[i]; + if (buffer->age() > MAX_AGE || buffer->size() != size) { + mBuffers.removeAt(i); + if (mBackBuffer == buffer) + mBackBuffer = nullptr; + delete buffer; } } - static const size_t MAX_BUFFERS = 5; + QWaylandShmBuffer *buffer = nullptr; + for (QWaylandShmBuffer *candidate : std::as_const(mBuffers)) { + if (candidate->busy()) + continue; + + if (!buffer || candidate->age() < buffer->age()) + buffer = candidate; + } + + if (buffer) + return buffer; + if (mBuffers.size() < MAX_BUFFERS) { QImage::Format format = QPlatformScreen::platformScreenForWindow(window())->format(); QWaylandShmBuffer *b = new QWaylandShmBuffer(mDisplay, size, format, waylandWindow()->scale()); @@ -327,11 +337,12 @@ bool QWaylandShmBackingStore::recreateBackBufferIfNeeded() mBackBuffer = buffer; - // ensure the new buffer is at the beginning of the list so next time getBuffer() will pick - // it if possible - if (mBuffers.front() != buffer) { - mBuffers.remove(buffer); - mBuffers.push_front(buffer); + for (QWaylandShmBuffer *buffer : std::as_const(mBuffers)) { + if (mBackBuffer == buffer) { + buffer->setAge(0); + } else { + buffer->setAge(buffer->age() + 1); + } } if (windowDecoration() && window()->isVisible() && oldSizeInBytes != newSizeInBytes) diff --git a/src/plugins/platforms/wayland/qwaylandshmbackingstore_p.h b/src/plugins/platforms/wayland/qwaylandshmbackingstore_p.h index 6d276bf7b30..0f38ba11e23 100644 --- a/src/plugins/platforms/wayland/qwaylandshmbackingstore_p.h +++ b/src/plugins/platforms/wayland/qwaylandshmbackingstore_p.h @@ -22,8 +22,6 @@ #include #include -#include - QT_BEGIN_NAMESPACE namespace QtWaylandClient { @@ -44,12 +42,17 @@ public: QImage *imageInsideMargins(const QMargins &margins); QRegion &dirtyRegion() { return mDirtyRegion; } + + uint age() const { return mAge; } + void setAge(uint age) { mAge = age; } + private: QImage mImage; struct wl_shm_pool *mShmPool = nullptr; QMargins mMargins; QImage *mMarginsImage = nullptr; QRegion mDirtyRegion; + uint mAge = 0; }; class Q_WAYLANDCLIENT_EXPORT QWaylandShmBackingStore : public QPlatformBackingStore @@ -84,7 +87,7 @@ private: QWaylandShmBuffer *getBuffer(const QSize &size, bool &bufferWasRecreated); QWaylandDisplay *mDisplay = nullptr; - std::list mBuffers; + QList mBuffers; QWaylandShmBuffer *mFrontBuffer = nullptr; QWaylandShmBuffer *mBackBuffer = nullptr; bool mPainting = false;