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