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