1// Copyright (C) 2016 The Qt Company Ltd.
2// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
3
4#include "qsgnode.h"
5#include "qsgnode_p.h"
6#include "qsgrenderer_p.h"
7#include "qsgnodeupdater_p.h"
8#include "qsgmaterial.h"
9
10#include "limits.h"
11
12QT_BEGIN_NAMESPACE
13
14Q_DECLARE_LOGGING_CATEGORY(lcQsgLeak)
15
16#ifndef QT_NO_DEBUG
17static int qt_node_count = 0;
18
19static void qt_print_node_count()
20{
21 qCDebug(lcQsgLeak, "Number of leaked nodes: %i", qt_node_count);
22 qt_node_count = -1;
23}
24#endif
25
26/*!
27 \group qtquick-scenegraph-nodes
28 \title Qt Quick Scene Graph Node classes
29 \brief Nodes that can be used as part of the scene graph.
30
31 This page lists the nodes in \l {Qt Quick}'s \l {scene graph}{Qt Quick Scene Graph}.
32 */
33
34/*!
35 \class QSGNode
36 \brief The QSGNode class is the base class for all nodes in the scene graph.
37
38 \inmodule QtQuick
39 \ingroup qtquick-scenegraph-nodes
40
41 The QSGNode class can be used as a child container. Children are added with
42 the appendChildNode(), prependChildNode(), insertChildNodeBefore() and
43 insertChildNodeAfter(). The order of nodes is important as geometry nodes
44 are rendered according to their ordering in the scene graph.
45
46 The scene graph nodes contain a mechanism that describes which
47 parts of the scene have changed. This includes the combined matrices,
48 accumulated opacity, changes to the node hierarchy, and so on. This
49 information can be used for optimizations inside the scene graph renderer.
50 For the renderer to properly render the nodes, it is important that users
51 call QSGNode::markDirty() with the correct flags when nodes are changed.
52 Most of the functions on the node classes will implicitly call markDirty().
53 For example, QSGNode::appendChildNode() will call markDirty() passing in
54 QSGNode::DirtyNodeAdded.
55
56 If nodes change every frame, the preprocess() function can be used to
57 apply changes to a node for every frame it is rendered. The use of
58 preprocess() must be explicitly enabled by setting the
59 QSGNode::UsePreprocess flag on the node.
60
61 The virtual isSubtreeBlocked() function can be used to disable a subtree all
62 together. Nodes in a blocked subtree will not be preprocessed() and not
63 rendered.
64
65 \note All classes with QSG prefix should be used solely on the scene graph's
66 rendering thread. See \l {Scene Graph and Rendering} for more information.
67 */
68
69/*!
70 \enum QSGNode::DirtyStateBit
71
72 Used in QSGNode::markDirty() to indicate how the scene graph has changed.
73
74 \value DirtyMatrix The matrix in a QSGTransformNode has changed.
75 \value DirtyNodeAdded A node was added.
76 \value DirtyNodeRemoved A node was removed.
77 \value DirtyGeometry The geometry of a QSGGeometryNode has changed.
78 \value DirtyMaterial The material of a QSGGeometryNode has changed.
79 \value DirtyOpacity The opacity of a QSGOpacityNode has changed.
80 \value DirtySubtreeBlocked The subtree has been blocked.
81
82 \omitvalue DirtyForceUpdate
83 \omitvalue DirtyUsePreprocess
84 \omitvalue DirtyPropagationMask
85
86 \sa QSGNode::markDirty()
87 */
88
89/*!
90 \enum QSGNode::Flag
91
92 The QSGNode::Flag enum describes flags on the QSGNode
93
94 \value OwnedByParent The node is owned by its parent and will be deleted
95 when the parent is deleted.
96 \value UsePreprocess The node's virtual preprocess() function will be called
97 before rendering starts.
98 \value OwnsGeometry Only valid for QSGGeometryNode and QSGClipNode.
99 The node has ownership over the QSGGeometry instance and will
100 delete it when the node is destroyed or a geometry is assigned.
101 \value OwnsMaterial Only valid for QSGGeometryNode. The node has ownership
102 over the material and will delete it when the node is destroyed or a material is assigned.
103 \value OwnsOpaqueMaterial Only valid for QSGGeometryNode. The node has
104 ownership over the opaque material and will delete it when the node is
105 destroyed or a material is assigned.
106 \value InternalReserved Reserved for internal use.
107
108 \omitvalue IsVisitableNode
109 */
110
111/*!
112 \enum QSGNode::NodeType
113
114 Can be used to figure out the type of node.
115
116 \value BasicNodeType The type of QSGNode
117 \value GeometryNodeType The type of QSGGeometryNode
118 \value TransformNodeType The type of QSGTransformNode
119 \value ClipNodeType The type of QSGClipNode
120 \value OpacityNodeType The type of QSGOpacityNode
121 \value RenderNodeType The type of QSGRenderNode
122
123 \omitvalue RootNodeType
124
125 \sa type()
126 */
127
128/*!
129 \fn QSGNode *QSGNode::childAtIndex(int i) const
130
131 Returns the child at index \a i.
132
133 Children are stored internally as a linked list, so iterating
134 over the children via the index is suboptimal.
135 */
136
137/*!
138 \fn int QSGNode::childCount() const
139
140 Returns the number of child nodes.
141 */
142
143/*!
144 \fn void QSGNode::clearDirty()
145
146 \internal
147 */
148
149/*!
150 \fn QSGNode *QSGNode::firstChild() const
151
152 Returns the first child of this node.
153
154 The children are stored in a linked list.
155 */
156
157/*!
158 \fn QSGNode *QSGNode::lastChild() const
159
160 Returns the last child of this node.
161
162 The children are stored as a linked list.
163 */
164
165/*!
166 \fn QSGNode::Flags QSGNode::flags() const
167
168 Returns the set of flags for this node.
169 */
170
171/*!
172 \fn QSGNode *QSGNode::nextSibling() const
173
174 Returns the node after this in the parent's list of children.
175
176 The children are stored as a linked list.
177 */
178
179/*!
180 \fn QSGNode *QSGNode::previousSibling() const
181
182 Returns the node before this in the parent's list of children.
183
184 The children are stored as a linked list.
185 */
186
187/*!
188 \fn QSGNode::Type QSGNode::type() const
189
190 Returns the type of this node. The node type must be one of the
191 predefined types defined in QSGNode::NodeType and can safely be
192 used to cast to the corresponding class.
193 */
194
195/*!
196 \fn QSGNode::DirtyState QSGNode::dirtyState() const
197
198 \internal
199 */
200
201/*!
202 \fn QSGNode *QSGNode::parent() const
203
204 Returns the parent node of this node.
205 */
206
207
208/*!
209 * Constructs a new node
210 */
211QSGNode::QSGNode()
212 : m_nodeFlags(OwnedByParent)
213{
214 init();
215}
216
217/*!
218 * Constructs a new node with the given node type.
219 *
220 * \internal
221 */
222QSGNode::QSGNode(NodeType type)
223 : m_parent(nullptr)
224 , m_type(type)
225 , m_firstChild(nullptr)
226 , m_lastChild(nullptr)
227 , m_nextSibling(nullptr)
228 , m_previousSibling(nullptr)
229 , m_subtreeRenderableCount(type == GeometryNodeType || type == RenderNodeType ? 1 : 0)
230 , m_nodeFlags(OwnedByParent)
231{
232 init();
233}
234
235/*!
236 * Constructs a new node with the given node type.
237 *
238 * \internal
239 */
240QSGNode::QSGNode(QSGNodePrivate &dd, NodeType type)
241 : m_parent(nullptr)
242 , m_type(type)
243 , m_firstChild(nullptr)
244 , m_lastChild(nullptr)
245 , m_nextSibling(nullptr)
246 , m_previousSibling(nullptr)
247 , m_subtreeRenderableCount(type == GeometryNodeType || type == RenderNodeType ? 1 : 0)
248 , m_nodeFlags(OwnedByParent)
249 , d_ptr(&dd)
250{
251 init();
252}
253
254/*!
255 * \internal
256 */
257void QSGNode::init()
258{
259#ifndef QT_NO_DEBUG
260 if (lcQsgLeak().isDebugEnabled()) {
261 ++qt_node_count;
262 static bool atexit_registered = false;
263 if (!atexit_registered) {
264 atexit(func: qt_print_node_count);
265 atexit_registered = true;
266 }
267 }
268#endif
269
270#ifdef QSG_RUNTIME_DESCRIPTION
271 if (d_ptr.isNull())
272 d_ptr.reset(other: new QSGNodePrivate());
273#endif
274}
275
276/*!
277 * Destroys the node.
278 *
279 * Every child of this node that has the flag QSGNode::OwnedByParent set,
280 * will also be deleted.
281 */
282QSGNode::~QSGNode()
283{
284#ifndef QT_NO_DEBUG
285 if (lcQsgLeak().isDebugEnabled()) {
286 --qt_node_count;
287 if (qt_node_count < 0)
288 qCDebug(lcQsgLeak, "Node destroyed after qt_print_node_count() was called.");
289 }
290#endif
291 destroy();
292}
293
294
295/*!
296 \fn void QSGNode::preprocess()
297
298 Override this function to do processing on the node before it is rendered.
299
300 Preprocessing needs to be explicitly enabled by setting the flag
301 QSGNode::UsePreprocess. The flag needs to be set before the node is added
302 to the scene graph and will cause the preprocess() function to be called
303 for every frame the node is rendered.
304
305 \warning Beware of deleting nodes while they are being preprocessed. It is
306 possible, with a small performance hit, to delete a single node during its
307 own preprocess call. Deleting a subtree which has nodes that also use
308 preprocessing may result in a segmentation fault. This is done for
309 performance reasons.
310 */
311
312
313
314
315/*!
316 Returns whether this node and its subtree is available for use.
317
318 Blocked subtrees will not get their dirty states updated and they
319 will not be rendered.
320
321 The QSGOpacityNode will return a blocked subtree when accumulated opacity
322 is 0, for instance.
323 */
324
325bool QSGNode::isSubtreeBlocked() const
326{
327 return false;
328}
329
330/*!
331 \internal
332 Detaches the node from the scene graph and deletes any children it owns.
333
334 This function is called from QSGNode's and QSGRootNode's destructor. It
335 should not be called explicitly in user code. QSGRootNode needs to call
336 destroy() because destroy() calls removeChildNode() which in turn calls
337 markDirty() which type-casts the node to QSGRootNode. This type-cast is not
338 valid at the time QSGNode's destructor is called because the node will
339 already be partially destroyed at that point.
340*/
341
342void QSGNode::destroy()
343{
344 if (m_parent) {
345 m_parent->removeChildNode(node: this);
346 Q_ASSERT(m_parent == nullptr);
347 }
348 while (m_firstChild) {
349 QSGNode *child = m_firstChild;
350 removeChildNode(node: child);
351 Q_ASSERT(child->m_parent == nullptr);
352 if (child->flags() & OwnedByParent)
353 delete child;
354 }
355
356 Q_ASSERT(m_firstChild == nullptr && m_lastChild == nullptr);
357}
358
359
360/*!
361 Prepends \a node to this node's the list of children.
362
363 Ordering of nodes is important as geometry nodes will be rendered in the
364 order they are added to the scene graph.
365 */
366
367void QSGNode::prependChildNode(QSGNode *node)
368{
369 //Q_ASSERT_X(!m_children.contains(node), "QSGNode::prependChildNode", "QSGNode is already a child!");
370 Q_ASSERT_X(!node->m_parent, "QSGNode::prependChildNode", "QSGNode already has a parent");
371
372#ifndef QT_NO_DEBUG
373 if (node->type() == QSGNode::GeometryNodeType) {
374 QSGGeometryNode *g = static_cast<QSGGeometryNode *>(node);
375 Q_ASSERT_X(g->material(), "QSGNode::prependChildNode", "QSGGeometryNode is missing material");
376 Q_ASSERT_X(g->geometry(), "QSGNode::prependChildNode", "QSGGeometryNode is missing geometry");
377 }
378#endif
379
380 if (m_firstChild)
381 m_firstChild->m_previousSibling = node;
382 else
383 m_lastChild = node;
384 node->m_nextSibling = m_firstChild;
385 m_firstChild = node;
386 node->m_parent = this;
387
388 node->markDirty(bits: DirtyNodeAdded);
389}
390
391/*!
392 Appends \a node to this node's list of children.
393
394 Ordering of nodes is important as geometry nodes will be rendered in the
395 order they are added to the scene graph.
396 */
397
398void QSGNode::appendChildNode(QSGNode *node)
399{
400 //Q_ASSERT_X(!m_children.contains(node), "QSGNode::appendChildNode", "QSGNode is already a child!");
401 Q_ASSERT_X(!node->m_parent, "QSGNode::appendChildNode", "QSGNode already has a parent");
402
403#ifndef QT_NO_DEBUG
404 if (node->type() == QSGNode::GeometryNodeType) {
405 QSGGeometryNode *g = static_cast<QSGGeometryNode *>(node);
406 Q_ASSERT_X(g->material(), "QSGNode::appendChildNode", "QSGGeometryNode is missing material");
407 Q_ASSERT_X(g->geometry(), "QSGNode::appendChildNode", "QSGGeometryNode is missing geometry");
408 }
409#endif
410
411 if (m_lastChild)
412 m_lastChild->m_nextSibling = node;
413 else
414 m_firstChild = node;
415 node->m_previousSibling = m_lastChild;
416 m_lastChild = node;
417 node->m_parent = this;
418
419 node->markDirty(bits: DirtyNodeAdded);
420}
421
422
423
424/*!
425 Inserts \a node to this node's list of children before the node specified with \a before.
426
427 Ordering of nodes is important as geometry nodes will be rendered in the
428 order they are added to the scene graph.
429 */
430
431void QSGNode::insertChildNodeBefore(QSGNode *node, QSGNode *before)
432{
433 //Q_ASSERT_X(!m_children.contains(node), "QSGNode::insertChildNodeBefore", "QSGNode is already a child!");
434 Q_ASSERT_X(!node->m_parent, "QSGNode::insertChildNodeBefore", "QSGNode already has a parent");
435 Q_ASSERT_X(before && before->m_parent == this, "QSGNode::insertChildNodeBefore", "The parent of \'before\' is wrong");
436
437#ifndef QT_NO_DEBUG
438 if (node->type() == QSGNode::GeometryNodeType) {
439 QSGGeometryNode *g = static_cast<QSGGeometryNode *>(node);
440 Q_ASSERT_X(g->material(), "QSGNode::insertChildNodeBefore", "QSGGeometryNode is missing material");
441 Q_ASSERT_X(g->geometry(), "QSGNode::insertChildNodeBefore", "QSGGeometryNode is missing geometry");
442 }
443#endif
444
445 QSGNode *previous = before->m_previousSibling;
446 if (previous)
447 previous->m_nextSibling = node;
448 else
449 m_firstChild = node;
450 node->m_previousSibling = previous;
451 node->m_nextSibling = before;
452 before->m_previousSibling = node;
453 node->m_parent = this;
454
455 node->markDirty(bits: DirtyNodeAdded);
456}
457
458
459
460/*!
461 Inserts \a node to this node's list of children after the node specified with \a after.
462
463 Ordering of nodes is important as geometry nodes will be rendered in the
464 order they are added to the scene graph.
465 */
466
467void QSGNode::insertChildNodeAfter(QSGNode *node, QSGNode *after)
468{
469 //Q_ASSERT_X(!m_children.contains(node), "QSGNode::insertChildNodeAfter", "QSGNode is already a child!");
470 Q_ASSERT_X(!node->m_parent, "QSGNode::insertChildNodeAfter", "QSGNode already has a parent");
471 Q_ASSERT_X(after && after->m_parent == this, "QSGNode::insertChildNodeAfter", "The parent of \'after\' is wrong");
472
473#ifndef QT_NO_DEBUG
474 if (node->type() == QSGNode::GeometryNodeType) {
475 QSGGeometryNode *g = static_cast<QSGGeometryNode *>(node);
476 Q_ASSERT_X(g->material(), "QSGNode::insertChildNodeAfter", "QSGGeometryNode is missing material");
477 Q_ASSERT_X(g->geometry(), "QSGNode::insertChildNodeAfter", "QSGGeometryNode is missing geometry");
478 }
479#endif
480
481 QSGNode *next = after->m_nextSibling;
482 if (next)
483 next->m_previousSibling = node;
484 else
485 m_lastChild = node;
486 node->m_nextSibling = next;
487 node->m_previousSibling = after;
488 after->m_nextSibling = node;
489 node->m_parent = this;
490
491 node->markDirty(bits: DirtyNodeAdded);
492}
493
494
495
496/*!
497 Removes \a node from this node's list of children.
498 */
499
500void QSGNode::removeChildNode(QSGNode *node)
501{
502 //Q_ASSERT(m_children.contains(node));
503 Q_ASSERT(node->parent() == this);
504
505 QSGNode *previous = node->m_previousSibling;
506 QSGNode *next = node->m_nextSibling;
507 if (previous)
508 previous->m_nextSibling = next;
509 else
510 m_firstChild = next;
511 if (next)
512 next->m_previousSibling = previous;
513 else
514 m_lastChild = previous;
515 node->m_previousSibling = nullptr;
516 node->m_nextSibling = nullptr;
517
518 node->markDirty(bits: DirtyNodeRemoved);
519 node->m_parent = nullptr;
520}
521
522
523/*!
524 Removes all child nodes from this node's list of children.
525 */
526
527void QSGNode::removeAllChildNodes()
528{
529 while (m_firstChild) {
530 QSGNode *node = m_firstChild;
531 m_firstChild = node->m_nextSibling;
532 node->m_nextSibling = nullptr;
533 if (m_firstChild)
534 m_firstChild->m_previousSibling = nullptr;
535 else
536 m_lastChild = nullptr;
537 node->markDirty(bits: DirtyNodeRemoved);
538 node->m_parent = nullptr;
539 }
540}
541
542/*!
543 * \internal
544 *
545 * Reparents all nodes of this node to \a newParent.
546 */
547void QSGNode::reparentChildNodesTo(QSGNode *newParent)
548{
549 for (QSGNode *c = firstChild(); c; c = firstChild()) {
550 removeChildNode(node: c);
551 newParent->appendChildNode(node: c);
552 }
553}
554
555
556int QSGNode::childCount() const
557{
558 int count = 0;
559 QSGNode *n = m_firstChild;
560 while (n) {
561 ++count;
562 n = n->m_nextSibling;
563 }
564 return count;
565}
566
567
568QSGNode *QSGNode::childAtIndex(int i) const
569{
570 QSGNode *n = m_firstChild;
571 while (i && n) {
572 --i;
573 n = n->m_nextSibling;
574 }
575 return n;
576}
577
578
579/*!
580 Sets the flag \a f on this node if \a enabled is true;
581 otherwise clears the flag.
582
583 \sa flags()
584*/
585
586void QSGNode::setFlag(Flag f, bool enabled)
587{
588 if (bool(m_nodeFlags & f) == enabled)
589 return;
590 m_nodeFlags ^= f;
591 Q_ASSERT(int(UsePreprocess) == int(DirtyUsePreprocess));
592 int changedFlag = f & UsePreprocess;
593 if (changedFlag)
594 markDirty(bits: DirtyState(changedFlag));
595}
596
597
598/*!
599 Sets the flags \a f on this node if \a enabled is true;
600 otherwise clears the flags.
601
602 \sa flags()
603*/
604
605void QSGNode::setFlags(Flags f, bool enabled)
606{
607 Flags oldFlags = m_nodeFlags;
608 if (enabled)
609 m_nodeFlags |= f;
610 else
611 m_nodeFlags &= ~f;
612 Q_ASSERT(int(UsePreprocess) == int(DirtyUsePreprocess));
613 int changedFlags = (oldFlags ^ m_nodeFlags) & UsePreprocess;
614 if (changedFlags)
615 markDirty(bits: DirtyState(changedFlags));
616}
617
618
619
620/*!
621 Notifies all connected renderers that the node has dirty \a bits.
622 */
623
624void QSGNode::markDirty(DirtyState bits)
625{
626 int renderableCountDiff = 0;
627 if (bits & DirtyNodeAdded)
628 renderableCountDiff += m_subtreeRenderableCount;
629 if (bits & DirtyNodeRemoved)
630 renderableCountDiff -= m_subtreeRenderableCount;
631
632 QSGNode *p = m_parent;
633 while (p) {
634 p->m_subtreeRenderableCount += renderableCountDiff;
635 if (p->type() == RootNodeType)
636 static_cast<QSGRootNode *>(p)->notifyNodeChange(node: this, state: bits);
637 p = p->m_parent;
638 }
639}
640
641void qsgnode_set_description(QSGNode *node, const QString &description)
642{
643#ifdef QSG_RUNTIME_DESCRIPTION
644 QSGNodePrivate::setDescription(node, description);
645#else
646 Q_UNUSED(node);
647 Q_UNUSED(description);
648#endif
649}
650
651/*!
652 \class QSGBasicGeometryNode
653 \brief The QSGBasicGeometryNode class serves as a baseclass for geometry based nodes.
654
655 \inmodule QtQuick
656
657 The QSGBasicGeometryNode class should not be used by itself. It is only encapsulates
658 shared functionality between the QSGGeometryNode and QSGClipNode classes.
659
660 \note All classes with QSG prefix should be used solely on the scene graph's
661 rendering thread. See \l {Scene Graph and Rendering} for more information.
662 */
663
664
665/*!
666 Creates a new basic geometry node of type \a type
667
668 \internal
669 */
670QSGBasicGeometryNode::QSGBasicGeometryNode(NodeType type)
671 : QSGNode(type)
672 , m_geometry(nullptr)
673 , m_matrix(nullptr)
674 , m_clip_list(nullptr)
675{
676}
677
678
679/*!
680 \internal
681 */
682QSGBasicGeometryNode::QSGBasicGeometryNode(QSGBasicGeometryNodePrivate &dd, NodeType type)
683 : QSGNode(dd, type)
684 , m_geometry(nullptr)
685 , m_matrix(nullptr)
686 , m_clip_list(nullptr)
687{
688}
689
690
691/*!
692 Deletes this QSGBasicGeometryNode.
693
694 If the node has the flag QSGNode::OwnsGeometry set, it will also delete the
695 geometry object it is pointing to. This flag is not set by default.
696 */
697
698QSGBasicGeometryNode::~QSGBasicGeometryNode()
699{
700 if (flags() & OwnsGeometry)
701 delete m_geometry;
702}
703
704
705/*!
706 \fn QSGGeometry *QSGBasicGeometryNode::geometry()
707
708 Returns this node's geometry.
709
710 The geometry is null by default.
711 */
712
713/*!
714 \fn const QSGGeometry *QSGBasicGeometryNode::geometry() const
715
716 Returns this node's geometry.
717
718 The geometry is null by default.
719 */
720
721/*!
722 \fn QMatrix4x4 *QSGBasicGeometryNode::matrix() const
723
724 Will be set during rendering to contain transformation of the geometry
725 for that rendering pass.
726
727 \internal
728 */
729
730/*!
731 \fn QSGClipNode *QSGBasicGeometryNode::clipList() const
732
733 Will be set during rendering to contain the clip of the geometry
734 for that rendering pass.
735
736 \internal
737 */
738
739/*!
740 \fn void QSGBasicGeometryNode::setRendererMatrix(const QMatrix4x4 *m)
741
742 \internal
743 */
744
745/*!
746 \fn void QSGBasicGeometryNode::setRendererClipList(const QSGClipNode *c)
747
748 \internal
749 */
750
751
752/*!
753 Sets the geometry of this node to \a geometry.
754
755 If the node has the flag QSGNode::OwnsGeometry set, it will also delete the
756 geometry object it is pointing to. This flag is not set by default.
757
758 If the geometry is changed without calling setGeometry() again, the user
759 must also mark the geometry as dirty using QSGNode::markDirty().
760
761 \sa markDirty()
762 */
763
764void QSGBasicGeometryNode::setGeometry(QSGGeometry *geometry)
765{
766 if ((flags() & OwnsGeometry) != 0 && m_geometry != geometry)
767 delete m_geometry;
768 m_geometry = geometry;
769 markDirty(bits: DirtyGeometry);
770}
771
772
773
774/*!
775 \class QSGGeometryNode
776 \brief The QSGGeometryNode class is used for all rendered content in the scene graph.
777
778 \inmodule QtQuick
779 \ingroup qtquick-scenegraph-nodes
780
781 The QSGGeometryNode consists of geometry and material. The geometry defines the mesh,
782 the vertices and their structure, to be drawn. The Material defines how the shape is
783 filled.
784
785 The following is a code snippet illustrating how to create a red
786 line using a QSGGeometryNode:
787 \code
788 QSGGeometry *geometry = new QSGGeometry(QSGGeometry::defaultAttributes_Point2D(), 2);
789 geometry->setDrawingMode(GL_LINES);
790 geometry->setLineWidth(3);
791 geometry->vertexDataAsPoint2D()[0].set(0, 0);
792 geometry->vertexDataAsPoint2D()[1].set(width(), height());
793
794 QSGFlatColorMaterial *material = new QSGFlatColorMaterial;
795 material->setColor(QColor(255, 0, 0));
796
797 QSGGeometryNode *node = new QSGGeometryNode;
798 node->setGeometry(geometry);
799 node->setFlag(QSGNode::OwnsGeometry);
800 node->setMaterial(material);
801 node->setFlag(QSGNode::OwnsMaterial);
802 \endcode
803
804 A geometry node must have both geometry and a normal material before it is added to
805 the scene graph. When the geometry and materials are changed after the node has
806 been added to the scene graph, the user should also mark them as dirty using
807 QSGNode::markDirty().
808
809 The geometry node supports two types of materials, the opaqueMaterial and the normal
810 material. The opaqueMaterial is used when the accumulated scene graph opacity at the
811 time of rendering is 1. The primary use case is to special case opaque rendering
812 to avoid an extra operation in the fragment shader can have significant performance
813 impact on embedded graphics chips. The opaque material is optional.
814
815 \note All classes with QSG prefix should be used solely on the scene graph's
816 rendering thread. See \l {Scene Graph and Rendering} for more information.
817
818 \sa QSGGeometry, QSGMaterial
819 */
820
821
822/*!
823 Creates a new geometry node without geometry and material.
824 */
825
826QSGGeometryNode::QSGGeometryNode()
827 : QSGBasicGeometryNode(GeometryNodeType)
828{
829}
830
831
832/*!
833 \internal
834 */
835QSGGeometryNode::QSGGeometryNode(QSGGeometryNodePrivate &dd)
836 : QSGBasicGeometryNode(dd, GeometryNodeType)
837 , m_render_order(0)
838 , m_material(nullptr)
839 , m_opaque_material(nullptr)
840 , m_opacity(1)
841{
842}
843
844
845/*!
846 Deletes this geometry node.
847
848 The flags QSGNode::OwnsMaterial, QSGNode::OwnsOpaqueMaterial and
849 QSGNode::OwnsGeometry decides whether the geometry node should also
850 delete the materials and geometry. By default, these flags are disabled.
851 */
852
853QSGGeometryNode::~QSGGeometryNode()
854{
855 if (flags() & OwnsMaterial)
856 delete m_material;
857 if (flags() & OwnsOpaqueMaterial)
858 delete m_opaque_material;
859}
860
861
862
863/*!
864 \fn int QSGGeometryNode::renderOrder() const
865
866 Returns the render order of this geometry node.
867
868 \internal
869 */
870
871/*!
872 \fn QSGMaterial *QSGGeometryNode::material() const
873
874 Returns the material of the QSGGeometryNode.
875
876 \sa setMaterial()
877 */
878
879/*!
880 \fn QSGMaterial *QSGGeometryNode::opaqueMaterial() const
881
882 Returns the opaque material of the QSGGeometryNode.
883
884 \sa setOpaqueMaterial()
885 */
886
887/*!
888 \fn qreal QSGGeometryNode::inheritedOpacity() const
889
890 Set during rendering to specify the inherited opacity for that
891 rendering pass.
892
893 \internal
894 */
895
896
897/*!
898 Sets the render order of this node to be \a order.
899
900 Geometry nodes are rendered in an order that visually looks like
901 low order nodes are rendered prior to high order nodes. For opaque
902 geometry there is little difference as z-testing will handle
903 the discard, but for translucent objects, the rendering should
904 normally be specified in the order of back-to-front.
905
906 The default render order is \c 0.
907
908 \internal
909 */
910void QSGGeometryNode::setRenderOrder(int order)
911{
912 m_render_order = order;
913}
914
915
916
917/*!
918 Sets the material of this geometry node to \a material.
919
920 Geometry nodes must have a material before they can be added to the
921 scene graph.
922
923 If the material is changed without calling setMaterial() again, the user
924 must also mark the material as dirty using QSGNode::markDirty().
925
926 */
927void QSGGeometryNode::setMaterial(QSGMaterial *material)
928{
929 if ((flags() & OwnsMaterial) != 0 && m_material != material)
930 delete m_material;
931 m_material = material;
932#ifndef QT_NO_DEBUG
933 if (m_material != nullptr && m_opaque_material == m_material)
934 qWarning(msg: "QSGGeometryNode: using same material for both opaque and translucent");
935#endif
936 markDirty(bits: DirtyMaterial);
937}
938
939
940
941/*!
942 Sets the opaque material of this geometry to \a material.
943
944 The opaque material will be preferred by the renderer over the
945 default material, as returned by the material() function, if
946 it is not null and the geometry item has an inherited opacity of
947 1.
948
949 The opaqueness refers to scene graph opacity, the material is still
950 allowed to set QSGMaterial::Blending to true and draw transparent
951 pixels.
952
953 If the material is changed without calling setOpaqueMaterial()
954 again, the user must also mark the opaque material as dirty using
955 QSGNode::markDirty().
956
957 */
958void QSGGeometryNode::setOpaqueMaterial(QSGMaterial *material)
959{
960 if ((flags() & OwnsOpaqueMaterial) != 0 && m_opaque_material != m_material)
961 delete m_opaque_material;
962 m_opaque_material = material;
963#ifndef QT_NO_DEBUG
964 if (m_opaque_material != nullptr && m_opaque_material == m_material)
965 qWarning(msg: "QSGGeometryNode: using same material for both opaque and translucent");
966#endif
967
968 markDirty(bits: DirtyMaterial);
969}
970
971
972
973/*!
974 Returns the material which should currently be used for geometry node.
975
976 If the inherited opacity of the node is 1 and there is an opaque material
977 set on this node, it will be returned; otherwise, the default material
978 will be returned.
979
980 \warning This function requires the scene graph above this item to be
981 completely free of dirty states, so it can only be called during rendering
982
983 \internal
984
985 \sa setMaterial, setOpaqueMaterial
986 */
987QSGMaterial *QSGGeometryNode::activeMaterial() const
988{
989 if (m_opaque_material && m_opacity > 0.999)
990 return m_opaque_material;
991 return m_material;
992}
993
994
995/*!
996 Sets the inherited opacity of this geometry to \a opacity.
997
998 This function is meant to be called by the node preprocessing
999 prior to rendering the tree, so it will not mark the tree as
1000 dirty.
1001
1002 \internal
1003 */
1004void QSGGeometryNode::setInheritedOpacity(qreal opacity)
1005{
1006 Q_ASSERT(opacity >= 0 && opacity <= 1);
1007 m_opacity = opacity;
1008}
1009
1010
1011/*!
1012 \class QSGClipNode
1013 \brief The QSGClipNode class implements the clipping functionality in the scene graph.
1014
1015 \inmodule QtQuick
1016 \ingroup qtquick-scenegraph-nodes
1017
1018 Clipping applies to the node's subtree and can be nested. Multiple clip nodes will be
1019 accumulated by intersecting all their geometries. The accumulation happens
1020 as part of the rendering.
1021
1022 Clip nodes must have a geometry before they can be added to the scene graph.
1023
1024 Clipping is usually implemented by using the stencil buffer.
1025
1026 \note All classes with QSG prefix should be used solely on the scene graph's
1027 rendering thread. See \l {Scene Graph and Rendering} for more information.
1028 */
1029
1030
1031
1032/*!
1033 Creates a new QSGClipNode without a geometry.
1034
1035 The clip node must have a geometry before it can be added to the
1036 scene graph.
1037 */
1038
1039QSGClipNode::QSGClipNode()
1040 : QSGBasicGeometryNode(ClipNodeType)
1041 , m_is_rectangular(false)
1042{
1043 Q_UNUSED(m_reserved);
1044}
1045
1046
1047
1048/*!
1049 Deletes this QSGClipNode.
1050
1051 If the flag QSGNode::OwnsGeometry is set, the geometry will also be
1052 deleted.
1053 */
1054
1055QSGClipNode::~QSGClipNode()
1056{
1057}
1058
1059
1060
1061/*!
1062 \fn bool QSGClipNode::isRectangular() const
1063
1064 Returns if this clip node has a rectangular clip.
1065 */
1066
1067
1068
1069/*!
1070 Sets whether this clip node has a rectangular clip to \a rectHint.
1071
1072 This is an optimization hint which means that the renderer can
1073 use scissoring instead of stencil, which is significantly faster.
1074
1075 When this hint is set and it is applicable, the clip region will be
1076 generated from clipRect() rather than geometry().
1077
1078 By default this property is \c false.
1079 */
1080
1081void QSGClipNode::setIsRectangular(bool rectHint)
1082{
1083 m_is_rectangular = rectHint;
1084}
1085
1086
1087
1088/*!
1089 \fn QRectF QSGClipNode::clipRect() const
1090
1091 Returns the clip rect of this node.
1092 */
1093
1094
1095/*!
1096 Sets the clip rect of this clip node to \a rect.
1097
1098 When a rectangular clip is set in combination with setIsRectangular
1099 the renderer may in some cases use a more optimal clip method.
1100 */
1101void QSGClipNode::setClipRect(const QRectF &rect)
1102{
1103 m_clip_rect = rect;
1104}
1105
1106
1107/*!
1108 \class QSGTransformNode
1109 \brief The QSGTransformNode class implements transformations in the scene graph.
1110
1111 \inmodule QtQuick
1112 \ingroup qtquick-scenegraph-nodes
1113
1114 Transformations apply the node's subtree and can be nested. Multiple transform nodes
1115 will be accumulated by intersecting all their matrices. The accumulation happens
1116 as part of the rendering.
1117
1118 The transform nodes implement a 4x4 matrix which in theory supports full 3D
1119 transformations. However, because the renderer optimizes for 2D use-cases rather
1120 than 3D use-cases, rendering a scene with full 3D transformations needs to
1121 be done with some care.
1122
1123 \note All classes with QSG prefix should be used solely on the scene graph's
1124 rendering thread. See \l {Scene Graph and Rendering} for more information.
1125
1126 */
1127
1128
1129/*!
1130 Create a new QSGTransformNode with its matrix set to the identity matrix.
1131 */
1132
1133QSGTransformNode::QSGTransformNode()
1134 : QSGNode(TransformNodeType)
1135{
1136}
1137
1138
1139
1140/*!
1141 Deletes this transform node.
1142 */
1143
1144QSGTransformNode::~QSGTransformNode()
1145{
1146}
1147
1148
1149
1150/*!
1151 \fn QMatrix4x4 QSGTransformNode::matrix() const
1152
1153 Returns this transform node's matrix.
1154 */
1155
1156
1157
1158/*!
1159 Sets this transform node's matrix to \a matrix.
1160 */
1161
1162void QSGTransformNode::setMatrix(const QMatrix4x4 &matrix)
1163{
1164 m_matrix = matrix;
1165 markDirty(bits: DirtyMatrix);
1166}
1167
1168/*!
1169 \fn const QMatrix4x4 &QSGTransformNode::combinedMatrix() const
1170
1171 Set during rendering to the combination of all parent matrices for
1172 that rendering pass.
1173
1174 \internal
1175 */
1176
1177
1178
1179/*!
1180 Sets the combined matrix of this matrix to \a transform.
1181
1182 This function is meant to be called by the node preprocessing
1183 prior to rendering the tree, so it will not mark the tree as
1184 dirty.
1185
1186 \internal
1187 */
1188void QSGTransformNode::setCombinedMatrix(const QMatrix4x4 &matrix)
1189{
1190 m_combined_matrix = matrix;
1191}
1192
1193
1194
1195/*!
1196 \class QSGRootNode
1197 \brief The QSGRootNode is the toplevel root of any scene graph.
1198
1199 The root node is used to attach a scene graph to a renderer.
1200
1201 \internal
1202 */
1203
1204
1205
1206/*!
1207 \fn QSGRootNode::QSGRootNode()
1208
1209 Creates a new root node.
1210 */
1211
1212QSGRootNode::QSGRootNode()
1213 : QSGNode(RootNodeType)
1214{
1215}
1216
1217
1218/*!
1219 Deletes the root node.
1220
1221 When a root node is deleted it removes itself from all of renderers
1222 that are referencing it.
1223 */
1224
1225QSGRootNode::~QSGRootNode()
1226{
1227 while (!m_renderers.isEmpty())
1228 m_renderers.constLast()->setRootNode(nullptr);
1229 destroy(); // Must call destroy() here because markDirty() casts this to QSGRootNode.
1230}
1231
1232
1233
1234/*!
1235 Called to notify all renderers that \a node has been marked as dirty
1236 with \a flags.
1237 */
1238
1239void QSGRootNode::notifyNodeChange(QSGNode *node, DirtyState state)
1240{
1241 for (int i=0; i<m_renderers.size(); ++i) {
1242 m_renderers.at(i)->nodeChanged(node, state);
1243 }
1244}
1245
1246
1247
1248/*!
1249 \class QSGOpacityNode
1250 \brief The QSGOpacityNode class is used to change opacity of nodes.
1251
1252 \inmodule QtQuick
1253 \ingroup qtquick-scenegraph-nodes
1254
1255 Opacity applies to its subtree and can be nested. Multiple opacity nodes
1256 will be accumulated by multiplying their opacity. The accumulation happens
1257 as part of the rendering.
1258
1259 When nested opacity gets below a certain threshold, the subtree might
1260 be marked as blocked, causing isSubtreeBlocked() to return true. This
1261 is done for performance reasons.
1262
1263 \note All classes with QSG prefix should be used solely on the scene graph's
1264 rendering thread. See \l {Scene Graph and Rendering} for more information.
1265 */
1266
1267
1268
1269/*!
1270 Constructs an opacity node with a default opacity of 1.
1271
1272 Opacity accumulates downwards in the scene graph so a node with two
1273 QSGOpacityNode instances above it, both with opacity of 0.5, will have
1274 effective opacity of 0.25.
1275
1276 The default opacity of nodes is 1.
1277 */
1278QSGOpacityNode::QSGOpacityNode()
1279 : QSGNode(OpacityNodeType)
1280{
1281}
1282
1283
1284
1285/*!
1286 Deletes the opacity node.
1287 */
1288
1289QSGOpacityNode::~QSGOpacityNode()
1290{
1291}
1292
1293
1294
1295/*!
1296 \fn qreal QSGOpacityNode::opacity() const
1297
1298 Returns this opacity node's opacity.
1299 */
1300
1301const qreal OPACITY_THRESHOLD = 0.001;
1302
1303/*!
1304 Sets the opacity of this node to \a opacity.
1305
1306 Before rendering the graph, the renderer will do an update pass
1307 over the subtree to propagate the opacity to its children.
1308
1309 The value will be bounded to the range 0 to 1.
1310 */
1311
1312void QSGOpacityNode::setOpacity(qreal opacity)
1313{
1314 opacity = qBound<qreal>(min: 0, val: opacity, max: 1);
1315 if (m_opacity == opacity)
1316 return;
1317 DirtyState dirtyState = DirtyOpacity;
1318
1319 if ((m_opacity < OPACITY_THRESHOLD && opacity >= OPACITY_THRESHOLD) // blocked to unblocked
1320 || (m_opacity >= OPACITY_THRESHOLD && opacity < OPACITY_THRESHOLD)) // unblocked to blocked
1321 dirtyState |= DirtySubtreeBlocked;
1322
1323 m_opacity = opacity;
1324 markDirty(bits: dirtyState);
1325}
1326
1327
1328
1329/*!
1330 \fn qreal QSGOpacityNode::combinedOpacity() const
1331
1332 Returns this node's accumulated opacity.
1333
1334 This value is calculated during rendering and only stored
1335 in the opacity node temporarily.
1336
1337 \internal
1338 */
1339
1340
1341
1342/*!
1343 Sets the combined opacity of this node to \a opacity.
1344
1345 This function is meant to be called by the node preprocessing
1346 prior to rendering the tree, so it will not mark the tree as
1347 dirty.
1348
1349 \internal
1350 */
1351
1352void QSGOpacityNode::setCombinedOpacity(qreal opacity)
1353{
1354 m_combined_opacity = opacity;
1355}
1356
1357
1358
1359/*!
1360 For performance reasons, we block the subtree when the opacity
1361 is below a certain threshold.
1362
1363 \internal
1364 */
1365
1366bool QSGOpacityNode::isSubtreeBlocked() const
1367{
1368 return m_opacity < OPACITY_THRESHOLD;
1369}
1370
1371
1372/*!
1373 \class QSGNodeVisitor
1374 \brief The QSGNodeVisitor class is a helper class for traversing the scene graph.
1375
1376 \internal
1377 */
1378
1379QSGNodeVisitor::~QSGNodeVisitor()
1380{
1381
1382}
1383
1384
1385void QSGNodeVisitor::visitNode(QSGNode *n)
1386{
1387 switch (n->type()) {
1388 case QSGNode::TransformNodeType: {
1389 QSGTransformNode *t = static_cast<QSGTransformNode *>(n);
1390 enterTransformNode(t);
1391 visitChildren(n: t);
1392 leaveTransformNode(t);
1393 break; }
1394 case QSGNode::GeometryNodeType: {
1395 QSGGeometryNode *g = static_cast<QSGGeometryNode *>(n);
1396 enterGeometryNode(g);
1397 visitChildren(n: g);
1398 leaveGeometryNode(g);
1399 break; }
1400 case QSGNode::ClipNodeType: {
1401 QSGClipNode *c = static_cast<QSGClipNode *>(n);
1402 enterClipNode(c);
1403 visitChildren(n: c);
1404 leaveClipNode(c);
1405 break; }
1406 case QSGNode::OpacityNodeType: {
1407 QSGOpacityNode *o = static_cast<QSGOpacityNode *>(n);
1408 enterOpacityNode(o);
1409 visitChildren(n: o);
1410 leaveOpacityNode(o);
1411 break; }
1412 default:
1413 visitChildren(n);
1414 break;
1415 }
1416}
1417
1418void QSGNodeVisitor::visitChildren(QSGNode *n)
1419{
1420 for (QSGNode *c = n->firstChild(); c; c = c->nextSibling())
1421 visitNode(n: c);
1422}
1423
1424#ifndef QT_NO_DEBUG_STREAM
1425QDebug operator<<(QDebug d, const QSGGeometryNode *n)
1426{
1427 if (!n) {
1428 d << "Geometry(null)";
1429 return d;
1430 }
1431 d << "GeometryNode(" << Qt::hex << (const void *) n << Qt::dec;
1432
1433 const QSGGeometry *g = n->geometry();
1434
1435 if (!g) {
1436 d << "no geometry";
1437 } else {
1438
1439 switch (g->drawingMode()) {
1440 case QSGGeometry::DrawTriangleStrip: d << "strip"; break;
1441 case QSGGeometry::DrawTriangleFan: d << "fan"; break;
1442 case QSGGeometry::DrawTriangles: d << "triangles"; break;
1443 default: break;
1444 }
1445
1446 d << "#V:" << g->vertexCount() << "#I:" << g->indexCount();
1447
1448 if (g->attributeCount() > 0 && g->attributes()->type == QSGGeometry::FloatType) {
1449 float x1 = 1e10, x2 = -1e10, y1=1e10, y2=-1e10;
1450 int stride = g->sizeOfVertex();
1451 for (int i = 0; i < g->vertexCount(); ++i) {
1452 float x = ((float *)((char *)const_cast<QSGGeometry *>(g)->vertexData() + i * stride))[0];
1453 float y = ((float *)((char *)const_cast<QSGGeometry *>(g)->vertexData() + i * stride))[1];
1454
1455 x1 = qMin(a: x1, b: x);
1456 x2 = qMax(a: x2, b: x);
1457 y1 = qMin(a: y1, b: y);
1458 y2 = qMax(a: y2, b: y);
1459 }
1460
1461 d << "x1=" << x1 << "y1=" << y1 << "x2=" << x2 << "y2=" << y2;
1462 }
1463 }
1464
1465 if (n->material())
1466 d << "materialtype=" << n->material()->type();
1467
1468
1469 d << ')';
1470#ifdef QSG_RUNTIME_DESCRIPTION
1471 d << QSGNodePrivate::description(node: n);
1472#endif
1473 return d;
1474}
1475
1476QDebug operator<<(QDebug d, const QSGClipNode *n)
1477{
1478 if (!n) {
1479 d << "ClipNode(null)";
1480 return d;
1481 }
1482 d << "ClipNode(" << Qt::hex << (const void *) n << Qt::dec;
1483
1484 if (n->childCount())
1485 d << "children=" << n->childCount();
1486
1487 d << "is rect?" << (n->isRectangular() ? "yes" : "no");
1488
1489 d << ')';
1490#ifdef QSG_RUNTIME_DESCRIPTION
1491 d << QSGNodePrivate::description(node: n);
1492#endif
1493 d << (n->isSubtreeBlocked() ? "*BLOCKED*" : "");
1494 return d;
1495}
1496
1497QDebug operator<<(QDebug d, const QSGTransformNode *n)
1498{
1499 if (!n) {
1500 d << "TransformNode(null)";
1501 return d;
1502 }
1503 const QMatrix4x4 m = n->matrix();
1504 d << "TransformNode(";
1505 d << Qt::hex << (const void *) n << Qt::dec;
1506 if (m.isIdentity())
1507 d << "identity";
1508 else if (m.determinant() == 1 && m(0, 0) == 1 && m(1, 1) == 1 && m(2, 2) == 1)
1509 d << "translate" << m(0, 3) << m(1, 3) << m(2, 3);
1510 else
1511 d << "det=" << n->matrix().determinant();
1512#ifdef QSG_RUNTIME_DESCRIPTION
1513 d << QSGNodePrivate::description(node: n);
1514#endif
1515 d << (n->isSubtreeBlocked() ? "*BLOCKED*" : "");
1516 d << ')';
1517 return d;
1518}
1519
1520QDebug operator<<(QDebug d, const QSGOpacityNode *n)
1521{
1522 if (!n) {
1523 d << "OpacityNode(null)";
1524 return d;
1525 }
1526 d << "OpacityNode(";
1527 d << Qt::hex << (const void *) n << Qt::dec;
1528 d << "opacity=" << n->opacity()
1529 << "combined=" << n->combinedOpacity()
1530 << (n->isSubtreeBlocked() ? "*BLOCKED*" : "");
1531#ifdef QSG_RUNTIME_DESCRIPTION
1532 d << QSGNodePrivate::description(node: n);
1533#endif
1534 d << ')';
1535 return d;
1536}
1537
1538
1539QDebug operator<<(QDebug d, const QSGRootNode *n)
1540{
1541 if (!n) {
1542 d << "RootNode(null)";
1543 return d;
1544 }
1545 QDebugStateSaver saver(d);
1546 d << "RootNode" << Qt::hex << (const void *) n << (n->isSubtreeBlocked() ? "*BLOCKED*" : "");
1547#ifdef QSG_RUNTIME_DESCRIPTION
1548 d << QSGNodePrivate::description(node: n);
1549#endif
1550 d << ')';
1551 return d;
1552}
1553
1554
1555
1556QDebug operator<<(QDebug d, const QSGNode *n)
1557{
1558 if (!n) {
1559 d << "Node(null)";
1560 return d;
1561 }
1562 switch (n->type()) {
1563 case QSGNode::GeometryNodeType:
1564 d << static_cast<const QSGGeometryNode *>(n);
1565 break;
1566 case QSGNode::TransformNodeType:
1567 d << static_cast<const QSGTransformNode *>(n);
1568 break;
1569 case QSGNode::ClipNodeType:
1570 d << static_cast<const QSGClipNode *>(n);
1571 break;
1572 case QSGNode::RootNodeType:
1573 d << static_cast<const QSGRootNode *>(n);
1574 break;
1575 case QSGNode::OpacityNodeType:
1576 d << static_cast<const QSGOpacityNode *>(n);
1577 break;
1578 case QSGNode::RenderNodeType:
1579 d << "RenderNode(" << Qt::hex << (const void *) n << Qt::dec
1580 << "flags=" << (int) n->flags() << Qt::dec
1581 << (n->isSubtreeBlocked() ? "*BLOCKED*" : "");
1582#ifdef QSG_RUNTIME_DESCRIPTION
1583 d << QSGNodePrivate::description(node: n);
1584#endif
1585 d << ')';
1586 break;
1587 default:
1588 d << "Node(" << Qt::hex << (const void *) n << Qt::dec
1589 << "flags=" << (int) n->flags() << Qt::dec
1590 << (n->isSubtreeBlocked() ? "*BLOCKED*" : "");
1591#ifdef QSG_RUNTIME_DESCRIPTION
1592 d << QSGNodePrivate::description(node: n);
1593#endif
1594 d << ')';
1595 break;
1596 }
1597 return d;
1598}
1599
1600#endif
1601
1602QT_END_NAMESPACE
1603

Provided by KDAB

Privacy Policy
Start learning QML with our Intro Training
Find out more

source code of qtdeclarative/src/quick/scenegraph/coreapi/qsgnode.cpp