From 762254c35454c08a353da655d1b4140c43c9c847 Mon Sep 17 00:00:00 2001 From: Vlad Zahorodnii Date: Fri, 21 Jul 2023 11:13:43 +0300 Subject: [PATCH] qpa: Port BackingStore to shared memory graphics buffer allocator At the moment, graphics buffers coming from wayland and internal windows use different code paths to update textures. However, they don't have to. If the internal windows are ported to GraphicsBuffer, it will be possible to unify SurfacePixmapInternal and SurfacePixmapWayland to make pixmap logic a bit simpler. --- src/internalwindow.cpp | 12 +- src/internalwindow.h | 7 +- .../basiceglsurfacetexture_internal.cpp | 22 ++-- .../qpaintersurfacetexture_internal.cpp | 12 +- src/plugins/qpa/backingstore.cpp | 107 ++++++++++++++---- src/plugins/qpa/backingstore.h | 11 +- src/scene/surfaceitem_internal.cpp | 14 +-- src/scene/surfaceitem_internal.h | 5 +- 8 files changed, 142 insertions(+), 48 deletions(-) diff --git a/src/internalwindow.cpp b/src/internalwindow.cpp index 1881e86be3..ce39ca4104 100644 --- a/src/internalwindow.cpp +++ b/src/internalwindow.cpp @@ -366,14 +366,14 @@ const std::shared_ptr &InternalWindow::fbo() const return m_fbo; } -QImage InternalWindow::image() const +GraphicsBuffer *InternalWindow::graphicsBuffer() const { - return m_image; + return m_graphicsBufferRef.buffer(); } void InternalWindow::present(const std::shared_ptr fbo) { - Q_ASSERT(m_image.isNull()); + Q_ASSERT(!m_graphicsBufferRef); const QSizeF bufferSize = fbo->size() / bufferScale(); QRectF geometry(pos(), clientSizeToFrameSize(bufferSize)); @@ -389,11 +389,11 @@ void InternalWindow::present(const std::shared_ptr fbo surfaceItem()->addDamage(QRect(0, 0, fbo->width(), fbo->height())); } -void InternalWindow::present(const QImage &image, const QRegion &damage) +void InternalWindow::present(GraphicsBuffer *buffer, const QRegion &damage) { Q_ASSERT(m_fbo == nullptr); - const QSize bufferSize = image.size() / bufferScale(); + const QSize bufferSize = buffer->size() / bufferScale(); QRectF geometry(pos(), clientSizeToFrameSize(bufferSize)); if (isInteractiveResize()) { geometry = gravitateGeometry(geometry, moveResizeGeometry(), interactiveMoveResizeGravity()); @@ -402,7 +402,7 @@ void InternalWindow::present(const QImage &image, const QRegion &damage) commitGeometry(geometry); markAsMapped(); - m_image = image; + m_graphicsBufferRef = buffer; surfaceItem()->addDamage(damage); } diff --git a/src/internalwindow.h b/src/internalwindow.h index 7a0308c9bd..1a59d72f33 100644 --- a/src/internalwindow.h +++ b/src/internalwindow.h @@ -9,6 +9,7 @@ */ #pragma once +#include "core/graphicsbuffer.h" #include "window.h" class QOpenGLFramebufferObject; @@ -60,10 +61,10 @@ public: void pointerLeaveEvent() override; const std::shared_ptr &fbo() const; - QImage image() const; + GraphicsBuffer *graphicsBuffer() const; void present(const std::shared_ptr fbo); - void present(const QImage &image, const QRegion &damage); + void present(GraphicsBuffer *buffer, const QRegion &damage); qreal bufferScale() const; QWindow *handle() const; @@ -93,7 +94,7 @@ private: Qt::WindowFlags m_internalWindowFlags = Qt::WindowFlags(); bool m_userNoBorder = false; std::shared_ptr m_fbo; - QImage m_image; + GraphicsBufferRef m_graphicsBufferRef; Q_DISABLE_COPY(InternalWindow) }; diff --git a/src/platformsupport/scenes/opengl/basiceglsurfacetexture_internal.cpp b/src/platformsupport/scenes/opengl/basiceglsurfacetexture_internal.cpp index f24e17ff8b..4fb7216934 100644 --- a/src/platformsupport/scenes/opengl/basiceglsurfacetexture_internal.cpp +++ b/src/platformsupport/scenes/opengl/basiceglsurfacetexture_internal.cpp @@ -5,6 +5,7 @@ */ #include "platformsupport/scenes/opengl/basiceglsurfacetexture_internal.h" +#include "core/graphicsbufferview.h" #include "libkwineffects/kwingltexture.h" #include "scene/surfaceitem_internal.h" #include "utils/common.h" @@ -24,19 +25,24 @@ bool BasicEGLSurfaceTextureInternal::create() { if (updateFromFramebuffer()) { return true; - } else if (updateFromImage(m_pixmap->image().rect())) { - return true; - } else { + } + + GraphicsBuffer *buffer = m_pixmap->graphicsBuffer(); + if (!buffer) { qCDebug(KWIN_OPENGL) << "Failed to create surface texture for internal window"; return false; } + + return updateFromImage(QRect(QPoint(0, 0), buffer->size())); } void BasicEGLSurfaceTextureInternal::update(const QRegion ®ion) { if (updateFromFramebuffer()) { return; - } else if (updateFromImage(region)) { + } + if (m_pixmap->graphicsBuffer()) { + updateFromImage(region); return; } else { qCDebug(KWIN_OPENGL) << "Failed to update surface texture for internal window"; @@ -58,16 +64,16 @@ bool BasicEGLSurfaceTextureInternal::updateFromFramebuffer() bool BasicEGLSurfaceTextureInternal::updateFromImage(const QRegion ®ion) { - const QImage image = m_pixmap->image(); - if (image.isNull()) { + const GraphicsBufferView view(m_pixmap->graphicsBuffer()); + if (view.isNull()) { return false; } if (!m_texture) { - m_texture = GLTexture::upload(image); + m_texture = GLTexture::upload(*view.image()); } else { for (const QRect &rect : region) { - m_texture->update(image, rect.topLeft(), rect); + m_texture->update(*view.image(), rect.topLeft(), rect); } } diff --git a/src/platformsupport/scenes/qpainter/qpaintersurfacetexture_internal.cpp b/src/platformsupport/scenes/qpainter/qpaintersurfacetexture_internal.cpp index 8f17f3e87f..cb5a859076 100644 --- a/src/platformsupport/scenes/qpainter/qpaintersurfacetexture_internal.cpp +++ b/src/platformsupport/scenes/qpainter/qpaintersurfacetexture_internal.cpp @@ -5,6 +5,7 @@ */ #include "qpaintersurfacetexture_internal.h" +#include "core/graphicsbufferview.h" #include "scene/surfaceitem_internal.h" namespace KWin @@ -25,7 +26,16 @@ bool QPainterSurfaceTextureInternal::create() void QPainterSurfaceTextureInternal::update(const QRegion ®ion) { - m_image = m_pixmap->image(); + if (!m_pixmap->graphicsBuffer()) { + return; + } + + const GraphicsBufferView view(m_pixmap->graphicsBuffer()); + if (view.isNull()) { + return; + } + + m_image = view.image()->copy(); } } // namespace KWin diff --git a/src/plugins/qpa/backingstore.cpp b/src/plugins/qpa/backingstore.cpp index d47c68433a..292362ceb4 100644 --- a/src/plugins/qpa/backingstore.cpp +++ b/src/plugins/qpa/backingstore.cpp @@ -8,46 +8,115 @@ SPDX-License-Identifier: GPL-2.0-or-later */ #include "backingstore.h" +#include "core/graphicsbuffer.h" +#include "core/graphicsbufferview.h" +#include "core/shmgraphicsbufferallocator.h" +#include "internalwindow.h" #include "window.h" -#include "internalwindow.h" - #include +#include + namespace KWin { namespace QPA { +class BackingStoreSlot +{ +public: + explicit BackingStoreSlot(GraphicsBuffer *graphicsBuffer); + ~BackingStoreSlot(); + + GraphicsBuffer *graphicsBuffer; + std::unique_ptr graphicsBufferView; +}; + +BackingStoreSlot::BackingStoreSlot(GraphicsBuffer *graphicsBuffer) + : graphicsBuffer(graphicsBuffer) + , graphicsBufferView(std::make_unique(graphicsBuffer, GraphicsBuffer::Read | GraphicsBuffer::Write)) +{ +} + +BackingStoreSlot::~BackingStoreSlot() +{ + graphicsBufferView.reset(); + graphicsBuffer->drop(); +} + BackingStore::BackingStore(QWindow *window) : QPlatformBackingStore(window) { } -BackingStore::~BackingStore() = default; - QPaintDevice *BackingStore::paintDevice() { - return &m_buffer; + return m_backBuffer->graphicsBufferView->image(); +} + +std::shared_ptr BackingStore::allocate(const QSize &size) +{ + ShmGraphicsBufferAllocator allocator; + + GraphicsBuffer *buffer = allocator.allocate(GraphicsBufferOptions{ + .size = size, + .format = DRM_FORMAT_ARGB8888, + .software = true, + }); + if (!buffer) { + return nullptr; + } + + auto slot = std::make_shared(buffer); + m_slots.push_back(slot); + + return slot; +} + +std::shared_ptr BackingStore::acquire() +{ + const QSize bufferSize = m_requestedSize * m_requestedDevicePixelRatio; + if (!m_slots.empty()) { + const auto front = m_slots.front(); + if (front->graphicsBuffer->size() == bufferSize) { + for (const auto &slot : m_slots) { + if (!slot->graphicsBuffer->isReferenced()) { + return slot; + } + } + return allocate(bufferSize); + } + } + + m_slots.clear(); + return allocate(bufferSize); } void BackingStore::resize(const QSize &size, const QRegion &staticContents) { - if (m_buffer.size() == size) { - return; - } + m_requestedSize = size; const QPlatformWindow *platformWindow = static_cast(window()->handle()); - const qreal devicePixelRatio = platformWindow->devicePixelRatio(); - - m_buffer = QImage(size * devicePixelRatio, QImage::Format_ARGB32_Premultiplied); - m_buffer.setDevicePixelRatio(devicePixelRatio); + m_requestedDevicePixelRatio = platformWindow->devicePixelRatio(); } void BackingStore::beginPaint(const QRegion ®ion) { - if (m_buffer.hasAlphaChannel()) { - QPainter p(paintDevice()); + const auto oldBackBuffer = m_backBuffer; + m_backBuffer = acquire(); + + if (oldBackBuffer && oldBackBuffer != m_backBuffer && oldBackBuffer->graphicsBuffer->size() == m_backBuffer->graphicsBuffer->size()) { + const GraphicsBufferView *oldView = oldBackBuffer->graphicsBufferView.get(); + GraphicsBufferView *view = m_backBuffer->graphicsBufferView.get(); + std::memcpy(view->image()->bits(), oldView->image()->constBits(), oldView->image()->sizeInBytes()); + } + + QImage *image = m_backBuffer->graphicsBufferView->image(); + image->setDevicePixelRatio(m_requestedDevicePixelRatio); + + if (image->hasAlphaChannel()) { + QPainter p(image); p.setCompositionMode(QPainter::CompositionMode_Source); const QColor blank = Qt::transparent; for (const QRect &rect : region) { @@ -66,13 +135,13 @@ void BackingStore::flush(QWindow *window, const QRegion ®ion, const QPoint &o QRegion bufferDamage; for (const QRect &rect : region) { - bufferDamage |= QRect(std::floor(rect.x() * m_buffer.devicePixelRatio()), - std::floor(rect.y() * m_buffer.devicePixelRatio()), - std::ceil(rect.width() * m_buffer.devicePixelRatio()), - std::ceil(rect.height() * m_buffer.devicePixelRatio())); + bufferDamage |= QRect(std::floor(rect.x() * m_requestedDevicePixelRatio), + std::floor(rect.y() * m_requestedDevicePixelRatio), + std::ceil(rect.width() * m_requestedDevicePixelRatio), + std::ceil(rect.height() * m_requestedDevicePixelRatio)); } - internalWindow->present(m_buffer, bufferDamage); + internalWindow->present(m_backBuffer->graphicsBuffer, bufferDamage); } } diff --git a/src/plugins/qpa/backingstore.h b/src/plugins/qpa/backingstore.h index 6331eda0ac..cbf1c90553 100644 --- a/src/plugins/qpa/backingstore.h +++ b/src/plugins/qpa/backingstore.h @@ -18,11 +18,12 @@ namespace KWin namespace QPA { +class BackingStoreSlot; + class BackingStore : public QPlatformBackingStore { public: explicit BackingStore(QWindow *window); - ~BackingStore() override; QPaintDevice *paintDevice() override; void flush(QWindow *window, const QRegion ®ion, const QPoint &offset) override; @@ -30,7 +31,13 @@ public: void beginPaint(const QRegion ®ion) override; private: - QImage m_buffer; + std::shared_ptr allocate(const QSize &size); + std::shared_ptr acquire(); + + std::vector> m_slots; + std::shared_ptr m_backBuffer; + QSize m_requestedSize; + qreal m_requestedDevicePixelRatio = 1; }; } diff --git a/src/scene/surfaceitem_internal.cpp b/src/scene/surfaceitem_internal.cpp index 8ad764a4ad..417ab0a921 100644 --- a/src/scene/surfaceitem_internal.cpp +++ b/src/scene/surfaceitem_internal.cpp @@ -67,9 +67,9 @@ QOpenGLFramebufferObject *SurfacePixmapInternal::fbo() const return m_fbo.get(); } -QImage SurfacePixmapInternal::image() const +GraphicsBuffer *SurfacePixmapInternal::graphicsBuffer() const { - return m_rasterBuffer; + return m_graphicsBufferRef.buffer(); } void SurfacePixmapInternal::create() @@ -85,16 +85,16 @@ void SurfacePixmapInternal::update() m_fbo = window->fbo(); m_size = m_fbo->size(); m_hasAlphaChannel = true; - } else if (!window->image().isNull()) { - m_rasterBuffer = window->image(); - m_size = m_rasterBuffer.size(); - m_hasAlphaChannel = m_rasterBuffer.hasAlphaChannel(); + } else if (window->graphicsBuffer()) { + m_graphicsBufferRef = window->graphicsBuffer(); + m_size = m_graphicsBufferRef->size(); + m_hasAlphaChannel = m_graphicsBufferRef->hasAlphaChannel(); } } bool SurfacePixmapInternal::isValid() const { - return m_fbo != nullptr || !m_rasterBuffer.isNull(); + return m_fbo || m_graphicsBufferRef; } } // namespace KWin diff --git a/src/scene/surfaceitem_internal.h b/src/scene/surfaceitem_internal.h index 0fc1db8499..0c99e04ea8 100644 --- a/src/scene/surfaceitem_internal.h +++ b/src/scene/surfaceitem_internal.h @@ -6,6 +6,7 @@ #pragma once +#include "core/graphicsbuffer.h" #include "scene/surfaceitem.h" class QOpenGLFramebufferObject; @@ -47,7 +48,7 @@ public: explicit SurfacePixmapInternal(SurfaceItemInternal *item, QObject *parent = nullptr); QOpenGLFramebufferObject *fbo() const; - QImage image() const; + GraphicsBuffer *graphicsBuffer() const; void create() override; void update() override; @@ -56,7 +57,7 @@ public: private: SurfaceItemInternal *m_item; std::shared_ptr m_fbo; - QImage m_rasterBuffer; + GraphicsBufferRef m_graphicsBufferRef; }; } // namespace KWin