diff --git a/.gitignore b/.gitignore index b33ca90774ad8cdad41881a7be14bd9cb2e219fc..5bc0f373661f77455b8df21c0a55b5f4b3af3b4a 100644 --- a/.gitignore +++ b/.gitignore @@ -40,7 +40,7 @@ examples/wayland/server-buffer/compositor/qwayland-server-*.cpp examples/wayland/server-buffer/compositor/qwayland-server-*.h examples/wayland/server-buffer/compositor/*-server-protocol.h examples/wayland/server-buffer/compositor/*-protocol.c -src/client/qwayland*.cpp +src/client/qwayland-*.cpp src/client/*-protocol.c src/compositor/*-server-protocol.h src/compositor/*-protocol.c diff --git a/src/client/client.pro b/src/client/client.pro index 2895e19d759b2f0dabd56670b6e1d2843c97bf3a..59234b14e74efeaf4bb7d217c8ae05437d6f5a05 100644 --- a/src/client/client.pro +++ b/src/client/client.pro @@ -73,6 +73,7 @@ SOURCES += qwaylandintegration.cpp \ qwaylandwindowmanagerintegration.cpp \ qwaylandinputcontext.cpp \ qwaylanddatadevice.cpp \ + qwaylandbuffer.cpp \ HEADERS += qwaylandintegration_p.h \ qwaylandnativeinterface_p.h \ diff --git a/src/client/qwaylandbuffer.cpp b/src/client/qwaylandbuffer.cpp new file mode 100644 index 0000000000000000000000000000000000000000..c6aace2c7ce88a20c8e1dc218882c98cf18f6d14 --- /dev/null +++ b/src/client/qwaylandbuffer.cpp @@ -0,0 +1,72 @@ +/**************************************************************************** +** +** Copyright (C) 2015 The Qt Company Ltd. +** Copyright (C) 2015 Giulio Camuffo. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the plugins of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL21$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 or version 3 as published by the Free +** Software Foundation and appearing in the file LICENSE.LGPLv21 and +** LICENSE.LGPLv3 included in the packaging of this file. Please review the +** following information to ensure the GNU Lesser General Public License +** requirements will be met: https://www.gnu.org/licenses/lgpl.html and +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** As a special exception, The Qt Company gives you certain additional +** rights. These rights are described in The Qt Company LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qwaylandbuffer_p.h" + +#include <QDebug> + +QT_BEGIN_NAMESPACE + +namespace QtWaylandClient { + +QWaylandBuffer::QWaylandBuffer() + : mBuffer(0) + , mBusy(false) +{ +} + +QWaylandBuffer::~QWaylandBuffer() +{ + if (mBuffer) + wl_buffer_destroy(mBuffer); +} + +void QWaylandBuffer::init(wl_buffer *buf) +{ + mBuffer = buf; + wl_buffer_add_listener(buf, &listener, this); +} + +void QWaylandBuffer::release(void *data, wl_buffer *) +{ + static_cast<QWaylandBuffer *>(data)->mBusy = false; +} + +const wl_buffer_listener QWaylandBuffer::listener = { + QWaylandBuffer::release +}; + +} + +QT_END_NAMESPACE diff --git a/src/client/qwaylandbuffer_p.h b/src/client/qwaylandbuffer_p.h index 6f8f7b269a9869c210468852eeb72309886baefa..7db6cfefec965ba36680a7078ecde2819a89e6d7 100644 --- a/src/client/qwaylandbuffer_p.h +++ b/src/client/qwaylandbuffer_p.h @@ -59,17 +59,25 @@ namespace QtWaylandClient { class Q_WAYLAND_CLIENT_EXPORT QWaylandBuffer { public: - QWaylandBuffer() - : mBuffer(0) - { - } - virtual ~QWaylandBuffer() { } + QWaylandBuffer(); + virtual ~QWaylandBuffer(); + void init(wl_buffer *buf); + wl_buffer *buffer() {return mBuffer;} virtual QSize size() const = 0; virtual int scale() const { return 1; } + void setBusy() { mBusy = true; } + bool busy() const { return mBusy; } + protected: struct wl_buffer *mBuffer; + +private: + bool mBusy; + + static void release(void *data, wl_buffer *); + static const wl_buffer_listener listener; }; } diff --git a/src/client/qwaylandshmbackingstore.cpp b/src/client/qwaylandshmbackingstore.cpp index f009e08116b242054514ed797f4034f62fdad172..3bbe24c318cdf96f8fd9da060a4af893195d0287 100644 --- a/src/client/qwaylandshmbackingstore.cpp +++ b/src/client/qwaylandshmbackingstore.cpp @@ -39,6 +39,7 @@ #include <QtCore/qdebug.h> #include <QtGui/QPainter> #include <QMutexLocker> +#include <QLoggingCategory> #include <wayland-client.h> #include <wayland-client-protocol.h> @@ -53,9 +54,14 @@ QT_BEGIN_NAMESPACE namespace QtWaylandClient { +Q_DECLARE_LOGGING_CATEGORY(logCategory) + +Q_LOGGING_CATEGORY(logCategory, "qt.qpa.wayland.backingstore") + QWaylandShmBuffer::QWaylandShmBuffer(QWaylandDisplay *display, const QSize &size, QImage::Format format, int scale) - : mShmPool(0) + : QWaylandBuffer() + , mShmPool(0) , mMarginsImage(0) { int stride = size.width() * 4; @@ -90,8 +96,8 @@ QWaylandShmBuffer::QWaylandShmBuffer(QWaylandDisplay *display, mImage.setDevicePixelRatio(qreal(scale)); mShmPool = wl_shm_create_pool(display->shm(), fd, alloc); - mBuffer = wl_shm_pool_create_buffer(mShmPool,0, size.width(), size.height(), - stride, wl_format); + init(wl_shm_pool_create_buffer(mShmPool,0, size.width(), size.height(), + stride, wl_format)); close(fd); } @@ -100,8 +106,6 @@ QWaylandShmBuffer::~QWaylandShmBuffer(void) delete mMarginsImage; if (mImage.constBits()) munmap((void *) mImage.constBits(), mImage.byteCount()); - if (mBuffer) - wl_buffer_destroy(mBuffer); if (mShmPool) wl_shm_pool_destroy(mShmPool); } @@ -139,9 +143,7 @@ QWaylandShmBackingStore::QWaylandShmBackingStore(QWindow *window) , mDisplay(QWaylandScreen::waylandScreenFromWindow(window)->display()) , mFrontBuffer(0) , mBackBuffer(0) - , mFrontBufferIsDirty(false) , mPainting(false) - , mFrameCallback(0) { } @@ -151,16 +153,10 @@ QWaylandShmBackingStore::~QWaylandShmBackingStore() if (QWaylandWindow *w = waylandWindow()) w->setBackingStore(Q_NULLPTR); - if (mFrameCallback) - wl_callback_destroy(mFrameCallback); - // if (mFrontBuffer == waylandWindow()->attached()) // waylandWindow()->attach(0); - if (mFrontBuffer != mBackBuffer) - delete mFrontBuffer; - - delete mBackBuffer; + qDeleteAll(mBuffers); } QPaintDevice *QWaylandShmBackingStore::paintDevice() @@ -173,11 +169,7 @@ void QWaylandShmBackingStore::beginPaint(const QRegion &) mPainting = true; ensureSize(); - QWaylandWindow *window = waylandWindow(); - if (window->attached() && mBackBuffer == window->attached() && mFrameCallback) - window->waitForFrameSync(); - - window->setCanResize(false); + waylandWindow()->setCanResize(false); } void QWaylandShmBackingStore::endPaint() @@ -188,11 +180,6 @@ void QWaylandShmBackingStore::endPaint() void QWaylandShmBackingStore::hidden() { - QMutexLocker lock(&mMutex); - if (mFrameCallback) { - wl_callback_destroy(mFrameCallback); - mFrameCallback = Q_NULLPTR; - } } void QWaylandShmBackingStore::ensureSize() @@ -219,35 +206,15 @@ void QWaylandShmBackingStore::flush(QWindow *window, const QRegion ®ion, cons mFrontBuffer = mBackBuffer; - if (mFrameCallback) { - mFrontBufferIsDirty = true; - return; - } - - mFrameCallback = waylandWindow()->frame(); - wl_callback_add_listener(mFrameCallback,&frameCallbackListener,this); QMargins margins = windowDecorationMargins(); - bool damageAll = false; - if (waylandWindow()->attached() != mFrontBuffer) { - delete waylandWindow()->attached(); - damageAll = true; - } waylandWindow()->attachOffset(mFrontBuffer); + mFrontBuffer->setBusy(); - if (damageAll) { - //need to damage it all, otherwise the attach offset may screw up - waylandWindow()->damage(QRect(QPoint(0,0), window->size())); - } else { - QVector<QRect> rects = region.rects(); - for (int i = 0; i < rects.size(); i++) { - QRect rect = rects.at(i); - rect.translate(margins.left(),margins.top()); - waylandWindow()->damage(rect); - } - } + QVector<QRect> rects = region.rects(); + foreach (const QRect &rect, rects) + waylandWindow()->damage(rect.translated(margins.left(), margins.top())); waylandWindow()->commit(); - mFrontBufferIsDirty = false; } void QWaylandShmBackingStore::resize(const QSize &size, const QRegion &) @@ -255,22 +222,65 @@ void QWaylandShmBackingStore::resize(const QSize &size, const QRegion &) mRequestedSize = size; } +QWaylandShmBuffer *QWaylandShmBackingStore::getBuffer(const QSize &size) +{ + foreach (QWaylandShmBuffer *b, mBuffers) { + if (!b->busy()) { + if (b->size() == size) { + return b; + } else { + mBuffers.removeOne(b); + if (mBackBuffer == b) + mBackBuffer = 0; + delete b; + } + } + } + + static const int MAX_BUFFERS = 5; + if (mBuffers.count() < MAX_BUFFERS) { + QImage::Format format = QPlatformScreen::platformScreenForWindow(window())->format(); + QWaylandShmBuffer *b = new QWaylandShmBuffer(mDisplay, size, format, waylandWindow()->scale()); + mBuffers.prepend(b); + return b; + } + return 0; +} + void QWaylandShmBackingStore::resize(const QSize &size) { QMargins margins = windowDecorationMargins(); int scale = waylandWindow()->scale(); QSize sizeWithMargins = (size + QSize(margins.left()+margins.right(),margins.top()+margins.bottom())) * scale; - QImage::Format format = QPlatformScreen::platformScreenForWindow(window())->format(); - - if (mBackBuffer != NULL && mBackBuffer->size() == sizeWithMargins) - return; - - if (mBackBuffer != mFrontBuffer) { - delete mBackBuffer; //we delete the attached buffer when we flush + // We look for a free buffer to draw into. If the buffer is not the last buffer we used, + // that is mBackBuffer, and the size is the same we memcpy the old content into the new + // buffer so that QPainter is happy to find the stuff it had drawn before. If the new + // buffer has a different size it needs to be redrawn completely anyway, and if the buffer + // is the same the stuff is there already. + // You can exercise the different codepaths with weston, switching between the gl and the + // pixman renderer. With the gl renderer release events are sent early so we can effectively + // run single buffered, while with the pixman renderer we have to use two. + QWaylandShmBuffer *buffer = getBuffer(sizeWithMargins); + while (!buffer) { + qCDebug(logCategory, "QWaylandShmBackingStore: stalling waiting for a buffer to be released from the compositor..."); + + mDisplay->blockingReadEvents(); + buffer = getBuffer(sizeWithMargins); } - mBackBuffer = new QWaylandShmBuffer(mDisplay, sizeWithMargins, format, scale); + int oldSize = mBackBuffer ? mBackBuffer->image()->byteCount() : 0; + // mBackBuffer may have been deleted here but if so it means its size was different so we wouldn't copy it anyway + if (mBackBuffer != buffer && oldSize == buffer->image()->byteCount()) { + memcpy(buffer->image()->bits(), mBackBuffer->image()->constBits(), buffer->image()->byteCount()); + } + mBackBuffer = buffer; + // ensure the new buffer is at the beginning of the list so next time getBuffer() will pick + // it if possible + if (mBuffers.first() != buffer) { + mBuffers.removeOne(buffer); + mBuffers.prepend(buffer); + } if (windowDecoration() && window()->isVisible()) windowDecoration()->update(); @@ -344,36 +354,6 @@ QImage QWaylandShmBackingStore::toImage() const } #endif // QT_NO_OPENGL -void QWaylandShmBackingStore::done(void *data, wl_callback *callback, uint32_t time) -{ - Q_UNUSED(time); - QWaylandShmBackingStore *self = - static_cast<QWaylandShmBackingStore *>(data); - if (callback != self->mFrameCallback) // others, like QWaylandWindow, may trigger callbacks too - return; - QMutexLocker lock(&self->mMutex); - QWaylandWindow *window = self->waylandWindow(); - wl_callback_destroy(self->mFrameCallback); - self->mFrameCallback = 0; - - - if (self->mFrontBufferIsDirty && !self->mPainting) { - self->mFrontBufferIsDirty = false; - self->mFrameCallback = wl_surface_frame(window->object()); - wl_callback_add_listener(self->mFrameCallback,&self->frameCallbackListener,self); - if (self->mFrontBuffer != window->attached()) { - delete window->attached(); - } - window->attachOffset(self->mFrontBuffer); - window->damage(QRect(QPoint(0,0),window->geometry().size())); - window->commit(); - } -} - -const struct wl_callback_listener QWaylandShmBackingStore::frameCallbackListener = { - QWaylandShmBackingStore::done -}; - } QT_END_NAMESPACE diff --git a/src/client/qwaylandshmbackingstore_p.h b/src/client/qwaylandshmbackingstore_p.h index a1a6e19c3ce5ae1d1f31cbfea1b6479a591cc10d..8b58c0b5edab2859df53f05e5dc8c5524ef4b8fa 100644 --- a/src/client/qwaylandshmbackingstore_p.h +++ b/src/client/qwaylandshmbackingstore_p.h @@ -51,6 +51,7 @@ #include <QtGui/QImage> #include <qpa/qplatformwindow.h> #include <QMutex> +#include <QLinkedList> QT_BEGIN_NAMESPACE @@ -107,22 +108,17 @@ public: private: void updateDecorations(); + QWaylandShmBuffer *getBuffer(const QSize &size); QWaylandDisplay *mDisplay; + QLinkedList<QWaylandShmBuffer *> mBuffers; QWaylandShmBuffer *mFrontBuffer; QWaylandShmBuffer *mBackBuffer; - bool mFrontBufferIsDirty; bool mPainting; QMutex mMutex; QSize mRequestedSize; Qt::WindowFlags mCurrentWindowFlags; - - static const struct wl_callback_listener frameCallbackListener; - static void done(void *data, - struct wl_callback *callback, - uint32_t time); - struct wl_callback *mFrameCallback; }; } diff --git a/src/client/qwaylandwindow.cpp b/src/client/qwaylandwindow.cpp index fa4e20f948b1538f81862226497ff36eac9c2e13..caf24c0aab9996bc8af457baa9774fdcbc1280e5 100644 --- a/src/client/qwaylandwindow.cpp +++ b/src/client/qwaylandwindow.cpp @@ -74,7 +74,6 @@ QWaylandWindow::QWaylandWindow(QWindow *window) , mMouseEventsInContentArea(false) , mMousePressedInContentArea(Qt::NoButton) , m_cursorShape(Qt::ArrowCursor) - , mBuffer(0) , mWaitingForFrameSync(false) , mFrameCallback(0) , mRequestResizeSent(false) @@ -412,9 +411,8 @@ void QWaylandWindow::requestResize() void QWaylandWindow::attach(QWaylandBuffer *buffer, int x, int y) { - mBuffer = buffer; - if (mBuffer) - attach(mBuffer->buffer(), x, y); + if (buffer) + attach(buffer->buffer(), x, y); else QtWayland::wl_surface::attach(0, 0, 0); } @@ -425,11 +423,6 @@ void QWaylandWindow::attachOffset(QWaylandBuffer *buffer) mOffset = QPoint(); } -QWaylandBuffer *QWaylandWindow::attached() const -{ - return mBuffer; -} - void QWaylandWindow::damage(const QRect &rect) { //We have to do sync stuff before calling damage, or we might @@ -439,9 +432,7 @@ void QWaylandWindow::damage(const QRect &rect) wl_callback_add_listener(mFrameCallback,&QWaylandWindow::callbackListener,this); mWaitingForFrameSync = true; } - if (mBuffer) { - damage(rect.x(), rect.y(), rect.width(), rect.height()); - } + damage(rect.x(), rect.y(), rect.width(), rect.height()); } const wl_callback_listener QWaylandWindow::callbackListener = { diff --git a/src/client/qwaylandwindow_p.h b/src/client/qwaylandwindow_p.h index be8f53088dd648ec44c2698f77412708ba4b10df..701a9cdf6d0ec891444635bf85ef138e69d24b17 100644 --- a/src/client/qwaylandwindow_p.h +++ b/src/client/qwaylandwindow_p.h @@ -208,7 +208,6 @@ protected: Qt::MouseButtons mMousePressedInContentArea; Qt::CursorShape m_cursorShape; - QWaylandBuffer *mBuffer; WId mWindowId; bool mWaitingForFrameSync; struct wl_callback *mFrameCallback; diff --git a/src/hardwareintegration/client/xcomposite-egl/qwaylandxcompositeeglwindow.cpp b/src/hardwareintegration/client/xcomposite-egl/qwaylandxcompositeeglwindow.cpp index dc9b1fb76c01fcb279ad5d1843b07db347624214..329cc7629af6c2e4c933ca590be71f41a0557468 100644 --- a/src/hardwareintegration/client/xcomposite-egl/qwaylandxcompositeeglwindow.cpp +++ b/src/hardwareintegration/client/xcomposite-egl/qwaylandxcompositeeglwindow.cpp @@ -56,6 +56,7 @@ QWaylandXCompositeEGLWindow::QWaylandXCompositeEGLWindow(QWindow *window, QWayla , m_xWindow(0) , m_config(q_configFromGLFormat(glxIntegration->eglDisplay(), window->format(), true, EGL_WINDOW_BIT | EGL_PIXMAP_BIT)) , m_surface(0) + , mBuffer(0) { } diff --git a/src/hardwareintegration/client/xcomposite-egl/qwaylandxcompositeeglwindow.h b/src/hardwareintegration/client/xcomposite-egl/qwaylandxcompositeeglwindow.h index c1e8242c255ecc87ef5d9f2b80bb9aaba0bfa233..489097f86546f5c6a57dec0fbbf11298d96c1a60 100644 --- a/src/hardwareintegration/client/xcomposite-egl/qwaylandxcompositeeglwindow.h +++ b/src/hardwareintegration/client/xcomposite-egl/qwaylandxcompositeeglwindow.h @@ -66,6 +66,7 @@ private: Window m_xWindow; EGLConfig m_config; EGLSurface m_surface; + QWaylandBuffer *mBuffer; }; } diff --git a/src/hardwareintegration/client/xcomposite-glx/qwaylandxcompositeglxwindow.cpp b/src/hardwareintegration/client/xcomposite-glx/qwaylandxcompositeglxwindow.cpp index 1675b8a5fa804045c232884f4804ef4b6710cffb..3fa4b471893664aa7244b9e9d74ae3a15b6a4717 100644 --- a/src/hardwareintegration/client/xcomposite-glx/qwaylandxcompositeglxwindow.cpp +++ b/src/hardwareintegration/client/xcomposite-glx/qwaylandxcompositeglxwindow.cpp @@ -50,6 +50,7 @@ QWaylandXCompositeGLXWindow::QWaylandXCompositeGLXWindow(QWindow *window, QWayla , m_glxIntegration(glxIntegration) , m_xWindow(0) , m_config(qglx_findConfig(glxIntegration->xDisplay(), glxIntegration->screen(), window->format(), GLX_WINDOW_BIT | GLX_PIXMAP_BIT)) + , mBuffer(0) { } diff --git a/src/hardwareintegration/client/xcomposite-glx/qwaylandxcompositeglxwindow.h b/src/hardwareintegration/client/xcomposite-glx/qwaylandxcompositeglxwindow.h index b0966d0f4e18cfddf3de743fe615bb142a5d3899..e6fee1cf05306fe1e7e1bddd1062e3c8e54eb472 100644 --- a/src/hardwareintegration/client/xcomposite-glx/qwaylandxcompositeglxwindow.h +++ b/src/hardwareintegration/client/xcomposite-glx/qwaylandxcompositeglxwindow.h @@ -65,6 +65,7 @@ private: Window m_xWindow; GLXFBConfig m_config; + QWaylandBuffer *mBuffer; }; }