Qt
Internal/Contributor docs for the Qt SDK. Note: These are NOT official API docs; those are found at https://doc.qt.io/
Loading...
Searching...
No Matches
qwaylandpresentationtime.cpp
Go to the documentation of this file.
1// Copyright (C) 2021 LG Electronics Inc.
2// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
3// Qt-Security score:critical reason:network-protocol
4
7
8#include <time.h>
9#include <QQuickWindow>
10#include <QtWaylandCompositor/QWaylandView>
11#include <QtWaylandCompositor/QWaylandQuickItem>
12
13QT_BEGIN_NAMESPACE
14
15/*!
16 * \qmltype PresentationTime
17 * \nativetype QWaylandPresentationTime
18 * \inqmlmodule QtWayland.Compositor.PresentationTime
19 * \since 6.3
20 * \brief Provides tracking the timing when a frame is presented on screen.
21 *
22 * The PresentationTime extension provides a way to track rendering timing
23 * for a surface. Client can request feedbacks associated with a surface,
24 * then compositor send events for the feedback with the time when the surface
25 * is presented on-screen.
26 *
27 * PresentationTime corresponds to the Wayland \c wp_presentation interface.
28 *
29 * To provide the functionality of the presentationtime extension in a compositor, create
30 * an instance of the PresentationTime component and add it to the list of extensions
31 * supported by the compositor:
32 *
33 * Then, call sendFeedback() when a surface is presented on screen.
34 * Usually, the timing can be obtained from drm page flip event.
35 *
36 * \qml
37 * import QtWayland.Compositor.PresentationTime
38 *
39 * WaylandCompositor {
40 * PresentationTime {
41 * id: presentationTime
42 * }
43 * }
44 * \endqml
45 */
46
47/*!
48 * \class QWaylandPresentationTime
49 * \inmodule QtWaylandCompositor
50 * \since 6.3
51 * \brief The QWaylandPresentationTime class is an extension to get timing for on-screen presentation.
52 *
53 * The QWaylandPresentationTime extension provides a way to track rendering timing
54 * for a surface. Client can request feedbacks associated with a surface,
55 * then compositor send events for the feedback with the time when the surface
56 * is presented on-screen.
57 *
58 * QWaylandPresentationTime corresponds to the Wayland \c wp_presentation interface.
59 */
60
61
62/*!
63 * Constructs a QWaylandPresentationTime object for \a compositor.
64 */
65QWaylandPresentationTime::QWaylandPresentationTime(QWaylandCompositor *compositor)
66 : QWaylandCompositorExtensionTemplate(compositor, *new QWaylandPresentationTimePrivate)
67{
68
69}
70
71/*!
72 * Constructs an empty QWaylandPresentationTime object.
73 */
74QWaylandPresentationTime::QWaylandPresentationTime()
75 : QWaylandCompositorExtensionTemplate(*new QWaylandPresentationTimePrivate)
76{
77}
78
79/*!
80 * Initializes the extension.
81 */
82void QWaylandPresentationTime::initialize()
83{
84 Q_D(QWaylandPresentationTime);
85
86 if (isInitialized()) {
87 qWarning() << "QWaylandPresentationTime is already initialized";
88 return;
89 }
90
91 QWaylandCompositor *compositor = this->compositor();
92 if (compositor == nullptr) {
93 qWarning() << "Failed to find QWaylandCompositor when initializing QWaylandPresentationTime";
94 return;
95 }
96
97 QWaylandCompositorExtensionTemplate::initialize();
98
99 d->init(compositor->display(), /* version */ 1);
100}
101
102QWaylandCompositor *QWaylandPresentationTime::compositor() const
103{
104 return qobject_cast<QWaylandCompositor *>(extensionContainer());
105}
106
107/*!
108 * \qmlmethod void PresentationTime::sendFeedback(Window window, int sequence, int sec, int nsec)
109 *
110 * Interface to notify that a frame is presented on screen using \a window.
111 * If your platform supports DRM events, \c page_flip_handler is the proper timing to send it.
112 * The \a sequence is the refresh counter. \a sec and \a nsec hold the
113 * seconds and nanoseconds parts of the presentation timestamp, respectively.
114 */
115
116/*!
117 * Interface to notify that a frame is presented on screen using \a window.
118 * If your platform supports DRM events, \c page_flip_handler is the proper timing to send it.
119 * The \a sequence is the refresh counter. \a tv_sec and \a tv_nsec hold the
120 * seconds and nanoseconds parts of the presentation timestamp, respectively.
121 */
122void QWaylandPresentationTime::sendFeedback(QQuickWindow *window, quint64 sequence, quint64 tv_sec, quint32 tv_nsec)
123{
124 if (!window)
125 return;
126
127 quint32 refresh_nsec = window->screen()->refreshRate() != 0 ? 1000000000 / window->screen()->refreshRate() : 0;
128
129 emit presented(sequence, tv_sec, tv_nsec, refresh_nsec);
130}
131
132/*!
133 * Returns the Wayland interface for the QWaylandPresentationTime.
134 */
135const struct wl_interface *QWaylandPresentationTime::interface()
136{
137 return QWaylandPresentationTimePrivate::interface();
138}
139
140/*!
141 * \internal
142 */
143QByteArray QWaylandPresentationTime::interfaceName()
144{
145 return QWaylandPresentationTimePrivate::interfaceName();
146}
147
148PresentationFeedback::PresentationFeedback(QWaylandPresentationTime *pTime, QWaylandSurface *surface, struct ::wl_client *client, uint32_t id, int version)
149 : wp_presentation_feedback(client, id, version)
150 , m_presentationTime(pTime)
151{
152 setSurface(surface);
153}
154
155void PresentationFeedback::setSurface(QWaylandSurface *qwls)
156{
157 if (!qwls) {
158 discard();
159 return;
160 }
161
162 m_surface = qwls;
163
164 connect(qwls, &QWaylandSurface::damaged, this, &PresentationFeedback::onSurfaceCommit);
165 connect(qwls, &QWaylandSurface::destroyed, this, &PresentationFeedback::discard);
166
167 QWaylandView *view = qwls ? qwls->primaryView() : nullptr;
168 //The surface has not committed yet.
169 if (!view) {
170 connect(qwls, &QWaylandSurface::hasContentChanged, this, &PresentationFeedback::onSurfaceMapped);
171 return;
172 }
173
174 maybeConnectToWindow(view);
175}
176
177void PresentationFeedback::onSurfaceCommit()
178{
179 // There is a new commit before sync so that discard this feedback.
180 if (m_committed) {
181 discard();
182 return;
183 }
184 m_committed = true;
185}
186
187void PresentationFeedback::onSurfaceMapped()
188{
189 QWaylandView *view = m_surface->primaryView();
190 if (!view) {
191 qWarning() << "The mapped surface has no view";
192 discard();
193 return;
194 }
195
196 maybeConnectToWindow(view);
197}
198
199void PresentationFeedback::maybeConnectToWindow(QWaylandView *view)
200{
201 QWaylandQuickItem *item = view ? qobject_cast<QWaylandQuickItem *>(view->renderObject()) : nullptr;
202 if (!item) {
203 qWarning() << "QWaylandPresentationTime only works with QtQuick compositors" << view;
204 discard();
205 return;
206 }
207
208 connect(item, &QQuickItem::windowChanged, this, &PresentationFeedback::onWindowChanged);
209 // wait for having window
210 if (!item->window()) {
211 return;
212 }
213
214 connectToWindow(item->window());
215}
216
217void PresentationFeedback::onWindowChanged()
218{
219 QWaylandQuickItem *item = qobject_cast<QWaylandQuickItem *>(sender());
220 QQuickWindow *window = item ? item->window() : nullptr;
221
222 if (!window) {
223 qWarning() << "QWaylandPresentationTime only works with QtQuick compositors" << item;
224 discard();
225 /* Actually, the commit is not discarded yet. If the related item has new window,
226 the commit can be presented on screen. So we can choose not to discard the feedback
227 until item has new window or the surface is destroyed. */
228 return;
229 }
230
231 // Check if the connected window is changed
232 if (m_connectedWindow && m_connectedWindow != window)
233 m_connectedWindow->disconnect(this);
234
235 connectToWindow(window);
236}
237
238void PresentationFeedback::connectToWindow(QQuickWindow *window)
239{
240 if (!window) {
241 discard();
242 return;
243 }
244
245 m_connectedWindow = window;
246
247 connect(window, &QQuickWindow::beforeSynchronizing, this, &PresentationFeedback::onSync);
248 connect(window, &QQuickWindow::afterFrameEnd, this, &PresentationFeedback::onSwapped);
249}
250
251void PresentationFeedback::onSync()
252{
253 QQuickWindow *window = qobject_cast<QQuickWindow *>(sender());
254
255 if (m_committed) {
256 disconnect(m_surface, &QWaylandSurface::damaged, this, &PresentationFeedback::onSurfaceCommit);
257 disconnect(window, &QQuickWindow::beforeSynchronizing, this, &PresentationFeedback::onSync);
258 m_sync = true;
259 }
260}
261
262void PresentationFeedback::onSwapped()
263{
264 QQuickWindow *window = qobject_cast<QQuickWindow *>(sender());
265
266 if (m_sync) {
267 disconnect(window, &QQuickWindow::afterFrameEnd, this, &PresentationFeedback::onSwapped);
268 connect(m_presentationTime, &QWaylandPresentationTime::presented, this, &PresentationFeedback::sendPresented);
269 }
270}
271
272void PresentationFeedback::discard()
273{
274 send_discarded();
275 destroy();
276}
277
279{
280 QWaylandCompositor *compositor = presentationTime()->compositor();
281 if (!compositor) {
282 qWarning() << "No compositor container to send sync_output";
283 return;
284 }
285
286 QWaylandView *view = surface()->primaryView();
287 QWaylandOutput *output = view ? view->output() : nullptr;
288 struct ::wl_resource *r = output ? output->resourceForClient(QWaylandClient::fromWlClient(compositor, resource()->client())) : nullptr;
289
290 if (r)
291 send_sync_output(r);
292}
293
294void PresentationFeedback::sendPresented(quint64 sequence, quint64 tv_sec, quint32 tv_nsec, quint32 refresh_nsec)
295{
297
298 send_presented(tv_sec >> 32, tv_sec, tv_nsec, refresh_nsec, sequence >> 32, sequence,
299 QtWaylandServer::wp_presentation_feedback::kind_vsync
300 | QtWaylandServer::wp_presentation_feedback::kind_hw_clock
301 | QtWaylandServer::wp_presentation_feedback::kind_hw_completion);
302
303 destroy();
304}
305
307{
308 wl_resource_destroy(resource()->handle);
309}
310
312{
313 Q_UNUSED(resource);
314 delete this;
315}
316
317QWaylandPresentationTimePrivate::QWaylandPresentationTimePrivate()
318{
319}
320
322{
323 send_clock_id(resource->handle, CLOCK_MONOTONIC);
324}
325
326void QWaylandPresentationTimePrivate::wp_presentation_feedback(Resource *resource, struct ::wl_resource *surface, uint32_t callback)
327{
328 Q_Q(QWaylandPresentationTime);
329
330 QWaylandSurface *qwls = QWaylandSurface::fromResource(surface);
331 if (!qwls)
332 return;
333
334 new PresentationFeedback(q, qwls, resource->client(), callback, /* version */ 1);
335}
336
337QT_END_NAMESPACE
338
339#include "moc_qwaylandpresentationtime_p_p.cpp"
340
341#include "moc_qwaylandpresentationtime_p.cpp"
void setSurface(QWaylandSurface *)
void wp_presentation_feedback_destroy_resource(Resource *resource) override
void wp_presentation_bind_resource(Resource *resource) override