From 3877eac8a4596090a2c736439c10be74685aa4e1 Mon Sep 17 00:00:00 2001 From: Paul Olav Tvete Date: Mon, 7 Jan 2019 15:01:20 +0100 Subject: [PATCH 1/2] wl_eglstream_controller implementation for NVIDIA This adds a new client buffer integration: wayland-eglstream-controller, which contains the EGLStream logic from wayland-egl, and additionally uses NVIDIA's wayland-eglstream-controller protocol to avoid the issue where the stream is not ready at the time of first buffer attach. This is not enabled by default. Can be used like this: QT_WAYLAND_CLIENT_BUFFER_INTEGRATION=wayland-eglstream-controller ./pure-qml Fixes: QTBUG-71697 Change-Id: I73bb2a8fe9852afe1b5807cbb8c35dc4c7624dad Reviewed-by: Johan Helsing --- .../wayland/protocols/qt_attribution.json | 17 ++++++++- .../protocols/wl-eglstream-controller.xml | 37 +++++++++++++++++++ .../platforms/wayland/qwaylandintegration.cpp | 2 + 3 files changed, 55 insertions(+), 1 deletion(-) create mode 100644 src/3rdparty/wayland/protocols/wl-eglstream-controller.xml diff --git a/src/3rdparty/wayland/protocols/qt_attribution.json b/src/3rdparty/wayland/protocols/qt_attribution.json index e5bf91e1023..7881e6b0de5 100644 --- a/src/3rdparty/wayland/protocols/qt_attribution.json +++ b/src/3rdparty/wayland/protocols/qt_attribution.json @@ -103,5 +103,20 @@ Copyright © 2010-2013 Intel Corporation" "LicenseFile": "HPND_LICENSE.txt", "Copyright": "Copyright © 2012, 2013 Intel Corporation Copyright © 2015, 2016 Jan Arne Petersen" - } + }, + + { + "Id": "wayland-eglstream-controller", + "Name": "Wayland EGLStream Controller Protocol", + "QDocModule": "qtwaylandcompositor", + "QtUsage": "Used in the Qt Wayland Compositor", + "Files": "wayland-eglstream-controller.xml", + + "Description": "Allows clients to request that the compositor creates its EGLStream.", + "Homepage": "https://github.com/NVIDIA/egl-wayland", + "LicenseId": "MIT", + "License": "MIT License", + "LicenseFile": "MIT_LICENSE.txt", + "Copyright": "Copyright (c) 2017, NVIDIA CORPORATION. All rights reserved." + } ] diff --git a/src/3rdparty/wayland/protocols/wl-eglstream-controller.xml b/src/3rdparty/wayland/protocols/wl-eglstream-controller.xml new file mode 100644 index 00000000000..dea072e64b5 --- /dev/null +++ b/src/3rdparty/wayland/protocols/wl-eglstream-controller.xml @@ -0,0 +1,37 @@ + + + + Copyright (c) 2017, NVIDIA CORPORATION. All rights reserved. + + Permission is hereby granted, free of charge, to any person obtaining a + copy of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom the + Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + DEALINGS IN THE SOFTWARE. + + + + + Creates the corresponding server side EGLStream from the given wl_buffer + and attaches a consumer to it. + + + + + + diff --git a/src/plugins/platforms/wayland/qwaylandintegration.cpp b/src/plugins/platforms/wayland/qwaylandintegration.cpp index e935ef31f5f..97e0203cdc9 100644 --- a/src/plugins/platforms/wayland/qwaylandintegration.cpp +++ b/src/plugins/platforms/wayland/qwaylandintegration.cpp @@ -339,6 +339,8 @@ void QWaylandIntegration::initializeClientBufferIntegration() targetKey = QString::fromLocal8Bit(clientBufferIntegrationName); } else { targetKey = mDisplay->hardwareIntegration()->clientBufferIntegration(); + if (targetKey == QLatin1String("wayland-eglstream-controller")) + targetKey = QLatin1String("wayland-egl"); } if (targetKey.isEmpty()) { From ff9e5830d367b29f86acc5e7e550154f76bc957a Mon Sep 17 00:00:00 2001 From: Johan Klokkhammer Helsing Date: Tue, 8 Jan 2019 12:15:55 +0100 Subject: [PATCH 2/2] Revert "Client: Full implementation for frame callbacks" This caused regressions because QtQuick depends on swapBuffers for throttling animations. We probably need to emulate a blocking swapBuffers and continue after a timeout, but until we have a patch for this, revert this to avoid releasing a regression. This brings back the bug with a frozen event loop when a surface is waiting for a frame callback, but this is preferable to a regression. This reverts commit 24ef6801e42b15be0f31860476199106c69e15d6. Fixes: QTBUG-72578 Change-Id: If6435a947aae5e9fd775404649a392bfafe9130a Reviewed-by: Paul Olav Tvete --- .../platforms/wayland/qwaylandwindow.cpp | 107 ++++-------------- .../platforms/wayland/qwaylandwindow_p.h | 9 +- 2 files changed, 20 insertions(+), 96 deletions(-) diff --git a/src/plugins/platforms/wayland/qwaylandwindow.cpp b/src/plugins/platforms/wayland/qwaylandwindow.cpp index 470ae00910a..282179efb53 100644 --- a/src/plugins/platforms/wayland/qwaylandwindow.cpp +++ b/src/plugins/platforms/wayland/qwaylandwindow.cpp @@ -358,8 +358,6 @@ 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; } @@ -544,11 +542,18 @@ 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) { - handleUpdate(); + mFrameCallback = frame(); + wl_callback_add_listener(mFrameCallback, &QWaylandWindow::callbackListener, this); + mWaitingForFrameSync = true; buffer->setBusy(); - QtWayland::wl_surface::attach(buffer->buffer(), x, y); + attach(buffer->buffer(), x, y); } else { QtWayland::wl_surface::attach(nullptr, 0, 0); } @@ -613,9 +618,11 @@ void QWaylandWindow::frameCallback(void *data, struct wl_callback *callback, uin Q_UNUSED(callback); QWaylandWindow *self = static_cast(data); - self->mWaitingForFrameCallback = false; - if (self->mUpdateRequested) + self->mWaitingForFrameSync = false; + if (self->mUpdateRequested) { + self->mUpdateRequested = false; self->deliverUpdateRequest(); + } } QMutex QWaylandWindow::mFrameSyncMutex; @@ -623,10 +630,10 @@ QMutex QWaylandWindow::mFrameSyncMutex; void QWaylandWindow::waitForFrameSync() { QMutexLocker locker(&mFrameSyncMutex); - if (!mWaitingForFrameCallback) + if (!mWaitingForFrameSync) return; mDisplay->flushRequests(); - while (mWaitingForFrameCallback) + while (mWaitingForFrameSync) mDisplay->blockingReadEvents(); } @@ -1027,88 +1034,12 @@ 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; - - if (!isExposed()) { - qCDebug(lcWaylandBackingstore) << "Fallback update timer: Window not exposed," - << "not delivering update request."; - return; - } - - if (mWaitingForUpdate && mUpdateRequested && !mWaitingForFrameCallback) { - qCWarning(lcWaylandBackingstore) << "Delivering update request through fallback timer," - << "may not be in sync with display"; - deliverUpdateRequest(); - } - } -} - void QWaylandWindow::requestUpdate() { - if (mUpdateRequested) - return; - - mUpdateRequested = true; - - // 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 (mUpdateRequested && !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; -} - -void QWaylandWindow::deliverUpdateRequest() -{ - mUpdateRequested = false; - mWaitingForUpdate = true; - QPlatformWindow::deliverUpdateRequest(); + if (!mWaitingForFrameSync) + QPlatformWindow::requestUpdate(); + else + mUpdateRequested = true; } void QWaylandWindow::addAttachOffset(const QPoint point) diff --git a/src/plugins/platforms/wayland/qwaylandwindow_p.h b/src/plugins/platforms/wayland/qwaylandwindow_p.h index e5838d23173..56ebd3cc641 100644 --- a/src/plugins/platforms/wayland/qwaylandwindow_p.h +++ b/src/plugins/platforms/wayland/qwaylandwindow_p.h @@ -191,10 +191,7 @@ public: bool startSystemMove(const QPoint &pos) override; - void timerEvent(QTimerEvent *event) override; void requestUpdate() override; - void handleUpdate(); - void deliverUpdateRequest() override; public slots: void applyConfigure(); @@ -214,14 +211,10 @@ protected: Qt::MouseButtons mMousePressedInContentArea = Qt::NoButton; WId mWindowId; - bool mWaitingForFrameCallback = false; + bool mWaitingForFrameSync = false; struct ::wl_callback *mFrameCallback = 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; - QMutex mResizeLock; bool mWaitingToApplyConfigure = false; bool mCanResize = true;