summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorLiang Qi <liang.qi@qt.io>2019-05-02 16:52:15 +0200
committerLiang Qi <liang.qi@qt.io>2019-05-02 16:52:15 +0200
commit8831c9139e42e6357bf1ee84cba3b3b8041da502 (patch)
tree73e918b8b28cef74152e1ec32d9dc869e56c7490
parent8b69d52db792c3910be53321df44b5c57baa694c (diff)
parentc1b65146f6b9ad98edea424742f7594712758377 (diff)
Merge remote-tracking branch 'origin/5.12' into 5.13
Conflicts: src/client/qwaylandwindow_p.h Change-Id: Ic4104c48267d1682bb58d6b38afc833c39515eae
-rw-r--r--src/client/qwaylanddisplay.cpp38
-rw-r--r--src/client/qwaylanddisplay_p.h3
-rw-r--r--src/client/qwaylandwindow.cpp180
-rw-r--r--src/client/qwaylandwindow_p.h17
-rw-r--r--src/compositor/compositor_api/compositor_api.pri1
-rw-r--r--src/compositor/doc/src/qtwaylandcompositor-qmltypes.qdoc4
-rw-r--r--src/hardwareintegration/client/wayland-egl/qwaylandglcontext.cpp25
-rw-r--r--src/hardwareintegration/client/xcomposite-egl/qwaylandxcompositeeglcontext.cpp2
-rw-r--r--src/hardwareintegration/client/xcomposite-glx/qwaylandxcompositeglxcontext.cpp2
9 files changed, 221 insertions, 51 deletions
diff --git a/src/client/qwaylanddisplay.cpp b/src/client/qwaylanddisplay.cpp
index eccc382bd..4a91c1c96 100644
--- a/src/client/qwaylanddisplay.cpp
+++ b/src/client/qwaylanddisplay.cpp
@@ -69,6 +69,8 @@
#include <QtWaylandClient/private/qwayland-text-input-unstable-v2.h>
+#include <QtCore/private/qcore_unix_p.h>
+
#include <QtCore/QAbstractEventDispatcher>
#include <QtGui/qpa/qwindowsysteminterface.h>
#include <QtGui/private/qguiapplication_p.h>
@@ -204,7 +206,6 @@ void QWaylandDisplay::flushRequests()
wl_display_flush(mDisplay);
}
-
void QWaylandDisplay::blockingReadEvents()
{
if (wl_display_dispatch(mDisplay) < 0) {
@@ -218,6 +219,41 @@ void QWaylandDisplay::exitWithError()
::exit(1);
}
+wl_event_queue *QWaylandDisplay::createEventQueue()
+{
+ return wl_display_create_queue(mDisplay);
+}
+
+void QWaylandDisplay::dispatchQueueWhile(wl_event_queue *queue, std::function<bool ()> condition, int timeout)
+{
+ if (!condition())
+ return;
+
+ QElapsedTimer timer;
+ timer.start();
+ struct pollfd pFd = qt_make_pollfd(wl_display_get_fd(mDisplay), POLLIN);
+ while (timeout == -1 || timer.elapsed() < timeout) {
+ while (wl_display_prepare_read_queue(mDisplay, queue) != 0)
+ wl_display_dispatch_queue_pending(mDisplay, queue);
+
+ wl_display_flush(mDisplay);
+
+ const int remaining = qMax(timeout - timer.elapsed(), 0ll);
+ const int pollTimeout = timeout == -1 ? -1 : remaining;
+ if (qt_poll_msecs(&pFd, 1, pollTimeout) > 0)
+ wl_display_read_events(mDisplay);
+ else
+ wl_display_cancel_read(mDisplay);
+
+ if (wl_display_dispatch_queue_pending(mDisplay, queue) < 0) {
+ checkError();
+ exitWithError();
+ }
+ if (!condition())
+ break;
+ }
+}
+
QWaylandScreen *QWaylandDisplay::screenForOutput(struct wl_output *output) const
{
for (int i = 0; i < mScreens.size(); ++i) {
diff --git a/src/client/qwaylanddisplay_p.h b/src/client/qwaylanddisplay_p.h
index f4b3c26c5..558d8d9b5 100644
--- a/src/client/qwaylanddisplay_p.h
+++ b/src/client/qwaylanddisplay_p.h
@@ -192,6 +192,9 @@ public:
void handleKeyboardFocusChanged(QWaylandInputDevice *inputDevice);
void handleWindowDestroyed(QWaylandWindow *window);
+ wl_event_queue *createEventQueue();
+ void dispatchQueueWhile(wl_event_queue *queue, std::function<bool()> condition, int timeout = -1);
+
public slots:
void blockingReadEvents();
void flushRequests();
diff --git a/src/client/qwaylandwindow.cpp b/src/client/qwaylandwindow.cpp
index 32c5b1e6e..4532bc236 100644
--- a/src/client/qwaylandwindow.cpp
+++ b/src/client/qwaylandwindow.cpp
@@ -62,6 +62,7 @@
#include <QtGui/private/qwindow_p.h>
#include <QtCore/QDebug>
+#include <QtCore/QThread>
QT_BEGIN_NAMESPACE
@@ -74,6 +75,7 @@ QWaylandWindow *QWaylandWindow::mMouseGrab = nullptr;
QWaylandWindow::QWaylandWindow(QWindow *window)
: QPlatformWindow(window)
, mDisplay(waylandScreen()->display())
+ , mFrameQueue(mDisplay->createEventQueue())
, mResizeAfterSwap(qEnvironmentVariableIsSet("QT_WAYLAND_RESIZE_AFTER_SWAP"))
{
static WId id = 1;
@@ -357,6 +359,8 @@ void QWaylandWindow::sendExposeEvent(const QRect &rect)
{
if (!(mShellSurface && mShellSurface->handleExpose(rect)))
QWindowSystemInterface::handleExposeEvent(window(), rect);
+ else
+ qCDebug(lcQpaWayland) << "sendExposeEvent: intercepted by shell extension, not sending";
mLastExposeGeometry = rect;
}
@@ -541,18 +545,11 @@ void QWaylandWindow::handleScreenRemoved(QScreen *qScreen)
void QWaylandWindow::attach(QWaylandBuffer *buffer, int x, int y)
{
Q_ASSERT(!buffer->committed());
- if (mFrameCallback) {
- wl_callback_destroy(mFrameCallback);
- mFrameCallback = nullptr;
- }
-
if (buffer) {
- mFrameCallback = frame();
- wl_callback_add_listener(mFrameCallback, &QWaylandWindow::callbackListener, this);
- mWaitingForFrameSync = true;
+ handleUpdate();
buffer->setBusy();
- attach(buffer->buffer(), x, y);
+ QtWayland::wl_surface::attach(buffer->buffer(), x, y);
} else {
QtWayland::wl_surface::attach(nullptr, 0, 0);
}
@@ -613,32 +610,61 @@ void QWaylandWindow::commit()
}
const wl_callback_listener QWaylandWindow::callbackListener = {
- QWaylandWindow::frameCallback
+ [](void *data, wl_callback *callback, uint32_t time) {
+ Q_UNUSED(callback);
+ Q_UNUSED(time);
+ auto *window = static_cast<QWaylandWindow*>(data);
+ if (window->thread() != QThread::currentThread())
+ QMetaObject::invokeMethod(window, [=] { window->handleFrameCallback(); }, Qt::QueuedConnection);
+ else
+ window->handleFrameCallback();
+ }
};
-void QWaylandWindow::frameCallback(void *data, struct wl_callback *callback, uint32_t time)
+void QWaylandWindow::handleFrameCallback()
{
- Q_UNUSED(time);
- Q_UNUSED(callback);
- QWaylandWindow *self = static_cast<QWaylandWindow*>(data);
+ bool wasExposed = isExposed();
- self->mWaitingForFrameSync = false;
- if (self->mUpdateRequested) {
- self->mUpdateRequested = false;
- self->deliverUpdateRequest();
+ if (mFrameCallbackTimerId != -1) {
+ killTimer(mFrameCallbackTimerId);
+ mFrameCallbackTimerId = -1;
}
+
+ mWaitingForFrameCallback = false;
+ mFrameCallbackTimedOut = false;
+
+ if (!wasExposed && isExposed())
+ sendExposeEvent(QRect(QPoint(), geometry().size()));
+ if (wasExposed && hasPendingUpdateRequest())
+ deliverUpdateRequest();
}
QMutex QWaylandWindow::mFrameSyncMutex;
-void QWaylandWindow::waitForFrameSync()
+bool QWaylandWindow::waitForFrameSync(int timeout)
{
QMutexLocker locker(&mFrameSyncMutex);
- if (!mWaitingForFrameSync)
- return;
- mDisplay->flushRequests();
- while (mWaitingForFrameSync)
- mDisplay->blockingReadEvents();
+ if (!mWaitingForFrameCallback)
+ return true;
+
+ wl_proxy_set_queue(reinterpret_cast<wl_proxy *>(mFrameCallback), mFrameQueue);
+ mDisplay->dispatchQueueWhile(mFrameQueue, [&]() { return mWaitingForFrameCallback; }, timeout);
+
+ if (mWaitingForFrameCallback) {
+ qCDebug(lcWaylandBackingstore) << "Didn't receive frame callback in time, window should now be inexposed";
+ mFrameCallbackTimedOut = true;
+ mWaitingForUpdate = false;
+ sendExposeEvent(QRect());
+ }
+
+ // Stop current frame timer if any, can't use killTimer directly, because we might be on a diffent thread
+ if (mFrameCallbackTimerId != -1) {
+ int id = mFrameCallbackTimerId;
+ mFrameCallbackTimerId = -1;
+ QMetaObject::invokeMethod(this, [=] { killTimer(id); }, Qt::QueuedConnection);
+ }
+
+ return !mWaitingForFrameCallback;
}
QMargins QWaylandWindow::frameMargins() const
@@ -976,6 +1002,9 @@ bool QWaylandWindow::isExposed() const
if (!window()->isVisible())
return false;
+ if (mFrameCallbackTimedOut)
+ return false;
+
if (mShellSurface)
return mShellSurface->isExposed();
@@ -1051,12 +1080,107 @@ QVariant QWaylandWindow::property(const QString &name, const QVariant &defaultVa
return m_properties.value(name, defaultValue);
}
+void QWaylandWindow::timerEvent(QTimerEvent *event)
+{
+ if (event->timerId() == mFallbackUpdateTimerId) {
+ killTimer(mFallbackUpdateTimerId);
+ mFallbackUpdateTimerId = -1;
+ qCDebug(lcWaylandBackingstore) << "mFallbackUpdateTimer timed out";
+
+ if (!isExposed()) {
+ qCDebug(lcWaylandBackingstore) << "Fallback update timer: Window not exposed,"
+ << "not delivering update request.";
+ return;
+ }
+
+ if (mWaitingForUpdate && hasPendingUpdateRequest() && !mWaitingForFrameCallback) {
+ qCWarning(lcWaylandBackingstore) << "Delivering update request through fallback timer,"
+ << "may not be in sync with display";
+ deliverUpdateRequest();
+ }
+ }
+
+ if (event->timerId() == mFrameCallbackTimerId) {
+ killTimer(mFrameCallbackTimerId);
+ mFrameCallbackTimerId = -1;
+ qCDebug(lcWaylandBackingstore) << "Didn't receive frame callback in time, window should now be inexposed";
+ mFrameCallbackTimedOut = true;
+ mWaitingForUpdate = false;
+ sendExposeEvent(QRect());
+ }
+}
+
void QWaylandWindow::requestUpdate()
{
- if (!mWaitingForFrameSync)
- QPlatformWindow::requestUpdate();
- else
- mUpdateRequested = true;
+ Q_ASSERT(hasPendingUpdateRequest()); // should be set by QPA
+
+ // If we have a frame callback all is good and will be taken care of there
+ if (mWaitingForFrameCallback)
+ return;
+
+ // If we've already called deliverUpdateRequest(), but haven't seen any attach+commit/swap yet
+ if (mWaitingForUpdate) {
+ // Ideally, we should just have returned here, but we're not guaranteed that the client
+ // will actually update, so start this timer to deliver another request update after a while
+ // *IF* the client doesn't update.
+ int fallbackTimeout = 100;
+ mFallbackUpdateTimerId = startTimer(fallbackTimeout);
+ return;
+ }
+
+ // Some applications (such as Qt Quick) depend on updates being delivered asynchronously,
+ // so use invokeMethod to delay the delivery a bit.
+ QMetaObject::invokeMethod(this, [this] {
+ // Things might have changed in the meantime
+ if (hasPendingUpdateRequest() && !mWaitingForUpdate && !mWaitingForFrameCallback)
+ deliverUpdateRequest();
+ }, Qt::QueuedConnection);
+}
+
+// Should be called whenever we commit a buffer (directly through wl_surface.commit or indirectly
+// with eglSwapBuffers) to know when it's time to commit the next one.
+// Can be called from the render thread (without locking anything) so make sure to not make races in this method.
+void QWaylandWindow::handleUpdate()
+{
+ // TODO: Should sync subsurfaces avoid requesting frame callbacks?
+
+ if (mFrameCallback) {
+ wl_callback_destroy(mFrameCallback);
+ mFrameCallback = nullptr;
+ }
+
+ if (mFallbackUpdateTimerId != -1) {
+ // Ideally, we would stop the fallback timer here, but since we're on another thread,
+ // it's not allowed. Instead we set mFallbackUpdateTimer to -1 here, so we'll just
+ // ignore it if it times out before it's cleaned up by the invokeMethod call.
+ int id = mFallbackUpdateTimerId;
+ mFallbackUpdateTimerId = -1;
+ QMetaObject::invokeMethod(this, [=] { killTimer(id); }, Qt::QueuedConnection);
+ }
+
+ mFrameCallback = frame();
+ wl_callback_add_listener(mFrameCallback, &QWaylandWindow::callbackListener, this);
+ mWaitingForFrameCallback = true;
+ mWaitingForUpdate = false;
+
+ // Stop current frame timer if any, can't use killTimer directly, see comment above.
+ if (mFrameCallbackTimerId != -1) {
+ int id = mFrameCallbackTimerId;
+ mFrameCallbackTimerId = -1;
+ QMetaObject::invokeMethod(this, [=] { killTimer(id); }, Qt::QueuedConnection);
+ }
+
+ // Start a timer for handling the case when the compositor stops sending frame callbacks.
+ QMetaObject::invokeMethod(this, [=] { // Again; can't do it directly
+ if (mWaitingForFrameCallback)
+ mFrameCallbackTimerId = startTimer(100);
+ }, Qt::QueuedConnection);
+}
+
+void QWaylandWindow::deliverUpdateRequest()
+{
+ mWaitingForUpdate = true;
+ QPlatformWindow::deliverUpdateRequest();
}
void QWaylandWindow::addAttachOffset(const QPoint point)
diff --git a/src/client/qwaylandwindow_p.h b/src/client/qwaylandwindow_p.h
index 7ab0ca74e..8c1ebe167 100644
--- a/src/client/qwaylandwindow_p.h
+++ b/src/client/qwaylandwindow_p.h
@@ -122,7 +122,7 @@ public:
void commit();
- void waitForFrameSync();
+ bool waitForFrameSync(int timeout);
QMargins frameMargins() const override;
QSize surfaceSize() const;
@@ -194,7 +194,10 @@ public:
bool startSystemMove(const QPoint &pos) override;
+ void timerEvent(QTimerEvent *event) override;
void requestUpdate() override;
+ void handleUpdate();
+ void deliverUpdateRequest() override;
public slots:
void applyConfigure();
@@ -217,10 +220,17 @@ protected:
Qt::MouseButtons mMousePressedInContentArea = Qt::NoButton;
WId mWindowId;
- bool mWaitingForFrameSync = false;
+ bool mWaitingForFrameCallback = false;
+ bool mFrameCallbackTimedOut = false; // Whether the frame callback has timed out
+ int mFrameCallbackTimerId = -1; // Started on commit, reset on frame callback
struct ::wl_callback *mFrameCallback = nullptr;
+ struct ::wl_event_queue *mFrameQueue = nullptr;
QWaitCondition mFrameSyncWait;
+ // True when we have called deliverRequestUpdate, but the client has not yet attached a new buffer
+ bool mWaitingForUpdate = false;
+ int mFallbackUpdateTimerId = -1; // Started when waiting for app to commit
+
QMutex mResizeLock;
bool mWaitingToApplyConfigure = false;
bool mCanResize = true;
@@ -259,11 +269,10 @@ private:
void handleMouseEventWithDecoration(QWaylandInputDevice *inputDevice, const QWaylandPointerEvent &e);
void handleScreenChanged();
- bool mUpdateRequested = false;
QRect mLastExposeGeometry;
static const wl_callback_listener callbackListener;
- static void frameCallback(void *data, struct wl_callback *wl_callback, uint32_t time);
+ void handleFrameCallback();
static QMutex mFrameSyncMutex;
static QWaylandWindow *mMouseGrab;
diff --git a/src/compositor/compositor_api/compositor_api.pri b/src/compositor/compositor_api/compositor_api.pri
index 3df061459..8dbe12ac1 100644
--- a/src/compositor/compositor_api/compositor_api.pri
+++ b/src/compositor/compositor_api/compositor_api.pri
@@ -17,6 +17,7 @@ HEADERS += \
compositor_api/qwaylandtouch.h \
compositor_api/qwaylandtouch_p.h \
compositor_api/qwaylandoutput.h \
+ compositor_api/qwaylandoutput_p.h \
compositor_api/qwaylandoutputmode.h \
compositor_api/qwaylandoutputmode_p.h \
compositor_api/qwaylandbufferref.h \
diff --git a/src/compositor/doc/src/qtwaylandcompositor-qmltypes.qdoc b/src/compositor/doc/src/qtwaylandcompositor-qmltypes.qdoc
index 1dadb7102..454fc3711 100644
--- a/src/compositor/doc/src/qtwaylandcompositor-qmltypes.qdoc
+++ b/src/compositor/doc/src/qtwaylandcompositor-qmltypes.qdoc
@@ -26,7 +26,7 @@
****************************************************************************/
/*!
- \qmlmodule QtWayland.Compositor 1.1
+ \qmlmodule QtWayland.Compositor 1.3
\title Qt Wayland Compositor QML Types
\ingroup qmlmodules
\brief Provides QML types for writing custom Wayland display servers.
@@ -38,7 +38,7 @@
import statement:
\code
- import QtWayland.Compositor 1.1
+ import QtWayland.Compositor 1.3
\endcode
To link against the module, add this line to your \l qmake \c .pro file:
diff --git a/src/hardwareintegration/client/wayland-egl/qwaylandglcontext.cpp b/src/hardwareintegration/client/wayland-egl/qwaylandglcontext.cpp
index 99a0f646b..bc1f74af9 100644
--- a/src/hardwareintegration/client/wayland-egl/qwaylandglcontext.cpp
+++ b/src/hardwareintegration/client/wayland-egl/qwaylandglcontext.cpp
@@ -325,7 +325,9 @@ QWaylandGLContext::QWaylandGLContext(EGLDisplay eglDisplay, QWaylandDisplay *dis
mSupportNonBlockingSwap = false;
}
if (!mSupportNonBlockingSwap) {
- qWarning() << "Non-blocking swap buffers not supported. Subsurface rendering can be affected.";
+ qWarning(lcQpaWayland) << "Non-blocking swap buffers not supported."
+ << "Subsurface rendering can be affected."
+ << "It may also cause the event loop to freeze in some situations";
}
updateGLFormat();
@@ -472,20 +474,15 @@ void QWaylandGLContext::swapBuffers(QPlatformSurface *surface)
eglMakeCurrent(currentDisplay, currentSurfaceDraw, currentSurfaceRead, currentContext);
}
-
- QWaylandSubSurface *sub = window->subSurfaceWindow();
- if (sub) {
- QMutexLocker l(sub->syncMutex());
-
- int si = (sub->isSync() && mSupportNonBlockingSwap) ? 0 : m_format.swapInterval();
-
- eglSwapInterval(m_eglDisplay, si);
- eglSwapBuffers(m_eglDisplay, eglSurface);
- } else {
- eglSwapInterval(m_eglDisplay, m_format.swapInterval());
- eglSwapBuffers(m_eglDisplay, eglSurface);
+ int swapInterval = mSupportNonBlockingSwap ? 0 : m_format.swapInterval();
+ eglSwapInterval(m_eglDisplay, swapInterval);
+ if (swapInterval == 0 && m_format.swapInterval() > 0) {
+ // Emulating a blocking swap
+ glFlush(); // Flush before waiting so we can swap more quickly when the frame event arrives
+ window->waitForFrameSync(100);
}
-
+ window->handleUpdate();
+ eglSwapBuffers(m_eglDisplay, eglSurface);
window->setCanResize(true);
}
diff --git a/src/hardwareintegration/client/xcomposite-egl/qwaylandxcompositeeglcontext.cpp b/src/hardwareintegration/client/xcomposite-egl/qwaylandxcompositeeglcontext.cpp
index c07ad5342..a6fead95c 100644
--- a/src/hardwareintegration/client/xcomposite-egl/qwaylandxcompositeeglcontext.cpp
+++ b/src/hardwareintegration/client/xcomposite-egl/qwaylandxcompositeeglcontext.cpp
@@ -65,7 +65,7 @@ void QWaylandXCompositeEGLContext::swapBuffers(QPlatformSurface *surface)
QSize size = w->geometry().size();
w->commit(w->buffer(), QRegion(0, 0, size.width(), size.height()));
- w->waitForFrameSync();
+ w->waitForFrameSync(100);
}
EGLSurface QWaylandXCompositeEGLContext::eglSurfaceForPlatformSurface(QPlatformSurface *surface)
diff --git a/src/hardwareintegration/client/xcomposite-glx/qwaylandxcompositeglxcontext.cpp b/src/hardwareintegration/client/xcomposite-glx/qwaylandxcompositeglxcontext.cpp
index 33ae2e038..351887416 100644
--- a/src/hardwareintegration/client/xcomposite-glx/qwaylandxcompositeglxcontext.cpp
+++ b/src/hardwareintegration/client/xcomposite-glx/qwaylandxcompositeglxcontext.cpp
@@ -93,7 +93,7 @@ void QWaylandXCompositeGLXContext::swapBuffers(QPlatformSurface *surface)
glXSwapBuffers(m_display, w->xWindow());
w->commit(w->buffer(), QRegion(0, 0, size.width(), size.height()));
- w->waitForFrameSync();
+ w->waitForFrameSync(100);
}
QFunctionPointer QWaylandXCompositeGLXContext::getProcAddress(const char *procName)