9#include <QtCore/qthreadpool.h>
12#include <QtCore/qtenvironmentvariables.h>
14#include <QtQuick/private/qsgcontext_p.h>
15#include <QtQuick/private/qsgrenderer_p.h>
17#include "graphobjects/qssgrenderroot_p.h"
18#include "graphobjects/qssgrenderlayer_p.h"
19#include "graphobjects/qssgrenderitem2d_p.h"
22#include "resourcemanager/qssgrenderbuffermanager_p.h"
26Q_STATIC_LOGGING_CATEGORY(lcLayerData,
"qt.quick3d.render.layerdata")
28static void reindexChildNodes(QSSGRenderNode &node,
const QSSGRenderNodeVersionType version, quint32 &dfsIdx, size_t &count)
30 Q_ASSERT(node.type != QSSGRenderNode::Type::Layer);
31 if (node.type != QSSGRenderNode::Type::Layer) {
35 if (node.h.version() != version)
36 node.h = QSSGRenderNodeHandle(0, version, dfsIdx);
37 for (QSSGRenderNode &chld : node.children)
38 reindexChildNodes(chld, version, ++dfsIdx, ++count);
42static void reindexLayerChildNodes(QSSGRenderLayer &layer,
const QSSGRenderNodeVersionType version, quint32 &dfsIdx, size_t &count)
44 Q_ASSERT(layer.type == QSSGRenderNode::Type::Layer);
45 if (layer.type == QSSGRenderNode::Type::Layer) {
46 layer.h = QSSGRenderNodeHandle(0, version, 0);
47 for (QSSGRenderNode &chld : layer.children)
48 reindexChildNodes(chld, version, ++dfsIdx, ++count);
52static void reindex(QSSGRenderRoot *rootNode,
const QSSGRenderNodeVersionType version, quint32 &dfsIdx, size_t &count)
55 Q_ASSERT(rootNode->type == QSSGRenderNode::Type::Root);
56 Q_ASSERT(dfsIdx == 0);
57 rootNode->h = QSSGRenderNodeHandle(0, version, 0);
58 for (QSSGRenderNode &chld : rootNode->children)
59 reindexLayerChildNodes(
static_cast<QSSGRenderLayer &>(chld), version, dfsIdx, count);
63enum class Insert { Back, Indexed };
67 Q_ASSERT_X(node.type != QSSGRenderNode::Type::Layer, Q_FUNC_INFO,
"Unexpected Layer node in child list!");
68 if constexpr (insert == Insert::Indexed)
69 outList[idx++] = &node;
71 outList.push_back(&node);
72 for (QSSGRenderNode &chld : node.children)
73 collectChildNodesFirst<insert>(chld, outList, idx);
79 if (node.getGlobalState(QSSGRenderNode::GlobalState::Active)) {
80 if constexpr (insert == Insert::Indexed)
81 outList[idx++] = &node;
83 outList.push_back(&node);
84 for (QSSGRenderNode &chld : node.children)
85 collectChildNodesSecond<insert>(chld, outList, idx);
93 Q_ASSERT(layer->type == QSSGRenderNode::Type::Layer);
95 for (QSSGRenderNode &chld : layer->children) {
96 if constexpr (discard == Discard::Inactive)
97 collectChildNodesSecond<insert>(chld, outList, idx);
99 collectChildNodesFirst<insert>(chld, outList, idx);
103 if constexpr (insert == Insert::Back)
104 idx = outList.size();
109 const QSSGRenderNodeVersionType version,
110 QSSGGlobalRenderNodeData::GlobalTransformStore &globalTransforms,
111 QSSGGlobalRenderNodeData::GlobalOpacityStore &globalOpacities)
113 using DirtyFlag = QSSGRenderNode::DirtyFlag;
114 using FlagT = QSSGRenderNode::FlagT;
115 constexpr DirtyFlag TransformAndOpacityDirty = DirtyFlag(FlagT(DirtyFlag::TransformDirty) | FlagT(DirtyFlag::OpacityDirty));
117 if (Q_UNLIKELY(!node || (node->h.version() != version)))
121 bool retval = forcedRebuild || node->isDirty(TransformAndOpacityDirty);
127 const bool alwaysDirty = node->isDirty(DirtyFlag::StickyDirty);
129 if (retval || alwaysDirty) {
130 const auto idx = node->h.index();
132 auto &globalTransform = globalTransforms[idx];
133 auto &globalOpacity = globalOpacities[idx];
134 globalOpacity = node->localOpacity;
135 globalTransform = node->localTransform;
137 if (QSSGRenderNode *parent = node->parent) {
138 const auto pidx = parent->h.index();
139 const auto pOpacity = globalOpacities[pidx];
140 globalOpacity *= pOpacity;
142 if (parent->type != QSSGRenderGraphObject::Type::Layer) {
143 const auto pTransform = globalTransforms[pidx];
144 globalTransform = pTransform * node->localTransform;
148 node->clearDirty(TransformAndOpacityDirty);
158 using LocalState = QSSGRenderNode::LocalState;
159 using GlobalState = QSSGRenderNode::GlobalState;
160 using DirtyFlag = QSSGRenderNode::DirtyFlag;
161 using FlagT = QSSGRenderNode::FlagT;
163 constexpr DirtyFlag ClearDirtyMask = DirtyFlag(FlagT(DirtyFlag::ActiveDirty) | FlagT(DirtyFlag::PickableDirty) | FlagT(DirtyFlag::ImportDirty));
169 if (Q_UNLIKELY(!node || (node->h.version() != version)))
172 const bool activeDirty = node->isDirty(DirtyFlag::ActiveDirty);
173 const bool pickableDirty = node->isDirty(DirtyFlag::PickableDirty);
174 const bool importedDirty = node->isDirty(DirtyFlag::ImportDirty);
176 const bool tagDirty = node->isDirty(DirtyFlag::TagDirty);
177 node->clearDirty(DirtyFlag::TagDirty);
179 const bool updateState = activeDirty || pickableDirty || importedDirty;
182 const QSSGRenderNode *parent = node->parent;
183 const bool hasParent = (parent !=
nullptr);
184 const bool globallyActive = node->getLocalState(LocalState::Active) && (!hasParent || parent->getGlobalState(GlobalState::Active));
185 node->flags = globallyActive ? (node->flags | FlagT(GlobalState::Active)) : (node->flags & ~FlagT(GlobalState::Active));
186 const bool globallyPickable = node->getLocalState(LocalState::Pickable) || (hasParent && parent->getGlobalState(GlobalState::Pickable));
187 node->flags = globallyPickable ? (node->flags | FlagT(GlobalState::Pickable)) : (node->flags & ~FlagT(GlobalState::Pickable));
188 const bool globallyImported = node->getLocalState(LocalState::Imported) || (hasParent && parent->getGlobalState(GlobalState::Imported));
189 node->flags = globallyImported ? (node->flags | FlagT(GlobalState::Imported)) : (node->flags & ~FlagT(GlobalState::Imported));
192 node->clearDirty(ClearDirtyMask);
196 | FlagT(pickableChangeFlag[pickableDirty])
197 | FlagT(tagChangeFlag[tagDirty]));
201 const VersionType version,
202 QSSGGlobalRenderNodeData::GlobalTransformStore &globalTransforms,
203 QSSGGlobalRenderNodeData::InstanceTransformStore &instanceTransforms)
205 if (Q_UNLIKELY(!node || (node->h.version() != version)))
208 constexpr bool retval =
true;
213 const auto idx = node->h.index();
214 QSSGRenderNode *parent = node->parent;
215 if (parent && parent->type != QSSGRenderGraphObject::Type::Layer && node->getLocalState(QSSGRenderNode::LocalState::Active)) {
216 const auto pidx = parent->h.index();
217 const auto &pGlobalTransform = globalTransforms[pidx];
218 QSSGRenderNode *instanceRoot = node->instanceRoot;
219 if (instanceRoot == node) {
220 instanceTransforms[idx] = { node->localTransform, pGlobalTransform };
221 }
else if (instanceRoot) {
222 auto &[nodeInstanceLocalTransform, nodeInstanceGlobalTransform] = instanceTransforms[idx];
223 auto &[instanceRootLocalTransform, instanceRootGlobalTransform] = instanceTransforms[instanceRoot->h.index()];
224 nodeInstanceGlobalTransform = instanceRootGlobalTransform;
227 nodeInstanceLocalTransform = node->localTransform;
230 if (p == instanceRoot) {
231 nodeInstanceLocalTransform = instanceRootLocalTransform * nodeInstanceLocalTransform;
234 nodeInstanceLocalTransform = p->localTransform * nodeInstanceLocalTransform;
240 QMatrix4x4 globalInstanceTransform = globalTransforms[pidx];
241 QMatrix4x4 localInstanceTransform = node->localTransform;
242 auto &localInstanceMatrix = *
reinterpret_cast<
float (*)[4][4]>(localInstanceTransform.data());
243 QVector3D localPos{localInstanceMatrix[3][0], localInstanceMatrix[3][1], localInstanceMatrix[3][2]};
244 localInstanceMatrix[3][0] = 0;
245 localInstanceMatrix[3][1] = 0;
246 localInstanceMatrix[3][2] = 0;
247 globalInstanceTransform = pGlobalTransform;
248 globalInstanceTransform.translate(localPos);
249 instanceTransforms[idx] = { localInstanceTransform, globalInstanceTransform };
252 instanceTransforms[idx] = { node->localTransform, {} };
258QSSGGlobalRenderNodeData::QSSGGlobalRenderNodeData(QSSGRenderRoot *root)
259#if QT_CONFIG(thread) && !defined(Q_OS_WASM)
260 : m_threadPool(
new QThreadPool)
269QSSGGlobalRenderNodeData::~QSSGGlobalRenderNodeData()
274void QSSGGlobalRenderNodeData::reindex()
286 if (Q_UNLIKELY(m_version == 0)) {
287 qCDebug(lcLayerData) <<
"Version wrap around detected, resetting to 1.";
291 ::reindex(m_rootNode, m_version, dfsIdx, m_nodeCount);
299 globalTransforms.resize(m_size, QMatrix4x4{ Qt::Uninitialized });
300 globalOpacities.resize(m_size, 1.0f);
301 instanceTransforms.resize(m_size, { QMatrix4x4{ Qt::Uninitialized }, QMatrix4x4{ Qt::Uninitialized } });
302 normalMatrices.resize(m_size, QMatrix3x3{});
303 meshes.resize(m_size,
nullptr);
304 materials.resize(m_size, {});
306 collectNodes(m_rootNode);
313void QSSGGlobalRenderNodeData::invalidate()
315 m_rootNode =
nullptr;
318QMatrix4x4 QSSGGlobalRenderNodeData::getGlobalTransform(QSSGRenderNodeHandle h,
const QMatrix4x4 &defaultValue)
const
321 const bool hasId = h.hasId();
322 const bool validVersion = hasId && (h.version() == m_version);
323 const auto index = h.index();
327 if (!validVersion || !(globalTransforms.size() > index))
330 return globalTransforms[index];
333QMatrix4x4 QSSGGlobalRenderNodeData::getGlobalTransform(QSSGRenderNodeHandle h)
const
335 return getGlobalTransform(h, QMatrix4x4{ Qt::Uninitialized });
338QMatrix4x4 QSSGGlobalRenderNodeData::getGlobalTransform(
const QSSGRenderNode &node)
const
340 return getGlobalTransform(node.h, node.localTransform);
343float QSSGGlobalRenderNodeData::getGlobalOpacity(QSSGRenderNodeHandle h,
float defaultValue)
const
345 const bool hasId = h.hasId();
346 const bool validVersion = hasId && (h.version() == m_version);
347 const auto index = h.index();
349 if (!validVersion || !(globalOpacities.size() > index))
352 return globalOpacities[index];
355float QSSGGlobalRenderNodeData::getGlobalOpacity(
const QSSGRenderNode &node)
const
357 return getGlobalOpacity(node.h, node.localOpacity);
362QThreadPool *QSSGGlobalRenderNodeData::threadPool()
const {
return nullptr; }
364QThreadPool *QSSGGlobalRenderNodeData::threadPool()
const {
return m_threadPool.get(); }
368QSSGGlobalRenderNodeData::LayerNodeView QSSGGlobalRenderNodeData::getLayerNodeView(QSSGRenderLayerHandle h)
const
370 const bool hasId = h.hasId();
371 const bool validVersion = hasId && (h.version() == m_version);
372 const auto index = h.index();
374 if (!validVersion || !(layerNodes.size() > index))
377 auto §ion = layerNodes[index];
379 return { nodes.data() + section.offset, qsizetype(section.size) };
382QSSGGlobalRenderNodeData::LayerNodeView QSSGGlobalRenderNodeData::getLayerNodeView(
const QSSGRenderLayer &layer)
const
384 return getLayerNodeView(layer.lh);
387void QSSGGlobalRenderNodeData::collectNodes(QSSGRenderRoot *rootNode)
392 Q_ASSERT(rootNode !=
nullptr);
395 nodes.resize(m_nodeCount,
nullptr);
399 quint32 layerIdx = 0;
400 for (QSSGRenderNode &chld : rootNode->children) {
401 Q_ASSERT(chld.type == QSSGRenderNode::Type::Layer);
402 QSSGRenderLayer *layer =
static_cast<QSSGRenderLayer *>(&chld);
403 const size_t offset = idx;
404 collectLayerChildNodes<Discard::None, Insert::Indexed>(layer, nodes, idx);
405 layer->lh = QSSGRenderLayerHandle(layer->h.context(), m_version, layerIdx++);
406 layerNodes.emplace_back(LayerNodeSection{offset , idx - offset});
412void QSSGGlobalRenderNodeData::updateGlobalState()
415 for (QSSGRenderNode *node : nodes)
416 QSSGRenderDataHelpers::updateGlobalNodeState(node, m_version);
419 for (QSSGRenderNode *node : nodes)
420 calcGlobalNodeDataIndexedImpl<QSSGRenderDataHelpers::Strategy::Initial>(node, m_version, globalTransforms, globalOpacities);
423 for (QSSGRenderNode *node : nodes)
424 QSSGRenderDataHelpers::calcInstanceTransforms(node, m_version, globalTransforms, instanceTransforms);
427QSSGGlobalRenderNodeData::InstanceTransforms QSSGGlobalRenderNodeData::getInstanceTransforms(
const QSSGRenderNode &node)
const
429 return getInstanceTransforms(node.h);
432QSSGGlobalRenderNodeData::InstanceTransforms QSSGGlobalRenderNodeData::getInstanceTransforms(QSSGRenderNodeHandle h)
const
434 const bool hasId = h.hasId();
435 const bool validVersion = hasId && (h.version() == m_version);
436 const auto index = h.index();
438 if (!validVersion || !(instanceTransforms.size() > index))
441 return instanceTransforms[index];
445 : m_gnd(globalNodeData)
446 , m_version(globalNodeData->version())
453 const bool hasId = h.hasId();
454 const bool validVersion = hasId && (h.version() == m_gnd->version());
455 const auto index = h.index();
456 if (!validVersion || !(m_gnd->normalMatrices.size() > index))
459 return m_gnd->normalMatrices[index];
464 return getNormalMatrix(model.h, QMatrix3x3{ Qt::Uninitialized });
469 const bool hasId = model.h.hasId();
470 const bool validVersion = hasId && (model.h.version() == m_gnd->version());
471 const auto index = model.h.index();
473 if (!validVersion || !(m_gnd->meshes.size() > index))
476 return m_gnd->meshes[index];
481 const bool hasId = model.h.hasId();
482 const bool validVersion = hasId && (model.h.version() == m_gnd->version());
483 const auto index = model.h.index();
485 if (!validVersion || !(m_gnd->materials.size() > index))
488 return m_gnd->materials[index];
491QSSGRenderModelData::ModelViewProjections
QSSGRenderModelData::getModelViewProjection(
const QSSGRenderModel &model)
const
493 return getModelViewProjection(model.h);
498 const bool hasId = h.hasId();
499 const bool validVersion = hasId && (h.version() == m_version);
500 const auto index = h.index();
502 if (!validVersion || !(modelViewProjections.size() > index))
505 return modelViewProjections[index];
510 const auto &bufferManager = renderer->contextInterface()->bufferManager();
512 const bool globalPickingEnabled = QSSGRendererPrivate::isGlobalPickingEnabled(*renderer);
514 for (
auto *model : models) {
523 if (
auto *theMesh = bufferManager->loadMesh(*model)) {
524 m_gnd->meshes[model->h.index()] = theMesh;
528 const float modelGlobalOpacity = m_gnd->getGlobalOpacity(*model);
529 const bool canModelBePickable = (modelGlobalOpacity > QSSGRendererPrivate::minimumRenderOpacity)
530 && (globalPickingEnabled
531 || model->getGlobalState(QSSGRenderModel::GlobalState::Pickable));
532 if (canModelBePickable) {
535 const QSSGMesh::Mesh mesh = bufferManager->loadLightmapMesh(*model);
538 theMesh->bvh = bufferManager->loadMeshBVH(mesh);
539 else if (model->geometry)
540 theMesh->bvh = bufferManager->loadMeshBVH(model->geometry);
541 else if (!model->meshPath.isNull())
542 theMesh->bvh = bufferManager->loadMeshBVH(model->meshPath);
545 const auto &roots = theMesh->bvh->roots();
546 for (qsizetype i = 0, end = qsizetype(roots.size()); i < end; ++i)
547 theMesh->subsets[i].bvhRoot = roots[i];
552 m_gnd->meshes[model->h.index()] =
nullptr;
560 bufferManager->commitBufferResourceUpdates();
565 for (
auto *model : models)
566 m_gnd->materials[model->h.index()] = model->materials;
571 const bool versionChanged = m_version != m_gnd->version();
573 m_version = m_gnd->version();
575 const QMatrix4x4 defaultModelViewProjection;
579 modelViewProjections.resize(m_gnd->storageSize(), { defaultModelViewProjection, defaultModelViewProjection });
582 QThreadPool *threadPool = (
static_cast<size_t>(models.size()) >= m_threadPoolThreshold)
583 ? m_gnd->threadPool() :
nullptr;
586#if QT_CONFIG(thread) && !defined(Q_OS_WASM)
587 bool matrixesDone =
false;
588 bool mvpsDone =
false;
592 const auto doNormalMatrices = [&]() {
593 for (
const QSSGRenderModel *model : std::as_const(models)) {
594 auto &normalMatrix = m_gnd->normalMatrices[model->h.index()];
595 const QMatrix4x4 &globalTransform = m_gnd->globalTransforms[model->h.index()];
596 QSSGRenderNode::calculateNormalMatrix(globalTransform, normalMatrix);
598#if QT_CONFIG(thread) && !defined(Q_OS_WASM)
600 std::lock_guard<std::mutex> guard(m_mutex);
608 const auto doMVPs = [&]() {
609 for (
const QSSGRenderModel *model : std::as_const(models)) {
611 const QMatrix4x4 &globalTransform = m_gnd->globalTransforms[model->h.index()];
612 auto &mvp = modelViewProjections[model->h.index()];
613 for (
const QSSGRenderCameraData &cameraData : renderCameraData)
614 QSSGRenderNode::calculateMVP(globalTransform, cameraData.viewProjection, mvp[mvpCount++]);
616#if QT_CONFIG(thread) && !defined(Q_OS_WASM)
618 std::lock_guard<std::mutex> guard(m_mutex);
626#define qssgTryThreadedStart(func)
629 } else if (!threadPool->tryStart(func)) {
630 qWarning("Unable to start thread for %s!", #func);
634#define qssgTryThreadedStart(func)
643 prepareMaterials(models);
644 prepareMeshData(models, renderer);
647#if QT_CONFIG(thread) && !defined(Q_OS_WASM)
649 std::unique_lock<std::mutex> guard(m_mutex);
650 m_cv.wait(guard, [&]() {
return matrixesDone && mvpsDone; });
658 const int ret = qEnvironmentVariableIntValue(
"QSSG_MODEL_DATA_THREAD_POOL_THRESHOLD");
659 return (ret > 0) ? quint32(ret) : 32u;
662bool QSSGRenderDataHelpers::updateGlobalNodeDataIndexed(QSSGRenderNode *node,
const VersionType version, QSSGGlobalRenderNodeData::GlobalTransformStore &globalTransforms, QSSGGlobalRenderNodeData::GlobalOpacityStore &globalOpacities)
664 return calcGlobalNodeDataIndexedImpl<
Strategy::Update>(node, version, globalTransforms, globalOpacities);
667bool QSSGRenderDataHelpers::calcGlobalVariablesIndexed(QSSGRenderNode *node,
const VersionType version, QSSGGlobalRenderNodeData::GlobalTransformStore &globalTransforms, QSSGGlobalRenderNodeData::GlobalOpacityStore &globalOpacities)
669 return calcGlobalNodeDataIndexedImpl<
Strategy::Initial>(node, version, globalTransforms, globalOpacities);
674 const auto foundIt = item2DRenderers.find(&item);
675 return (foundIt != item2DRenderers.cend()) ? foundIt->second : Item2DRenderer{};
680 const bool hasId = h.hasId();
681 const bool validVersion = hasId && (h.version() == m_version);
682 const auto index = h.index();
684 if (!validVersion || !(modelViewProjections.size() > index))
687 return modelViewProjections[index];
692 return getModelViewProjection(item.h);
697 m_gnd = globalNodeData;
698 m_version = globalNodeData->version();
708 const auto itemCount = size_t(items.size());
713 const bool versionChanged = m_version != m_gnd->version();
715 m_version = m_gnd->version();
717 const QMatrix4x4 defaultModelViewProjection;
719 const auto &rhiCtx = renderer->contextInterface()->rhiContext();
722 modelViewProjections.resize(m_gnd->storageSize(), { defaultModelViewProjection, defaultModelViewProjection });
724 const auto &clipSpaceCorrMatrix = rhiCtx->rhi()->clipSpaceCorrMatrix();
727 const auto doMVPs = [&]() {
728 for (
const QSSGRenderItem2D *item : std::as_const(items)) {
730 const QMatrix4x4 &globalTransform = m_gnd->globalTransforms[item->h.index()];
731 auto &mvps = modelViewProjections[item->h.index()];
732 for (
const QSSGRenderCameraData &cameraData : renderCameraData) {
733 const QMatrix4x4 &mvp = cameraData.viewProjection * globalTransform;
734 mvps[mvpCount++] = clipSpaceCorrMatrix * mvp * flipMatrix;
743 QSGRenderContext *sgRc = QSSGRendererPrivate::getSgRenderContext(*renderer);
744 const bool contextChanged = (item2DRenderContext && item2DRenderContext != sgRc);
745 item2DRenderContext = sgRc;
747 for (
const QSSGRenderItem2D *theItem2D : std::as_const(items)) {
748 auto item2DRenderer = getItem2DRenderer(*theItem2D);
749 if (contextChanged) {
750 delete item2DRenderer;
753 item2DRenderer.clear();
756 if (!item2DRenderer) {
757 item2DRenderer = sgRc->createRenderer(QSGRendererInterface::RenderMode3D);
758 QObject::connect(item2DRenderer, SIGNAL(sceneGraphChanged()), theItem2D->m_frontEndObject, SLOT(update()));
761 if (item2DRenderer->rootNode() != theItem2D->m_rootNode) {
762 item2DRenderer->setRootNode(theItem2D->m_rootNode);
763 theItem2D->m_rootNode->markDirty(QSGNode::DirtyForceUpdate);
764 item2DRenderer->nodeChanged(theItem2D->m_rootNode, QSGNode::DirtyForceUpdate);
767 item2DRenderers[theItem2D] = item2DRenderer;
773 const auto foundIt = item2DRenderers.find(&item);
774 if (foundIt != item2DRenderers.cend()) {
775 delete foundIt->second;
776 item2DRenderers.erase(foundIt);
785 for (
auto &it : item2DRenderers)
787 item2DRenderers.clear();
788 item2DRenderContext =
nullptr;
789 modelViewProjections.clear();
friend class QSSGRenderer
void releaseRenderData(const QSSGRenderItem2D &item)
void updateItem2DData(QSSGItem2DsView &items, QSSGRenderer *renderer, const QSSGRenderCameraDataList &renderCameraData)
Item2DRenderer getItem2DRenderer(const QSSGRenderItem2D &item) const
ModelViewProjections getModelViewProjection(const QSSGRenderItem2D &item) const
ModelViewProjections getModelViewProjection(QSSGRenderNodeHandle h) const
ModelViewProjections getModelViewProjection(QSSGRenderNodeHandle h) const
void updateModelData(QSSGModelsView &models, QSSGRenderer *renderer, const QSSGRenderCameraDataList &renderCameraData)
QMatrix3x3 getNormalMatrix(QSSGRenderNodeHandle h, QMatrix3x3 defaultValue) const
static bool calcGlobalNodeDataIndexedImpl(QSSGRenderNode *node, const QSSGRenderNodeVersionType version, QSSGGlobalRenderNodeData::GlobalTransformStore &globalTransforms, QSSGGlobalRenderNodeData::GlobalOpacityStore &globalOpacities)
static void collectLayerChildNodes(QSSGRenderLayer *layer, QSSGGlobalRenderNodeData::NodeStore &outList, size_t &idx)
static void collectChildNodesFirst(QSSGRenderNode &node, QSSGGlobalRenderNodeData::NodeStore &outList, size_t &idx)
#define qssgTryThreadedStart(func)
static void collectChildNodesSecond(QSSGRenderNode &node, QSSGGlobalRenderNodeData::NodeStore &outList, size_t &idx)