Reuse wl_buffer in ShmPool

A new Wayland::Buffer class is provided which wraps a:
* wl_buffer
* size
* stride
* memory address of data represented by the buffer

In addition the Buffer knows whether the compositor has released it or
not. This allows the ShmPool to reuse the buffer in case the same size
and stride is requested.

This is currently most relevant for the CursorData. Instead of keeping
the wl_buffer, the QImage for the cursor is kept and each time the cursor
is set for the surface a new buffer is requested from the ShmPool.

The ShmPool now either reuses an existing buffer or creates a new one.
Furthermore the ShmPool takes care of releasing all buffers at EOL.
This commit is contained in:
Martin Gräßlin 2013-06-18 11:48:23 +02:00
parent 0f09f00210
commit 53c09ce7bd
2 changed files with 156 additions and 33 deletions

View file

@ -254,6 +254,15 @@ static void keyboardHandleModifiers(void *data, wl_keyboard *keyboard, uint32_t
Q_UNUSED(group)
}
static void bufferRelease(void *data, wl_buffer *wl_buffer)
{
Buffer *buffer = reinterpret_cast<Buffer*>(data);
if (buffer->buffer() != wl_buffer) {
return;
}
buffer->setReleased(true);
}
// handlers
static const struct wl_registry_listener s_registryListener = {
registryHandleGlobal,
@ -286,9 +295,12 @@ static const struct wl_seat_listener s_seatListener = {
seatHandleCapabilities
};
CursorData::CursorData(ShmPool *pool)
: m_cursor(NULL)
, m_valid(init(pool))
static const struct wl_buffer_listener s_bufferListener = {
bufferRelease
};
CursorData::CursorData()
: m_valid(init())
{
}
@ -296,7 +308,7 @@ CursorData::~CursorData()
{
}
bool CursorData::init(ShmPool *pool)
bool CursorData::init()
{
QScopedPointer<xcb_xfixes_get_cursor_image_reply_t, QScopedPointerPodDeleter> cursor(
xcb_xfixes_get_cursor_image_reply(connection(),
@ -311,13 +323,9 @@ bool CursorData::init(ShmPool *pool)
if (cursorImage.isNull()) {
return false;
}
m_size = QSize(cursor->width, cursor->height);
m_cursor = pool->createBuffer(cursorImage);
if (!m_cursor) {
qDebug() << "Creating cursor buffer failed";
return false;
}
// the backend for the cursorImage is destroyed once the xcb cursor goes out of scope
// because of that we create a copy
m_cursor = cursorImage.copy();
m_hotSpot = QPoint(cursor->xhot, cursor->yhot);
return true;
@ -362,7 +370,7 @@ void X11CursorTracker::cursorChanged(uint32_t serial)
if (!pool) {
return;
}
CursorData cursor(pool);
CursorData cursor;
if (cursor.isValid()) {
// TODO: discard unused cursors after some time?
m_cursors.insert(serial, cursor);
@ -372,9 +380,14 @@ void X11CursorTracker::cursorChanged(uint32_t serial)
void X11CursorTracker::installCursor(const CursorData& cursor)
{
const QImage &cursorImage = cursor.cursor();
wl_buffer *buffer = m_backend->shmPool()->createBuffer(cursorImage);
if (!buffer) {
return;
}
wl_pointer_set_cursor(m_pointer, m_enteredSerial, m_cursor, cursor.hotSpot().x(), cursor.hotSpot().y());
wl_surface_attach(m_cursor, cursor.cursor(), 0, 0);
wl_surface_damage(m_cursor, 0, 0, cursor.size().width(), cursor.size().height());
wl_surface_attach(m_cursor, buffer, 0, 0);
wl_surface_damage(m_cursor, 0, 0, cursorImage.width(), cursorImage.height());
wl_surface_commit(m_cursor);
}
@ -391,6 +404,26 @@ void X11CursorTracker::resetCursor()
}
}
Buffer::Buffer(wl_buffer* buffer, const QSize& size, int32_t stride, void* address)
: m_nativeBuffer(buffer)
, m_released(false)
, m_size(size)
, m_stride(stride)
, m_address(address)
{
wl_buffer_add_listener(m_nativeBuffer, &s_bufferListener, this);
}
Buffer::~Buffer()
{
wl_buffer_destroy(m_nativeBuffer);
}
void Buffer::copy(const void* src)
{
memcpy(m_address, src, m_size.height()*m_stride);
}
ShmPool::ShmPool(wl_shm *shm)
: m_shm(shm)
, m_pool(NULL)
@ -404,6 +437,7 @@ ShmPool::ShmPool(wl_shm *shm)
ShmPool::~ShmPool()
{
qDeleteAll(m_buffers);
if (m_poolData) {
munmap(m_poolData, m_size);
}
@ -441,13 +475,49 @@ wl_buffer *ShmPool::createBuffer(const QImage& image)
if (image.isNull() || !m_valid) {
return NULL;
}
// TODO: test whether buffer needs resizing
wl_buffer *buffer = wl_shm_pool_create_buffer(m_pool, m_offset, image.width(), image.height(),
image.bytesPerLine(), WL_SHM_FORMAT_ARGB8888);
if (buffer) {
memcpy((char *)m_poolData + m_offset, image.bits(), image.byteCount());
m_offset += image.byteCount();
Buffer *buffer = getBuffer(image.size(), image.bytesPerLine());
if (!buffer) {
return NULL;
}
buffer->copy(image.bits());
return buffer->buffer();
}
wl_buffer *ShmPool::createBuffer(const QSize &size, int32_t stride, const void *src)
{
if (size.isNull() || !m_valid) {
return NULL;
}
Buffer *buffer = getBuffer(size, stride);
if (!buffer) {
return NULL;
}
buffer->copy(src);
return buffer->buffer();
}
Buffer *ShmPool::getBuffer(const QSize &size, int32_t stride)
{
Q_FOREACH (Buffer *buffer, m_buffers) {
if (!buffer->isReleased()) {
continue;
}
if (buffer->size() != size || buffer->stride() != stride) {
continue;
}
buffer->setReleased(false);
return buffer;
}
// TODO: test whether buffer needs resizing
// we don't have a buffer which we could reuse - need to create a new one
wl_buffer *native = wl_shm_pool_create_buffer(m_pool, m_offset, size.width(), size.height(),
stride, WL_SHM_FORMAT_ARGB8888);
if (!native) {
return NULL;
}
Buffer *buffer = new Buffer(native, size, stride, (char *)m_poolData + m_offset);
m_offset += size.height() * stride;
m_buffers.append(buffer);
return buffer;
}

View file

@ -23,6 +23,7 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
#include <kwinglobals.h>
// Qt
#include <QHash>
#include <QImage>
#include <QObject>
#include <QPoint>
#include <QSize>
@ -45,17 +46,15 @@ class WaylandBackend;
class CursorData
{
public:
CursorData(ShmPool *pool);
CursorData();
~CursorData();
bool isValid() const;
const QPoint &hotSpot() const;
const QSize &size() const;
wl_buffer *cursor() const;
const QImage &cursor() const;
private:
bool init(ShmPool *pool);
wl_buffer *m_cursor;
bool init();
QImage m_cursor;
QPoint m_hotSpot;
QSize m_size;
bool m_valid;
};
@ -80,6 +79,27 @@ private:
uint32_t m_lastX11Cursor;
};
class Buffer
{
public:
Buffer(wl_buffer *buffer, const QSize &size, int32_t stride, void *address);
~Buffer();
void copy(const void *src);
void setReleased(bool released);
wl_buffer *buffer() const;
void *address() const;
const QSize &size() const;
int32_t stride() const;
bool isReleased() const;
private:
wl_buffer *m_nativeBuffer;
bool m_released;
QSize m_size;
int32_t m_stride;
void *m_address;
};
class ShmPool
{
public:
@ -87,8 +107,10 @@ public:
~ShmPool();
bool isValid() const;
wl_buffer *createBuffer(const QImage &image);
wl_buffer *createBuffer(const QSize &size, int32_t stride, const void *src);
private:
bool createPool();
Buffer* getBuffer(const QSize &size, int32_t stride);
wl_shm *m_shm;
wl_shm_pool *m_pool;
void *m_poolData;
@ -96,6 +118,7 @@ private:
QScopedPointer<QTemporaryFile> m_tmpFile;
bool m_valid;
int m_offset;
QList<Buffer*> m_buffers;
};
class WaylandSeat
@ -177,17 +200,11 @@ const QPoint& CursorData::hotSpot() const
}
inline
wl_buffer* CursorData::cursor() const
const QImage &CursorData::cursor() const
{
return m_cursor;
}
inline
const QSize& CursorData::size() const
{
return m_size;
}
inline
wl_seat *WaylandSeat::seat()
{
@ -248,6 +265,42 @@ const QSize &WaylandBackend::shellSurfaceSize() const
return m_shellSurfaceSize;
}
inline
void* Buffer::address() const
{
return m_address;
}
inline
wl_buffer* Buffer::buffer() const
{
return m_nativeBuffer;
}
inline
const QSize& Buffer::size() const
{
return m_size;
}
inline
int32_t Buffer::stride() const
{
return m_stride;
}
inline
bool Buffer::isReleased() const
{
return m_released;
}
inline
void Buffer::setReleased(bool released)
{
m_released = released;
}
} // namespace Wayland
} // namespace KWin