diff options
author | Elvis Lee <kwangwoong.lee> | 2021-08-26 17:32:09 +0900 |
---|---|---|
committer | Eskil Abrahamsen Blomfeldt <eskil.abrahamsen-blomfeldt@qt.io> | 2021-12-10 19:02:09 +0100 |
commit | 8e89118c0a33f7c865714bf86be29a475d004b51 (patch) | |
tree | e85eb99c16910a98bcaacb98160a1004a797b6de | |
parent | 0ef4f6f239c8ad590eeb1a0a6a7c8ba254305fb1 (diff) |
Support presentation-time protocol
With the protocol, client knows accurate presentation timing
which can support vsync-based rendering for smooth video
playback with video/audio synchronization.
Also, the protocol provides metrics from client to on-screen
for optimizing frame latency.
Tested with weston-presentation-shm client which also requires
xdg shell.
Change-Id: I3b286420e7221aa1c9b81bf7ae9ae70d3cbe6eb6
Reviewed-by: Eskil Abrahamsen Blomfeldt <eskil.abrahamsen-blomfeldt@qt.io>
-rw-r--r-- | src/3rdparty/protocol/presentation-time.xml | 266 | ||||
-rw-r--r-- | src/3rdparty/protocol/qt_attribution.json | 17 | ||||
-rw-r--r-- | src/compositor/CMakeLists.txt | 2 | ||||
-rw-r--r-- | src/compositor/extensions/extensions.pri | 4 | ||||
-rw-r--r-- | src/compositor/extensions/qwaylandpresentationtime.cpp | 355 | ||||
-rw-r--r-- | src/compositor/extensions/qwaylandpresentationtime_p.h | 63 | ||||
-rw-r--r-- | src/compositor/extensions/qwaylandpresentationtime_p_p.h | 107 | ||||
-rw-r--r-- | src/imports/compositor-extensions/CMakeLists.txt | 1 | ||||
-rw-r--r-- | src/imports/compositor-extensions/presentationtime/CMakeLists.txt | 19 | ||||
-rw-r--r-- | src/imports/compositor-extensions/presentationtime/qwaylandcompositorpresentationtimeplugin.cpp | 59 | ||||
-rw-r--r-- | sync.profile | 2 |
11 files changed, 895 insertions, 0 deletions
diff --git a/src/3rdparty/protocol/presentation-time.xml b/src/3rdparty/protocol/presentation-time.xml new file mode 100644 index 000000000..d1731f036 --- /dev/null +++ b/src/3rdparty/protocol/presentation-time.xml @@ -0,0 +1,266 @@ +<?xml version="1.0" encoding="UTF-8"?> +<protocol name="presentation_time"> +<!-- wrap:70 --> + + <copyright> + Copyright © 2013-2014 Collabora, Ltd. + + 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 (including the next + paragraph) 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. + </copyright> + + <interface name="wp_presentation" version="1"> + <description summary="timed presentation related wl_surface requests"> + +<!-- Introduction --> + + The main feature of this interface is accurate presentation + timing feedback to ensure smooth video playback while maintaining + audio/video synchronization. Some features use the concept of a + presentation clock, which is defined in the + presentation.clock_id event. + + A content update for a wl_surface is submitted by a + wl_surface.commit request. Request 'feedback' associates with + the wl_surface.commit and provides feedback on the content + update, particularly the final realized presentation time. + +<!-- Completing presentation --> + + When the final realized presentation time is available, e.g. + after a framebuffer flip completes, the requested + presentation_feedback.presented events are sent. The final + presentation time can differ from the compositor's predicted + display update time and the update's target time, especially + when the compositor misses its target vertical blanking period. + </description> + + <enum name="error"> + <description summary="fatal presentation errors"> + These fatal protocol errors may be emitted in response to + illegal presentation requests. + </description> + <entry name="invalid_timestamp" value="0" + summary="invalid value in tv_nsec"/> + <entry name="invalid_flag" value="1" + summary="invalid flag"/> + </enum> + + <request name="destroy" type="destructor"> + <description summary="unbind from the presentation interface"> + Informs the server that the client will no longer be using + this protocol object. Existing objects created by this object + are not affected. + </description> + </request> + + <request name="feedback"> + <description summary="request presentation feedback information"> + Request presentation feedback for the current content submission + on the given surface. This creates a new presentation_feedback + object, which will deliver the feedback information once. If + multiple presentation_feedback objects are created for the same + submission, they will all deliver the same information. + + For details on what information is returned, see the + presentation_feedback interface. + </description> + <arg name="surface" type="object" interface="wl_surface" + summary="target surface"/> + <arg name="callback" type="new_id" interface="wp_presentation_feedback" + summary="new feedback object"/> + </request> + + <event name="clock_id"> + <description summary="clock ID for timestamps"> + This event tells the client in which clock domain the + compositor interprets the timestamps used by the presentation + extension. This clock is called the presentation clock. + + The compositor sends this event when the client binds to the + presentation interface. The presentation clock does not change + during the lifetime of the client connection. + + The clock identifier is platform dependent. On Linux/glibc, + the identifier value is one of the clockid_t values accepted + by clock_gettime(). clock_gettime() is defined by + POSIX.1-2001. + + Timestamps in this clock domain are expressed as tv_sec_hi, + tv_sec_lo, tv_nsec triples, each component being an unsigned + 32-bit value. Whole seconds are in tv_sec which is a 64-bit + value combined from tv_sec_hi and tv_sec_lo, and the + additional fractional part in tv_nsec as nanoseconds. Hence, + for valid timestamps tv_nsec must be in [0, 999999999]. + + Note that clock_id applies only to the presentation clock, + and implies nothing about e.g. the timestamps used in the + Wayland core protocol input events. + + Compositors should prefer a clock which does not jump and is + not slewed e.g. by NTP. The absolute value of the clock is + irrelevant. Precision of one millisecond or better is + recommended. Clients must be able to query the current clock + value directly, not by asking the compositor. + </description> + <arg name="clk_id" type="uint" summary="platform clock identifier"/> + </event> + + </interface> + + <interface name="wp_presentation_feedback" version="1"> + <description summary="presentation time feedback event"> + A presentation_feedback object returns an indication that a + wl_surface content update has become visible to the user. + One object corresponds to one content update submission + (wl_surface.commit). There are two possible outcomes: the + content update is presented to the user, and a presentation + timestamp delivered; or, the user did not see the content + update because it was superseded or its surface destroyed, + and the content update is discarded. + + Once a presentation_feedback object has delivered a 'presented' + or 'discarded' event it is automatically destroyed. + </description> + + <event name="sync_output"> + <description summary="presentation synchronized to this output"> + As presentation can be synchronized to only one output at a + time, this event tells which output it was. This event is only + sent prior to the presented event. + + As clients may bind to the same global wl_output multiple + times, this event is sent for each bound instance that matches + the synchronized output. If a client has not bound to the + right wl_output global at all, this event is not sent. + </description> + <arg name="output" type="object" interface="wl_output" + summary="presentation output"/> + </event> + + <enum name="kind" bitfield="true"> + <description summary="bitmask of flags in presented event"> + These flags provide information about how the presentation of + the related content update was done. The intent is to help + clients assess the reliability of the feedback and the visual + quality with respect to possible tearing and timings. The + flags are: + + VSYNC: + The presentation was synchronized to the "vertical retrace" by + the display hardware such that tearing does not happen. + Relying on user space scheduling is not acceptable for this + flag. If presentation is done by a copy to the active + frontbuffer, then it must guarantee that tearing cannot + happen. + + HW_CLOCK: + The display hardware provided measurements that the hardware + driver converted into a presentation timestamp. Sampling a + clock in user space is not acceptable for this flag. + + HW_COMPLETION: + The display hardware signalled that it started using the new + image content. The opposite of this is e.g. a timer being used + to guess when the display hardware has switched to the new + image content. + + ZERO_COPY: + The presentation of this update was done zero-copy. This means + the buffer from the client was given to display hardware as + is, without copying it. Compositing with OpenGL counts as + copying, even if textured directly from the client buffer. + Possible zero-copy cases include direct scanout of a + fullscreen surface and a surface on a hardware overlay. + </description> + <entry name="vsync" value="0x1" summary="presentation was vsync'd"/> + <entry name="hw_clock" value="0x2" + summary="hardware provided the presentation timestamp"/> + <entry name="hw_completion" value="0x4" + summary="hardware signalled the start of the presentation"/> + <entry name="zero_copy" value="0x8" + summary="presentation was done zero-copy"/> + </enum> + + <event name="presented"> + <description summary="the content update was displayed"> + The associated content update was displayed to the user at the + indicated time (tv_sec_hi/lo, tv_nsec). For the interpretation of + the timestamp, see presentation.clock_id event. + + The timestamp corresponds to the time when the content update + turned into light the first time on the surface's main output. + Compositors may approximate this from the framebuffer flip + completion events from the system, and the latency of the + physical display path if known. + + This event is preceded by all related sync_output events + telling which output's refresh cycle the feedback corresponds + to, i.e. the main output for the surface. Compositors are + recommended to choose the output containing the largest part + of the wl_surface, or keeping the output they previously + chose. Having a stable presentation output association helps + clients predict future output refreshes (vblank). + + The 'refresh' argument gives the compositor's prediction of how + many nanoseconds after tv_sec, tv_nsec the very next output + refresh may occur. This is to further aid clients in + predicting future refreshes, i.e., estimating the timestamps + targeting the next few vblanks. If such prediction cannot + usefully be done, the argument is zero. + + If the output does not have a constant refresh rate, explicit + video mode switches excluded, then the refresh argument must + be zero. + + The 64-bit value combined from seq_hi and seq_lo is the value + of the output's vertical retrace counter when the content + update was first scanned out to the display. This value must + be compatible with the definition of MSC in + GLX_OML_sync_control specification. Note, that if the display + path has a non-zero latency, the time instant specified by + this counter may differ from the timestamp's. + + If the output does not have a concept of vertical retrace or a + refresh cycle, or the output device is self-refreshing without + a way to query the refresh count, then the arguments seq_hi + and seq_lo must be zero. + </description> + <arg name="tv_sec_hi" type="uint" + summary="high 32 bits of the seconds part of the presentation timestamp"/> + <arg name="tv_sec_lo" type="uint" + summary="low 32 bits of the seconds part of the presentation timestamp"/> + <arg name="tv_nsec" type="uint" + summary="nanoseconds part of the presentation timestamp"/> + <arg name="refresh" type="uint" summary="nanoseconds till next refresh"/> + <arg name="seq_hi" type="uint" + summary="high 32 bits of refresh counter"/> + <arg name="seq_lo" type="uint" + summary="low 32 bits of refresh counter"/> + <arg name="flags" type="uint" enum="kind" summary="combination of 'kind' values"/> + </event> + + <event name="discarded"> + <description summary="the content update was not displayed"> + The content update was never displayed to the user. + </description> + </event> + </interface> + +</protocol> diff --git a/src/3rdparty/protocol/qt_attribution.json b/src/3rdparty/protocol/qt_attribution.json index 745fafe5e..e5aaabcb1 100644 --- a/src/3rdparty/protocol/qt_attribution.json +++ b/src/3rdparty/protocol/qt_attribution.json @@ -249,5 +249,22 @@ "License": "MIT License", "LicenseFile": "MIT_LICENSE.txt", "Copyright": "Copyright (c) 2017, NVIDIA CORPORATION. All rights reserved." + }, + + { + "Id": "presentation-time.xml", + "Name": "Presentation Time Protocol", + "QDocModule": "qtwaylandcompositor", + "QtUsage": "Used in the Qt Wayland Compositor", + "Files": "presentation-time.xml", + + "Description": "The presentaton time protocol is a way to get presentation timing feedback.", + "Homepage": "https://wayland.freedesktop.org", + "Version": "1", + "DownloadLocation": "https://gitlab.freedesktop.org/wayland/wayland-protocols/raw/1.18/stable/presentation-time/presentation-time.xml", + "LicenseId": "MIT", + "License": "MIT License", + "LicenseFile": "MIT_LICENSE.txt", + "Copyright": "Copyright © 2013, 2014 Collabora, Ltd." } ] diff --git a/src/compositor/CMakeLists.txt b/src/compositor/CMakeLists.txt index 95118afbd..560597bb8 100644 --- a/src/compositor/CMakeLists.txt +++ b/src/compositor/CMakeLists.txt @@ -93,6 +93,7 @@ qt6_generate_wayland_protocol_server_sources(WaylandCompositor FILES ${CMAKE_CURRENT_SOURCE_DIR}/../3rdparty/protocol/idle-inhibit-unstable-v1.xml ${CMAKE_CURRENT_SOURCE_DIR}/../3rdparty/protocol/ivi-application.xml + ${CMAKE_CURRENT_SOURCE_DIR}/../3rdparty/protocol/presentation-time.xml ${CMAKE_CURRENT_SOURCE_DIR}/../3rdparty/protocol/scaler.xml ${CMAKE_CURRENT_SOURCE_DIR}/../3rdparty/protocol/text-input-unstable-v2.xml ${CMAKE_CURRENT_SOURCE_DIR}/../3rdparty/protocol/text-input-unstable-v4-wip.xml @@ -159,6 +160,7 @@ qt_internal_extend_target(WaylandCompositor CONDITION QT_FEATURE_wayland_composi extensions/qwaylandquickxdgoutputv1.cpp extensions/qwaylandquickxdgoutputv1.h extensions/qwaylandwlshellintegration.cpp extensions/qwaylandwlshellintegration_p.h extensions/qwaylandxdgshellintegration.cpp extensions/qwaylandxdgshellintegration_p.h + extensions/qwaylandpresentationtime.cpp extensions/qwaylandpresentationtime_p.h extensions/qwaylandpresentationtime_p_p.h LIBRARIES Qt::QmlPrivate Qt::QuickPrivate diff --git a/src/compositor/extensions/extensions.pri b/src/compositor/extensions/extensions.pri index f00eae571..a3d329b8f 100644 --- a/src/compositor/extensions/extensions.pri +++ b/src/compositor/extensions/extensions.pri @@ -18,6 +18,7 @@ WAYLANDSERVERSOURCES += \ ../3rdparty/protocol/ivi-application.xml \ ../3rdparty/protocol/idle-inhibit-unstable-v1.xml \ ../extensions/qt-texture-sharing-unstable-v1.xml \ + ../3rdparty/protocol/presentation-time.xml \ HEADERS += \ extensions/qwlqttouch_p.h \ @@ -82,6 +83,8 @@ qtConfig(wayland-compositor-quick) { extensions/qwaylandwlshellintegration_p.h \ extensions/qwaylandquickxdgoutputv1.h \ extensions/qwaylandxdgshellintegration_p.h \ + extensions/qwaylandpresentationtime_p.h \ + extensions/qwaylandpresentationtime_p_p.h \ SOURCES += \ extensions/qwaylandquickshellintegration.cpp \ @@ -90,6 +93,7 @@ qtConfig(wayland-compositor-quick) { extensions/qwaylandwlshellintegration.cpp \ extensions/qwaylandquickxdgoutputv1.cpp \ extensions/qwaylandxdgshellintegration.cpp \ + extensions/qwaylandpresentationtime.cpp \ qtConfig(opengl) { HEADERS += \ diff --git a/src/compositor/extensions/qwaylandpresentationtime.cpp b/src/compositor/extensions/qwaylandpresentationtime.cpp new file mode 100644 index 000000000..81bed5e87 --- /dev/null +++ b/src/compositor/extensions/qwaylandpresentationtime.cpp @@ -0,0 +1,355 @@ +/**************************************************************************** +** +** Copyright (C) 2021 LG Electronics Inc. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the QtWaylandCompositor module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:GPL$ +** 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 https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 or (at your option) any later version +** approved by the KDE Free Qt Foundation. The licenses are as published by +** the Free Software Foundation and appearing in the file LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qwaylandpresentationtime_p.h" +#include "qwaylandpresentationtime_p_p.h" + +#include <time.h> +#include <QQuickWindow> +#include <QtWaylandCompositor/QWaylandView> +#include <QtWaylandCompositor/QWaylandQuickItem> + +QT_BEGIN_NAMESPACE + +/*! + * \qmltype PresentationTime + * \instantiates QWaylandPresentationTime + * \inqmlmodule QtWayland.Compositor.PresentationTime + * \since 6.3 + * \brief Provides tracking the timing when a frame is presented on screen. + * + * The PresentationTime extension provides a way to track rendering timing + * for a surface. Client can request feedbacks associated with a surface, + * then compositor send events for the feedback with the time when the surface + * is presented on-screen. + * + * PresentationTime corresponds to the Wayland \c wp_presentation interface. + * + * To provide the functionality of the presentationtime extension in a compositor, create + * an instance of the PresentationTime component and add it to the list of extensions + * supported by the compositor: + * + * Then, call sendFeedback() when a surface is presented on screen. + * Usually, the timing can be obtained from drm page flip event. + * + * \qml + * import QtWayland.Compositor.PresentationTime + * + * WaylandCompositor { + * PresentationTime { + * id: presentationTime + * } + * } + * \endqml + */ + +/*! + * \class QWaylandPresentationTime + * \inmodule QtWaylandCompositor + * \since 6.3 + * \brief The QWaylandPresentationTime class is an extension to get timing for on-screen presentation. + * + * The QWaylandPresentationTime extension provides a way to track rendering timing + * for a surface. Client can request feedbacks associated with a surface, + * then compositor send events for the feedback with the time when the surface + * is presented on-screen. + * + * QWaylandPresentationTime corresponds to the Wayland \c wp_presentation interface. + */ + + +/*! + * Constructs a QWaylandPresentationTime object for \a compositor. + */ +QWaylandPresentationTime::QWaylandPresentationTime(QWaylandCompositor *compositor) + : QWaylandCompositorExtensionTemplate(compositor, *new QWaylandPresentationTimePrivate) +{ + +} + +/*! + * Constructs an empty QWaylandPresentationTime object. + */ +QWaylandPresentationTime::QWaylandPresentationTime() + : QWaylandCompositorExtensionTemplate(*new QWaylandPresentationTimePrivate) +{ +} + +/*! + * Initializes the extension. + */ +void QWaylandPresentationTime::initialize() +{ + Q_D(QWaylandPresentationTime); + + if (isInitialized()) { + qWarning() << "QWaylandPresentationTime is already initialized"; + return; + } + + QWaylandCompositor *compositor = this->compositor(); + if (compositor == nullptr) { + qWarning() << "Failed to find QWaylandCompositor when initializing QWaylandPresentationTime"; + return; + } + + QWaylandCompositorExtensionTemplate::initialize(); + + d->init(compositor->display(), /* version */ 1); +} + +QWaylandCompositor *QWaylandPresentationTime::compositor() const +{ + return qobject_cast<QWaylandCompositor *>(extensionContainer()); +} + +/*! + * \qmlmethod void QWaylandCompositor::PresentationTime::sendFeedback(Window window, int sequence, int sec, int nsec) + * + * Interface to notify that a frame is presented on screen. + * If your platform support drm event, page_flip_handler is proper timing to send it. + */ + +/*! + * Interface to notify that a frame is presented on screen. + * If your platform support drm event, page_flip_handler is proper timing to send it. + */ +void QWaylandPresentationTime::sendFeedback(QQuickWindow *window, quint64 sequence, quint64 tv_sec, quint32 tv_nsec) +{ + if (!window) + return; + + quint32 refresh_nsec = window->screen()->refreshRate() != 0 ? 1000000000 / window->screen()->refreshRate() : 0; + + emit presented(sequence, tv_sec, tv_nsec, refresh_nsec); +} + +/*! + * Returns the Wayland interface for the QWaylandPresentationTime. + */ +const struct wl_interface *QWaylandPresentationTime::interface() +{ + return QWaylandPresentationTimePrivate::interface(); +} + +/*! + * \internal + */ +QByteArray QWaylandPresentationTime::interfaceName() +{ + return QWaylandPresentationTimePrivate::interfaceName(); +} + +PresentationFeedback::PresentationFeedback(QWaylandPresentationTime *pTime, QWaylandSurface *surface, struct ::wl_client *client, uint32_t id, int version) + : wp_presentation_feedback(client, id, version) + , m_presentationTime(pTime) +{ + setSurface(surface); +} + +void PresentationFeedback::setSurface(QWaylandSurface *qwls) +{ + if (!qwls) { + discard(); + return; + } + + m_surface = qwls; + + connect(qwls, &QWaylandSurface::damaged, this, &PresentationFeedback::onSurfaceCommit); + connect(qwls, &QWaylandSurface::destroyed, this, &PresentationFeedback::discard); + + QWaylandView *view = qwls ? qwls->primaryView() : nullptr; + //The surface has not committed yet. + if (!view) { + connect(qwls, &QWaylandSurface::hasContentChanged, this, &PresentationFeedback::onSurfaceMapped); + return; + } + + maybeConnectToWindow(view); +} + +void PresentationFeedback::onSurfaceCommit() +{ + // There is a new commit before sync so that discard this feedback. + if (m_committed) { + discard(); + return; + } + m_committed = true; +} + +void PresentationFeedback::onSurfaceMapped() +{ + QWaylandView *view = m_surface->primaryView(); + if (!view) { + qWarning() << "The mapped surface has no view"; + discard(); + return; + } + + maybeConnectToWindow(view); +} + +void PresentationFeedback::maybeConnectToWindow(QWaylandView *view) +{ + QWaylandQuickItem *item = view ? qobject_cast<QWaylandQuickItem *>(view->renderObject()) : nullptr; + if (!item) { + qWarning() << "QWaylandPresentationTime only works with QtQuick compositors" << view; + discard(); + return; + } + + connect(item, &QQuickItem::windowChanged, this, &PresentationFeedback::onWindowChanged); + // wait for having window + if (!item->window()) { + return; + } + + connectToWindow(item->window()); +} + +void PresentationFeedback::onWindowChanged() +{ + QWaylandQuickItem *item = qobject_cast<QWaylandQuickItem *>(sender()); + QQuickWindow *window = item ? item->window() : nullptr; + + if (!window) { + qWarning() << "QWaylandPresentationTime only works with QtQuick compositors" << item; + discard(); + /* Actually, the commit is not discarded yet. If the related item has new window, + the commit can be presented on screen. So we can choose not to discard the feedback + until item has new window or the surface is destroyed. */ + return; + } + + // Check if the connected window is changed + if (m_connectedWindow && m_connectedWindow != window) + m_connectedWindow->disconnect(this); + + connectToWindow(window); +} + +void PresentationFeedback::connectToWindow(QQuickWindow *window) +{ + if (!window) { + discard(); + return; + } + + m_connectedWindow = window; + + connect(window, &QQuickWindow::beforeSynchronizing, this, &PresentationFeedback::onSync); +} + +void PresentationFeedback::onSync() +{ + QQuickWindow *window = qobject_cast<QQuickWindow *>(sender()); + + if (m_committed) { + disconnect(m_surface, &QWaylandSurface::damaged, this, &PresentationFeedback::onSurfaceCommit); + disconnect(window, &QQuickWindow::beforeSynchronizing, this, &PresentationFeedback::onSync); + connect(window, &QQuickWindow::afterFrameEnd, this, &PresentationFeedback::onSwapped); + } +} + +void PresentationFeedback::onSwapped() +{ + QQuickWindow *window = qobject_cast<QQuickWindow *>(sender()); + + disconnect(window, &QQuickWindow::afterFrameEnd, this, &PresentationFeedback::onSwapped); + connect(m_presentationTime, &QWaylandPresentationTime::presented, this, &PresentationFeedback::sendPresented); +} + +void PresentationFeedback::discard() +{ + send_discarded(); + destroy(); +} + +void PresentationFeedback::sendSyncOutput() +{ + QWaylandCompositor *compositor = presentationTime()->compositor(); + if (!compositor) { + qWarning() << "No compositor container to send sync_output"; + return; + } + + QWaylandView *view = surface()->primaryView(); + QWaylandOutput *output = view ? view->output() : nullptr; + struct ::wl_resource *r = output ? output->resourceForClient(QWaylandClient::fromWlClient(compositor, resource()->client())) : nullptr; + + if (r) + send_sync_output(r); +} + +void PresentationFeedback::sendPresented(quint64 sequence, quint64 tv_sec, quint32 tv_nsec, quint32 refresh_nsec) +{ + sendSyncOutput(); + + send_presented(tv_sec >> 32, tv_sec, tv_nsec, refresh_nsec, sequence >> 32, sequence, + QtWaylandServer::wp_presentation_feedback::kind_vsync + | QtWaylandServer::wp_presentation_feedback::kind_hw_clock + | QtWaylandServer::wp_presentation_feedback::kind_hw_completion); + + destroy(); +} + +void PresentationFeedback::destroy() +{ + wl_resource_destroy(resource()->handle); +} + +void PresentationFeedback::wp_presentation_feedback_destroy_resource(Resource *resource) +{ + Q_UNUSED(resource); + delete this; +} + +QWaylandPresentationTimePrivate::QWaylandPresentationTimePrivate() +{ +} + +void QWaylandPresentationTimePrivate::wp_presentation_bind_resource(Resource *resource) +{ + send_clock_id(resource->handle, CLOCK_MONOTONIC); +} + +void QWaylandPresentationTimePrivate::wp_presentation_feedback(Resource *resource, struct ::wl_resource *surface, uint32_t callback) +{ + Q_Q(QWaylandPresentationTime); + + QWaylandSurface *qwls = QWaylandSurface::fromResource(surface); + if (!qwls) + return; + + new PresentationFeedback(q, qwls, resource->client(), callback, /* version */ 1); +} + +QT_END_NAMESPACE diff --git a/src/compositor/extensions/qwaylandpresentationtime_p.h b/src/compositor/extensions/qwaylandpresentationtime_p.h new file mode 100644 index 000000000..0e66063d1 --- /dev/null +++ b/src/compositor/extensions/qwaylandpresentationtime_p.h @@ -0,0 +1,63 @@ +/**************************************************************************** +** +** Copyright (C) 2021 LG Electronics Inc. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the QtWaylandCompositor module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:GPL$ +** 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 https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 or (at your option) any later version +** approved by the KDE Free Qt Foundation. The licenses are as published by +** the Free Software Foundation and appearing in the file LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QWAYLANDPRESENTATIONTIME_P_H +#define QWAYLANDPRESENTATIONTIME_P_H + +#include <QObject> +#include <QtWaylandCompositor/qwaylandcompositorextension.h> + +QT_BEGIN_NAMESPACE + +class QQuickWindow; +class QWaylandPresentationTimePrivate; + +class Q_WAYLAND_COMPOSITOR_EXPORT QWaylandPresentationTime : public QWaylandCompositorExtensionTemplate<QWaylandPresentationTime> +{ + Q_OBJECT + Q_DECLARE_PRIVATE(QWaylandPresentationTime) +public: + QWaylandPresentationTime(); + QWaylandPresentationTime(QWaylandCompositor *compositor); + + QWaylandCompositor *compositor() const; + void initialize() override; + + Q_INVOKABLE void sendFeedback(QQuickWindow *window, quint64 sequence, quint64 tv_sec, quint32 tv_nsec); + + static const struct wl_interface *interface(); + static QByteArray interfaceName(); + +signals: + void presented(quint64 sequence, quint64 tv_sec, quint32 tv_nsec, quint32 refresh_nsec); +}; + +QT_END_NAMESPACE + +#endif diff --git a/src/compositor/extensions/qwaylandpresentationtime_p_p.h b/src/compositor/extensions/qwaylandpresentationtime_p_p.h new file mode 100644 index 000000000..7f984958b --- /dev/null +++ b/src/compositor/extensions/qwaylandpresentationtime_p_p.h @@ -0,0 +1,107 @@ +/**************************************************************************** +** +** Copyright (C) 2021 LG Electronics Inc. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the QtWaylandCompositor module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:GPL$ +** 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 https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 or (at your option) any later version +** approved by the KDE Free Qt Foundation. The licenses are as published by +** the Free Software Foundation and appearing in the file LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QWAYLANDPRESENTATIONTIME_P_P_H +#define QWAYLANDPRESENTATIONTIME_P_P_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include <QtWaylandCompositor/private/qwaylandcompositorextension_p.h> +#include <QtWaylandCompositor/private/qwayland-server-presentation-time.h> + +#include <QObject> +#include <QPointer> +#include <QMultiMap> + +QT_BEGIN_NAMESPACE + + +class QWaylandSurface; +class QWaylandView; +class QQuickWindow; + +class PresentationFeedback : public QObject, public QtWaylandServer::wp_presentation_feedback +{ + Q_OBJECT +public: + PresentationFeedback(QWaylandPresentationTime *, QWaylandSurface *, struct ::wl_client *, uint32_t, int); + + void setSurface(QWaylandSurface *); + QWaylandSurface *surface() { return m_surface; } + + void destroy(); + void sendSyncOutput(); + +private Q_SLOTS: + void discard(); + void onSurfaceCommit(); + void onSurfaceMapped(); + void onWindowChanged(); + void onSync(); + void onSwapped(); + void sendPresented(quint64 sequence, quint64 tv_sec, quint32 tv_nsec, quint32 refresh_nsec); + +private: + QWaylandPresentationTime *presentationTime() { return m_presentationTime; } + void maybeConnectToWindow(QWaylandView *); + void connectToWindow(QQuickWindow *); + + void wp_presentation_feedback_destroy_resource(Resource *resource) override; + +public: + QWaylandPresentationTime *m_presentationTime = nullptr; + QWaylandSurface *m_surface = nullptr; + QQuickWindow *m_connectedWindow = nullptr; + + bool m_committed = false; +}; + +class QWaylandPresentationTimePrivate : public QWaylandCompositorExtensionPrivate, public QtWaylandServer::wp_presentation +{ + Q_DECLARE_PUBLIC(QWaylandPresentationTime) +public: + QWaylandPresentationTimePrivate(); + +protected: + void wp_presentation_feedback(Resource *resource, struct ::wl_resource *surface, uint32_t callback) override; + void wp_presentation_bind_resource(Resource *resource) override; +}; + +QT_END_NAMESPACE + +#endif diff --git a/src/imports/compositor-extensions/CMakeLists.txt b/src/imports/compositor-extensions/CMakeLists.txt index 895e6c9ce..356a4c09f 100644 --- a/src/imports/compositor-extensions/CMakeLists.txt +++ b/src/imports/compositor-extensions/CMakeLists.txt @@ -4,3 +4,4 @@ add_subdirectory(xdgshell) add_subdirectory(iviapplication) add_subdirectory(wlshell) add_subdirectory(qtshell) +add_subdirectory(presentationtime) diff --git a/src/imports/compositor-extensions/presentationtime/CMakeLists.txt b/src/imports/compositor-extensions/presentationtime/CMakeLists.txt new file mode 100644 index 000000000..d94a438bf --- /dev/null +++ b/src/imports/compositor-extensions/presentationtime/CMakeLists.txt @@ -0,0 +1,19 @@ +##################################################################### +## qwaylandcompositorpresentationtimeplugin Plugin: +##################################################################### + +qt_internal_add_qml_module(WaylandCompositorPresentationTime + URI "QtWayland.Compositor.PresentationTime" + VERSION "${PROJECT_VERSION}" + CLASS_NAME QWaylandCompositorPresentationTimePlugin + PLUGIN_TARGET WaylandCompositorPresentationTime + NO_PLUGIN_OPTIONAL + NO_GENERATE_PLUGIN_SOURCE + NO_GENERATE_QMLTYPES + SOURCES + qwaylandcompositorpresentationtimeplugin.cpp + LIBRARIES + Qt::Core + Qt::Gui + Qt::WaylandCompositorPrivate +) diff --git a/src/imports/compositor-extensions/presentationtime/qwaylandcompositorpresentationtimeplugin.cpp b/src/imports/compositor-extensions/presentationtime/qwaylandcompositorpresentationtimeplugin.cpp new file mode 100644 index 000000000..05ae4a466 --- /dev/null +++ b/src/imports/compositor-extensions/presentationtime/qwaylandcompositorpresentationtimeplugin.cpp @@ -0,0 +1,59 @@ +/**************************************************************************** +** +** Copyright (C) 2021 LG Electronics Inc. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtWaylandCompositor module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:GPL$ +** 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 https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 or (at your option) any later version +** approved by the KDE Free Qt Foundation. The licenses are as published by +** the Free Software Foundation and appearing in the file LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include <QtQml/qqmlextensionplugin.h> +#include <QtQml/qqml.h> + +#include <QtWaylandCompositor/qwaylandquickextension.h> +#include <QtWaylandCompositor/private/qwaylandpresentationtime_p.h> + +QT_BEGIN_NAMESPACE + +Q_COMPOSITOR_DECLARE_QUICK_EXTENSION_CLASS(QWaylandPresentationTime) + +class QWaylandCompositorPresentationTimePlugin : public QQmlExtensionPlugin +{ + Q_OBJECT + Q_PLUGIN_METADATA(IID QQmlExtensionInterface_iid) +public: + void registerTypes(const char *uri) override + { + Q_ASSERT(QLatin1String(uri) == QLatin1String("QtWayland.Compositor.PresentationTime")); + defineModule(uri); + } + + static void defineModule(const char *uri) + { + qmlRegisterModule(uri, QT_VERSION_MAJOR, QT_VERSION_MINOR); + qmlRegisterType<QWaylandPresentationTime>(uri, 1, 0, "PresentationTime"); + } +}; +QT_END_NAMESPACE + +#include "qwaylandcompositorpresentationtimeplugin.moc" diff --git a/sync.profile b/sync.profile index 8bd0b2a92..0ce336221 100644 --- a/sync.profile +++ b/sync.profile @@ -76,6 +76,7 @@ "^qwayland-server-xdg-decoration-unstable-v1.h", "^qwayland-server-xdg-output-unstable-v1.h", "^qwayland-server-xdg-shell.h", + "^qwayland-server-presentation-time.h", "^wayland-hardware-integration-server-protocol.h", "^wayland-idle-inhibit-unstable-v1-server-protocol.h", "^wayland-ivi-application-server-protocol.h", @@ -95,6 +96,7 @@ "^wayland-xdg-shell-server-protocol.h", "^qwayland-server-qt-shell-unstable-v1.h", "^wayland-qt-shell-unstable-v1-server-protocol.h", + "^wayland-presentation-time-server-protocol.h", ], "$basedir/src/plugins/hardwareintegration/compositor/linux-dmabuf-unstable-v1" => [ "^qwayland-server-linux-dmabuf-unstable-v1.h", |