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.
This commit is contained in:
Vlad Zahorodnii 2023-07-21 11:13:43 +03:00
parent 84149945f6
commit 762254c354
8 changed files with 142 additions and 48 deletions

View file

@ -366,14 +366,14 @@ const std::shared_ptr<QOpenGLFramebufferObject> &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<QOpenGLFramebufferObject> 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<QOpenGLFramebufferObject> 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);
}

View file

@ -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<QOpenGLFramebufferObject> &fbo() const;
QImage image() const;
GraphicsBuffer *graphicsBuffer() const;
void present(const std::shared_ptr<QOpenGLFramebufferObject> 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<QOpenGLFramebufferObject> m_fbo;
QImage m_image;
GraphicsBufferRef m_graphicsBufferRef;
Q_DISABLE_COPY(InternalWindow)
};

View file

@ -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 &region)
{
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 &region)
{
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);
}
}

View file

@ -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 &region)
{
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

View file

@ -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 <QPainter>
#include <libdrm/drm_fourcc.h>
namespace KWin
{
namespace QPA
{
class BackingStoreSlot
{
public:
explicit BackingStoreSlot(GraphicsBuffer *graphicsBuffer);
~BackingStoreSlot();
GraphicsBuffer *graphicsBuffer;
std::unique_ptr<GraphicsBufferView> graphicsBufferView;
};
BackingStoreSlot::BackingStoreSlot(GraphicsBuffer *graphicsBuffer)
: graphicsBuffer(graphicsBuffer)
, graphicsBufferView(std::make_unique<GraphicsBufferView>(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<BackingStoreSlot> 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<BackingStoreSlot>(buffer);
m_slots.push_back(slot);
return slot;
}
std::shared_ptr<BackingStoreSlot> 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<QPlatformWindow *>(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 &region)
{
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 &region, 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);
}
}

View file

@ -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 &region, const QPoint &offset) override;
@ -30,7 +31,13 @@ public:
void beginPaint(const QRegion &region) override;
private:
QImage m_buffer;
std::shared_ptr<BackingStoreSlot> allocate(const QSize &size);
std::shared_ptr<BackingStoreSlot> acquire();
std::vector<std::shared_ptr<BackingStoreSlot>> m_slots;
std::shared_ptr<BackingStoreSlot> m_backBuffer;
QSize m_requestedSize;
qreal m_requestedDevicePixelRatio = 1;
};
}

View file

@ -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

View file

@ -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<QOpenGLFramebufferObject> m_fbo;
QImage m_rasterBuffer;
GraphicsBufferRef m_graphicsBufferRef;
};
} // namespace KWin