Qt
Internal/Contributor docs for the Qt SDK. <b>Note:</b> These are NOT official API docs; those are found <a href='https://doc.qt.io/'>here</a>.
Loading...
Searching...
No Matches
qquickanimatorjob.cpp
Go to the documentation of this file.
1// Copyright (C) 2016 The Qt Company Ltd.
2// Copyright (C) 2016 Gunnar Sletta <gunnar@sletta.org>
3// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
4
7#include "qquickanimator_p.h"
9#include <private/qquickitem_p.h>
10#if QT_CONFIG(quick_shadereffect)
11#include <private/qquickshadereffect_p.h>
12#endif
13#include <private/qanimationgroupjob_p.h>
14
15#include <qcoreapplication.h>
16#include <qdebug.h>
17
19
21{
22 QHash<QQuickItem *, QQuickTransformAnimatorJob::Helper *> store;
24
26 mutex.lock();
28 if (!helper) {
30 helper->item = item;
31 store[item] = helper;
32 } else {
33 ++helper->ref;
34 }
35 mutex.unlock();
36 return helper;
37 }
38
40 mutex.lock();
41 if (--helper->ref == 0) {
42 store.remove(helper->item);
43 delete helper;
44 }
45 mutex.unlock();
46 }
47};
48Q_GLOBAL_STATIC(QQuickTransformAnimatorHelperStore, qquick_transform_animatorjob_helper_store);
49
52 : m_controller(nullptr)
53 , m_internalState(State_Stopped)
54{
55 m_job.reset(job);
56
58
60
61 // Instead of setting duration to job->duration() we need to set it to -1 so that
62 // it runs as long as the job is running on the render thread. If we gave it
63 // an explicit duration, it would be stopped, potentially stopping the RT animation
64 // prematurely.
65 // This means that the animation driver will tick on the GUI thread as long
66 // as the animation is running on the render thread, but this overhead will
67 // be negligiblie compared to animating and re-rendering the scene on the render thread.
68 m_duration = -1;
69
70 QObject *ctx = findAnimationContext(animation);
71 if (!ctx) {
72 qWarning("QtQuick: unable to find animation context for RT animation...");
73 return;
74 }
75
76 QQuickWindow *window = qobject_cast<QQuickWindow *>(ctx);
77 if (window) {
78 setWindow(window);
79 } else {
81 if (item->window())
82 setWindow(item->window());
83 connect(item, &QQuickItem::windowChanged, this, &QQuickAnimatorProxyJob::windowChanged);
84 }
85}
86
88{
89 m_job->setLoopCount(loopCount);
90}
91
93{
94 if (m_job && m_controller)
95 m_controller->cancel(m_job);
96 m_job.reset();
97}
98
99QObject *QQuickAnimatorProxyJob::findAnimationContext(QQuickAbstractAnimation *a)
100{
101 QObject *p = a->parent();
102 while (p != nullptr && qobject_cast<QQuickWindow *>(p) == nullptr && qobject_cast<QQuickItem *>(p) == nullptr)
103 p = p->parent();
104 return p;
105}
106
108{
109 if (m_internalState != State_Running)
110 return;
111
112 // Copy current loop number from the job
113 // we could make currentLoop() virtual but it would be less efficient
114 m_currentLoop = m_job->currentLoop();
115
116 // A proxy which is being ticked should be associated with a window, (see
117 // setWindow() below). If we get here when there is no more controller we
118 // have a problem.
119 Q_ASSERT(m_controller);
120
121 // We do a simple check here to see if the animator has run and stopped on
122 // the render thread. isPendingStart() will perform a check against jobs
123 // that have been scheduled for start, but that will not yet have entered
124 // the actual running state.
125 // Secondly, we make an unprotected read of the job's state to figure out
126 // if it is running, but this is ok, since we're only reading the state
127 // and if the render thread should happen to be writing it concurrently,
128 // we might get the wrong value for this update, but then we'll simply
129 // pick it up on the next iterationm when the job is stopped and render
130 // thread is no longer using it.
131 if (!m_controller->isPendingStart(m_job)
132 && !m_job->isRunning()) {
133 stop();
134 }
135}
136
138{
139 if (m_state == Running) {
140 m_internalState = State_Starting;
141 if (m_controller) {
142 m_internalState = State_Running;
143 m_controller->start(m_job);
144 }
145
146 } else if (newState == Stopped) {
147 m_internalState = State_Stopped;
148 if (m_controller) {
149 syncBackCurrentValues();
150 m_controller->cancel(m_job);
151 }
152 }
153}
154
156{
157 d << "QuickAnimatorProxyJob("<< Qt::hex << (const void *) this << Qt::dec
158 << "state:" << state() << "duration:" << duration()
159 << "proxying: (" << job() << ')';
160}
161
166
167void QQuickAnimatorProxyJob::setWindow(QQuickWindow *window)
168{
169 if (!window) {
170 if (m_job && m_controller) {
173 m_controller->cancel(m_job);
174 }
175
176 m_controller = nullptr;
177 stop();
178
179 } else if (!m_controller && m_job) {
180 m_controller = QQuickWindowPrivate::get(window)->animationController.get();
181 if (window->isSceneGraphInitialized())
182 readyToAnimate();
183 else
185 }
186}
187
189{
190 if (m_controller) {
192 readyToAnimate();
193 }
194}
195
196void QQuickAnimatorProxyJob::readyToAnimate()
197{
198 Q_ASSERT(m_controller);
199 if (m_internalState == State_Starting) {
200 m_internalState = State_Running;
201 m_controller->start(m_job);
202 }
203}
204
206{
207 if (job->isRenderThreadJob()) {
208 static_cast<QQuickAnimatorJob *>(job)->writeBack();
209
210 } else if (job->isGroup()) {
211 QAnimationGroupJob *g = static_cast<QAnimationGroupJob *>(job);
212 for (QAbstractAnimationJob *a : *g->children())
214 }
215
216}
217
218void QQuickAnimatorProxyJob::syncBackCurrentValues()
219{
220 if (m_job)
222}
223
225 : m_target(nullptr)
226 , m_controller(nullptr)
227 , m_from(0)
228 , m_to(0)
229 , m_value(0)
230 , m_duration(0)
231 , m_isTransform(false)
232 , m_isUniform(false)
233{
234 m_isRenderThreadJob = true;
235}
236
238{
239 d << "QuickAnimatorJob(" << Qt::hex << (const void *) this << Qt::dec
240 << ") state:" << state() << "duration:" << duration()
241 << "target:" << m_target << "value:" << m_value;
242}
243
248
250{
251 qreal rangeMin = m_from;
252 qreal rangeMax = m_to;
253 if (m_from > m_to) {
254 rangeMax = m_from;
255 rangeMin = m_to;
256 }
257 m_value = qBound(rangeMin, m_value, rangeMax);
258}
259
261{
262 qreal value = m_to;
263 if (m_controller) {
265 value = m_value;
267 }
268 return value;
269}
270
275
280
286
288{
289 if (m_helper)
290 qquick_transform_animatorjob_helper_store()->release(m_helper);
291}
292
294{
295 // In the extremely unlikely event that the target of an animator has been
296 // changed into a new item that sits in the exact same pointer address, we
297 // want to force syncing it again.
298 if (m_helper && m_target)
299 m_helper->wasSynced = false;
301}
302
304{
305 // If the target has changed or become null, release and reset the helper
306 if (m_helper && (m_helper->item != m_target || !m_target)) {
307 qquick_transform_animatorjob_helper_store()->release(m_helper);
308 m_helper = nullptr;
309 }
310
311 if (!m_target) {
312 invalidate();
313 return;
314 }
315
316 if (!m_helper) {
317 m_helper = qquick_transform_animatorjob_helper_store()->acquire(m_target);
318
319 // This is a bit superfluous, but it ends up being simpler than the
320 // alternative. When an item happens to land on the same address as a
321 // previous item, that helper might not have been fully cleaned up by
322 // the time it gets taken back into use. As an alternative to storing
323 // connections to each and every item's QObject::destroyed() and
324 // having to clean those up afterwards, we simply sync all helpers on
325 // the first run. The sync is only done once for the run of an
326 // animation and it is a fairly light function (compared to storing
327 // potentially thousands of connections and managing their lifetime.
328 m_helper->wasSynced = false;
329 }
330
331 m_helper->sync();
332}
333
335{
336 if (m_helper)
337 m_helper->node = nullptr;
338}
339
341{
346
348#if QT_CONFIG(quick_shadereffect)
349 if (d->extra.isAllocated()
350 && d->extra->layer
351 && d->extra->layer->enabled()) {
352 d = QQuickItemPrivate::get(d->extra->layer->m_effectSource);
353 }
354#endif
355
356 quint32 dirty = mask & d->dirtyAttributes;
357
358 if (!wasSynced) {
359 dirty = 0xffffffffu;
360 wasSynced = true;
361 }
362
363 // We update the node before checking on dirty, as the node might have changed without the animator running
364 node = d->itemNode();
365
366 if (dirty == 0)
367 return;
368
369 if (dirty & QQuickItemPrivate::Position) {
370 dx = item->x();
371 dy = item->y();
372 }
373
375 scale = item->scale();
377 }
378
381 ox = o.x();
382 oy = o.y();
383 }
384}
385
387{
388 if (!wasChanged || !node)
389 return;
390
392 m.translate(dx, dy);
393 m.translate(ox, oy);
394 m.scale(scale);
395 m.rotate(rotation, 0, 0, 1);
396 m.translate(-ox, -oy);
397 node->setMatrix(m);
398
399 wasChanged = false;
400}
401
407
409{
410 if (m_target)
411 m_target->setX(value());
412}
413
415{
416 if (!m_helper)
417 return;
418
421 m_helper->wasChanged = true;
422}
423
425{
426 if (m_target)
427 m_target->setY(value());
428}
429
431{
432 if (!m_helper)
433 return;
434
437 m_helper->wasChanged = true;
438}
439
445
447{
448 if (!m_helper)
449 return;
450
453 m_helper->wasChanged = true;
454}
455
456
461
465
467{
468 if (!m_helper)
469 return;
470
471 float t = progress(time);
472
473 switch (m_direction) {
476 // The logic in _q_interpolateClockwise comes out a bit wrong
477 // for the case of X->0 where 0<X<360. It ends on 360 which it
478 // shouldn't.
479 if (t == 1)
480 m_value = m_to;
481 break;
484 break;
487 break;
489 m_value = m_from + (m_to - m_from) * t;
490 break;
491 }
493 m_helper->wasChanged = true;
494}
495
501
502
507
509{
510 if (!m_target) {
511 invalidate();
512 return;
513 }
514
516#if QT_CONFIG(quick_shadereffect)
517 if (d->extra.isAllocated()
518 && d->extra->layer
519 && d->extra->layer->enabled()) {
520 d = QQuickItemPrivate::get(d->extra->layer->m_effectSource);
521 }
522#endif
523
524 m_opacityNode = d->opacityNode();
525
526 if (!m_opacityNode) {
527 m_opacityNode = new QSGOpacityNode();
528
529 /* The item node subtree is like this
530 *
531 * itemNode
532 * (opacityNode) optional
533 * (clipNode) optional
534 * (rootNode) optional
535 * children / paintNode
536 *
537 * If the opacity node doesn't exist, we need to insert it into
538 * the hierarchy between itemNode and clipNode or rootNode. If
539 * neither clip or root exists, we need to reparent all children
540 * from itemNode to opacityNode.
541 */
542 QSGNode *iNode = d->itemNode();
543 QSGNode *child = d->childContainerNode();
544 if (child != iNode) {
545 if (child->parent())
546 child->parent()->removeChildNode(child);
547 m_opacityNode->appendChildNode(child);
548 iNode->appendChildNode(m_opacityNode);
549 } else {
550 iNode->reparentChildNodesTo(m_opacityNode);
551 iNode->appendChildNode(m_opacityNode);
552 }
553
554 d->extra.value().opacityNode = m_opacityNode;
556 }
557 Q_ASSERT(m_opacityNode);
558}
559
561{
562 m_opacityNode = nullptr;
563}
564
570
572{
573 if (!m_opacityNode)
574 return;
575
577 m_opacityNode->setOpacity(m_value);
578}
579
580#if QT_CONFIG(quick_shadereffect)
581QQuickUniformAnimatorJob::QQuickUniformAnimatorJob()
582{
583 m_isUniform = true;
584}
585
586void QQuickUniformAnimatorJob::setTarget(QQuickItem *target)
587{
588 // Check target is of expected type
589 if (qobject_cast<QQuickShaderEffect *>(target) != nullptr)
590 m_target = target;
591}
592
593void QQuickUniformAnimatorJob::updateCurrentTime(int time)
594{
595 if (!m_effect || m_target != m_effect)
596 return;
597
598 m_value = m_from + (m_to - m_from) * progress(time);
599 m_effect->updateUniformValue(m_uniform, m_value);
600}
601
602void QQuickUniformAnimatorJob::writeBack()
603{
604 if (m_target)
605 m_target->setProperty(m_uniform, value());
606}
607
608void QQuickUniformAnimatorJob::postSync()
609{
610 if (!m_target) {
611 invalidate();
612 return;
613 }
614
615 m_effect = qobject_cast<QQuickShaderEffect *>(m_target);
616}
617
618void QQuickUniformAnimatorJob::invalidate()
619{
620
621}
622#endif
623
625
626#include "moc_qquickanimatorjob_p.cpp"
QAbstractAnimationJob::State m_state
QAbstractAnimationJob::State state() const
void setLoopCount(int loopCount)
QAbstractAnimationJob::Direction m_direction
\inmodule QtCore
qreal valueForProgress(qreal progress) const
Return the effective progress for the easing curve at progress.
QGraphicsWidget * window() const
bool remove(const Key &key)
Removes the item that has the key from the hash.
Definition qhash.h:958
T value(const Key &key) const noexcept
Definition qhash.h:1054
The QMatrix4x4 class represents a 4x4 transformation matrix in 3D space.
Definition qmatrix4x4.h:25
void translate(const QVector3D &vector)
Multiplies this matrix by another that translates coordinates by the components of vector.
\inmodule QtCore
Definition qmutex.h:281
void unlock() noexcept
Unlocks the mutex.
Definition qmutex.h:289
void lock() noexcept
Locks the mutex.
Definition qmutex.h:286
\inmodule QtCore
Definition qobject.h:103
QObject * parent() const
Returns a pointer to the parent object.
Definition qobject.h:346
static QMetaObject::Connection connect(const QObject *sender, const char *signal, const QObject *receiver, const char *member, Qt::ConnectionType=Qt::AutoConnection)
\threadsafe
Definition qobject.cpp:2960
\inmodule QtCore\reentrant
Definition qpoint.h:217
bool isPendingStart(const QSharedPointer< QAbstractAnimationJob > &job) const
void start(const QSharedPointer< QAbstractAnimationJob > &job)
void cancel(const QSharedPointer< QAbstractAnimationJob > &job)
void debugAnimation(QDebug d) const override
QPointer< QQuickItem > m_target
QQuickItem * target() const
virtual void setTarget(QQuickItem *target)
QQuickAnimatorController * m_controller
QQuickAnimatorController * controller() const
qreal progress(int time) const
virtual void initialize(QQuickAnimatorController *controller)
int duration() const override
void updateLoopCount(int) override
int duration() const override
const QSharedPointer< QAbstractAnimationJob > & job() const
QQuickAnimatorProxyJob(QAbstractAnimationJob *job, QQuickAbstractAnimation *animation)
void updateState(QAbstractAnimationJob::State newState, QAbstractAnimationJob::State oldState) override
void debugAnimation(QDebug d) const override
void windowChanged(QQuickWindow *window)
void updateCurrentTime(int) override
static QQuickItemPrivate * get(QQuickItem *item)
The QQuickItem class provides the most basic of all visual items in \l {Qt Quick}.
Definition qquickitem.h:63
void setOpacity(qreal)
void setScale(qreal)
void setRotation(qreal)
qreal x
\qmlproperty real QtQuick::Item::x \qmlproperty real QtQuick::Item::y \qmlproperty real QtQuick::Item...
Definition qquickitem.h:72
qreal y
Defines the item's y position relative to its parent.
Definition qquickitem.h:73
qreal rotation
\qmlproperty real QtQuick::Item::rotation This property holds the rotation of the item in degrees clo...
Definition qquickitem.h:106
qreal scale
\qmlproperty real QtQuick::Item::scale This property holds the scale factor for this item.
Definition qquickitem.h:107
void setX(qreal)
void setY(qreal)
QPointF transformOriginPoint
Definition qquickitem.h:109
void updateCurrentTime(int time) override
void updateCurrentTime(int time) override
void updateCurrentTime(int time) override
void setTarget(QQuickItem *item) override
static QQuickWindowPrivate * get(QQuickWindow *c)
\qmltype Window \instantiates QQuickWindow \inqmlmodule QtQuick
void sceneGraphInitialized()
\qmlsignal QtQuick::Window::frameSwapped()
void updateCurrentTime(int time) override
void writeBack() override
void updateCurrentTime(int time) override
void writeBack() override
\group qtquick-scenegraph-nodes \title Qt Quick Scene Graph Node classes
Definition qsgnode.h:37
void appendChildNode(QSGNode *node)
Appends node to this node's list of children.
Definition qsgnode.cpp:398
The QSGOpacityNode class is used to change opacity of nodes.
Definition qsgnode.h:276
void setOpacity(qreal opacity)
Sets the opacity of this node to opacity.
Definition qsgnode.cpp:1312
T * data() const noexcept
Returns the value of the pointer referenced by this object.
\inmodule QtCore
Definition qvariant.h:65
float toFloat(bool *ok=nullptr) const
Returns the variant as a float if the variant has userType() \l QMetaType::Double,...
EGLContext ctx
void newState(QList< State > &states, const char *token, const char *lexem, bool pre)
Combined button and popup list for selecting options.
QTextStream & hex(QTextStream &stream)
Calls QTextStream::setIntegerBase(16) on stream and returns stream.
QTextStream & dec(QTextStream &stream)
Calls QTextStream::setIntegerBase(10) on stream and returns stream.
QPointer< QCocoaFileDialogHelper > m_helper
EGLOutputLayerEXT EGLint EGLAttrib value
[5]
#define Q_GLOBAL_STATIC(TYPE, NAME,...)
#define qWarning
Definition qlogging.h:166
constexpr const T & qBound(const T &min, const T &val, const T &max)
Definition qminmax.h:44
const GLfloat * m
GLboolean GLboolean GLboolean GLboolean a
[7]
GLfloat GLfloat f
GLenum target
GLboolean GLboolean g
GLint GLint GLint GLint GLint GLint GLint GLbitfield mask
GLdouble GLdouble t
Definition qopenglext.h:243
GLfloat GLfloat p
[1]
GLenum GLenum GLenum GLenum GLenum scale
QVariant _q_interpolateClockwiseRotation(qreal &f, qreal &t, qreal progress)
QVariant _q_interpolateShortestRotation(qreal &f, qreal &t, qreal progress)
\qmltype RotationAnimation \instantiates QQuickRotationAnimation \inqmlmodule QtQuick\inherits Proper...
QVariant _q_interpolateCounterclockwiseRotation(qreal &f, qreal &t, qreal progress)
QVariant _q_interpolateClockwiseRotation(qreal &f, qreal &t, qreal progress)
static void qquick_syncback_helper(QAbstractAnimationJob *job)
QVariant _q_interpolateShortestRotation(qreal &f, qreal &t, qreal progress)
\qmltype RotationAnimation \instantiates QQuickRotationAnimation \inqmlmodule QtQuick\inherits Proper...
QVariant _q_interpolateCounterclockwiseRotation(qreal &f, qreal &t, qreal progress)
QQuickItem * qobject_cast< QQuickItem * >(QObject *o)
Definition qquickitem.h:492
#define Q_ASSERT(cond)
Definition qrandom.cpp:47
unsigned int quint32
Definition qtypes.h:50
double qreal
Definition qtypes.h:187
QObject::connect nullptr
myObject disconnect()
[26]
QPropertyAnimation animation
[0]
QGraphicsItem * item
QLayoutItem * child
[0]
aWidget window() -> setWindowTitle("New Window Title")
[2]
void release(QQuickTransformAnimatorJob::Helper *helper)
QHash< QQuickItem *, QQuickTransformAnimatorJob::Helper * > store
QQuickTransformAnimatorJob::Helper * acquire(QQuickItem *item)