9#include <QtCore/qthreadpool.h>
12#include <QtQuick/private/qsgcontext_p.h>
13#include <QtQuick/private/qsgrenderer_p.h>
15#include "graphobjects/qssgrenderroot_p.h"
16#include "graphobjects/qssgrenderlayer_p.h"
17#include "graphobjects/qssgrenderitem2d_p.h"
20#include "resourcemanager/qssgrenderbuffermanager_p.h"
23#include <condition_variable>
29static void reindexChildNodes(QSSGRenderNode &node,
const QSSGRenderNodeVersionType version, quint32 &dfsIdx, size_t &count)
31 Q_ASSERT(node.type != QSSGRenderNode::Type::Layer);
32 if (node.type != QSSGRenderNode::Type::Layer) {
36 if (node.h.version() != version)
37 node.h = QSSGRenderNodeHandle(0, version, dfsIdx);
38 for (QSSGRenderNode &chld : node.children)
39 reindexChildNodes(chld, version, ++dfsIdx, ++count);
43static void reindexLayerChildNodes(QSSGRenderLayer &layer,
const QSSGRenderNodeVersionType version, quint32 &dfsIdx, size_t &count)
45 Q_ASSERT(layer.type == QSSGRenderNode::Type::Layer);
46 if (layer.type == QSSGRenderNode::Type::Layer) {
47 layer.h = QSSGRenderNodeHandle(0, version, 0);
48 for (QSSGRenderNode &chld : layer.children)
49 reindexChildNodes(chld, version, ++dfsIdx, ++count);
53static void reindex(QSSGRenderRoot *rootNode,
const QSSGRenderNodeVersionType version, quint32 &dfsIdx, size_t &count)
56 Q_ASSERT(rootNode->type == QSSGRenderNode::Type::Root);
57 Q_ASSERT(dfsIdx == 0);
58 rootNode->h = QSSGRenderNodeHandle(0, version, 0);
59 for (QSSGRenderNode &chld : rootNode->children)
60 reindexLayerChildNodes(
static_cast<QSSGRenderLayer &>(chld), version, dfsIdx, count);
68 Q_ASSERT_X(node.type != QSSGRenderNode::Type::Layer, Q_FUNC_INFO,
"Unexpected Layer node in child list!");
70 outList[idx++] = &node;
72 outList.push_back(&node);
73 for (QSSGRenderNode &chld : node.children)
74 collectChildNodesFirst<insert>(chld, outList, idx);
80 if (node.getGlobalState(QSSGRenderNode::GlobalState::Active)) {
82 outList[idx++] = &node;
84 outList.push_back(&node);
85 for (QSSGRenderNode &chld : node.children)
86 collectChildNodesSecond<insert>(chld, outList, idx);
94 Q_ASSERT(layer->type == QSSGRenderNode::Type::Layer);
96 for (QSSGRenderNode &chld : layer->children) {
97 if constexpr (discard == Discard::Inactive)
98 collectChildNodesSecond<insert>(chld, outList, idx);
100 collectChildNodesFirst<insert>(chld, outList, idx);
105 idx = outList.size();
110 const QSSGRenderNodeVersionType version,
111 QSSGGlobalRenderNodeData::GlobalTransformStore &globalTransforms,
112 QSSGGlobalRenderNodeData::GlobalOpacityStore &globalOpacities)
114 using DirtyFlag = QSSGRenderNode::DirtyFlag;
115 using FlagT = QSSGRenderNode::FlagT;
116 constexpr DirtyFlag TransformAndOpacityDirty = DirtyFlag(FlagT(DirtyFlag::TransformDirty) | FlagT(DirtyFlag::OpacityDirty));
118 if (Q_UNLIKELY(!node || (node->h.version() != version)))
122 bool retval = forcedRebuild || node->isDirty(TransformAndOpacityDirty);
128 const bool alwaysDirty = node->isDirty(DirtyFlag::StickyDirty);
130 if (retval || alwaysDirty) {
131 const auto idx = node->h.index();
133 auto &globalTransform = globalTransforms[idx];
134 auto &globalOpacity = globalOpacities[idx];
135 globalOpacity = node->localOpacity;
136 globalTransform = node->localTransform;
138 if (QSSGRenderNode *parent = node->parent) {
139 const auto pidx = parent->h.index();
140 const auto pOpacity = globalOpacities[pidx];
141 globalOpacity *= pOpacity;
143 if (parent->type != QSSGRenderGraphObject::Type::Layer) {
144 const auto pTransform = globalTransforms[pidx];
145 globalTransform = pTransform * node->localTransform;
149 node->clearDirty(TransformAndOpacityDirty);
159 using LocalState = QSSGRenderNode::LocalState;
160 using GlobalState = QSSGRenderNode::GlobalState;
161 using DirtyFlag = QSSGRenderNode::DirtyFlag;
162 using FlagT = QSSGRenderNode::FlagT;
164 constexpr DirtyFlag ClearDirtyMask = DirtyFlag(FlagT(DirtyFlag::ActiveDirty) | FlagT(DirtyFlag::PickableDirty) | FlagT(DirtyFlag::ImportDirty));
166 if (Q_UNLIKELY(!node || (node->h.version() != version)))
169 const bool activeDirty = node->isDirty(DirtyFlag::ActiveDirty);
170 const bool pickableDirty = node->isDirty(DirtyFlag::PickableDirty);
171 const bool importedDirty = node->isDirty(DirtyFlag::ImportDirty);
173 const bool updateState = activeDirty || pickableDirty || importedDirty;
176 const QSSGRenderNode *parent = node->parent;
177 const bool hasParent = (parent !=
nullptr);
178 const bool globallyActive = node->getLocalState(LocalState::Active) && (!hasParent || parent->getGlobalState(GlobalState::Active));
179 node->flags = globallyActive ? (node->flags | FlagT(GlobalState::Active)) : (node->flags & ~FlagT(GlobalState::Active));
180 const bool globallyPickable = node->getLocalState(LocalState::Pickable) || (hasParent && parent->getGlobalState(GlobalState::Pickable));
181 node->flags = globallyPickable ? (node->flags | FlagT(GlobalState::Pickable)) : (node->flags & ~FlagT(GlobalState::Pickable));
182 const bool globallyImported = node->getLocalState(LocalState::Imported) || (hasParent && parent->getGlobalState(GlobalState::Imported));
183 node->flags = globallyImported ? (node->flags | FlagT(GlobalState::Imported)) : (node->flags & ~FlagT(GlobalState::Imported));
186 node->clearDirty(ClearDirtyMask);
193 const VersionType version,
194 QSSGGlobalRenderNodeData::GlobalTransformStore &globalTransforms,
195 QSSGGlobalRenderNodeData::InstanceTransformStore &instanceTransforms)
197 if (Q_UNLIKELY(!node || (node->h.version() != version)))
200 constexpr bool retval =
true;
205 const auto idx = node->h.index();
206 QSSGRenderNode *parent = node->parent;
207 if (parent && parent->type != QSSGRenderGraphObject::Type::Layer && node->getLocalState(QSSGRenderNode::LocalState::Active)) {
208 const auto pidx = parent->h.index();
209 const auto &pGlobalTransform = globalTransforms[pidx];
210 QSSGRenderNode *instanceRoot = node->instanceRoot;
211 if (instanceRoot == node) {
212 instanceTransforms[idx] = { node->localTransform, pGlobalTransform };
213 }
else if (instanceRoot) {
214 auto &[nodeInstanceLocalTransform, nodeInstanceGlobalTransform] = instanceTransforms[idx];
215 auto &[instanceRootLocalTransform, instanceRootGlobalTransform] = instanceTransforms[instanceRoot->h.index()];
216 nodeInstanceGlobalTransform = instanceRootGlobalTransform;
219 nodeInstanceLocalTransform = node->localTransform;
222 if (p == instanceRoot) {
223 nodeInstanceLocalTransform = instanceRootLocalTransform * nodeInstanceLocalTransform;
226 nodeInstanceLocalTransform = p->localTransform * nodeInstanceLocalTransform;
232 QMatrix4x4 globalInstanceTransform = globalTransforms[pidx];
233 QMatrix4x4 localInstanceTransform = node->localTransform;
234 auto &localInstanceMatrix = *
reinterpret_cast<
float (*)[4][4]>(localInstanceTransform.data());
235 QVector3D localPos{localInstanceMatrix[3][0], localInstanceMatrix[3][1], localInstanceMatrix[3][2]};
236 localInstanceMatrix[3][0] = 0;
237 localInstanceMatrix[3][1] = 0;
238 localInstanceMatrix[3][2] = 0;
239 globalInstanceTransform = pGlobalTransform;
240 globalInstanceTransform.translate(localPos);
241 instanceTransforms[idx] = { localInstanceTransform, globalInstanceTransform };
244 instanceTransforms[idx] = { node->localTransform, {} };
250QSSGGlobalRenderNodeData::QSSGGlobalRenderNodeData(QSSGRenderRoot *root)
251#if QT_CONFIG(thread) && !defined(Q_OS_WASM)
252 : m_threadPool(
new QThreadPool)
261QSSGGlobalRenderNodeData::~QSSGGlobalRenderNodeData()
266void QSSGGlobalRenderNodeData::reindex()
278 if (Q_UNLIKELY(m_version == 0)) {
279 qCDebug(lcLayerData) <<
"Version wrap around detected, resetting to 1.";
283 ::reindex(m_rootNode, m_version, dfsIdx, m_nodeCount);
291 globalTransforms.resize(m_size, QMatrix4x4{ Qt::Uninitialized });
292 globalOpacities.resize(m_size, 1.0f);
293 instanceTransforms.resize(m_size, { QMatrix4x4{ Qt::Uninitialized }, QMatrix4x4{ Qt::Uninitialized } });
294 normalMatrices.resize(m_size, QMatrix3x3{});
295 meshes.resize(m_size,
nullptr);
296 materials.resize(m_size, {});
298 collectNodes(m_rootNode);
305void QSSGGlobalRenderNodeData::invalidate()
307 m_rootNode =
nullptr;
310QMatrix4x4 QSSGGlobalRenderNodeData::getGlobalTransform(QSSGRenderNodeHandle h,
const QMatrix4x4 &defaultValue)
const
313 const bool hasId = h.hasId();
314 const bool validVersion = hasId && (h.version() == m_version);
315 const auto index = h.index();
319 if (!validVersion || !(globalTransforms.size() > index))
322 return globalTransforms[index];
325QMatrix4x4 QSSGGlobalRenderNodeData::getGlobalTransform(QSSGRenderNodeHandle h)
const
327 return getGlobalTransform(h, QMatrix4x4{ Qt::Uninitialized });
330QMatrix4x4 QSSGGlobalRenderNodeData::getGlobalTransform(
const QSSGRenderNode &node)
const
332 return getGlobalTransform(node.h, node.localTransform);
335float QSSGGlobalRenderNodeData::getGlobalOpacity(QSSGRenderNodeHandle h,
float defaultValue)
const
337 const bool hasId = h.hasId();
338 const bool validVersion = hasId && (h.version() == m_version);
339 const auto index = h.index();
341 if (!validVersion || !(globalOpacities.size() > index))
344 return globalOpacities[index];
347float QSSGGlobalRenderNodeData::getGlobalOpacity(
const QSSGRenderNode &node)
const
349 return getGlobalOpacity(node.h, node.localOpacity);
354QThreadPool *QSSGGlobalRenderNodeData::threadPool()
const {
return nullptr; }
356QThreadPool *QSSGGlobalRenderNodeData::threadPool()
const {
return m_threadPool.get(); }
360QSSGGlobalRenderNodeData::LayerNodeView QSSGGlobalRenderNodeData::getLayerNodeView(QSSGRenderLayerHandle h)
const
362 const bool hasId = h.hasId();
363 const bool validVersion = hasId && (h.version() == m_version);
364 const auto index = h.index();
366 if (!validVersion || !(layerNodes.size() > index))
369 auto §ion = layerNodes[index];
371 return { nodes.data() + section.offset, qsizetype(section.size) };
374QSSGGlobalRenderNodeData::LayerNodeView QSSGGlobalRenderNodeData::getLayerNodeView(
const QSSGRenderLayer &layer)
const
376 return getLayerNodeView(layer.lh);
379void QSSGGlobalRenderNodeData::collectNodes(QSSGRenderRoot *rootNode)
384 Q_ASSERT(rootNode !=
nullptr);
387 nodes.resize(m_nodeCount,
nullptr);
391 quint32 layerIdx = 0;
392 for (QSSGRenderNode &chld : rootNode->children) {
393 Q_ASSERT(chld.type == QSSGRenderNode::Type::Layer);
394 QSSGRenderLayer *layer =
static_cast<QSSGRenderLayer *>(&chld);
395 const size_t offset = idx;
396 collectLayerChildNodes<Discard::None, Insert::Indexed>(layer, nodes, idx);
397 layer->lh = QSSGRenderLayerHandle(layer->h.context(), m_version, layerIdx++);
398 layerNodes.emplace_back(LayerNodeSection{offset , idx - offset});
404void QSSGGlobalRenderNodeData::updateGlobalState()
407 for (QSSGRenderNode *node : nodes)
408 QSSGRenderDataHelpers::updateGlobalNodeState(node, m_version);
411 for (QSSGRenderNode *node : nodes)
412 calcGlobalNodeDataIndexedImpl<QSSGRenderDataHelpers::Strategy::Initial>(node, m_version, globalTransforms, globalOpacities);
415 for (QSSGRenderNode *node : nodes)
416 QSSGRenderDataHelpers::calcInstanceTransforms(node, m_version, globalTransforms, instanceTransforms);
419QSSGGlobalRenderNodeData::InstanceTransforms QSSGGlobalRenderNodeData::getInstanceTransforms(
const QSSGRenderNode &node)
const
421 return getInstanceTransforms(node.h);
424QSSGGlobalRenderNodeData::InstanceTransforms QSSGGlobalRenderNodeData::getInstanceTransforms(QSSGRenderNodeHandle h)
const
426 const bool hasId = h.hasId();
427 const bool validVersion = hasId && (h.version() == m_version);
428 const auto index = h.index();
430 if (!validVersion || !(instanceTransforms.size() > index))
433 return instanceTransforms[index];
437 : m_gnd(globalNodeData)
438 , m_version(globalNodeData->version())
445 const bool hasId = h.hasId();
446 const bool validVersion = hasId && (h.version() == m_gnd->version());
447 const auto index = h.index();
448 if (!validVersion || !(m_gnd->normalMatrices.size() > index))
451 return m_gnd->normalMatrices[index];
456 return getNormalMatrix(model.h, QMatrix3x3{ Qt::Uninitialized });
461 const bool hasId = model.h.hasId();
462 const bool validVersion = hasId && (model.h.version() == m_gnd->version());
463 const auto index = model.h.index();
465 if (!validVersion || !(m_gnd->meshes.size() > index))
468 return m_gnd->meshes[index];
473 const bool hasId = model.h.hasId();
474 const bool validVersion = hasId && (model.h.version() == m_gnd->version());
475 const auto index = model.h.index();
477 if (!validVersion || !(m_gnd->materials.size() > index))
480 return m_gnd->materials[index];
483QSSGRenderModelData::ModelViewProjections
QSSGRenderModelData::getModelViewProjection(
const QSSGRenderModel &model)
const
485 return getModelViewProjection(model.h);
490 const bool hasId = h.hasId();
491 const bool validVersion = hasId && (h.version() == m_version);
492 const auto index = h.index();
494 if (!validVersion || !(modelViewProjections.size() > index))
497 return modelViewProjections[index];
502 const auto &bufferManager = renderer->contextInterface()->bufferManager();
504 const bool globalPickingEnabled = QSSGRendererPrivate::isGlobalPickingEnabled(*renderer);
506 for (
auto *model : models) {
515 if (
auto *theMesh = bufferManager->loadMesh(*model)) {
516 m_gnd->meshes[model->h.index()] = theMesh;
520 const float modelGlobalOpacity = m_gnd->getGlobalOpacity(*model);
521 const bool canModelBePickable = (modelGlobalOpacity > QSSGRendererPrivate::minimumRenderOpacity)
522 && (globalPickingEnabled
523 || model->getGlobalState(QSSGRenderModel::GlobalState::Pickable));
524 if (canModelBePickable) {
527 const QSSGMesh::Mesh mesh = bufferManager->loadLightmapMesh(*model);
530 theMesh->bvh = bufferManager->loadMeshBVH(mesh);
531 else if (model->geometry)
532 theMesh->bvh = bufferManager->loadMeshBVH(model->geometry);
533 else if (!model->meshPath.isNull())
534 theMesh->bvh = bufferManager->loadMeshBVH(model->meshPath);
537 const auto &roots = theMesh->bvh->roots();
538 for (qsizetype i = 0, end = qsizetype(roots.size()); i < end; ++i)
539 theMesh->subsets[i].bvhRoot = roots[i];
544 m_gnd->meshes[model->h.index()] =
nullptr;
552 bufferManager->commitBufferResourceUpdates();
557 for (
auto *model : models)
558 m_gnd->materials[model->h.index()] = model->materials;
563 const bool versionChanged = m_version != m_gnd->version();
565 m_version = m_gnd->version();
567 const QMatrix4x4 defaultModelViewProjection;
571 modelViewProjections.resize(m_gnd->storageSize(), { defaultModelViewProjection, defaultModelViewProjection });
573#if QT_CONFIG(thread) && !defined(Q_OS_WASM)
574 bool matrixesDone =
false;
575 bool mvpsDone =
false;
577 std::condition_variable cv;
581 const auto doNormalMatrices = [&]() {
582 for (
const QSSGRenderModel *model : std::as_const(models)) {
583 auto &normalMatrix = m_gnd->normalMatrices[model->h.index()];
584 const QMatrix4x4 &globalTransform = m_gnd->globalTransforms[model->h.index()];
585 QSSGRenderNode::calculateNormalMatrix(globalTransform, normalMatrix);
587#if QT_CONFIG(thread) && !defined(Q_OS_WASM)
588 std::lock_guard<std::mutex> guard(mutex);
595 const auto doMVPs = [&]() {
596 for (
const QSSGRenderModel *model : std::as_const(models)) {
598 const QMatrix4x4 &globalTransform = m_gnd->globalTransforms[model->h.index()];
599 auto &mvp = modelViewProjections[model->h.index()];
600 for (
const QSSGRenderCameraData &cameraData : renderCameraData)
601 QSSGRenderNode::calculateMVP(globalTransform, cameraData.viewProjection, mvp[mvpCount++]);
603#if QT_CONFIG(thread) && !defined(Q_OS_WASM)
604 std::lock_guard<std::mutex> guard(mutex);
611 auto *threadPool = m_gnd->threadPool();
612#define qssgTryThreadedStart(func)
615 } else if (!threadPool->tryStart(func)) {
616 qWarning("Unable to start thread for %s!", #func);
620#define qssgTryThreadedStart(func)
629 prepareMaterials(models);
630 prepareMeshData(models, renderer);
633#if QT_CONFIG(thread) && !defined(Q_OS_WASM)
634 std::unique_lock<std::mutex> guard(mutex);
635 cv.wait(guard, [&]() {
return matrixesDone && mvpsDone; });
639bool QSSGRenderDataHelpers::updateGlobalNodeDataIndexed(QSSGRenderNode *node,
const VersionType version, QSSGGlobalRenderNodeData::GlobalTransformStore &globalTransforms, QSSGGlobalRenderNodeData::GlobalOpacityStore &globalOpacities)
641 return calcGlobalNodeDataIndexedImpl<
Strategy::Update>(node, version, globalTransforms, globalOpacities);
644bool QSSGRenderDataHelpers::calcGlobalVariablesIndexed(QSSGRenderNode *node,
const VersionType version, QSSGGlobalRenderNodeData::GlobalTransformStore &globalTransforms, QSSGGlobalRenderNodeData::GlobalOpacityStore &globalOpacities)
646 return calcGlobalNodeDataIndexedImpl<
Strategy::Initial>(node, version, globalTransforms, globalOpacities);
651 const auto foundIt = item2DRenderers.find(&item);
652 return (foundIt != item2DRenderers.cend()) ? foundIt->second : Item2DRenderer{};
657 const bool hasId = h.hasId();
658 const bool validVersion = hasId && (h.version() == m_version);
659 const auto index = h.index();
661 if (!validVersion || !(modelViewProjections.size() > index))
664 return modelViewProjections[index];
669 return getModelViewProjection(item.h);
674 m_gnd = globalNodeData;
675 m_version = globalNodeData->version();
685 const auto itemCount = size_t(items.size());
690 const bool versionChanged = m_version != m_gnd->version();
692 m_version = m_gnd->version();
694 const QMatrix4x4 defaultModelViewProjection;
696 const auto &rhiCtx = renderer->contextInterface()->rhiContext();
699 modelViewProjections.resize(m_gnd->storageSize(), { defaultModelViewProjection, defaultModelViewProjection });
701 const auto &clipSpaceCorrMatrix = rhiCtx->rhi()->clipSpaceCorrMatrix();
704 const auto doMVPs = [&]() {
705 for (
const QSSGRenderItem2D *item : std::as_const(items)) {
707 const QMatrix4x4 &globalTransform = m_gnd->globalTransforms[item->h.index()];
708 auto &mvps = modelViewProjections[item->h.index()];
709 for (
const QSSGRenderCameraData &cameraData : renderCameraData) {
710 const QMatrix4x4 &mvp = cameraData.viewProjection * globalTransform;
711 mvps[mvpCount++] = clipSpaceCorrMatrix * mvp * flipMatrix;
720 QSGRenderContext *sgRc = QSSGRendererPrivate::getSgRenderContext(*renderer);
721 const bool contextChanged = (item2DRenderContext && item2DRenderContext != sgRc);
722 item2DRenderContext = sgRc;
724 for (
const QSSGRenderItem2D *theItem2D : std::as_const(items)) {
725 auto item2DRenderer = getItem2DRenderer(*theItem2D);
726 if (contextChanged) {
727 delete item2DRenderer;
730 item2DRenderer.clear();
733 if (!item2DRenderer) {
734 item2DRenderer = sgRc->createRenderer(QSGRendererInterface::RenderMode3D);
735 QObject::connect(item2DRenderer, SIGNAL(sceneGraphChanged()), theItem2D->m_frontEndObject, SLOT(update()));
738 if (item2DRenderer->rootNode() != theItem2D->m_rootNode) {
739 item2DRenderer->setRootNode(theItem2D->m_rootNode);
740 theItem2D->m_rootNode->markDirty(QSGNode::DirtyForceUpdate);
741 item2DRenderer->nodeChanged(theItem2D->m_rootNode, QSGNode::DirtyForceUpdate);
744 item2DRenderers[theItem2D] = item2DRenderer;
750 const auto foundIt = item2DRenderers.find(&item);
751 if (foundIt != item2DRenderers.cend()) {
752 delete foundIt->second;
753 item2DRenderers.erase(foundIt);
762 for (
auto &it : item2DRenderers)
764 item2DRenderers.clear();
765 item2DRenderContext =
nullptr;
766 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
QT_BEGIN_NAMESPACE Q_STATIC_LOGGING_CATEGORY(lcSynthesizedIterableAccess, "qt.iterable.synthesized", QtWarningMsg)
static bool calcGlobalNodeDataIndexedImpl(QSSGRenderNode *node, const QSSGRenderNodeVersionType version, QSSGGlobalRenderNodeData::GlobalTransformStore &globalTransforms, QSSGGlobalRenderNodeData::GlobalOpacityStore &globalOpacities)
static void reindex(QSSGRenderRoot *rootNode, const QSSGRenderNodeVersionType version, quint32 &dfsIdx, size_t &count)
static void reindexLayerChildNodes(QSSGRenderLayer &layer, const QSSGRenderNodeVersionType version, quint32 &dfsIdx, size_t &count)
static QT_BEGIN_NAMESPACE void reindexChildNodes(QSSGRenderNode &node, const QSSGRenderNodeVersionType version, quint32 &dfsIdx, size_t &count)
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)