Revision control
++ // Copyright (C) 2016 The Qt Company Ltd.
++ // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
++ // Qt-Security score:significant reason:default
++
++ #include "qsgnode.h"
++ #include "qsgnode_p.h"
++ #include "qsgrenderer_p.h"
++ #include "qsgnodeupdater_p.h"
++ #include "qsgmaterial.h"
++
++ #include <algorithm>
++
++ #include "limits.h"
++
++ QT_BEGIN_NAMESPACE
++
++ #ifndef QT_NO_DEBUG
++ static int qt_node_count = 0;
++
++ static void qt_print_node_count()
++ {
++ qCDebug(lcQsgLeak, "Number of leaked nodes: %i", qt_node_count);
++ qt_node_count = -1;
++ }
++ #endif
++
++ /*!
++ \group qtquick-scenegraph-nodes
++ \title Qt Quick Scene Graph Node classes
++ \brief Nodes that can be used as part of the scene graph.
++
++ This page lists the nodes in \l {Qt Quick}'s \l {Qt Quick Scene Graph}{scene graph}.
++ */
++
++ /*!
++ \class QSGNode
++ \brief The QSGNode class is the base class for all nodes in the scene graph.
++
++ \inmodule QtQuick
++ \ingroup qtquick-scenegraph-nodes
++
++ The QSGNode class can be used as a child container. Children are added with
++ the appendChildNode(), prependChildNode(), insertChildNodeBefore() and
++ insertChildNodeAfter(). The order of nodes is important as geometry nodes
++ are rendered according to their ordering in the scene graph.
++
++ The scene graph nodes contain a mechanism that describes which
++ parts of the scene have changed. This includes the combined matrices,
++ accumulated opacity, changes to the node hierarchy, and so on. This
++ information can be used for optimizations inside the scene graph renderer.
++ For the renderer to properly render the nodes, it is important that users
++ call QSGNode::markDirty() with the correct flags when nodes are changed.
++ Most of the functions on the node classes will implicitly call markDirty().
++ For example, QSGNode::appendChildNode() will call markDirty() passing in
++ QSGNode::DirtyNodeAdded.
++
++ If nodes change every frame, the preprocess() function can be used to
++ apply changes to a node for every frame it is rendered. The use of
++ preprocess() must be explicitly enabled by setting the
++ QSGNode::UsePreprocess flag on the node.
++
++ The virtual isSubtreeBlocked() function can be used to disable a subtree all
++ together. Nodes in a blocked subtree will not be preprocessed() and not
++ rendered.
++
++ \note All classes with QSG prefix should be used solely on the scene graph's
++ rendering thread. See \l {Scene Graph and Rendering} for more information.
++ */
++
++ /*!
++ \enum QSGNode::DirtyStateBit
++
++ Used in QSGNode::markDirty() to indicate how the scene graph has changed.
++
++ \value DirtyMatrix The matrix in a QSGTransformNode has changed.
++ \value DirtyNodeAdded A node was added.
++ \value DirtyNodeRemoved A node was removed.
++ \value DirtyGeometry The geometry of a QSGGeometryNode has changed.
++ \value DirtyMaterial The material of a QSGGeometryNode has changed.
++ \value DirtyOpacity The opacity of a QSGOpacityNode has changed.
++ \value DirtySubtreeBlocked The subtree has been blocked.
++
++ \omitvalue DirtyForceUpdate
++ \omitvalue DirtyUsePreprocess
++ \omitvalue DirtyPropagationMask
++
++ \sa QSGNode::markDirty()
++ */
++
++ /*!
++ \enum QSGNode::Flag
++
++ The QSGNode::Flag enum describes flags on the QSGNode
++
++ \value OwnedByParent The node is owned by its parent and will be deleted
++ when the parent is deleted.
++ \value UsePreprocess The node's virtual preprocess() function will be called
++ before rendering starts.
++ \value OwnsGeometry Only valid for QSGGeometryNode and QSGClipNode.
++ The node has ownership over the QSGGeometry instance and will
++ delete it when the node is destroyed or a geometry is assigned.
++ \value OwnsMaterial Only valid for QSGGeometryNode. The node has ownership
++ over the material and will delete it when the node is destroyed or a material is assigned.
++ \value OwnsOpaqueMaterial Only valid for QSGGeometryNode. The node has
++ ownership over the opaque material and will delete it when the node is
++ destroyed or a material is assigned.
++ \value InternalReserved Reserved for internal use.
++
++ \omitvalue IsVisitableNode
++ */
++
++ /*!
++ \enum QSGNode::NodeType
++
++ Can be used to figure out the type of node.
++
++ \value BasicNodeType The type of QSGNode
++ \value GeometryNodeType The type of QSGGeometryNode
++ \value TransformNodeType The type of QSGTransformNode
++ \value ClipNodeType The type of QSGClipNode
++ \value OpacityNodeType The type of QSGOpacityNode
++ \value RenderNodeType The type of QSGRenderNode
++
++ \omitvalue RootNodeType
++
++ \sa type()
++ */
++
++ /*!
++ \fn QSGNode *QSGNode::childAtIndex(int i) const
++
++ Returns the child at index \a i.
++
++ Children are stored internally as a linked list, so iterating
++ over the children via the index is suboptimal.
++ */
++
++ /*!
++ \fn int QSGNode::childCount() const
++
++ Returns the number of child nodes.
++ */
++
++ /*!
++ \fn void QSGNode::clearDirty()
++
++ \internal
++ */
++
++ /*!
++ \fn QSGNode *QSGNode::firstChild() const
++
++ Returns the first child of this node.
++
++ The children are stored in a linked list.
++ */
++
++ /*!
++ \fn QSGNode *QSGNode::lastChild() const
++
++ Returns the last child of this node.
++
++ The children are stored as a linked list.
++ */
++
++ /*!
++ \fn QSGNode::Flags QSGNode::flags() const
++
++ Returns the set of flags for this node.
++ */
++
++ /*!
++ \fn QSGNode *QSGNode::nextSibling() const
++
++ Returns the node after this in the parent's list of children.
++
++ The children are stored as a linked list.
++ */
++
++ /*!
++ \fn QSGNode *QSGNode::previousSibling() const
++
++ Returns the node before this in the parent's list of children.
++
++ The children are stored as a linked list.
++ */
++
++ /*!
++ \fn QSGNode::Type QSGNode::type() const
++
++ Returns the type of this node. The node type must be one of the
++ predefined types defined in QSGNode::NodeType and can safely be
++ used to cast to the corresponding class.
++ */
++
++ /*!
++ \fn QSGNode::DirtyState QSGNode::dirtyState() const
++
++ \internal
++ */
++
++ /*!
++ \fn QSGNode *QSGNode::parent() const
++
++ Returns the parent node of this node.
++ */
++
++
++ /*!
++ * Constructs a new node
++ */
++ QSGNode::QSGNode()
++ : m_nodeFlags(OwnedByParent)
++ {
++ init();
++ }
++
++ /*!
++ * Constructs a new node with the given node type.
++ *
++ * \internal
++ */
++ QSGNode::QSGNode(NodeType type)
++ : m_parent(nullptr)
++ , m_type(type)
++ , m_firstChild(nullptr)
++ , m_lastChild(nullptr)
++ , m_nextSibling(nullptr)
++ , m_previousSibling(nullptr)
++ , m_subtreeRenderableCount(type == GeometryNodeType || type == RenderNodeType ? 1 : 0)
++ , m_nodeFlags(OwnedByParent)
++ {
++ init();
++ }
++
++ /*!
++ * Constructs a new node with the given node type.
++ *
++ * \internal
++ */
++ QSGNode::QSGNode(QSGNodePrivate &dd, NodeType type)
++ : m_parent(nullptr)
++ , m_type(type)
++ , m_firstChild(nullptr)
++ , m_lastChild(nullptr)
++ , m_nextSibling(nullptr)
++ , m_previousSibling(nullptr)
++ , m_subtreeRenderableCount(type == GeometryNodeType || type == RenderNodeType ? 1 : 0)
++ , m_nodeFlags(OwnedByParent)
++ , d_ptr(&dd)
++ {
++ init();
++ }
++
++ /*!
++ * \internal
++ */
++ void QSGNode::init()
++ {
++ #ifndef QT_NO_DEBUG
++ if (lcQsgLeak().isDebugEnabled()) {
++ ++qt_node_count;
++ static bool atexit_registered = false;
++ if (!atexit_registered) {
++ atexit(qt_print_node_count);
++ atexit_registered = true;
++ }
++ }
++ #endif
++
++ #ifdef QSG_RUNTIME_DESCRIPTION
++ if (d_ptr.isNull())
++ d_ptr.reset(new QSGNodePrivate());
++ #endif
++ }
++
++ /*!
++ * Destroys the node.
++ *
++ * Every child of this node that has the flag QSGNode::OwnedByParent set,
++ * will also be deleted.
++ */
++ QSGNode::~QSGNode()
++ {
++ #ifndef QT_NO_DEBUG
++ if (lcQsgLeak().isDebugEnabled()) {
++ --qt_node_count;
++ if (qt_node_count < 0)
++ qCDebug(lcQsgLeak, "Node destroyed after qt_print_node_count() was called.");
++ }
++ #endif
++ destroy();
++ }
++
++
++ /*!
++ \fn void QSGNode::preprocess()
++
++ Override this function to do processing on the node before it is rendered.
++
++ Preprocessing needs to be explicitly enabled by setting the flag
++ QSGNode::UsePreprocess. The flag needs to be set before the node is added
++ to the scene graph and will cause the preprocess() function to be called
++ for every frame the node is rendered.
++
++ \warning Beware of deleting nodes while they are being preprocessed. It is
++ possible, with a small performance hit, to delete a single node during its
++ own preprocess call. Deleting a subtree which has nodes that also use
++ preprocessing may result in a segmentation fault. This is done for
++ performance reasons.
++ */
++
++
++
++
++ /*!
++ Returns whether this node and its subtree is available for use.
++
++ Blocked subtrees will not get their dirty states updated and they
++ will not be rendered.
++
++ The QSGOpacityNode will return a blocked subtree when accumulated opacity
++ is 0, for instance.
++ */
++
++ bool QSGNode::isSubtreeBlocked() const
++ {
++ return false;
++ }
++
++ /*!
++ \internal
++ Detaches the node from the scene graph and deletes any children it owns.
++
++ This function is called from QSGNode's and QSGRootNode's destructor. It
++ should not be called explicitly in user code. QSGRootNode needs to call
++ destroy() because destroy() calls removeChildNode() which in turn calls
++ markDirty() which type-casts the node to QSGRootNode. This type-cast is not
++ valid at the time QSGNode's destructor is called because the node will
++ already be partially destroyed at that point.
++ */
++
++ void QSGNode::destroy()
++ {
++ if (m_parent) {
++ m_parent->removeChildNode(this);
++ Q_ASSERT(m_parent == nullptr);
++ }
++ while (m_firstChild) {
++ QSGNode *child = m_firstChild;
++ removeChildNode(child);
++ Q_ASSERT(child->m_parent == nullptr);
++ if (child->flags() & OwnedByParent)
++ delete child;
++ }
++
++ Q_ASSERT(m_firstChild == nullptr && m_lastChild == nullptr);
++ }
++
++ quint8 QSGNodePrivate::mutabilityGroup(const QSGNode *node)
++ {
++ return !node->d_ptr.isNull()
++ ? node->d_ptr->m_mutabilityGroup
++ : 0;
++ }
++
++ /*!
++ \internal
++
++ Sets the mutability group of this node. Recurses to children.
++ */
++ void QSGNodePrivate::setMutabilityGroupOfSubtree(QSGNode *node, quint8 group)
++ {
++ Q_ASSERT((group & 0xf) == group);
++
++ const quint8 currentMutabilityGroup = mutabilityGroup(node);
++ if (currentMutabilityGroup != group) {
++ if (node->d_ptr.isNull())
++ node->d_ptr.reset(new QSGNodePrivate());
++ node->d_ptr->m_mutabilityGroup = group;
++ node->markDirty(QSGNode::DirtyGeometry);
++ }
++
++ for (int i = 0; i < node->childCount(); ++i)
++ QSGNodePrivate::setMutabilityGroupOfSubtree(node->childAtIndex(i), group);
++ }
++
++ /*!
++ Prepends \a node to this node's the list of children.
++
++ Ordering of nodes is important as geometry nodes will be rendered in the
++ order they are added to the scene graph.
++ */
++
++ void QSGNode::prependChildNode(QSGNode *node)
++ {
++ //Q_ASSERT_X(!m_children.contains(node), "QSGNode::prependChildNode", "QSGNode is already a child!");
++ Q_ASSERT_X(!node->m_parent, "QSGNode::prependChildNode", "QSGNode already has a parent");
++
++ #ifndef QT_NO_DEBUG
++ if (node->type() == QSGNode::GeometryNodeType) {
++ QSGGeometryNode *g = static_cast<QSGGeometryNode *>(node);
++ Q_ASSERT_X(g->material(), "QSGNode::prependChildNode", "QSGGeometryNode is missing material");
++ Q_ASSERT_X(g->geometry(), "QSGNode::prependChildNode", "QSGGeometryNode is missing geometry");
++ }
++ #endif
++
++ if (m_firstChild)
++ m_firstChild->m_previousSibling = node;
++ else
++ m_lastChild = node;
++ node->m_nextSibling = m_firstChild;
++ m_firstChild = node;
++ node->m_parent = this;
++
++ node->markDirty(DirtyNodeAdded);
++ }
++
++ /*!
++ Appends \a node to this node's list of children.
++
++ Ordering of nodes is important as geometry nodes will be rendered in the
++ order they are added to the scene graph.
++ */
++
++ void QSGNode::appendChildNode(QSGNode *node)
++ {
++ //Q_ASSERT_X(!m_children.contains(node), "QSGNode::appendChildNode", "QSGNode is already a child!");
++ Q_ASSERT_X(!node->m_parent, "QSGNode::appendChildNode", "QSGNode already has a parent");
++
++ #ifndef QT_NO_DEBUG
++ if (node->type() == QSGNode::GeometryNodeType) {
++ QSGGeometryNode *g = static_cast<QSGGeometryNode *>(node);
++ Q_ASSERT_X(g->material(), "QSGNode::appendChildNode", "QSGGeometryNode is missing material");
++ Q_ASSERT_X(g->geometry(), "QSGNode::appendChildNode", "QSGGeometryNode is missing geometry");
++ }
++ #endif
++
++ if (m_lastChild)
++ m_lastChild->m_nextSibling = node;
++ else
++ m_firstChild = node;
++ node->m_previousSibling = m_lastChild;
++ m_lastChild = node;
++ node->m_parent = this;
++
++ node->markDirty(DirtyNodeAdded);
++ }
++
++
++
++ /*!
++ Inserts \a node to this node's list of children before the node specified with \a before.
++
++ Ordering of nodes is important as geometry nodes will be rendered in the
++ order they are added to the scene graph.
++ */
++
++ void QSGNode::insertChildNodeBefore(QSGNode *node, QSGNode *before)
++ {
++ //Q_ASSERT_X(!m_children.contains(node), "QSGNode::insertChildNodeBefore", "QSGNode is already a child!");
++ Q_ASSERT_X(!node->m_parent, "QSGNode::insertChildNodeBefore", "QSGNode already has a parent");
++ Q_ASSERT_X(before && before->m_parent == this, "QSGNode::insertChildNodeBefore", "The parent of \'before\' is wrong");
++
++ #ifndef QT_NO_DEBUG
++ if (node->type() == QSGNode::GeometryNodeType) {
++ QSGGeometryNode *g = static_cast<QSGGeometryNode *>(node);
++ Q_ASSERT_X(g->material(), "QSGNode::insertChildNodeBefore", "QSGGeometryNode is missing material");
++ Q_ASSERT_X(g->geometry(), "QSGNode::insertChildNodeBefore", "QSGGeometryNode is missing geometry");
++ }
++ #endif
++
++ QSGNode *previous = before->m_previousSibling;
++ if (previous)
++ previous->m_nextSibling = node;
++ else
++ m_firstChild = node;
++ node->m_previousSibling = previous;
++ node->m_nextSibling = before;
++ before->m_previousSibling = node;
++ node->m_parent = this;
++
++ node->markDirty(DirtyNodeAdded);
++ }
++
++
++
++ /*!
++ Inserts \a node to this node's list of children after the node specified with \a after.
++
++ Ordering of nodes is important as geometry nodes will be rendered in the
++ order they are added to the scene graph.
++ */
++
++ void QSGNode::insertChildNodeAfter(QSGNode *node, QSGNode *after)
++ {
++ //Q_ASSERT_X(!m_children.contains(node), "QSGNode::insertChildNodeAfter", "QSGNode is already a child!");
++ Q_ASSERT_X(!node->m_parent, "QSGNode::insertChildNodeAfter", "QSGNode already has a parent");
++ Q_ASSERT_X(after && after->m_parent == this, "QSGNode::insertChildNodeAfter", "The parent of \'after\' is wrong");
++
++ #ifndef QT_NO_DEBUG
++ if (node->type() == QSGNode::GeometryNodeType) {
++ QSGGeometryNode *g = static_cast<QSGGeometryNode *>(node);
++ Q_ASSERT_X(g->material(), "QSGNode::insertChildNodeAfter", "QSGGeometryNode is missing material");
++ Q_ASSERT_X(g->geometry(), "QSGNode::insertChildNodeAfter", "QSGGeometryNode is missing geometry");
++ }
++ #endif
++
++ QSGNode *next = after->m_nextSibling;
++ if (next)
++ next->m_previousSibling = node;
++ else
++ m_lastChild = node;
++ node->m_nextSibling = next;
++ node->m_previousSibling = after;
++ after->m_nextSibling = node;
++ node->m_parent = this;
++
++ node->markDirty(DirtyNodeAdded);
++ }
++
++
++
++ /*!
++ Removes \a node from this node's list of children.
++ */
++
++ void QSGNode::removeChildNode(QSGNode *node)
++ {
++ //Q_ASSERT(m_children.contains(node));
++ Q_ASSERT(node->parent() == this);
++
++ QSGNode *previous = node->m_previousSibling;
++ QSGNode *next = node->m_nextSibling;
++ if (previous)
++ previous->m_nextSibling = next;
++ else
++ m_firstChild = next;
++ if (next)
++ next->m_previousSibling = previous;
++ else
++ m_lastChild = previous;
++ node->m_previousSibling = nullptr;
++ node->m_nextSibling = nullptr;
++
++ node->markDirty(DirtyNodeRemoved);
++ node->m_parent = nullptr;
++ }
++
++
++ /*!
++ Removes all child nodes from this node's list of children.
++ */
++
++ void QSGNode::removeAllChildNodes()
++ {
++ while (m_firstChild) {
++ QSGNode *node = m_firstChild;
++ m_firstChild = node->m_nextSibling;
++ node->m_nextSibling = nullptr;
++ if (m_firstChild)
++ m_firstChild->m_previousSibling = nullptr;
++ else
++ m_lastChild = nullptr;
++ node->markDirty(DirtyNodeRemoved);
++ node->m_parent = nullptr;
++ }
++ }
++
++ /*!
++ * \internal
++ *
++ * Reparents all nodes of this node to \a newParent.
++ */
++ void QSGNode::reparentChildNodesTo(QSGNode *newParent)
++ {
++ for (QSGNode *c = firstChild(); c; c = firstChild()) {
++ removeChildNode(c);
++ newParent->appendChildNode(c);
++ }
++ }
++
++
++ int QSGNode::childCount() const
++ {
++ int count = 0;
++ QSGNode *n = m_firstChild;
++ while (n) {
++ ++count;
++ n = n->m_nextSibling;
++ }
++ return count;
++ }
++
++
++ QSGNode *QSGNode::childAtIndex(int i) const
++ {
++ QSGNode *n = m_firstChild;
++ while (i && n) {
++ --i;
++ n = n->m_nextSibling;
++ }
++ return n;
++ }
++
++
++ /*!
++ Sets the flag \a f on this node if \a enabled is true;
++ otherwise clears the flag.
++
++ \sa flags()
++ */
++
++ void QSGNode::setFlag(Flag f, bool enabled)
++ {
++ if (bool(m_nodeFlags & f) == enabled)
++ return;
++ m_nodeFlags ^= f;
++ Q_ASSERT(int(UsePreprocess) == int(DirtyUsePreprocess));
++ int changedFlag = f & UsePreprocess;
++ if (changedFlag)
++ markDirty(DirtyState(changedFlag));
++ }
++
++
++ /*!
++ Sets the flags \a f on this node if \a enabled is true;
++ otherwise clears the flags.
++
++ \sa flags()
++ */
++
++ void QSGNode::setFlags(Flags f, bool enabled)
++ {
++ Flags oldFlags = m_nodeFlags;
++ if (enabled)
++ m_nodeFlags |= f;
++ else
++ m_nodeFlags &= ~f;
++ Q_ASSERT(int(UsePreprocess) == int(DirtyUsePreprocess));
++ int changedFlags = (oldFlags ^ m_nodeFlags) & UsePreprocess;
++ if (changedFlags)
++ markDirty(DirtyState(changedFlags));
++ }
++
++
++
++ /*!
++ Notifies all connected renderers that the node has dirty \a bits.
++ */
++
++ void QSGNode::markDirty(DirtyState bits)
++ {
++ int renderableCountDiff = 0;
++ if (bits & DirtyNodeAdded)
++ renderableCountDiff += m_subtreeRenderableCount;
++ if (bits & DirtyNodeRemoved)
++ renderableCountDiff -= m_subtreeRenderableCount;
++
++ QSGNode *p = m_parent;
++ while (p) {
++ p->m_subtreeRenderableCount += renderableCountDiff;
++ if (p->type() == RootNodeType)
++ static_cast<QSGRootNode *>(p)->notifyNodeChange(this, bits);
++ p = p->m_parent;
++ }
++ }
++
++ void qsgnode_set_description(QSGNode *node, const QString &description)
++ {
++ #ifdef QSG_RUNTIME_DESCRIPTION
++ QSGNodePrivate::setDescription(node, description);
++ #else
++ Q_UNUSED(node);
++ Q_UNUSED(description);
++ #endif
++ }
++
++ /*!
++ \class QSGBasicGeometryNode
++ \brief The QSGBasicGeometryNode class serves as a baseclass for geometry based nodes.
++
++ \inmodule QtQuick
++
++ The QSGBasicGeometryNode class should not be used by itself. It is only encapsulates
++ shared functionality between the QSGGeometryNode and QSGClipNode classes.
++
++ \note All classes with QSG prefix should be used solely on the scene graph's
++ rendering thread. See \l {Scene Graph and Rendering} for more information.
++ */
++
++
++ /*!
++ Creates a new basic geometry node of type \a type
++
++ \internal
++ */
++ QSGBasicGeometryNode::QSGBasicGeometryNode(NodeType type)
++ : QSGNode(type)
++ , m_geometry(nullptr)
++ , m_matrix(nullptr)
++ , m_clip_list(nullptr)
++ {
++ }
++
++
++ /*!
++ \internal
++ */
++ QSGBasicGeometryNode::QSGBasicGeometryNode(QSGBasicGeometryNodePrivate &dd, NodeType type)
++ : QSGNode(dd, type)
++ , m_geometry(nullptr)
++ , m_matrix(nullptr)
++ , m_clip_list(nullptr)
++ {
++ }
++
++
++ /*!
++ Deletes this QSGBasicGeometryNode.
++
++ If the node has the flag QSGNode::OwnsGeometry set, it will also delete the
++ geometry object it is pointing to. This flag is not set by default.
++ */
++
++ QSGBasicGeometryNode::~QSGBasicGeometryNode()
++ {
++ if (flags() & OwnsGeometry)
++ delete m_geometry;
++ }
++
++
++ /*!
++ \fn QSGGeometry *QSGBasicGeometryNode::geometry()
++
++ Returns this node's geometry.
++
++ The geometry is null by default.
++ */
++
++ /*!
++ \fn const QSGGeometry *QSGBasicGeometryNode::geometry() const
++
++ Returns this node's geometry.
++
++ The geometry is null by default.
++ */
++
++ /*!
++ \fn QMatrix4x4 *QSGBasicGeometryNode::matrix() const
++
++ Will be set during rendering to contain transformation of the geometry
++ for that rendering pass.
++
++ \internal
++ */
++
++ /*!
++ \fn QSGClipNode *QSGBasicGeometryNode::clipList() const
++
++ Will be set during rendering to contain the clip of the geometry
++ for that rendering pass.
++
++ \internal
++ */
++
++ /*!
++ \fn void QSGBasicGeometryNode::setRendererMatrix(const QMatrix4x4 *m)
++
++ \internal
++ */
++
++ /*!
++ \fn void QSGBasicGeometryNode::setRendererClipList(const QSGClipNode *c)
++
++ \internal
++ */
++
++
++ /*!
++ Sets the geometry of this node to \a geometry.
++
++ If the node has the flag QSGNode::OwnsGeometry set, it will also delete the
++ geometry object it is pointing to. This flag is not set by default.
++
++ If the geometry is changed without calling setGeometry() again, the user
++ must also mark the geometry as dirty using QSGNode::markDirty().
++
++ \sa markDirty()
++ */
++
++ void QSGBasicGeometryNode::setGeometry(QSGGeometry *geometry)
++ {
++ if ((flags() & OwnsGeometry) != 0 && m_geometry != geometry)
++ delete m_geometry;
++ m_geometry = geometry;
++ markDirty(DirtyGeometry);
++ }
++
++
++
++ /*!
++ \class QSGGeometryNode
++ \brief The QSGGeometryNode class is used for all rendered content in the scene graph.
++
++ \inmodule QtQuick
++ \ingroup qtquick-scenegraph-nodes
++
++ The QSGGeometryNode consists of geometry and material. The geometry defines the mesh,
++ the vertices and their structure, to be drawn. The Material defines how the shape is
++ filled.
++
++ The following is a code snippet illustrating how to create a red
++ line using a QSGGeometryNode:
++ \code
++ QSGGeometry *geometry = new QSGGeometry(QSGGeometry::defaultAttributes_Point2D(), 2);
++ geometry->setDrawingMode(QSGGeometry::DrawLines);
++ geometry->setLineWidth(3);
++ geometry->vertexDataAsPoint2D()[0].set(0, 0);
++ geometry->vertexDataAsPoint2D()[1].set(width(), height());
++
++ QSGFlatColorMaterial *material = new QSGFlatColorMaterial;
++ material->setColor(QColor(255, 0, 0));
++
++ QSGGeometryNode *node = new QSGGeometryNode;
++ node->setGeometry(geometry);
++ node->setFlag(QSGNode::OwnsGeometry);
++ node->setMaterial(material);
++ node->setFlag(QSGNode::OwnsMaterial);
++ \endcode
++
++ A geometry node must have both geometry and a normal material before it is added to
++ the scene graph. When the geometry and materials are changed after the node has
++ been added to the scene graph, the user should also mark them as dirty using
++ QSGNode::markDirty().
++
++ The geometry node supports two types of materials, the opaqueMaterial and the normal
++ material. The opaqueMaterial is used when the accumulated scene graph opacity at the
++ time of rendering is 1. The primary use case is to special case opaque rendering
++ to avoid an extra operation in the fragment shader can have significant performance
++ impact on embedded graphics chips. The opaque material is optional.
++
++ \note All classes with QSG prefix should be used solely on the scene graph's
++ rendering thread. See \l {Scene Graph and Rendering} for more information.
++
++ \sa QSGGeometry, QSGMaterial
++ */
++
++
++ /*!
++ Creates a new geometry node without geometry and material.
++ */
++
++ QSGGeometryNode::QSGGeometryNode()
++ : QSGBasicGeometryNode(GeometryNodeType)
++ {
++ }
++
++
++ /*!
++ \internal
++ */
++ QSGGeometryNode::QSGGeometryNode(QSGGeometryNodePrivate &dd)
++ : QSGBasicGeometryNode(dd, GeometryNodeType)
++ , m_render_order(0)
++ , m_material(nullptr)
++ , m_opaque_material(nullptr)
++ , m_opacity(1)
++ {
++ }
++
++
++ /*!
++ Deletes this geometry node.
++
++ The flags QSGNode::OwnsMaterial, QSGNode::OwnsOpaqueMaterial and
++ QSGNode::OwnsGeometry decides whether the geometry node should also
++ delete the materials and geometry. By default, these flags are disabled.
++ */
++
++ QSGGeometryNode::~QSGGeometryNode()
++ {
++ if (flags() & OwnsMaterial)
++ delete m_material;
++ if (flags() & OwnsOpaqueMaterial)
++ delete m_opaque_material;
++ }
++
++
++
++ /*!
++ \fn int QSGGeometryNode::renderOrder() const
++
++ Returns the render order of this geometry node.
++
++ \internal
++ */
++
++ /*!
++ \fn QSGMaterial *QSGGeometryNode::material() const
++
++ Returns the material of the QSGGeometryNode.
++
++ \sa setMaterial()
++ */
++
++ /*!
++ \fn QSGMaterial *QSGGeometryNode::opaqueMaterial() const
++
++ Returns the opaque material of the QSGGeometryNode.
++
++ \sa setOpaqueMaterial()
++ */
++
++ /*!
++ \fn qreal QSGGeometryNode::inheritedOpacity() const
++
++ Set during rendering to specify the inherited opacity for that
++ rendering pass.
++
++ \internal
++ */
++
++
++ /*!
++ Sets the render order of this node to be \a order.
++
++ Geometry nodes are rendered in an order that visually looks like
++ low order nodes are rendered prior to high order nodes. For opaque
++ geometry there is little difference as z-testing will handle
++ the discard, but for translucent objects, the rendering should
++ normally be specified in the order of back-to-front.
++
++ The default render order is \c 0.
++
++ \internal
++ */
++ void QSGGeometryNode::setRenderOrder(int order)
++ {
++ m_render_order = order;
++ }
++
++
++
++ /*!
++ Sets the material of this geometry node to \a material.
++
++ Geometry nodes must have a material before they can be added to the
++ scene graph.
++
++ If the material is changed without calling setMaterial() again, the user
++ must also mark the material as dirty using QSGNode::markDirty().
++
++ */
++ void QSGGeometryNode::setMaterial(QSGMaterial *material)
++ {
++ if ((flags() & OwnsMaterial) != 0 && m_material != material)
++ delete m_material;
++ m_material = material;
++ #ifndef QT_NO_DEBUG
++ if (m_material != nullptr && m_opaque_material == m_material)
++ qWarning("QSGGeometryNode: using same material for both opaque and translucent");
++ #endif
++ markDirty(DirtyMaterial);
++ }
++
++
++
++ /*!
++ Sets the opaque material of this geometry to \a material.
++
++ The opaque material will be preferred by the renderer over the
++ default material, as returned by the material() function, if
++ it is not null and the geometry item has an inherited opacity of
++ 1.
++
++ The opaqueness refers to scene graph opacity, the material is still
++ allowed to set QSGMaterial::Blending to true and draw transparent
++ pixels.
++
++ If the material is changed without calling setOpaqueMaterial()
++ again, the user must also mark the opaque material as dirty using
++ QSGNode::markDirty().
++
++ */
++ void QSGGeometryNode::setOpaqueMaterial(QSGMaterial *material)
++ {
++ if ((flags() & OwnsOpaqueMaterial) != 0 && m_opaque_material != m_material)
++ delete m_opaque_material;
++ m_opaque_material = material;
++ #ifndef QT_NO_DEBUG
++ if (m_opaque_material != nullptr && m_opaque_material == m_material)
++ qWarning("QSGGeometryNode: using same material for both opaque and translucent");
++ #endif
++
++ markDirty(DirtyMaterial);
++ }
++
++
++
++ /*!
++ Returns the material which should currently be used for geometry node.
++
++ If the inherited opacity of the node is 1 and there is an opaque material
++ set on this node, it will be returned; otherwise, the default material
++ will be returned.
++
++ \warning This function requires the scene graph above this item to be
++ completely free of dirty states, so it can only be called during rendering
++
++ \internal
++
++ \sa setMaterial, setOpaqueMaterial
++ */
++ QSGMaterial *QSGGeometryNode::activeMaterial() const
++ {
++ if (m_opaque_material && m_opacity > 0.999)
++ return m_opaque_material;
++ return m_material;
++ }
++
++
++ /*!
++ Sets the inherited opacity of this geometry to \a opacity.
++
++ This function is meant to be called by the node preprocessing
++ prior to rendering the tree, so it will not mark the tree as
++ dirty.
++
++ \internal
++ */
++ void QSGGeometryNode::setInheritedOpacity(qreal opacity)
++ {
++ Q_ASSERT(opacity >= 0 && opacity <= 1);
++ m_opacity = opacity;
++ }
++
++
++ /*!
++ \class QSGClipNode
++ \brief The QSGClipNode class implements the clipping functionality in the scene graph.
++
++ \inmodule QtQuick
++ \ingroup qtquick-scenegraph-nodes
++
++ Clipping applies to the node's subtree and can be nested. Multiple clip nodes will be
++ accumulated by intersecting all their geometries. The accumulation happens
++ as part of the rendering.
++
++ Clip nodes must have a geometry before they can be added to the scene graph.
++
++ Clipping is usually implemented by using the stencil buffer.
++
++ \note All classes with QSG prefix should be used solely on the scene graph's
++ rendering thread. See \l {Scene Graph and Rendering} for more information.
++ */
++
++
++
++ /*!
++ Creates a new QSGClipNode without a geometry.
++
++ The clip node must have a geometry before it can be added to the
++ scene graph.
++ */
++
++ QSGClipNode::QSGClipNode()
++ : QSGBasicGeometryNode(ClipNodeType)
++ , m_is_rectangular(false)
++ {
++ Q_UNUSED(m_reserved);
++ }
++
++
++
++ /*!
++ Deletes this QSGClipNode.
++
++ If the flag QSGNode::OwnsGeometry is set, the geometry will also be
++ deleted.
++ */
++
++ QSGClipNode::~QSGClipNode()
++ {
++ }
++
++
++
++ /*!
++ \fn bool QSGClipNode::isRectangular() const
++
++ Returns if this clip node has a rectangular clip.
++ */
++
++
++
++ /*!
++ Sets whether this clip node has a rectangular clip to \a rectHint.
++
++ This is an optimization hint which means that the renderer can
++ use scissoring instead of stencil, which is significantly faster.
++
++ When this hint is set and it is applicable, the clip region will be
++ generated from clipRect() rather than geometry().
++
++ By default this property is \c false.
++ */
++
++ void QSGClipNode::setIsRectangular(bool rectHint)
++ {
++ m_is_rectangular = rectHint;
++ }
++
++
++
++ /*!
++ \fn QRectF QSGClipNode::clipRect() const
++
++ Returns the clip rect of this node.
++ */
++
++
++ /*!
++ Sets the clip rect of this clip node to \a rect.
++
++ When a rectangular clip is set in combination with setIsRectangular
++ the renderer may in some cases use a more optimal clip method.
++ */
++ void QSGClipNode::setClipRect(const QRectF &rect)
++ {
++ m_clip_rect = rect;
++ }
++
++
++ /*!
++ \class QSGTransformNode
++ \brief The QSGTransformNode class implements transformations in the scene graph.
++
++ \inmodule QtQuick
++ \ingroup qtquick-scenegraph-nodes
++
++ Transformations apply the node's subtree and can be nested. Multiple transform nodes
++ will be accumulated by intersecting all their matrices. The accumulation happens
++ as part of the rendering.
++
++ The transform nodes implement a 4x4 matrix which in theory supports full 3D
++ transformations. However, because the renderer optimizes for 2D use-cases rather
++ than 3D use-cases, rendering a scene with full 3D transformations needs to
++ be done with some care.
++
++ \note All classes with QSG prefix should be used solely on the scene graph's
++ rendering thread. See \l {Scene Graph and Rendering} for more information.
++
++ */
++
++
++ /*!
++ Create a new QSGTransformNode with its matrix set to the identity matrix.
++ */
++
++ QSGTransformNode::QSGTransformNode()
++ : QSGNode(TransformNodeType)
++ {
++ }
++
++
++
++ /*!
++ Deletes this transform node.
++ */
++
++ QSGTransformNode::~QSGTransformNode()
++ {
++ }
++
++
++
++ /*!
++ \fn QMatrix4x4 QSGTransformNode::matrix() const
++
++ Returns this transform node's matrix.
++ */
++
++
++
++ /*!
++ Sets this transform node's matrix to \a matrix.
++ */
++
++ void QSGTransformNode::setMatrix(const QMatrix4x4 &matrix)
++ {
++ m_matrix = matrix;
++ markDirty(DirtyMatrix);
++ }
++
++ /*!
++ \fn const QMatrix4x4 &QSGTransformNode::combinedMatrix() const
++
++ Set during rendering to the combination of all parent matrices for
++ that rendering pass.
++
++ \internal
++ */
++
++
++
++ /*!
++ Sets the combined matrix of this matrix to \a transform.
++
++ This function is meant to be called by the node preprocessing
++ prior to rendering the tree, so it will not mark the tree as
++ dirty.
++
++ \internal
++ */
++ void QSGTransformNode::setCombinedMatrix(const QMatrix4x4 &matrix)
++ {
++ m_combined_matrix = matrix;
++ }
++
++
++
++ /*!
++ \class QSGRootNode
++ \brief The QSGRootNode is the toplevel root of any scene graph.
++
++ The root node is used to attach a scene graph to a renderer.
++
++ \internal
++ */
++
++
++
++ /*!
++ \fn QSGRootNode::QSGRootNode()
++
++ Creates a new root node.
++ */
++
++ QSGRootNode::QSGRootNode()
++ : QSGNode(RootNodeType)
++ {
++ }
++
++
++ /*!
++ Deletes the root node.
++
++ When a root node is deleted it removes itself from all of renderers
++ that are referencing it.
++ */
++
++ QSGRootNode::~QSGRootNode()
++ {
++ while (!m_renderers.isEmpty())
++ m_renderers.constLast()->setRootNode(nullptr);
++ destroy(); // Must call destroy() here because markDirty() casts this to QSGRootNode.
++ }
++
++
++
++ /*!
++ Called to notify all renderers that \a node has been marked as dirty
++ with \a flags.
++ */
++
++ void QSGRootNode::notifyNodeChange(QSGNode *node, DirtyState state)
++ {
++ for (int i=0; i<m_renderers.size(); ++i) {
++ m_renderers.at(i)->nodeChanged(node, state);
++ }
++ }
++
++
++
++ /*!
++ \class QSGOpacityNode
++ \brief The QSGOpacityNode class is used to change opacity of nodes.
++
++ \inmodule QtQuick
++ \ingroup qtquick-scenegraph-nodes
++
++ Opacity applies to its subtree and can be nested. Multiple opacity nodes
++ will be accumulated by multiplying their opacity. The accumulation happens
++ as part of the rendering.
++
++ When nested opacity gets below a certain threshold, the subtree might
++ be marked as blocked, causing isSubtreeBlocked() to return true. This
++ is done for performance reasons.
++
++ \note All classes with QSG prefix should be used solely on the scene graph's
++ rendering thread. See \l {Scene Graph and Rendering} for more information.
++ */
++
++
++
++ /*!
++ Constructs an opacity node with a default opacity of 1.
++
++ Opacity accumulates downwards in the scene graph so a node with two
++ QSGOpacityNode instances above it, both with opacity of 0.5, will have
++ effective opacity of 0.25.
++
++ The default opacity of nodes is 1.
++ */
++ QSGOpacityNode::QSGOpacityNode()
++ : QSGNode(OpacityNodeType)
++ {
++ }
++
++
++
++ /*!
++ Deletes the opacity node.
++ */
++
++ QSGOpacityNode::~QSGOpacityNode()
++ {
++ }
++
++
++
++ /*!
++ \fn qreal QSGOpacityNode::opacity() const
++
++ Returns this opacity node's opacity.
++ */
++
++ const qreal OPACITY_THRESHOLD = 0.001;
++
++ /*!
++ Sets the opacity of this node to \a opacity.
++
++ Before rendering the graph, the renderer will do an update pass
++ over the subtree to propagate the opacity to its children.
++
++ The value will be bounded to the range 0 to 1.
++ */
++
++ void QSGOpacityNode::setOpacity(qreal opacity)
++ {
++ opacity = std::clamp(opacity, qreal(0.0), qreal(1.0));
++ if (m_opacity == opacity)
++ return;
++ DirtyState dirtyState = DirtyOpacity;
++
++ if ((m_opacity < OPACITY_THRESHOLD && opacity >= OPACITY_THRESHOLD) // blocked to unblocked
++ || (m_opacity >= OPACITY_THRESHOLD && opacity < OPACITY_THRESHOLD)) // unblocked to blocked
++ dirtyState |= DirtySubtreeBlocked;
++
++ m_opacity = opacity;
++ markDirty(dirtyState);
++ }
++
++
++
++ /*!
++ \fn qreal QSGOpacityNode::combinedOpacity() const
++
++ Returns this node's accumulated opacity.
++
++ This value is calculated during rendering and only stored
++ in the opacity node temporarily.
++
++ \internal
++ */
++
++
++
++ /*!
++ Sets the combined opacity of this node to \a opacity.
++
++ This function is meant to be called by the node preprocessing
++ prior to rendering the tree, so it will not mark the tree as
++ dirty.
++
++ \internal
++ */
++
++ void QSGOpacityNode::setCombinedOpacity(qreal opacity)
++ {
++ m_combined_opacity = opacity;
++ }
++
++
++
++ /*!
++ For performance reasons, we block the subtree when the opacity
++ is below a certain threshold.
++
++ \internal
++ */
++
++ bool QSGOpacityNode::isSubtreeBlocked() const
++ {
++ return m_opacity < OPACITY_THRESHOLD;
++ }
++
++
++ /*!
++ \class QSGNodeVisitor
++ \brief The QSGNodeVisitor class is a helper class for traversing the scene graph.
++
++ \internal
++ */
++
++ QSGNodeVisitor::~QSGNodeVisitor()
++ {
++
++ }
++
++
++ void QSGNodeVisitor::visitNode(QSGNode *n)
++ {
++ switch (n->type()) {
++ case QSGNode::TransformNodeType: {
++ QSGTransformNode *t = static_cast<QSGTransformNode *>(n);
++ enterTransformNode(t);
++ visitChildren(t);
++ leaveTransformNode(t);
++ break; }
++ case QSGNode::GeometryNodeType: {
++ QSGGeometryNode *g = static_cast<QSGGeometryNode *>(n);
++ enterGeometryNode(g);
++ visitChildren(g);
++ leaveGeometryNode(g);
++ break; }
++ case QSGNode::ClipNodeType: {
++ QSGClipNode *c = static_cast<QSGClipNode *>(n);
++ enterClipNode(c);
++ visitChildren(c);
++ leaveClipNode(c);
++ break; }
++ case QSGNode::OpacityNodeType: {
++ QSGOpacityNode *o = static_cast<QSGOpacityNode *>(n);
++ enterOpacityNode(o);
++ visitChildren(o);
++ leaveOpacityNode(o);
++ break; }
++ default:
++ visitChildren(n);
++ break;
++ }
++ }
++
++ void QSGNodeVisitor::visitChildren(QSGNode *n)
++ {
++ for (QSGNode *c = n->firstChild(); c; c = c->nextSibling())
++ visitNode(c);
++ }
++
++ #ifndef QT_NO_DEBUG_STREAM
++ QDebug operator<<(QDebug d, const QSGGeometryNode *n)
++ {
++ if (!n) {
++ d << "Geometry(null)";
++ return d;
++ }
++ d << "GeometryNode(" << Qt::hex << (const void *) n << Qt::dec;
++
++ const QSGGeometry *g = n->geometry();
++
++ if (!g) {
++ d << "no geometry";
++ } else {
++
++ switch (g->drawingMode()) {
++ case QSGGeometry::DrawTriangleStrip: d << "strip"; break;
++ case QSGGeometry::DrawTriangleFan: d << "fan"; break;
++ case QSGGeometry::DrawTriangles: d << "triangles"; break;
++ default: break;
++ }
++
++ d << "#V:" << g->vertexCount() << "#I:" << g->indexCount();
++
++ if (g->attributeCount() > 0 && g->attributes()->type == QSGGeometry::FloatType) {
++ float x1 = 1e10, x2 = -1e10, y1=1e10, y2=-1e10;
++ int stride = g->sizeOfVertex();
++ for (int i = 0; i < g->vertexCount(); ++i) {
++ float x = ((float *)((char *)const_cast<QSGGeometry *>(g)->vertexData() + i * stride))[0];
++ float y = ((float *)((char *)const_cast<QSGGeometry *>(g)->vertexData() + i * stride))[1];
++
++ x1 = qMin(x1, x);
++ x2 = qMax(x2, x);
++ y1 = qMin(y1, y);
++ y2 = qMax(y2, y);
++ }
++
++ d << "x1=" << x1 << "y1=" << y1 << "x2=" << x2 << "y2=" << y2;
++ }
++ }
++
++ if (n->material())
++ d << "materialtype=" << n->material()->type();
++
++
++ d << ')';
++ #ifdef QSG_RUNTIME_DESCRIPTION
++ d << QSGNodePrivate::description(n);
++ #endif
++ return d;
++ }
++
++ QDebug operator<<(QDebug d, const QSGClipNode *n)
++ {
++ if (!n) {
++ d << "ClipNode(null)";
++ return d;
++ }
++ d << "ClipNode(" << Qt::hex << (const void *) n << Qt::dec;
++
++ if (n->childCount())
++ d << "children=" << n->childCount();
++
++ d << "is rect?" << (n->isRectangular() ? "yes" : "no");
++
++ d << ')';
++ #ifdef QSG_RUNTIME_DESCRIPTION
++ d << QSGNodePrivate::description(n);
++ #endif
++ d << (n->isSubtreeBlocked() ? "*BLOCKED*" : "");
++ return d;
++ }
++
++ QDebug operator<<(QDebug d, const QSGTransformNode *n)
++ {
++ if (!n) {
++ d << "TransformNode(null)";
++ return d;
++ }
++ const QMatrix4x4 m = n->matrix();
++ d << "TransformNode(";
++ d << Qt::hex << (const void *) n << Qt::dec;
++ if (m.isIdentity())
++ d << "identity";
++ else if (m.determinant() == 1 && m(0, 0) == 1 && m(1, 1) == 1 && m(2, 2) == 1)
++ d << "translate" << m(0, 3) << m(1, 3) << m(2, 3);
++ else
++ d << "det=" << n->matrix().determinant();
++ #ifdef QSG_RUNTIME_DESCRIPTION
++ d << QSGNodePrivate::description(n);
++ #endif
++ d << (n->isSubtreeBlocked() ? "*BLOCKED*" : "");
++ d << ')';
++ return d;
++ }
++
++ QDebug operator<<(QDebug d, const QSGOpacityNode *n)
++ {
++ if (!n) {
++ d << "OpacityNode(null)";
++ return d;
++ }
++ d << "OpacityNode(";
++ d << Qt::hex << (const void *) n << Qt::dec;
++ d << "opacity=" << n->opacity()
++ << "combined=" << n->combinedOpacity()
++ << (n->isSubtreeBlocked() ? "*BLOCKED*" : "");
++ #ifdef QSG_RUNTIME_DESCRIPTION
++ d << QSGNodePrivate::description(n);
++ #endif
++ d << ')';
++ return d;
++ }
++
++
++ QDebug operator<<(QDebug d, const QSGRootNode *n)
++ {
++ if (!n) {
++ d << "RootNode(null)";
++ return d;
++ }
++ QDebugStateSaver saver(d);
++ d << "RootNode" << Qt::hex << (const void *) n << (n->isSubtreeBlocked() ? "*BLOCKED*" : "");
++ #ifdef QSG_RUNTIME_DESCRIPTION
++ d << QSGNodePrivate::description(n);
++ #endif
++ d << ')';
++ return d;
++ }
++
++
++
++ QDebug operator<<(QDebug d, const QSGNode *n)
++ {
++ if (!n) {
++ d << "Node(null)";
++ return d;
++ }
++ switch (n->type()) {
++ case QSGNode::GeometryNodeType:
++ d << static_cast<const QSGGeometryNode *>(n);
++ break;
++ case QSGNode::TransformNodeType:
++ d << static_cast<const QSGTransformNode *>(n);
++ break;
++ case QSGNode::ClipNodeType:
++ d << static_cast<const QSGClipNode *>(n);
++ break;
++ case QSGNode::RootNodeType:
++ d << static_cast<const QSGRootNode *>(n);
++ break;
++ case QSGNode::OpacityNodeType:
++ d << static_cast<const QSGOpacityNode *>(n);
++ break;
++ case QSGNode::RenderNodeType:
++ d << "RenderNode(" << Qt::hex << (const void *) n << Qt::dec
++ << "flags=" << (int) n->flags() << Qt::dec
++ << (n->isSubtreeBlocked() ? "*BLOCKED*" : "");
++ #ifdef QSG_RUNTIME_DESCRIPTION
++ d << QSGNodePrivate::description(n);
++ #endif
++ d << ')';
++ break;
++ default:
++ d << "Node(" << Qt::hex << (const void *) n << Qt::dec
++ << "flags=" << (int) n->flags() << Qt::dec
++ << (n->isSubtreeBlocked() ? "*BLOCKED*" : "");
++ #ifdef QSG_RUNTIME_DESCRIPTION
++ d << QSGNodePrivate::description(n);
++ #endif
++ d << ')';
++ break;
++ }
++ return d;
++ }
++
++ #endif
++
++ QT_END_NAMESPACE