1// Copyright (C) 2025 The Qt Company Ltd.
2// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
3
4#include "qssgrenderdata_p.h"
5
6#if QT_CONFIG(thread)
7#include <QtCore/qthreadpool.h>
8#endif // QT_CONFIG(thread)
9
10#include "graphobjects/qssgrenderroot_p.h"
11#include "graphobjects/qssgrenderlayer_p.h"
12#include "qssgrendercontextcore.h"
13#include "qssgrenderer_p.h"
14#include "resourcemanager/qssgrenderbuffermanager_p.h"
15
16QT_BEGIN_NAMESPACE
17
18static void reindexChildNodes(QSSGRenderNode &node, const quint32 version, quint32 &dfsIdx, size_t &count)
19{
20 Q_ASSERT(node.type != QSSGRenderNode::Type::Layer);
21 if (node.type != QSSGRenderNode::Type::Layer) {
22 // Note: In the case of import scenes the version and index might already
23 // have been set. We therefore assume nodes with same version is already
24 // indexed.
25 if (node.h.version() != version)
26 node.h = QSSGRenderNodeHandle(0, version, dfsIdx);
27 for (QSSGRenderNode &chld : node.children)
28 reindexChildNodes(node&: chld, version, dfsIdx&: ++dfsIdx, count&: ++count);
29 }
30}
31
32static void reindexLayerChildNodes(QSSGRenderLayer &layer, const quint32 version, quint32 &dfsIdx, size_t &count)
33{
34 Q_ASSERT(layer.type == QSSGRenderNode::Type::Layer);
35 if (layer.type == QSSGRenderNode::Type::Layer) {
36 layer.h = QSSGRenderNodeHandle(0, version, 0); // Layer nodes are always indexed at 0;
37 for (QSSGRenderNode &chld : layer.children)
38 reindexChildNodes(node&: chld, version, dfsIdx&: ++dfsIdx, count&: ++count);
39 }
40}
41
42static void reindex(QSSGRenderRoot *rootNode, const quint32 version, quint32 &dfsIdx, size_t &count)
43{
44 if (rootNode) {
45 Q_ASSERT(rootNode->type == QSSGRenderNode::Type::Root);
46 Q_ASSERT(dfsIdx == 0);
47 rootNode->h = QSSGRenderNodeHandle(0, version, 0);
48 for (QSSGRenderNode &chld : rootNode->children) // These should be layer nodes
49 reindexLayerChildNodes(layer&: static_cast<QSSGRenderLayer &>(chld), version, dfsIdx, count);
50 }
51}
52
53enum class Insert { Back, Indexed };
54template <Insert insert = Insert::Back>
55static void collectChildNodesFirst(QSSGRenderNode &node, QSSGGlobalRenderNodeData::NodeStore &outList, [[maybe_unused]] size_t &idx)
56{
57 Q_ASSERT_X(node.type != QSSGRenderNode::Type::Layer, Q_FUNC_INFO, "Unexpected Layer node in child list!");
58 if constexpr (insert == Insert::Indexed)
59 outList[idx++] = &node;
60 else
61 outList.push_back(x: &node);
62 for (QSSGRenderNode &chld : node.children)
63 collectChildNodesFirst<insert>(chld, outList, idx);
64}
65
66template <Insert insert = Insert::Back>
67static void collectChildNodesSecond(QSSGRenderNode &node, QSSGGlobalRenderNodeData::NodeStore &outList, [[maybe_unused]] size_t &idx)
68{
69 if (node.getGlobalState(stateFlag: QSSGRenderNode::GlobalState::Active)) {
70 if constexpr (insert == Insert::Indexed)
71 outList[idx++] = &node;
72 else
73 outList.push_back(x: &node);
74 for (QSSGRenderNode &chld : node.children)
75 collectChildNodesSecond<insert>(chld, outList, idx);
76 }
77}
78
79enum class Discard { None, Inactive };
80template <Discard discard = Discard::None, Insert insert = Insert::Back>
81static void collectLayerChildNodes(QSSGRenderLayer *layer, QSSGGlobalRenderNodeData::NodeStore &outList, [[maybe_unused]] size_t &idx)
82{
83 Q_ASSERT(layer->type == QSSGRenderNode::Type::Layer);
84 if (layer) {
85 for (QSSGRenderNode &chld : layer->children) {
86 if constexpr (discard == Discard::Inactive)
87 collectChildNodesSecond<insert>(chld, outList, idx);
88 else
89 collectChildNodesFirst<insert>(chld, outList, idx);
90 }
91 }
92
93 if constexpr (insert == Insert::Back)
94 idx = outList.size();
95}
96
97template <QSSGRenderDataHelpers::Strategy Strategy>
98static bool calcGlobalNodeDataIndexedImpl(QSSGRenderNode *node,
99 const quint32 version,
100 QSSGGlobalRenderNodeData::GlobalTransformStore &globalTransforms,
101 QSSGGlobalRenderNodeData::GlobalOpacityStore &globalOpacities)
102{
103 using DirtyFlag = QSSGRenderNode::DirtyFlag;
104 using FlagT = QSSGRenderNode::FlagT;
105 constexpr DirtyFlag TransformAndOpacityDirty = DirtyFlag(FlagT(DirtyFlag::TransformDirty) | FlagT(DirtyFlag::OpacityDirty));
106
107 if (Q_UNLIKELY(!node || (node->h.version() != version)))
108 return false;
109
110 constexpr bool forcedRebuilf = (Strategy == QSSGRenderDataHelpers::Strategy::Initial);
111 bool retval = forcedRebuilf || node->isDirty(dirtyFlag: TransformAndOpacityDirty);
112
113 if (retval) {
114 const auto idx = node->h.index();
115
116 auto &globalTransform = globalTransforms[idx];
117 auto &globalOpacity = globalOpacities[idx];
118 globalOpacity = node->localOpacity;
119 globalTransform = node->localTransform;
120
121 if (QSSGRenderNode *parent = node->parent) {
122 const auto pidx = parent->h.index();
123 const auto pOpacity = globalOpacities[pidx];
124 globalOpacity *= pOpacity;
125
126 if (parent->type != QSSGRenderGraphObject::Type::Layer) {
127 const auto pTransform = globalTransforms[pidx];
128 globalTransform = pTransform * node->localTransform;
129 }
130 }
131 // Clear dirty flags (Transform, Opacity, Active, Pickable)
132 node->clearDirty(dirtyFlag: TransformAndOpacityDirty);
133 }
134
135 // We always clear dirty in a reasonable manner but if we aren't active
136 // there is no reason to tell the universe if we are dirty or not.
137 return retval;
138}
139
140QSSGRenderDataHelpers::GlobalStateResult QSSGRenderDataHelpers::updateGlobalNodeState(QSSGRenderNode *node, const quint32 version)
141{
142 using LocalState = QSSGRenderNode::LocalState;
143 using GlobalState = QSSGRenderNode::GlobalState;
144 using DirtyFlag = QSSGRenderNode::DirtyFlag;
145 using FlagT = QSSGRenderNode::FlagT;
146
147 constexpr DirtyFlag ActiveOrPickableDirty = DirtyFlag(FlagT(DirtyFlag::ActiveDirty) | FlagT(DirtyFlag::PickableDirty));
148
149 if (Q_UNLIKELY(!node || (node->h.version() != version)))
150 return GlobalStateResult::None;
151
152 const bool activeDirty = node->isDirty(dirtyFlag: DirtyFlag::ActiveDirty);
153 const bool pickableDirty = node->isDirty(dirtyFlag: DirtyFlag::PickableDirty);
154
155 const bool updateState = activeDirty || pickableDirty;
156
157 if (updateState) {
158 const QSSGRenderNode *parent = node->parent;
159 const bool hasParent = (parent != nullptr);
160 const bool globallyActive = node->getLocalState(stateFlag: LocalState::Active) && (!hasParent || parent->getGlobalState(stateFlag: GlobalState::Active));
161 node->flags = globallyActive ? (node->flags | FlagT(GlobalState::Active)) : (node->flags & ~FlagT(GlobalState::Active));
162 const bool globallyPickable = node->getLocalState(stateFlag: LocalState::Pickable) || (hasParent && parent->getGlobalState(stateFlag: GlobalState::Pickable));
163 node->flags = globallyPickable ? (node->flags | FlagT(GlobalState::Pickable)) : (node->flags & ~FlagT(GlobalState::Pickable));
164
165 // Clear dirty flags (Transform, Opacity, Active, Pickable)
166 node->clearDirty(dirtyFlag: ActiveOrPickableDirty);
167 }
168
169 return GlobalStateResult((activeDirty << 1) | (pickableDirty << 2));
170}
171
172bool QSSGRenderDataHelpers::calcInstanceTransforms(QSSGRenderNode *node,
173 const quint32 version,
174 QSSGGlobalRenderNodeData::GlobalTransformStore &globalTransforms,
175 QSSGGlobalRenderNodeData::InstanceTransformStore &instanceTransforms)
176{
177 if (Q_UNLIKELY(!node || (node->h.version() != version)))
178 return false;
179
180 constexpr bool retval = true;
181
182 // NOTE: We've already calculated the global states and transforms at this point
183 // so if the node isn't active we don't need to do anything.
184 // We're also assuming the node list is stored depth first order.
185 const auto idx = node->h.index();
186 QSSGRenderNode *parent = node->parent;
187 if (parent && parent->type != QSSGRenderGraphObject::Type::Layer && node->getLocalState(stateFlag: QSSGRenderNode::LocalState::Active)) {
188 const auto pidx = parent->h.index();
189 const auto &pGlobalTransform = globalTransforms[pidx];
190 QSSGRenderNode *instanceRoot = node->instanceRoot;
191 if (instanceRoot == node) {
192 instanceTransforms[idx] = { .local: node->localTransform, .global: pGlobalTransform };
193 } else if (instanceRoot) {
194 auto &[nodeInstanceLocalTransform, nodeInstanceGlobalTransform] = instanceTransforms[idx];
195 auto &[instanceRootLocalTransform, instanceRootGlobalTransform] = instanceTransforms[instanceRoot->h.index()];
196 nodeInstanceGlobalTransform = instanceRootGlobalTransform;
197 //### technically O(n^2) -- we could cache localInstanceTransform if every node in the
198 // tree is guaranteed to have the same instance root. That would require an API change.
199 nodeInstanceLocalTransform = node->localTransform;
200 auto *p = parent;
201 while (p) {
202 if (p == instanceRoot) {
203 nodeInstanceLocalTransform = instanceRootLocalTransform * nodeInstanceLocalTransform;
204 break;
205 }
206 nodeInstanceLocalTransform = p->localTransform * nodeInstanceLocalTransform;
207 p = p->parent;
208 }
209 } else {
210 // By default, we do magic: translation is applied to the global instance transform,
211 // while scale/rotation is local
212 QMatrix4x4 globalInstanceTransform = globalTransforms[pidx];
213 QMatrix4x4 localInstanceTransform = node->localTransform;
214 auto &localInstanceMatrix = *reinterpret_cast<float (*)[4][4]>(localInstanceTransform.data());
215 QVector3D localPos{localInstanceMatrix[3][0], localInstanceMatrix[3][1], localInstanceMatrix[3][2]};
216 localInstanceMatrix[3][0] = 0;
217 localInstanceMatrix[3][1] = 0;
218 localInstanceMatrix[3][2] = 0;
219 globalInstanceTransform = pGlobalTransform;
220 globalInstanceTransform.translate(vector: localPos);
221 instanceTransforms[idx] = { .local: localInstanceTransform, .global: globalInstanceTransform };
222 }
223 } else {
224 instanceTransforms[idx] = { .local: node->localTransform, .global: {} };
225 }
226
227 return retval;
228}
229
230QSSGGlobalRenderNodeData::QSSGGlobalRenderNodeData()
231#if QT_CONFIG(thread)
232 : m_threadPool(new QThreadPool)
233#endif // QT_CONFIG(thread)
234{
235
236}
237
238QSSGGlobalRenderNodeData::~QSSGGlobalRenderNodeData()
239{
240
241}
242
243void QSSGGlobalRenderNodeData::reindex(QSSGRenderRoot *rootNode)
244{
245 if (rootNode) {
246 quint32 dfsIdx = 0;
247 m_nodeCount = 0;
248
249 // If the window changes the window root node changes as well,
250 // this check ensures we accidentally don't reindex with the
251 // same version, as that can cause problems.
252 // The start version should be set to the last layer's last version.
253 if (m_version == rootNode->startVersion())
254 m_version = rootNode->startVersion() + 1;
255 else
256 ++m_version;
257
258 ::reindex(rootNode, version: m_version, dfsIdx, count&: m_nodeCount);
259
260 // Actual storage size (Some nodes, like the layers, will all use index 0).
261 // NOTE: This can differ from the node count, as nodes are collected for each
262 // layer. Since layers can reference nodes outside their view the node list
263 // will contain duplicate nodes when import scene is used!
264 m_size = dfsIdx + 1;
265
266 globalTransforms.resize(new_size: m_size, x: QMatrix4x4{ Qt::Uninitialized });
267 globalOpacities.resize(new_size: m_size, x: 1.0f);
268 instanceTransforms.resize(new_size: m_size, x: { .local: QMatrix4x4{ Qt::Uninitialized }, .global: QMatrix4x4{ Qt::Uninitialized } });
269
270 collectNodes(rootNode);
271 // NOTE: If the tree was dirty we force a full rebuild of the global transforms etc. since
272 // the stored data is invalid for the new index order.
273 updateGlobalState();
274 }
275}
276
277QMatrix4x4 QSSGGlobalRenderNodeData::getGlobalTransform(QSSGRenderNodeHandle h, QMatrix4x4 defaultValue) const
278{
279 // Ensure we have an valid index.
280 const bool hasId = h.hasId();
281 const bool validVersion = hasId && (h.version() == m_version);
282 const auto index = h.index();
283
284 // NOTE: In effect we are returning the local transform here in some cases, which
285 // is why we don't assert or have hints about the likelyhood of branching here.
286 if (!validVersion || !(globalTransforms.size() > index))
287 return defaultValue;
288
289 return globalTransforms[index];
290}
291
292QMatrix4x4 QSSGGlobalRenderNodeData::getGlobalTransform(QSSGRenderNodeHandle h) const
293{
294 return getGlobalTransform(h, defaultValue: QMatrix4x4{ Qt::Uninitialized });
295}
296
297QMatrix4x4 QSSGGlobalRenderNodeData::getGlobalTransform(const QSSGRenderNode &node) const
298{
299 return getGlobalTransform(h: node.h, defaultValue: node.localTransform);
300}
301
302float QSSGGlobalRenderNodeData::getGlobalOpacity(QSSGRenderNodeHandle h, float defaultValue) const
303{
304 const bool hasId = h.hasId();
305 const bool validVersion = hasId && (h.version() == m_version);
306 const auto index = h.index();
307
308 if (!validVersion || !(globalOpacities.size() > index))
309 return defaultValue;
310
311 return globalOpacities[index];
312}
313
314float QSSGGlobalRenderNodeData::getGlobalOpacity(const QSSGRenderNode &node) const
315{
316 return getGlobalOpacity(h: node.h, defaultValue: node.localOpacity);
317}
318
319#if QT_CONFIG(thread)
320const std::unique_ptr<QThreadPool> &QSSGGlobalRenderNodeData::threadPool() const { return m_threadPool; }
321#endif // QT_CONFIG(thread)
322
323QSSGGlobalRenderNodeData::LayerNodeView QSSGGlobalRenderNodeData::getLayerNodeView(QSSGRenderLayerHandle h) const
324{
325 const bool hasId = h.hasId();
326 const bool validVersion = hasId && (h.version() == m_version);
327 const auto index = h.index();
328
329 if (!validVersion || !(layerNodes.size() > index))
330 return { };
331
332 auto &seciont = layerNodes[index];
333
334 return { nodes.data() + seciont.offset, qsizetype(seciont.size) };
335}
336
337QSSGGlobalRenderNodeData::LayerNodeView QSSGGlobalRenderNodeData::getLayerNodeView(const QSSGRenderLayer &layer) const
338{
339 return getLayerNodeView(h: layer.lh);
340}
341
342void QSSGGlobalRenderNodeData::collectNodes(QSSGRenderRoot *rootNode)
343{
344 // 1. Collect all the nodes and create views into the node storage for each layer
345 // 2. Update the global state
346 // 3. Update the global data
347 Q_ASSERT(rootNode != nullptr);
348
349 nodes.clear();
350 nodes.resize(new_size: m_nodeCount, x: nullptr);
351 layerNodes.clear();
352
353 size_t idx = 0;
354 quint32 layerIdx = 0;
355 for (QSSGRenderNode &chld : rootNode->children) {
356 Q_ASSERT(chld.type == QSSGRenderNode::Type::Layer);
357 QSSGRenderLayer *layer = static_cast<QSSGRenderLayer *>(&chld);
358 const size_t offset = idx;
359 collectLayerChildNodes<Discard::None, Insert::Indexed>(layer, outList&: nodes, idx);
360 layer->lh = QSSGRenderLayerHandle(layer->h.context(), m_version, layerIdx++);
361 layerNodes.emplace_back(args: LayerNodeSection{.offset: offset , .size: idx - offset});
362 }
363
364 nodes.resize(new_size: idx /* idx == next_idx == size */);
365}
366
367void QSSGGlobalRenderNodeData::updateGlobalState()
368{
369 // Update the active and pickable state
370 for (QSSGRenderNode *node : nodes)
371 QSSGRenderDataHelpers::updateGlobalNodeState(node, version: m_version);
372
373 // Recalculate ALL the global transforms and opacities.
374 for (QSSGRenderNode *node : nodes)
375 calcGlobalNodeDataIndexedImpl<QSSGRenderDataHelpers::Strategy::Initial>(node, version: m_version, globalTransforms, globalOpacities);
376
377 // FIXME: We shouldn't need to re-create all the instance transforms even when instancing isn't used...
378 for (QSSGRenderNode *node : nodes)
379 QSSGRenderDataHelpers::calcInstanceTransforms(node, version: m_version, globalTransforms, instanceTransforms);
380}
381
382QSSGGlobalRenderNodeData::InstanceTransforms QSSGGlobalRenderNodeData::getInstanceTransforms(const QSSGRenderNode &node) const
383{
384 return getInstanceTransforms(h: node.h);
385}
386
387QSSGGlobalRenderNodeData::InstanceTransforms QSSGGlobalRenderNodeData::getInstanceTransforms(QSSGRenderNodeHandle h) const
388{
389 const bool hasId = h.hasId();
390 const bool validVersion = hasId && (h.version() == m_version);
391 const auto index = h.index();
392
393 if (!validVersion || !(instanceTransforms.size() > index))
394 return { };
395
396 return instanceTransforms[index];
397}
398
399QSSGRenderModelData::QSSGRenderModelData(const QSSGGlobalRenderNodeDataPtr &globalNodeData)
400 : m_gnd(globalNodeData)
401 , m_version(globalNodeData->version())
402{
403
404}
405
406QMatrix3x3 QSSGRenderModelData::getNormalMatrix(QSSGRenderModelHandle h, QMatrix3x3 defaultValue) const
407{
408 const bool hasId = h.hasId();
409 const bool validVersion = hasId && (h.version() == m_version);
410 const auto index = h.index();
411 if (!validVersion || !(normalMatrices.size() > index))
412 return defaultValue;
413
414 return normalMatrices[index];
415}
416
417QMatrix3x3 QSSGRenderModelData::getNormalMatrix(const QSSGRenderModel &model) const
418{
419 return getNormalMatrix(h: model.mh, defaultValue: QMatrix3x3{ Qt::Uninitialized });
420}
421
422QSSGRenderMesh *QSSGRenderModelData::getMesh(QSSGRenderModelHandle h) const
423{
424 const bool hasId = h.hasId();
425 const bool validVersion = hasId && (h.version() == m_version);
426 const auto index = h.index();
427
428 if (!validVersion || !(meshes.size() > index))
429 return nullptr;
430
431 return meshes[index];
432}
433
434QSSGRenderMesh *QSSGRenderModelData::getMesh(const QSSGRenderModel &model) const
435{
436 return getMesh(h: model.mh);
437}
438
439QSSGRenderModelData::MaterialList QSSGRenderModelData::getMaterials(QSSGRenderModelHandle h) const
440{
441 const bool hasId = h.hasId();
442 const bool validVersion = hasId && (h.version() == m_version);
443 const auto index = h.index();
444
445 if (!validVersion || !(materials.size() > index))
446 return {};
447
448 return materials[index];
449}
450
451QSSGRenderModelData::MaterialList QSSGRenderModelData::getMaterials(const QSSGRenderModel &model) const
452{
453 return getMaterials(h: model.mh);
454}
455
456QSSGRenderModelData::ModelViewProjections QSSGRenderModelData::getModelViewProjection(const QSSGRenderModel &model) const
457{
458 return getModelViewProjection(h: model.mh);
459}
460
461QSSGRenderModelData::ModelViewProjections QSSGRenderModelData::getModelViewProjection(QSSGRenderModelHandle h) const
462{
463 const bool hasId = h.hasId();
464 const bool validVersion = hasId && (h.version() == m_version);
465 const auto index = h.index();
466
467 if (!validVersion || !(modelViewProjections.size() > index))
468 return {};
469
470 return modelViewProjections[index];
471}
472
473void QSSGRenderModelData::prepareMeshData(const QSSGModelsView &models, QSSGRenderer *renderer)
474{
475 const auto &bufferManager = renderer->contextInterface()->bufferManager();
476
477 const bool globalPickingEnabled = QSSGRendererPrivate::isGlobalPickingEnabled(renderer: *renderer);
478
479 for (auto *model : models) {
480 // It's up to the BufferManager to employ the appropriate caching mechanisms, so
481 // loadMesh() is expected to be fast if already loaded. Note that preparing
482 // the same QSSGRenderModel in different QQuickWindows (possible when a
483 // scene is shared between View3Ds where the View3Ds belong to different
484 // windows) leads to a different QSSGRenderMesh since the BufferManager is,
485 // very correctly, per window, and so per scenegraph render thread.
486
487 // Ensure we have a mesh
488 if (auto *theMesh = bufferManager->loadMesh(model: *model)) {
489 meshes[model->mh.index()] = theMesh;
490 // Completely transparent models cannot be pickable. But models with completely
491 // transparent materials still are. This allows the artist to control pickability
492 // in a somewhat fine-grained style.
493 const float modelGlobalOpacity = m_gnd->getGlobalOpacity(node: *model);
494 const bool canModelBePickable = (modelGlobalOpacity > QSSGRendererPrivate::minimumRenderOpacity)
495 && (globalPickingEnabled
496 || model->getGlobalState(stateFlag: QSSGRenderModel::GlobalState::Pickable));
497 if (canModelBePickable) {
498 // Check if there is BVH data, if not generate it
499 if (!theMesh->bvh) {
500 const QSSGMesh::Mesh mesh = bufferManager->loadLightmapMesh(model: *model);
501
502 if (mesh.isValid())
503 theMesh->bvh = bufferManager->loadMeshBVH(mesh);
504 else if (model->geometry)
505 theMesh->bvh = bufferManager->loadMeshBVH(geometry: model->geometry);
506 else if (!model->meshPath.isNull())
507 theMesh->bvh = bufferManager->loadMeshBVH(inSourcePath: model->meshPath);
508
509 if (theMesh->bvh) {
510 const auto &roots = theMesh->bvh->roots();
511 for (qsizetype i = 0, end = qsizetype(roots.size()); i < end; ++i)
512 theMesh->subsets[i].bvhRoot = roots[i];
513 }
514 }
515 }
516 } else {
517 const size_t index = model->mh.index();
518 if (QSSG_GUARD(meshes.size() > index))
519 meshes[model->mh.index()] = nullptr;
520 }
521 }
522
523 // Now is the time to kick off the vertex/index buffer updates for all the
524 // new meshes (and their submeshes). This here is the last possible place
525 // to kick this off because the rest of the rendering pipeline will only
526 // see the individual sub-objects as "renderable objects".
527 bufferManager->commitBufferResourceUpdates();
528}
529
530void QSSGRenderModelData::prepareMaterials(const QSSGModelsView &models)
531{
532 for (auto *model : models) {
533 const size_t index = model->mh.index();
534 if (QSSG_GUARD(materials.size() > index))
535 materials[index] = model->materials;
536 }
537}
538
539void QSSGRenderModelData::updateModelData(QSSGModelsView &models, QSSGRenderer *renderer, const QSSGRenderCameraDataList &renderCameraData)
540{
541 const auto modelCount = size_t(models.size());
542 const bool versionChanged = m_version != m_gnd->version();
543 const bool storageSizeChanged = (normalMatrices.size() < modelCount);
544
545 const QMatrix3x3 defaultNormalMatrix;
546 const QMatrix4x4 defaultModelViewProjection;
547
548 // resize the storage if needed
549 modelViewProjections.resize(new_size: modelCount, x: { defaultModelViewProjection, defaultModelViewProjection });
550 normalMatrices.resize(new_size: modelCount, x: defaultNormalMatrix);
551 meshes.resize(new_size: modelCount, x: nullptr);
552 materials.resize(new_size: modelCount, x: {});
553
554 // If the version or storage size changed we need to re-index the models.
555 // NOTE: Node data's version is incremented when the node graph changes and starts at 1.
556 if (versionChanged || storageSizeChanged) {
557 m_version = m_gnd->version();
558
559 for (quint32 i = 0; i < modelCount; ++i) {
560 QSSGRenderModel *model = models[i];
561 model->mh = QSSGRenderModelHandle(model->h.context(), model->h.version(), i);
562 }
563 }
564
565 // - Normal matrices
566 const auto doNormalMatrices = [&]() {
567 for (const QSSGRenderModel *model : std::as_const(t&: models)) {
568 auto &normalMatrix = normalMatrices[model->mh.index()];
569 const QMatrix4x4 globalTransform = m_gnd->getGlobalTransform(node: *model);
570 QSSGRenderNode::calculateNormalMatrix(globalTransform, outNormalMatrix&: normalMatrix);
571 }
572 };
573
574 // - MVPs
575 const auto doMVPs = [&]() {
576 for (const QSSGRenderModel *model : std::as_const(t&: models)) {
577 int mvpCount = 0;
578 const QMatrix4x4 globalTransform = m_gnd->getGlobalTransform(node: *model);
579 auto &mvp = modelViewProjections[model->mh.index()];
580 for (const QSSGRenderCameraData &cameraData : renderCameraData)
581 QSSGRenderNode::calculateMVP(globalTransform, inViewProjection: cameraData.viewProjection, outMVP&: mvp[mvpCount++]);
582 }
583 };
584
585#if QT_CONFIG(thread)
586 const auto &threadPool = m_gnd->threadPool();
587#define qssgTryThreadedStart(func) \
588 if (!threadPool->tryStart(func)) { \
589 qWarning("Unable to start thread for %s!", #func); \
590 func(); \
591 }
592#define qssgTryWaitForDone() \
593 threadPool->waitForDone();
594#else
595#define qssgTryThreadedStart(func) \
596 func();
597#define qssgTryWaitForDone() \
598 /* no-op */
599#endif // QT_CONFIG(thread)
600
601 qssgTryThreadedStart(doNormalMatrices);
602 qssgTryThreadedStart(doMVPs);
603
604 // While we are waiting for the threads to finish, we can also prepare and update
605 // the materials and meshes.
606 prepareMaterials(models);
607 prepareMeshData(models, renderer);
608
609 // Wait for the threads to finish
610 qssgTryWaitForDone();
611}
612
613bool QSSGRenderDataHelpers::updateGlobalNodeDataIndexed(QSSGRenderNode *node, const quint32 version, QSSGGlobalRenderNodeData::GlobalTransformStore &globalTransforms, QSSGGlobalRenderNodeData::GlobalOpacityStore &globalOpacities)
614{
615 return calcGlobalNodeDataIndexedImpl<Strategy::Update>(node, version, globalTransforms, globalOpacities);
616}
617
618bool QSSGRenderDataHelpers::calcGlobalVariablesIndexed(QSSGRenderNode *node, const quint32 version, QSSGGlobalRenderNodeData::GlobalTransformStore &globalTransforms, QSSGGlobalRenderNodeData::GlobalOpacityStore &globalOpacities)
619{
620 return calcGlobalNodeDataIndexedImpl<Strategy::Initial>(node, version, globalTransforms, globalOpacities);
621}
622
623QT_END_NAMESPACE
624

source code of qtquick3d/src/runtimerender/rendererimpl/qssgrenderdata.cpp