From 53c09ce7bd1d950dfbb822d24d2d352b8d245f3f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Martin=20Gr=C3=A4=C3=9Flin?= Date: Tue, 18 Jun 2013 11:48:23 +0200 Subject: [PATCH] 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. --- wayland_backend.cpp | 108 ++++++++++++++++++++++++++++++++++++-------- wayland_backend.h | 79 ++++++++++++++++++++++++++------ 2 files changed, 155 insertions(+), 32 deletions(-) diff --git a/wayland_backend.cpp b/wayland_backend.cpp index 0a3619cffd..dae7557a57 100644 --- a/wayland_backend.cpp +++ b/wayland_backend.cpp @@ -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(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 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; } + 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 - 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(); + // 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; } diff --git a/wayland_backend.h b/wayland_backend.h index 40afdd83f9..45ad5c48f7 100644 --- a/wayland_backend.h +++ b/wayland_backend.h @@ -23,6 +23,7 @@ along with this program. If not, see . #include // Qt #include +#include #include #include #include @@ -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 m_tmpFile; bool m_valid; int m_offset; + QList 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