summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorElvis Lee <kwangwoong.lee>2021-08-26 17:32:09 +0900
committerEskil Abrahamsen Blomfeldt <eskil.abrahamsen-blomfeldt@qt.io>2021-12-10 19:02:09 +0100
commit8e89118c0a33f7c865714bf86be29a475d004b51 (patch)
treee85eb99c16910a98bcaacb98160a1004a797b6de
parent0ef4f6f239c8ad590eeb1a0a6a7c8ba254305fb1 (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.xml266
-rw-r--r--src/3rdparty/protocol/qt_attribution.json17
-rw-r--r--src/compositor/CMakeLists.txt2
-rw-r--r--src/compositor/extensions/extensions.pri4
-rw-r--r--src/compositor/extensions/qwaylandpresentationtime.cpp355
-rw-r--r--src/compositor/extensions/qwaylandpresentationtime_p.h63
-rw-r--r--src/compositor/extensions/qwaylandpresentationtime_p_p.h107
-rw-r--r--src/imports/compositor-extensions/CMakeLists.txt1
-rw-r--r--src/imports/compositor-extensions/presentationtime/CMakeLists.txt19
-rw-r--r--src/imports/compositor-extensions/presentationtime/qwaylandcompositorpresentationtimeplugin.cpp59
-rw-r--r--sync.profile2
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",