core: Introduce base GraphicsBuffer

Currently, there exists the separation between the buffers provided by
the clients and the buffers created by the compositor.

In hindsight, this separation is not great because it leads to
specialized code paths in the output backend. For example, we have a
separate code path for direct scanout and presenting composited frame.
But you could view the latter case as "direct scanout of a compositor
buffer".

The main idea behind the buffer type is to provide a base buffer type
for client buffers and composited frame buffers (not drm fbs) that we
could pass around, import as textures, etc.
This commit is contained in:
Vlad Zahorodnii 2023-04-12 11:05:26 +03:00
parent c77b5909de
commit 3ac4f8a7dc
8 changed files with 126 additions and 75 deletions

View file

@ -42,6 +42,7 @@ target_sources(kwin PRIVATE
core/colorlut.cpp
core/colorpipelinestage.cpp
core/colortransformation.cpp
core/graphicsbuffer.cpp
core/inputbackend.cpp
core/inputdevice.cpp
core/output.cpp

View file

@ -0,0 +1,55 @@
/*
SPDX-FileCopyrightText: 2023 Vlad Zahorodnii <vlad.zahorodnii@kde.org>
SPDX-License-Identifier: GPL-2.0-or-later
*/
#include "core/graphicsbuffer.h"
namespace KWin
{
GraphicsBuffer::GraphicsBuffer(QObject *parent)
: QObject(parent)
{
}
bool GraphicsBuffer::isReferenced() const
{
return m_refCount > 0;
}
bool GraphicsBuffer::isDropped() const
{
return m_dropped;
}
void GraphicsBuffer::ref()
{
++m_refCount;
}
void GraphicsBuffer::unref()
{
Q_ASSERT(m_refCount > 0);
--m_refCount;
if (!m_refCount) {
if (m_dropped) {
delete this;
} else {
Q_EMIT released();
}
}
}
void GraphicsBuffer::drop()
{
m_dropped = true;
Q_EMIT dropped();
if (!m_refCount) {
delete this;
}
}
} // namespace KWin

59
src/core/graphicsbuffer.h Normal file
View file

@ -0,0 +1,59 @@
/*
SPDX-FileCopyrightText: 2023 Vlad Zahorodnii <vlad.zahorodnii@kde.org>
SPDX-License-Identifier: GPL-2.0-or-later
*/
#pragma once
#include "kwin_export.h"
#include <QObject>
namespace KWin
{
/**
* The GraphicsBuffer class represents a chunk of memory containing graphics data.
*
* A graphics buffer can be referenced. In which case, it won't be destroyed until all
* references are dropped. You can use the isDropped() function to check whether the
* buffer has been marked as destroyed.
*/
class KWIN_EXPORT GraphicsBuffer : public QObject
{
Q_OBJECT
public:
explicit GraphicsBuffer(QObject *parent = nullptr);
/**
* This enum type is used to specify the corner where the origin is. That is, the
* buffer corner where 0,0 is located.
*/
enum class Origin {
TopLeft,
BottomLeft,
};
bool isReferenced() const;
bool isDropped() const;
void ref();
void unref();
void drop();
virtual QSize size() const = 0;
virtual bool hasAlphaChannel() const = 0;
virtual Origin origin() const = 0;
Q_SIGNALS:
void released();
void dropped();
protected:
int m_refCount = 0;
bool m_dropped = false;
};
} // namespace KWin

View file

@ -873,7 +873,7 @@ void TestWaylandSurface::testDestroyAttachedBuffer()
// Let's try to destroy it
delete m_shm;
m_shm = nullptr;
QTRY_VERIFY(serverSurface->buffer()->isDestroyed());
QTRY_VERIFY(serverSurface->buffer()->isDropped());
}
void TestWaylandSurface::testDestroyWithPendingCallback()

View file

@ -7,7 +7,7 @@
#include "clientbuffer.h"
#include "clientbuffer_p.h"
#include "qwayland-server-wayland.h"
#include <wayland-server-protocol.h>
namespace KWaylandServer
{
@ -30,6 +30,12 @@ void ClientBuffer::initialize(wl_resource *resource)
{
Q_D(ClientBuffer);
d->resource = resource;
connect(this, &GraphicsBuffer::dropped, [d]() {
d->resource = nullptr;
});
connect(this, &GraphicsBuffer::released, [d]() {
wl_buffer_send_release(d->resource);
});
}
wl_resource *ClientBuffer::resource() const
@ -38,47 +44,4 @@ wl_resource *ClientBuffer::resource() const
return d->resource;
}
bool ClientBuffer::isReferenced() const
{
Q_D(const ClientBuffer);
return d->refCount > 0;
}
bool ClientBuffer::isDestroyed() const
{
Q_D(const ClientBuffer);
return d->isDestroyed;
}
void ClientBuffer::ref()
{
Q_D(ClientBuffer);
d->refCount++;
}
void ClientBuffer::unref()
{
Q_D(ClientBuffer);
Q_ASSERT(d->refCount > 0);
--d->refCount;
if (!isReferenced()) {
if (isDestroyed()) {
delete this;
} else {
wl_buffer_send_release(d->resource);
}
}
}
void ClientBuffer::markAsDestroyed()
{
Q_D(ClientBuffer);
if (!isReferenced()) {
delete this;
} else {
d->resource = nullptr;
d->isDestroyed = true;
}
}
} // namespace KWaylandServer

View file

@ -6,7 +6,7 @@
#pragma once
#include "kwin_export.h"
#include "core/graphicsbuffer.h"
#include <QImage>
#include <QObject>
@ -27,7 +27,7 @@ class ClientBufferPrivate;
* You can use the isDestroyed() function to check whether the wl_buffer object has been
* destroyed.
*/
class KWIN_EXPORT ClientBuffer : public QObject
class KWIN_EXPORT ClientBuffer : public KWin::GraphicsBuffer
{
Q_OBJECT
Q_DECLARE_PRIVATE(ClientBuffer)
@ -35,37 +35,12 @@ class KWIN_EXPORT ClientBuffer : public QObject
public:
~ClientBuffer() override;
/**
* This enum type is used to specify the corner where the origin is. That's it, the
* buffer corner where 0,0 is located.
*/
enum class Origin {
TopLeft,
BottomLeft,
};
bool isReferenced() const;
bool isDestroyed() const;
void ref();
void unref();
/**
* Returns the wl_resource for this ClientBuffer. If the buffer is destroyed, @c null
* will be returned.
*/
wl_resource *resource() const;
/**
* Returns the size in the native pixels. The returned size is unaffected by buffer
* scale or other surface transforms, e.g. @c wp_viewport.
*/
virtual QSize size() const = 0;
virtual bool hasAlphaChannel() const = 0;
virtual Origin origin() const = 0;
void markAsDestroyed(); ///< @internal
protected:
ClientBuffer(ClientBufferPrivate &dd);
ClientBuffer(wl_resource *resource, ClientBufferPrivate &dd);

View file

@ -17,9 +17,7 @@ public:
{
}
int refCount = 0;
wl_resource *resource = nullptr;
bool isDestroyed = false;
};
} // namespace KWaylandServer

View file

@ -232,7 +232,7 @@ void bufferDestroyCallback(wl_listener *listener, void *data)
ClientBuffer *buffer = displayPrivate->q->clientBufferForResource(static_cast<wl_resource *>(data));
displayPrivate->unregisterClientBuffer(buffer);
buffer->markAsDestroyed();
buffer->drop();
}
ClientBufferDestroyListener::ClientBufferDestroyListener(Display *display, ClientBuffer *buffer)