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
360
361/*!
362 Prepends \a node to this node's the list of children.
363
364 Ordering of nodes is important as geometry nodes will be rendered in the
365 order they are added to the scene graph.
366 */
367
368void QSGNode::prependChildNode(QSGNode *node)
369{
370 //Q_ASSERT_X(!m_children.contains(node), "QSGNode::prependChildNode", "QSGNode is already a child!");
371 Q_ASSERT_X(!node->m_parent, "QSGNode::prependChildNode", "QSGNode already has a parent");
372
373#ifndef QT_NO_DEBUG
374 if (node->type() == QSGNode::GeometryNodeType) {
375 QSGGeometryNode *g = static_cast<QSGGeometryNode *>(node);
376 Q_ASSERT_X(g->material(), "QSGNode::prependChildNode", "QSGGeometryNode is missing material");
377 Q_ASSERT_X(g->geometry(), "QSGNode::prependChildNode", "QSGGeometryNode is missing geometry");
378 }
379#endif
380
381 if (m_firstChild)
382 m_firstChild->m_previousSibling = node;
383 else
384 m_lastChild = node;
385 node->m_nextSibling = m_firstChild;
386 m_firstChild = node;
387 node->m_parent = this;
388
389 node->markDirty(DirtyNodeAdded);
390}
391
392/*!
393 Appends \a node to this node's list of children.
394
395 Ordering of nodes is important as geometry nodes will be rendered in the
396 order they are added to the scene graph.
397 */
398
399void QSGNode::appendChildNode(QSGNode *node)
400{
401 //Q_ASSERT_X(!m_children.contains(node), "QSGNode::appendChildNode", "QSGNode is already a child!");
402 Q_ASSERT_X(!node->m_parent, "QSGNode::appendChildNode", "QSGNode already has a parent");
403
404#ifndef QT_NO_DEBUG
405 if (node->type() == QSGNode::GeometryNodeType) {
406 QSGGeometryNode *g = static_cast<QSGGeometryNode *>(node);
407 Q_ASSERT_X(g->material(), "QSGNode::appendChildNode", "QSGGeometryNode is missing material");
408 Q_ASSERT_X(g->geometry(), "QSGNode::appendChildNode", "QSGGeometryNode is missing geometry");
409 }
410#endif
411
412 if (m_lastChild)
413 m_lastChild->m_nextSibling = node;
414 else
415 m_firstChild = node;
416 node->m_previousSibling = m_lastChild;
417 m_lastChild = node;
418 node->m_parent = this;
419
420 node->markDirty(DirtyNodeAdded);
421}
422
423
424
425/*!
426 Inserts \a node to this node's list of children before the node specified with \a before.
427
428 Ordering of nodes is important as geometry nodes will be rendered in the
429 order they are added to the scene graph.
430 */
431
432void QSGNode::insertChildNodeBefore(QSGNode *node, QSGNode *before)
433{
434 //Q_ASSERT_X(!m_children.contains(node), "QSGNode::insertChildNodeBefore", "QSGNode is already a child!");
435 Q_ASSERT_X(!node->m_parent, "QSGNode::insertChildNodeBefore", "QSGNode already has a parent");
436 Q_ASSERT_X(before && before->m_parent == this, "QSGNode::insertChildNodeBefore", "The parent of \'before\' is wrong");
437
438#ifndef QT_NO_DEBUG
439 if (node->type() == QSGNode::GeometryNodeType) {
440 QSGGeometryNode *g = static_cast<QSGGeometryNode *>(node);
441 Q_ASSERT_X(g->material(), "QSGNode::insertChildNodeBefore", "QSGGeometryNode is missing material");
442 Q_ASSERT_X(g->geometry(), "QSGNode::insertChildNodeBefore", "QSGGeometryNode is missing geometry");
443 }
444#endif
445
446 QSGNode *previous = before->m_previousSibling;
447 if (previous)
448 previous->m_nextSibling = node;
449 else
450 m_firstChild = node;
451 node->m_previousSibling = previous;
452 node->m_nextSibling = before;
453 before->m_previousSibling = node;
454 node->m_parent = this;
455
456 node->markDirty(DirtyNodeAdded);
457}
458
459
460
461/*!
462 Inserts \a node to this node's list of children after the node specified with \a after.
463
464 Ordering of nodes is important as geometry nodes will be rendered in the
465 order they are added to the scene graph.
466 */
467
468void QSGNode::insertChildNodeAfter(QSGNode *node, QSGNode *after)
469{
470 //Q_ASSERT_X(!m_children.contains(node), "QSGNode::insertChildNodeAfter", "QSGNode is already a child!");
471 Q_ASSERT_X(!node->m_parent, "QSGNode::insertChildNodeAfter", "QSGNode already has a parent");
472 Q_ASSERT_X(after && after->m_parent == this, "QSGNode::insertChildNodeAfter", "The parent of \'after\' is wrong");
473
474#ifndef QT_NO_DEBUG
475 if (node->type() == QSGNode::GeometryNodeType) {
476 QSGGeometryNode *g = static_cast<QSGGeometryNode *>(node);
477 Q_ASSERT_X(g->material(), "QSGNode::insertChildNodeAfter", "QSGGeometryNode is missing material");
478 Q_ASSERT_X(g->geometry(), "QSGNode::insertChildNodeAfter", "QSGGeometryNode is missing geometry");
479 }
480#endif
481
482 QSGNode *next = after->m_nextSibling;
483 if (next)
484 next->m_previousSibling = node;
485 else
486 m_lastChild = node;
487 node->m_nextSibling = next;
488 node->m_previousSibling = after;
489 after->m_nextSibling = node;
490 node->m_parent = this;
491
492 node->markDirty(DirtyNodeAdded);
493}
494
495
496
497/*!
498 Removes \a node from this node's list of children.
499 */
500
501void QSGNode::removeChildNode(QSGNode *node)
502{
503 //Q_ASSERT(m_children.contains(node));
504 Q_ASSERT(node->parent() == this);
505
506 QSGNode *previous = node->m_previousSibling;
507 QSGNode *next = node->m_nextSibling;
508 if (previous)
509 previous->m_nextSibling = next;
510 else
511 m_firstChild = next;
512 if (next)
513 next->m_previousSibling = previous;
514 else
515 m_lastChild = previous;
516 node->m_previousSibling = nullptr;
517 node->m_nextSibling = nullptr;
518
519 node->markDirty(DirtyNodeRemoved);
520 node->m_parent = nullptr;
521}
522
523
524/*!
525 Removes all child nodes from this node's list of children.
526 */
527
528void QSGNode::removeAllChildNodes()
529{
530 while (m_firstChild) {
531 QSGNode *node = m_firstChild;
532 m_firstChild = node->m_nextSibling;
533 node->m_nextSibling = nullptr;
534 if (m_firstChild)
535 m_firstChild->m_previousSibling = nullptr;
536 else
537 m_lastChild = nullptr;
538 node->markDirty(DirtyNodeRemoved);
539 node->m_parent = nullptr;
540 }
541}
542
543/*!
544 * \internal
545 *
546 * Reparents all nodes of this node to \a newParent.
547 */
548void QSGNode::reparentChildNodesTo(QSGNode *newParent)
549{
550 for (QSGNode *c = firstChild(); c; c = firstChild()) {
551 removeChildNode(c);
552 newParent->appendChildNode(c);
553 }
554}
555
556
557int QSGNode::childCount() const
558{
559 int count = 0;
560 QSGNode *n = m_firstChild;
561 while (n) {
562 ++count;
563 n = n->m_nextSibling;
564 }
565 return count;
566}
567
568
569QSGNode *QSGNode::childAtIndex(int i) const
570{
571 QSGNode *n = m_firstChild;
572 while (i && n) {
573 --i;
574 n = n->m_nextSibling;
575 }
576 return n;
577}
578
579
580/*!
581 Sets the flag \a f on this node if \a enabled is true;
582 otherwise clears the flag.
583
584 \sa flags()
585*/
586
587void QSGNode::setFlag(Flag f, bool enabled)
588{
589 if (bool(m_nodeFlags & f) == enabled)
590 return;
591 m_nodeFlags ^= f;
592 Q_ASSERT(int(UsePreprocess) == int(DirtyUsePreprocess));
593 int changedFlag = f & UsePreprocess;
594 if (changedFlag)
595 markDirty(DirtyState(changedFlag));
596}
597
598
599/*!
600 Sets the flags \a f on this node if \a enabled is true;
601 otherwise clears the flags.
602
603 \sa flags()
604*/
605
606void QSGNode::setFlags(Flags f, bool enabled)
607{
608 Flags oldFlags = m_nodeFlags;
609 if (enabled)
610 m_nodeFlags |= f;
611 else
612 m_nodeFlags &= ~f;
613 Q_ASSERT(int(UsePreprocess) == int(DirtyUsePreprocess));
614 int changedFlags = (oldFlags ^ m_nodeFlags) & UsePreprocess;
615 if (changedFlags)
616 markDirty(DirtyState(changedFlags));
617}
618
619
620
621/*!
622 Notifies all connected renderers that the node has dirty \a bits.
623 */
624
625void QSGNode::markDirty(DirtyState bits)
626{
627 int renderableCountDiff = 0;
628 if (bits & DirtyNodeAdded)
629 renderableCountDiff += m_subtreeRenderableCount;
630 if (bits & DirtyNodeRemoved)
631 renderableCountDiff -= m_subtreeRenderableCount;
632
633 QSGNode *p = m_parent;
634 while (p) {
635 p->m_subtreeRenderableCount += renderableCountDiff;
636 if (p->type() == RootNodeType)
637 static_cast<QSGRootNode *>(p)->notifyNodeChange(this, bits);
638 p = p->m_parent;
639 }
640}
641
642void qsgnode_set_description(QSGNode *node, const QString &description)
643{
645 QSGNodePrivate::setDescription(node, description);
646#else
647 Q_UNUSED(node);
648 Q_UNUSED(description);
649#endif
650}
651
652/*!
653 \class QSGBasicGeometryNode
654 \brief The QSGBasicGeometryNode class serves as a baseclass for geometry based nodes.
655
656 \inmodule QtQuick
657
658 The QSGBasicGeometryNode class should not be used by itself. It is only encapsulates
659 shared functionality between the QSGGeometryNode and QSGClipNode classes.
660
661 \note All classes with QSG prefix should be used solely on the scene graph's
662 rendering thread. See \l {Scene Graph and Rendering} for more information.
663 */
664
665
666/*!
667 Creates a new basic geometry node of type \a type
668
669 \internal
670 */
671QSGBasicGeometryNode::QSGBasicGeometryNode(NodeType type)
672 : QSGNode(type)
673 , m_geometry(nullptr)
674 , m_matrix(nullptr)
675 , m_clip_list(nullptr)
676{
677}
678
679
680/*!
681 \internal
682 */
683QSGBasicGeometryNode::QSGBasicGeometryNode(QSGBasicGeometryNodePrivate &dd, NodeType type)
684 : QSGNode(dd, type)
685 , m_geometry(nullptr)
686 , m_matrix(nullptr)
687 , m_clip_list(nullptr)
688{
689}
690
691
692/*!
693 Deletes this QSGBasicGeometryNode.
694
695 If the node has the flag QSGNode::OwnsGeometry set, it will also delete the
696 geometry object it is pointing to. This flag is not set by default.
697 */
698
699QSGBasicGeometryNode::~QSGBasicGeometryNode()
700{
701 if (flags() & OwnsGeometry)
702 delete m_geometry;
703}
704
705
706/*!
707 \fn QSGGeometry *QSGBasicGeometryNode::geometry()
708
709 Returns this node's geometry.
710
711 The geometry is null by default.
712 */
713
714/*!
715 \fn const QSGGeometry *QSGBasicGeometryNode::geometry() const
716
717 Returns this node's geometry.
718
719 The geometry is null by default.
720 */
721
722/*!
723 \fn QMatrix4x4 *QSGBasicGeometryNode::matrix() const
724
725 Will be set during rendering to contain transformation of the geometry
726 for that rendering pass.
727
728 \internal
729 */
730
731/*!
732 \fn QSGClipNode *QSGBasicGeometryNode::clipList() const
733
734 Will be set during rendering to contain the clip of the geometry
735 for that rendering pass.
736
737 \internal
738 */
739
740/*!
741 \fn void QSGBasicGeometryNode::setRendererMatrix(const QMatrix4x4 *m)
742
743 \internal
744 */
745
746/*!
747 \fn void QSGBasicGeometryNode::setRendererClipList(const QSGClipNode *c)
748
749 \internal
750 */
751
752
753/*!
754 Sets the geometry of this node to \a geometry.
755
756 If the node has the flag QSGNode::OwnsGeometry set, it will also delete the
757 geometry object it is pointing to. This flag is not set by default.
758
759 If the geometry is changed without calling setGeometry() again, the user
760 must also mark the geometry as dirty using QSGNode::markDirty().
761
762 \sa markDirty()
763 */
764
765void QSGBasicGeometryNode::setGeometry(QSGGeometry *geometry)
766{
767 if ((flags() & OwnsGeometry) != 0 && m_geometry != geometry)
768 delete m_geometry;
769 m_geometry = geometry;
770 markDirty(DirtyGeometry);
771}
772
773
774
775/*!
776 \class QSGGeometryNode
777 \brief The QSGGeometryNode class is used for all rendered content in the scene graph.
778
779 \inmodule QtQuick
780 \ingroup qtquick-scenegraph-nodes
781
782 The QSGGeometryNode consists of geometry and material. The geometry defines the mesh,
783 the vertices and their structure, to be drawn. The Material defines how the shape is
784 filled.
785
786 The following is a code snippet illustrating how to create a red
787 line using a QSGGeometryNode:
788 \code
789 QSGGeometry *geometry = new QSGGeometry(QSGGeometry::defaultAttributes_Point2D(), 2);
790 geometry->setDrawingMode(QSGGeometry::DrawLines);
791 geometry->setLineWidth(3);
792 geometry->vertexDataAsPoint2D()[0].set(0, 0);
793 geometry->vertexDataAsPoint2D()[1].set(width(), height());
794
795 QSGFlatColorMaterial *material = new QSGFlatColorMaterial;
796 material->setColor(QColor(255, 0, 0));
797
798 QSGGeometryNode *node = new QSGGeometryNode;
799 node->setGeometry(geometry);
800 node->setFlag(QSGNode::OwnsGeometry);
801 node->setMaterial(material);
802 node->setFlag(QSGNode::OwnsMaterial);
803 \endcode
804
805 A geometry node must have both geometry and a normal material before it is added to
806 the scene graph. When the geometry and materials are changed after the node has
807 been added to the scene graph, the user should also mark them as dirty using
808 QSGNode::markDirty().
809
810 The geometry node supports two types of materials, the opaqueMaterial and the normal
811 material. The opaqueMaterial is used when the accumulated scene graph opacity at the
812 time of rendering is 1. The primary use case is to special case opaque rendering
813 to avoid an extra operation in the fragment shader can have significant performance
814 impact on embedded graphics chips. The opaque material is optional.
815
816 \note All classes with QSG prefix should be used solely on the scene graph's
817 rendering thread. See \l {Scene Graph and Rendering} for more information.
818
819 \sa QSGGeometry, QSGMaterial
820 */
821
822
823/*!
824 Creates a new geometry node without geometry and material.
825 */
826
827QSGGeometryNode::QSGGeometryNode()
828 : QSGBasicGeometryNode(GeometryNodeType)
829{
830}
831
832
833/*!
834 \internal
835 */
836QSGGeometryNode::QSGGeometryNode(QSGGeometryNodePrivate &dd)
837 : QSGBasicGeometryNode(dd, GeometryNodeType)
838 , m_render_order(0)
839 , m_material(nullptr)
840 , m_opaque_material(nullptr)
841 , m_opacity(1)
842{
843}
844
845
846/*!
847 Deletes this geometry node.
848
849 The flags QSGNode::OwnsMaterial, QSGNode::OwnsOpaqueMaterial and
850 QSGNode::OwnsGeometry decides whether the geometry node should also
851 delete the materials and geometry. By default, these flags are disabled.
852 */
853
854QSGGeometryNode::~QSGGeometryNode()
855{
856 if (flags() & OwnsMaterial)
857 delete m_material;
858 if (flags() & OwnsOpaqueMaterial)
859 delete m_opaque_material;
860}
861
862
863
864/*!
865 \fn int QSGGeometryNode::renderOrder() const
866
867 Returns the render order of this geometry node.
868
869 \internal
870 */
871
872/*!
873 \fn QSGMaterial *QSGGeometryNode::material() const
874
875 Returns the material of the QSGGeometryNode.
876
877 \sa setMaterial()
878 */
879
880/*!
881 \fn QSGMaterial *QSGGeometryNode::opaqueMaterial() const
882
883 Returns the opaque material of the QSGGeometryNode.
884
885 \sa setOpaqueMaterial()
886 */
887
888/*!
889 \fn qreal QSGGeometryNode::inheritedOpacity() const
890
891 Set during rendering to specify the inherited opacity for that
892 rendering pass.
893
894 \internal
895 */
896
897
898/*!
899 Sets the render order of this node to be \a order.
900
901 Geometry nodes are rendered in an order that visually looks like
902 low order nodes are rendered prior to high order nodes. For opaque
903 geometry there is little difference as z-testing will handle
904 the discard, but for translucent objects, the rendering should
905 normally be specified in the order of back-to-front.
906
907 The default render order is \c 0.
908
909 \internal
910 */
911void QSGGeometryNode::setRenderOrder(int order)
912{
913 m_render_order = order;
914}
915
916
917
918/*!
919 Sets the material of this geometry node to \a material.
920
921 Geometry nodes must have a material before they can be added to the
922 scene graph.
923
924 If the material is changed without calling setMaterial() again, the user
925 must also mark the material as dirty using QSGNode::markDirty().
926
927 */
928void QSGGeometryNode::setMaterial(QSGMaterial *material)
929{
930 if ((flags() & OwnsMaterial) != 0 && m_material != material)
931 delete m_material;
932 m_material = material;
933#ifndef QT_NO_DEBUG
934 if (m_material != nullptr && m_opaque_material == m_material)
935 qWarning("QSGGeometryNode: using same material for both opaque and translucent");
936#endif
937 markDirty(DirtyMaterial);
938}
939
940
941
942/*!
943 Sets the opaque material of this geometry to \a material.
944
945 The opaque material will be preferred by the renderer over the
946 default material, as returned by the material() function, if
947 it is not null and the geometry item has an inherited opacity of
948 1.
949
950 The opaqueness refers to scene graph opacity, the material is still
951 allowed to set QSGMaterial::Blending to true and draw transparent
952 pixels.
953
954 If the material is changed without calling setOpaqueMaterial()
955 again, the user must also mark the opaque material as dirty using
956 QSGNode::markDirty().
957
958 */
959void QSGGeometryNode::setOpaqueMaterial(QSGMaterial *material)
960{
961 if ((flags() & OwnsOpaqueMaterial) != 0 && m_opaque_material != m_material)
962 delete m_opaque_material;
963 m_opaque_material = material;
964#ifndef QT_NO_DEBUG
965 if (m_opaque_material != nullptr && m_opaque_material == m_material)
966 qWarning("QSGGeometryNode: using same material for both opaque and translucent");
967#endif
968
969 markDirty(DirtyMaterial);
970}
971
972
973
974/*!
975 Returns the material which should currently be used for geometry node.
976
977 If the inherited opacity of the node is 1 and there is an opaque material
978 set on this node, it will be returned; otherwise, the default material
979 will be returned.
980
981 \warning This function requires the scene graph above this item to be
982 completely free of dirty states, so it can only be called during rendering
983
984 \internal
985
986 \sa setMaterial, setOpaqueMaterial
987 */
988QSGMaterial *QSGGeometryNode::activeMaterial() const
989{
990 if (m_opaque_material && m_opacity > 0.999)
991 return m_opaque_material;
992 return m_material;
993}
994
995
996/*!
997 Sets the inherited opacity of this geometry to \a opacity.
998
999 This function is meant to be called by the node preprocessing
1000 prior to rendering the tree, so it will not mark the tree as
1001 dirty.
1002
1003 \internal
1004 */
1005void QSGGeometryNode::setInheritedOpacity(qreal opacity)
1006{
1007 Q_ASSERT(opacity >= 0 && opacity <= 1);
1008 m_opacity = opacity;
1009}
1010
1011
1012/*!
1013 \class QSGClipNode
1014 \brief The QSGClipNode class implements the clipping functionality in the scene graph.
1015
1016 \inmodule QtQuick
1017 \ingroup qtquick-scenegraph-nodes
1018
1019 Clipping applies to the node's subtree and can be nested. Multiple clip nodes will be
1020 accumulated by intersecting all their geometries. The accumulation happens
1021 as part of the rendering.
1022
1023 Clip nodes must have a geometry before they can be added to the scene graph.
1024
1025 Clipping is usually implemented by using the stencil buffer.
1026
1027 \note All classes with QSG prefix should be used solely on the scene graph's
1028 rendering thread. See \l {Scene Graph and Rendering} for more information.
1029 */
1030
1031
1032
1033/*!
1034 Creates a new QSGClipNode without a geometry.
1035
1036 The clip node must have a geometry before it can be added to the
1037 scene graph.
1038 */
1039
1040QSGClipNode::QSGClipNode()
1041 : QSGBasicGeometryNode(ClipNodeType)
1042 , m_is_rectangular(false)
1043{
1044 Q_UNUSED(m_reserved);
1045}
1046
1047
1048
1049/*!
1050 Deletes this QSGClipNode.
1051
1052 If the flag QSGNode::OwnsGeometry is set, the geometry will also be
1053 deleted.
1054 */
1055
1056QSGClipNode::~QSGClipNode()
1057{
1058}
1059
1060
1061
1062/*!
1063 \fn bool QSGClipNode::isRectangular() const
1064
1065 Returns if this clip node has a rectangular clip.
1066 */
1067
1068
1069
1070/*!
1071 Sets whether this clip node has a rectangular clip to \a rectHint.
1072
1073 This is an optimization hint which means that the renderer can
1074 use scissoring instead of stencil, which is significantly faster.
1075
1076 When this hint is set and it is applicable, the clip region will be
1077 generated from clipRect() rather than geometry().
1078
1079 By default this property is \c false.
1080 */
1081
1082void QSGClipNode::setIsRectangular(bool rectHint)
1083{
1084 m_is_rectangular = rectHint;
1085}
1086
1087
1088
1089/*!
1090 \fn QRectF QSGClipNode::clipRect() const
1091
1092 Returns the clip rect of this node.
1093 */
1094
1095
1096/*!
1097 Sets the clip rect of this clip node to \a rect.
1098
1099 When a rectangular clip is set in combination with setIsRectangular
1100 the renderer may in some cases use a more optimal clip method.
1101 */
1102void QSGClipNode::setClipRect(const QRectF &rect)
1103{
1104 m_clip_rect = rect;
1105}
1106
1107
1108/*!
1109 \class QSGTransformNode
1110 \brief The QSGTransformNode class implements transformations in the scene graph.
1111
1112 \inmodule QtQuick
1113 \ingroup qtquick-scenegraph-nodes
1114
1115 Transformations apply the node's subtree and can be nested. Multiple transform nodes
1116 will be accumulated by intersecting all their matrices. The accumulation happens
1117 as part of the rendering.
1118
1119 The transform nodes implement a 4x4 matrix which in theory supports full 3D
1120 transformations. However, because the renderer optimizes for 2D use-cases rather
1121 than 3D use-cases, rendering a scene with full 3D transformations needs to
1122 be done with some care.
1123
1124 \note All classes with QSG prefix should be used solely on the scene graph's
1125 rendering thread. See \l {Scene Graph and Rendering} for more information.
1126
1127 */
1128
1129
1130/*!
1131 Create a new QSGTransformNode with its matrix set to the identity matrix.
1132 */
1133
1134QSGTransformNode::QSGTransformNode()
1135 : QSGNode(TransformNodeType)
1136{
1137}
1138
1139
1140
1141/*!
1142 Deletes this transform node.
1143 */
1144
1145QSGTransformNode::~QSGTransformNode()
1146{
1147}
1148
1149
1150
1151/*!
1152 \fn QMatrix4x4 QSGTransformNode::matrix() const
1153
1154 Returns this transform node's matrix.
1155 */
1156
1157
1158
1159/*!
1160 Sets this transform node's matrix to \a matrix.
1161 */
1162
1163void QSGTransformNode::setMatrix(const QMatrix4x4 &matrix)
1164{
1165 m_matrix = matrix;
1166 markDirty(DirtyMatrix);
1167}
1168
1169/*!
1170 \fn const QMatrix4x4 &QSGTransformNode::combinedMatrix() const
1171
1172 Set during rendering to the combination of all parent matrices for
1173 that rendering pass.
1174
1175 \internal
1176 */
1177
1178
1179
1180/*!
1181 Sets the combined matrix of this matrix to \a transform.
1182
1183 This function is meant to be called by the node preprocessing
1184 prior to rendering the tree, so it will not mark the tree as
1185 dirty.
1186
1187 \internal
1188 */
1189void QSGTransformNode::setCombinedMatrix(const QMatrix4x4 &matrix)
1190{
1191 m_combined_matrix = matrix;
1192}
1193
1194
1195
1196/*!
1197 \class QSGRootNode
1198 \brief The QSGRootNode is the toplevel root of any scene graph.
1199
1200 The root node is used to attach a scene graph to a renderer.
1201
1202 \internal
1203 */
1204
1205
1206
1207/*!
1208 \fn QSGRootNode::QSGRootNode()
1209
1210 Creates a new root node.
1211 */
1212
1213QSGRootNode::QSGRootNode()
1214 : QSGNode(RootNodeType)
1215{
1216}
1217
1218
1219/*!
1220 Deletes the root node.
1221
1222 When a root node is deleted it removes itself from all of renderers
1223 that are referencing it.
1224 */
1225
1226QSGRootNode::~QSGRootNode()
1227{
1228 while (!m_renderers.isEmpty())
1229 m_renderers.constLast()->setRootNode(nullptr);
1230 destroy(); // Must call destroy() here because markDirty() casts this to QSGRootNode.
1231}
1232
1233
1234
1235/*!
1236 Called to notify all renderers that \a node has been marked as dirty
1237 with \a flags.
1238 */
1239
1240void QSGRootNode::notifyNodeChange(QSGNode *node, DirtyState state)
1241{
1242 for (int i=0; i<m_renderers.size(); ++i) {
1243 m_renderers.at(i)->nodeChanged(node, state);
1244 }
1245}
1246
1247
1248
1249/*!
1250 \class QSGOpacityNode
1251 \brief The QSGOpacityNode class is used to change opacity of nodes.
1252
1253 \inmodule QtQuick
1254 \ingroup qtquick-scenegraph-nodes
1255
1256 Opacity applies to its subtree and can be nested. Multiple opacity nodes
1257 will be accumulated by multiplying their opacity. The accumulation happens
1258 as part of the rendering.
1259
1260 When nested opacity gets below a certain threshold, the subtree might
1261 be marked as blocked, causing isSubtreeBlocked() to return true. This
1262 is done for performance reasons.
1263
1264 \note All classes with QSG prefix should be used solely on the scene graph's
1265 rendering thread. See \l {Scene Graph and Rendering} for more information.
1266 */
1267
1268
1269
1270/*!
1271 Constructs an opacity node with a default opacity of 1.
1272
1273 Opacity accumulates downwards in the scene graph so a node with two
1274 QSGOpacityNode instances above it, both with opacity of 0.5, will have
1275 effective opacity of 0.25.
1276
1277 The default opacity of nodes is 1.
1278 */
1279QSGOpacityNode::QSGOpacityNode()
1280 : QSGNode(OpacityNodeType)
1281{
1282}
1283
1284
1285
1286/*!
1287 Deletes the opacity node.
1288 */
1289
1290QSGOpacityNode::~QSGOpacityNode()
1291{
1292}
1293
1294
1295
1296/*!
1297 \fn qreal QSGOpacityNode::opacity() const
1298
1299 Returns this opacity node's opacity.
1300 */
1301
1303
1304/*!
1305 Sets the opacity of this node to \a opacity.
1306
1307 Before rendering the graph, the renderer will do an update pass
1308 over the subtree to propagate the opacity to its children.
1309
1310 The value will be bounded to the range 0 to 1.
1311 */
1312
1313void QSGOpacityNode::setOpacity(qreal opacity)
1314{
1315 opacity = std::clamp(opacity, qreal(0.0), qreal(1.0));
1316 if (m_opacity == opacity)
1317 return;
1318 DirtyState dirtyState = DirtyOpacity;
1319
1320 if ((m_opacity < OPACITY_THRESHOLD && opacity >= OPACITY_THRESHOLD) // blocked to unblocked
1321 || (m_opacity >= OPACITY_THRESHOLD && opacity < OPACITY_THRESHOLD)) // unblocked to blocked
1322 dirtyState |= DirtySubtreeBlocked;
1323
1324 m_opacity = opacity;
1325 markDirty(dirtyState);
1326}
1327
1328
1329
1330/*!
1331 \fn qreal QSGOpacityNode::combinedOpacity() const
1332
1333 Returns this node's accumulated opacity.
1334
1335 This value is calculated during rendering and only stored
1336 in the opacity node temporarily.
1337
1338 \internal
1339 */
1340
1341
1342
1343/*!
1344 Sets the combined opacity of this node to \a opacity.
1345
1346 This function is meant to be called by the node preprocessing
1347 prior to rendering the tree, so it will not mark the tree as
1348 dirty.
1349
1350 \internal
1351 */
1352
1353void QSGOpacityNode::setCombinedOpacity(qreal opacity)
1354{
1355 m_combined_opacity = opacity;
1356}
1357
1358
1359
1360/*!
1361 For performance reasons, we block the subtree when the opacity
1362 is below a certain threshold.
1363
1364 \internal
1365 */
1366
1367bool QSGOpacityNode::isSubtreeBlocked() const
1368{
1369 return m_opacity < OPACITY_THRESHOLD;
1370}
1371
1372
1373/*!
1374 \class QSGNodeVisitor
1375 \brief The QSGNodeVisitor class is a helper class for traversing the scene graph.
1376
1377 \internal
1378 */
1379
1380QSGNodeVisitor::~QSGNodeVisitor()
1381{
1382
1383}
1384
1385
1386void QSGNodeVisitor::visitNode(QSGNode *n)
1387{
1388 switch (n->type()) {
1389 case QSGNode::TransformNodeType: {
1390 QSGTransformNode *t = static_cast<QSGTransformNode *>(n);
1391 enterTransformNode(t);
1392 visitChildren(t);
1393 leaveTransformNode(t);
1394 break; }
1395 case QSGNode::GeometryNodeType: {
1396 QSGGeometryNode *g = static_cast<QSGGeometryNode *>(n);
1397 enterGeometryNode(g);
1398 visitChildren(g);
1399 leaveGeometryNode(g);
1400 break; }
1401 case QSGNode::ClipNodeType: {
1402 QSGClipNode *c = static_cast<QSGClipNode *>(n);
1403 enterClipNode(c);
1404 visitChildren(c);
1405 leaveClipNode(c);
1406 break; }
1407 case QSGNode::OpacityNodeType: {
1408 QSGOpacityNode *o = static_cast<QSGOpacityNode *>(n);
1409 enterOpacityNode(o);
1410 visitChildren(o);
1411 leaveOpacityNode(o);
1412 break; }
1413 default:
1414 visitChildren(n);
1415 break;
1416 }
1417}
1418
1419void QSGNodeVisitor::visitChildren(QSGNode *n)
1420{
1421 for (QSGNode *c = n->firstChild(); c; c = c->nextSibling())
1422 visitNode(c);
1423}
1424
1425#ifndef QT_NO_DEBUG_STREAM
1426QDebug operator<<(QDebug d, const QSGGeometryNode *n)
1427{
1428 if (!n) {
1429 d << "Geometry(null)";
1430 return d;
1431 }
1432 d << "GeometryNode(" << Qt::hex << (const void *) n << Qt::dec;
1433
1434 const QSGGeometry *g = n->geometry();
1435
1436 if (!g) {
1437 d << "no geometry";
1438 } else {
1439
1440 switch (g->drawingMode()) {
1441 case QSGGeometry::DrawTriangleStrip: d << "strip"; break;
1442 case QSGGeometry::DrawTriangleFan: d << "fan"; break;
1443 case QSGGeometry::DrawTriangles: d << "triangles"; break;
1444 default: break;
1445 }
1446
1447 d << "#V:" << g->vertexCount() << "#I:" << g->indexCount();
1448
1449 if (g->attributeCount() > 0 && g->attributes()->type == QSGGeometry::FloatType) {
1450 float x1 = 1e10, x2 = -1e10, y1=1e10, y2=-1e10;
1451 int stride = g->sizeOfVertex();
1452 for (int i = 0; i < g->vertexCount(); ++i) {
1453 float x = ((float *)((char *)const_cast<QSGGeometry *>(g)->vertexData() + i * stride))[0];
1454 float y = ((float *)((char *)const_cast<QSGGeometry *>(g)->vertexData() + i * stride))[1];
1455
1456 x1 = qMin(x1, x);
1457 x2 = qMax(x2, x);
1458 y1 = qMin(y1, y);
1459 y2 = qMax(y2, y);
1460 }
1461
1462 d << "x1=" << x1 << "y1=" << y1 << "x2=" << x2 << "y2=" << y2;
1463 }
1464 }
1465
1466 if (n->material())
1467 d << "materialtype=" << n->material()->type();
1468
1469
1470 d << ')';
1472 d << QSGNodePrivate::description(n);
1473#endif
1474 return d;
1475}
1476
1477QDebug operator<<(QDebug d, const QSGClipNode *n)
1478{
1479 if (!n) {
1480 d << "ClipNode(null)";
1481 return d;
1482 }
1483 d << "ClipNode(" << Qt::hex << (const void *) n << Qt::dec;
1484
1485 if (n->childCount())
1486 d << "children=" << n->childCount();
1487
1488 d << "is rect?" << (n->isRectangular() ? "yes" : "no");
1489
1490 d << ')';
1492 d << QSGNodePrivate::description(n);
1493#endif
1494 d << (n->isSubtreeBlocked() ? "*BLOCKED*" : "");
1495 return d;
1496}
1497
1498QDebug operator<<(QDebug d, const QSGTransformNode *n)
1499{
1500 if (!n) {
1501 d << "TransformNode(null)";
1502 return d;
1503 }
1504 const QMatrix4x4 m = n->matrix();
1505 d << "TransformNode(";
1506 d << Qt::hex << (const void *) n << Qt::dec;
1507 if (m.isIdentity())
1508 d << "identity";
1509 else if (m.determinant() == 1 && m(0, 0) == 1 && m(1, 1) == 1 && m(2, 2) == 1)
1510 d << "translate" << m(0, 3) << m(1, 3) << m(2, 3);
1511 else
1512 d << "det=" << n->matrix().determinant();
1514 d << QSGNodePrivate::description(n);
1515#endif
1516 d << (n->isSubtreeBlocked() ? "*BLOCKED*" : "");
1517 d << ')';
1518 return d;
1519}
1520
1521QDebug operator<<(QDebug d, const QSGOpacityNode *n)
1522{
1523 if (!n) {
1524 d << "OpacityNode(null)";
1525 return d;
1526 }
1527 d << "OpacityNode(";
1528 d << Qt::hex << (const void *) n << Qt::dec;
1529 d << "opacity=" << n->opacity()
1530 << "combined=" << n->combinedOpacity()
1531 << (n->isSubtreeBlocked() ? "*BLOCKED*" : "");
1533 d << QSGNodePrivate::description(n);
1534#endif
1535 d << ')';
1536 return d;
1537}
1538
1539
1540QDebug operator<<(QDebug d, const QSGRootNode *n)
1541{
1542 if (!n) {
1543 d << "RootNode(null)";
1544 return d;
1545 }
1546 QDebugStateSaver saver(d);
1547 d << "RootNode" << Qt::hex << (const void *) n << (n->isSubtreeBlocked() ? "*BLOCKED*" : "");
1549 d << QSGNodePrivate::description(n);
1550#endif
1551 d << ')';
1552 return d;
1553}
1554
1555
1556
1557QDebug operator<<(QDebug d, const QSGNode *n)
1558{
1559 if (!n) {
1560 d << "Node(null)";
1561 return d;
1562 }
1563 switch (n->type()) {
1564 case QSGNode::GeometryNodeType:
1565 d << static_cast<const QSGGeometryNode *>(n);
1566 break;
1567 case QSGNode::TransformNodeType:
1568 d << static_cast<const QSGTransformNode *>(n);
1569 break;
1570 case QSGNode::ClipNodeType:
1571 d << static_cast<const QSGClipNode *>(n);
1572 break;
1573 case QSGNode::RootNodeType:
1574 d << static_cast<const QSGRootNode *>(n);
1575 break;
1576 case QSGNode::OpacityNodeType:
1577 d << static_cast<const QSGOpacityNode *>(n);
1578 break;
1579 case QSGNode::RenderNodeType:
1580 d << "RenderNode(" << Qt::hex << (const void *) n << Qt::dec
1581 << "flags=" << (int) n->flags() << Qt::dec
1582 << (n->isSubtreeBlocked() ? "*BLOCKED*" : "");
1584 d << QSGNodePrivate::description(n);
1585#endif
1586 d << ')';
1587 break;
1588 default:
1589 d << "Node(" << Qt::hex << (const void *) n << Qt::dec
1590 << "flags=" << (int) n->flags() << Qt::dec
1591 << (n->isSubtreeBlocked() ? "*BLOCKED*" : "");
1593 d << QSGNodePrivate::description(n);
1594#endif
1595 d << ')';
1596 break;
1597 }
1598 return d;
1599}
1600
1601#endif
1602
1603QT_END_NAMESPACE
QDebug operator<<(QDebug d, const QSGRootNode *n)
Definition qsgnode.cpp:1540
QDebug operator<<(QDebug d, const QSGClipNode *n)
Definition qsgnode.cpp:1477
QDebug operator<<(QDebug d, const QSGNode *n)
Definition qsgnode.cpp:1557
QDebug operator<<(QDebug d, const QSGTransformNode *n)
Definition qsgnode.cpp:1498
void qsgnode_set_description(QSGNode *node, const QString &description)
Definition qsgnode.cpp:642
QDebug operator<<(QDebug d, const QSGOpacityNode *n)
Definition qsgnode.cpp:1521
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:1426
const qreal OPACITY_THRESHOLD
Definition qsgnode.cpp:1302
#define QSG_RUNTIME_DESCRIPTION
Definition qsgnode.h:17