7#include <QtCore/qthreadpool.h>
10#include <QtQuick/private/qsgcontext_p.h>
11#include <QtQuick/private/qsgrenderer_p.h>
13#include "graphobjects/qssgrenderroot_p.h"
14#include "graphobjects/qssgrenderlayer_p.h"
15#include "graphobjects/qssgrenderitem2d_p.h"
18#include "resourcemanager/qssgrenderbuffermanager_p.h"
22static void reindexChildNodes(QSSGRenderNode &node,
const quint32 version, quint32 &dfsIdx, size_t &count)
24 Q_ASSERT(node.type != QSSGRenderNode::Type::Layer);
25 if (node.type != QSSGRenderNode::Type::Layer) {
29 if (node.h.version() != version)
30 node.h = QSSGRenderNodeHandle(0, version, dfsIdx);
31 for (QSSGRenderNode &chld : node.children)
32 reindexChildNodes(chld, version, ++dfsIdx, ++count);
36static void reindexLayerChildNodes(QSSGRenderLayer &layer,
const quint32 version, quint32 &dfsIdx, size_t &count)
38 Q_ASSERT(layer.type == QSSGRenderNode::Type::Layer);
39 if (layer.type == QSSGRenderNode::Type::Layer) {
40 layer.h = QSSGRenderNodeHandle(0, version, 0);
41 for (QSSGRenderNode &chld : layer.children)
42 reindexChildNodes(chld, version, ++dfsIdx, ++count);
46static void reindex(QSSGRenderRoot *rootNode,
const quint32 version, quint32 &dfsIdx, size_t &count)
49 Q_ASSERT(rootNode->type == QSSGRenderNode::Type::Root);
50 Q_ASSERT(dfsIdx == 0);
51 rootNode->h = QSSGRenderNodeHandle(0, version, 0);
52 for (QSSGRenderNode &chld : rootNode->children)
53 reindexLayerChildNodes(
static_cast<QSSGRenderLayer &>(chld), version, dfsIdx, count);
57enum class Insert { Back, Indexed };
61 Q_ASSERT_X(node.type != QSSGRenderNode::Type::Layer, Q_FUNC_INFO,
"Unexpected Layer node in child list!");
62 if constexpr (insert == Insert::Indexed)
63 outList[idx++] = &node;
65 outList.push_back(&node);
66 for (QSSGRenderNode &chld : node.children)
67 collectChildNodesFirst<insert>(chld, outList, idx);
73 if (node.getGlobalState(QSSGRenderNode::GlobalState::Active)) {
74 if constexpr (insert == Insert::Indexed)
75 outList[idx++] = &node;
77 outList.push_back(&node);
78 for (QSSGRenderNode &chld : node.children)
79 collectChildNodesSecond<insert>(chld, outList, idx);
87 Q_ASSERT(layer->type == QSSGRenderNode::Type::Layer);
89 for (QSSGRenderNode &chld : layer->children) {
90 if constexpr (discard == Discard::Inactive)
91 collectChildNodesSecond<insert>(chld, outList, idx);
93 collectChildNodesFirst<insert>(chld, outList, idx);
97 if constexpr (insert == Insert::Back)
103 const quint32 version,
104 QSSGGlobalRenderNodeData::GlobalTransformStore &globalTransforms,
105 QSSGGlobalRenderNodeData::GlobalOpacityStore &globalOpacities)
107 using DirtyFlag = QSSGRenderNode::DirtyFlag;
108 using FlagT = QSSGRenderNode::FlagT;
109 constexpr DirtyFlag TransformAndOpacityDirty = DirtyFlag(FlagT(DirtyFlag::TransformDirty) | FlagT(DirtyFlag::OpacityDirty));
111 if (Q_UNLIKELY(!node || (node->h.version() != version)))
115 bool retval = forcedRebuilf || node->isDirty(TransformAndOpacityDirty);
118 const auto idx = node->h.index();
120 auto &globalTransform = globalTransforms[idx];
121 auto &globalOpacity = globalOpacities[idx];
122 globalOpacity = node->localOpacity;
123 globalTransform = node->localTransform;
125 if (QSSGRenderNode *parent = node->parent) {
126 const auto pidx = parent->h.index();
127 const auto pOpacity = globalOpacities[pidx];
128 globalOpacity *= pOpacity;
130 if (parent->type != QSSGRenderGraphObject::Type::Layer) {
131 const auto pTransform = globalTransforms[pidx];
132 globalTransform = pTransform * node->localTransform;
136 node->clearDirty(TransformAndOpacityDirty);
146 using LocalState = QSSGRenderNode::LocalState;
147 using GlobalState = QSSGRenderNode::GlobalState;
148 using DirtyFlag = QSSGRenderNode::DirtyFlag;
149 using FlagT = QSSGRenderNode::FlagT;
151 constexpr DirtyFlag ActiveOrPickableDirty = DirtyFlag(FlagT(DirtyFlag::ActiveDirty) | FlagT(DirtyFlag::PickableDirty));
153 if (Q_UNLIKELY(!node || (node->h.version() != version)))
156 const bool activeDirty = node->isDirty(DirtyFlag::ActiveDirty);
157 const bool pickableDirty = node->isDirty(DirtyFlag::PickableDirty);
159 const bool updateState = activeDirty || pickableDirty;
162 const QSSGRenderNode *parent = node->parent;
163 const bool hasParent = (parent !=
nullptr);
164 const bool globallyActive = node->getLocalState(LocalState::Active) && (!hasParent || parent->getGlobalState(GlobalState::Active));
165 node->flags = globallyActive ? (node->flags | FlagT(GlobalState::Active)) : (node->flags & ~FlagT(GlobalState::Active));
166 const bool globallyPickable = node->getLocalState(LocalState::Pickable) || (hasParent && parent->getGlobalState(GlobalState::Pickable));
167 node->flags = globallyPickable ? (node->flags | FlagT(GlobalState::Pickable)) : (node->flags & ~FlagT(GlobalState::Pickable));
170 node->clearDirty(ActiveOrPickableDirty);
177 const quint32 version,
178 QSSGGlobalRenderNodeData::GlobalTransformStore &globalTransforms,
179 QSSGGlobalRenderNodeData::InstanceTransformStore &instanceTransforms)
181 if (Q_UNLIKELY(!node || (node->h.version() != version)))
184 constexpr bool retval =
true;
189 const auto idx = node->h.index();
190 QSSGRenderNode *parent = node->parent;
191 if (parent && parent->type != QSSGRenderGraphObject::Type::Layer && node->getLocalState(QSSGRenderNode::LocalState::Active)) {
192 const auto pidx = parent->h.index();
193 const auto &pGlobalTransform = globalTransforms[pidx];
194 QSSGRenderNode *instanceRoot = node->instanceRoot;
195 if (instanceRoot == node) {
196 instanceTransforms[idx] = { node->localTransform, pGlobalTransform };
197 }
else if (instanceRoot) {
198 auto &[nodeInstanceLocalTransform, nodeInstanceGlobalTransform] = instanceTransforms[idx];
199 auto &[instanceRootLocalTransform, instanceRootGlobalTransform] = instanceTransforms[instanceRoot->h.index()];
200 nodeInstanceGlobalTransform = instanceRootGlobalTransform;
203 nodeInstanceLocalTransform = node->localTransform;
206 if (p == instanceRoot) {
207 nodeInstanceLocalTransform = instanceRootLocalTransform * nodeInstanceLocalTransform;
210 nodeInstanceLocalTransform = p->localTransform * nodeInstanceLocalTransform;
216 QMatrix4x4 globalInstanceTransform = globalTransforms[pidx];
217 QMatrix4x4 localInstanceTransform = node->localTransform;
218 auto &localInstanceMatrix = *
reinterpret_cast<
float (*)[4][4]>(localInstanceTransform.data());
219 QVector3D localPos{localInstanceMatrix[3][0], localInstanceMatrix[3][1], localInstanceMatrix[3][2]};
220 localInstanceMatrix[3][0] = 0;
221 localInstanceMatrix[3][1] = 0;
222 localInstanceMatrix[3][2] = 0;
223 globalInstanceTransform = pGlobalTransform;
224 globalInstanceTransform.translate(localPos);
225 instanceTransforms[idx] = { localInstanceTransform, globalInstanceTransform };
228 instanceTransforms[idx] = { node->localTransform, {} };
257 if (m_version == rootNode->startVersion())
258 m_version = rootNode->startVersion() + 1;
262 ::reindex(rootNode, m_version, dfsIdx, m_nodeCount);
270 globalTransforms.resize(m_size, QMatrix4x4{ Qt::Uninitialized });
271 globalOpacities.resize(m_size, 1.0f);
272 instanceTransforms.resize(m_size, { QMatrix4x4{ Qt::Uninitialized }, QMatrix4x4{ Qt::Uninitialized } });
274 collectNodes(rootNode);
284 const bool hasId = h.hasId();
285 const bool validVersion = hasId && (h.version() == m_version);
286 const auto index = h.index();
290 if (!validVersion || !(globalTransforms.size() > index))
293 return globalTransforms[index];
298 return getGlobalTransform(h, QMatrix4x4{ Qt::Uninitialized });
303 return getGlobalTransform(node.h, node.localTransform);
308 const bool hasId = h.hasId();
309 const bool validVersion = hasId && (h.version() == m_version);
310 const auto index = h.index();
312 if (!validVersion || !(globalOpacities.size() > index))
315 return globalOpacities[index];
324const std::unique_ptr<QThreadPool> &QSSGGlobalRenderNodeData::threadPool()
const {
return m_threadPool; }
329 const bool hasId = h.hasId();
330 const bool validVersion = hasId && (h.version() == m_version);
331 const auto index = h.index();
333 if (!validVersion || !(layerNodes.size() > index))
336 auto &seciont = layerNodes[index];
338 return { nodes.data() + seciont.offset, qsizetype(seciont.size) };
343 return getLayerNodeView(layer.lh);
351 Q_ASSERT(rootNode !=
nullptr);
354 nodes.resize(m_nodeCount,
nullptr);
358 quint32 layerIdx = 0;
359 for (QSSGRenderNode &chld : rootNode->children) {
360 Q_ASSERT(chld.type == QSSGRenderNode::Type::Layer);
361 QSSGRenderLayer *layer =
static_cast<QSSGRenderLayer *>(&chld);
362 const size_t offset = idx;
363 collectLayerChildNodes<Discard::None, Insert::Indexed>(layer, nodes, idx);
364 layer->lh = QSSGRenderLayerHandle(layer->h.context(), m_version, layerIdx++);
365 layerNodes.emplace_back(LayerNodeSection{offset , idx - offset});
374 for (QSSGRenderNode *node : nodes)
375 QSSGRenderDataHelpers::updateGlobalNodeState(node, m_version);
378 for (QSSGRenderNode *node : nodes)
379 calcGlobalNodeDataIndexedImpl<QSSGRenderDataHelpers::Strategy::Initial>(node, m_version, globalTransforms, globalOpacities);
382 for (QSSGRenderNode *node : nodes)
383 QSSGRenderDataHelpers::calcInstanceTransforms(node, m_version, globalTransforms, instanceTransforms);
388 return getInstanceTransforms(node.h);
393 const bool hasId = h.hasId();
394 const bool validVersion = hasId && (h.version() == m_version);
395 const auto index = h.index();
397 if (!validVersion || !(instanceTransforms.size() > index))
400 return instanceTransforms[index];
404 : m_gnd(globalNodeData)
405 , m_version(globalNodeData->version())
412 const bool hasId = h.hasId();
413 const bool validVersion = hasId && (h.version() == m_version);
414 const auto index = h.index();
415 if (!validVersion || !(normalMatrices.size() > index))
418 return normalMatrices[index];
423 return getNormalMatrix(model.mh, QMatrix3x3{ Qt::Uninitialized });
428 const bool hasId = h.hasId();
429 const bool validVersion = hasId && (h.version() == m_version);
430 const auto index = h.index();
432 if (!validVersion || !(meshes.size() > index))
435 return meshes[index];
440 return getMesh(model.mh);
445 const bool hasId = h.hasId();
446 const bool validVersion = hasId && (h.version() == m_version);
447 const auto index = h.index();
449 if (!validVersion || !(materials.size() > index))
452 return materials[index];
457 return getMaterials(model.mh);
460QSSGRenderModelData::ModelViewProjections
QSSGRenderModelData::getModelViewProjection(
const QSSGRenderModel &model)
const
462 return getModelViewProjection(model.mh);
467 const bool hasId = h.hasId();
468 const bool validVersion = hasId && (h.version() == m_version);
469 const auto index = h.index();
471 if (!validVersion || !(modelViewProjections.size() > index))
474 return modelViewProjections[index];
479 const auto &bufferManager = renderer->contextInterface()->bufferManager();
481 const bool globalPickingEnabled = QSSGRendererPrivate::isGlobalPickingEnabled(*renderer);
483 for (
auto *model : models) {
492 if (
auto *theMesh = bufferManager->loadMesh(*model)) {
493 meshes[model->mh.index()] = theMesh;
497 const float modelGlobalOpacity = m_gnd->getGlobalOpacity(*model);
498 const bool canModelBePickable = (modelGlobalOpacity > QSSGRendererPrivate::minimumRenderOpacity)
499 && (globalPickingEnabled
500 || model->getGlobalState(QSSGRenderModel::GlobalState::Pickable));
501 if (canModelBePickable) {
504 const QSSGMesh::Mesh mesh = bufferManager->loadLightmapMesh(*model);
507 theMesh->bvh = bufferManager->loadMeshBVH(mesh);
508 else if (model->geometry)
509 theMesh->bvh = bufferManager->loadMeshBVH(model->geometry);
510 else if (!model->meshPath.isNull())
511 theMesh->bvh = bufferManager->loadMeshBVH(model->meshPath);
514 const auto &roots = theMesh->bvh->roots();
515 for (qsizetype i = 0, end = qsizetype(roots.size()); i < end; ++i)
516 theMesh->subsets[i].bvhRoot = roots[i];
521 const size_t index = model->mh.index();
522 if (QSSG_GUARD(meshes.size() > index))
523 meshes[model->mh.index()] =
nullptr;
531 bufferManager->commitBufferResourceUpdates();
536 for (
auto *model : models) {
537 const size_t index = model->mh.index();
538 if (QSSG_GUARD(materials.size() > index))
539 materials[index] = model->materials;
545 const auto modelCount = size_t(models.size());
546 const bool versionChanged = m_version != m_gnd->version();
547 const bool storageSizeChanged = (normalMatrices.size() < modelCount);
552 const bool reIndexNeeded = versionChanged || storageSizeChanged ||
true;
554 const QMatrix3x3 defaultNormalMatrix;
555 const QMatrix4x4 defaultModelViewProjection;
558 modelViewProjections.resize(modelCount, { defaultModelViewProjection, defaultModelViewProjection });
559 normalMatrices.resize(modelCount, defaultNormalMatrix);
560 meshes.resize(modelCount,
nullptr);
561 materials.resize(modelCount, {});
565 m_version = m_gnd->version();
567 for (quint32 i = 0; i < modelCount; ++i) {
568 QSSGRenderModel *model = models[i];
569 model->mh = QSSGRenderModelHandle(model->h.context(), model->h.version(), i);
574 const auto doNormalMatrices = [&]() {
575 for (
const QSSGRenderModel *model : std::as_const(models)) {
576 auto &normalMatrix = normalMatrices[model->mh.index()];
577 const QMatrix4x4 globalTransform = m_gnd->getGlobalTransform(*model);
578 QSSGRenderNode::calculateNormalMatrix(globalTransform, normalMatrix);
583 const auto doMVPs = [&]() {
584 for (
const QSSGRenderModel *model : std::as_const(models)) {
586 const QMatrix4x4 globalTransform = m_gnd->getGlobalTransform(*model);
587 auto &mvp = modelViewProjections[model->mh.index()];
588 for (
const QSSGRenderCameraData &cameraData : renderCameraData)
589 QSSGRenderNode::calculateMVP(globalTransform, cameraData.viewProjection, mvp[mvpCount++]);
594 const auto &threadPool = m_gnd->threadPool();
595#define qssgTryThreadedStart(func)
596 if (!threadPool->tryStart(func)) {
597 qWarning("Unable to start thread for %s!", #func);
600#define qssgTryWaitForDone()
601 threadPool->waitForDone();
603#define qssgTryThreadedStart(func)
605#define qssgTryWaitForDone()
614 prepareMaterials(models);
615 prepareMeshData(models, renderer);
621bool QSSGRenderDataHelpers::updateGlobalNodeDataIndexed(QSSGRenderNode *node,
const quint32 version, QSSGGlobalRenderNodeData::GlobalTransformStore &globalTransforms, QSSGGlobalRenderNodeData::GlobalOpacityStore &globalOpacities)
623 return calcGlobalNodeDataIndexedImpl<
Strategy::Update>(node, version, globalTransforms, globalOpacities);
626bool QSSGRenderDataHelpers::calcGlobalVariablesIndexed(QSSGRenderNode *node,
const quint32 version, QSSGGlobalRenderNodeData::GlobalTransformStore &globalTransforms, QSSGGlobalRenderNodeData::GlobalOpacityStore &globalOpacities)
628 return calcGlobalNodeDataIndexedImpl<
Strategy::Initial>(node, version, globalTransforms, globalOpacities);
633 const auto foundIt = item2DRenderers.find(&item);
634 return (foundIt != item2DRenderers.cend()) ? foundIt->second : Item2DRenderer{};
639 const bool hasId = h.hasId();
640 const bool validVersion = hasId && (h.version() == m_version);
641 const auto index = h.index();
643 if (!validVersion || !(modelViewProjections.size() > index))
646 return modelViewProjections[index];
651 return getModelViewProjection(item.ih);
656 m_gnd = globalNodeData;
657 m_version = globalNodeData->version();
667 const auto itemCount = size_t(items.size());
672 const bool versionChanged = m_version != m_gnd->version();
673 const bool storageSizeChanged = (modelViewProjections.size() < itemCount);
677 const bool reIndexNeeded = versionChanged || storageSizeChanged ||
true;
679 const QMatrix4x4 defaultModelViewProjection;
681 const auto &rhiCtx = renderer->contextInterface()->rhiContext();
684 modelViewProjections.resize(itemCount, { defaultModelViewProjection, defaultModelViewProjection });
688 m_version = m_gnd->version();
690 for (quint32 i = 0; i < itemCount; ++i) {
691 QSSGRenderItem2D *item = items[i];
692 item->ih = QSSGRenderItem2DHandle(item->h.context(), item->h.version(), i);
697 const auto &clipSpaceCorrMatrix = rhiCtx->rhi()->clipSpaceCorrMatrix();
700 const auto doMVPs = [&]() {
701 for (
const QSSGRenderItem2D *item : std::as_const(items)) {
703 const QMatrix4x4 globalTransform = m_gnd->getGlobalTransform(*item);
704 auto &mvps = modelViewProjections[item->ih.index()];
705 for (
const QSSGRenderCameraData &cameraData : renderCameraData) {
706 const QMatrix4x4 &mvp = cameraData.viewProjection * globalTransform;
707 mvps[mvpCount++] = clipSpaceCorrMatrix * mvp * flipMatrix;
716 QSGRenderContext *sgRc = QSSGRendererPrivate::getSgRenderContext(*renderer);
717 const bool contextChanged = (item2DRenderContext && item2DRenderContext != sgRc);
718 item2DRenderContext = sgRc;
720 if (contextChanged || !rpd)
721 rpd.reset(rhiCtx->mainRenderPassDescriptor()->newCompatibleRenderPassDescriptor());
723 for (
const QSSGRenderItem2D *theItem2D : std::as_const(items)) {
724 auto item2DRenderer = getItem2DRenderer(*theItem2D);
725 if (contextChanged) {
726 delete item2DRenderer;
729 item2DRenderer.clear();
732 if (!item2DRenderer) {
733 item2DRenderer = sgRc->createRenderer(QSGRendererInterface::RenderMode3D);
734 QObject::connect(item2DRenderer, SIGNAL(sceneGraphChanged()), theItem2D->m_frontEndObject, SLOT(update()));
737 if (item2DRenderer->rootNode() != theItem2D->m_rootNode) {
738 item2DRenderer->setRootNode(theItem2D->m_rootNode);
739 theItem2D->m_rootNode->markDirty(QSGNode::DirtyForceUpdate);
740 item2DRenderer->nodeChanged(theItem2D->m_rootNode, QSGNode::DirtyForceUpdate);
743 item2DRenderers[theItem2D] = item2DRenderer;
749 const auto foundIt = item2DRenderers.find(&item);
750 if (foundIt != item2DRenderers.cend()) {
751 delete foundIt->second;
752 item2DRenderers.erase(foundIt);
761 for (
auto &it : item2DRenderers)
764 item2DRenderers.clear();
765 item2DRenderContext =
nullptr;
766 modelViewProjections.clear();
InstanceTransforms getInstanceTransforms(QSSGRenderNodeHandle h) const
float getGlobalOpacity(const QSSGRenderNode &node) const
float getGlobalOpacity(QSSGRenderNodeHandle h, float defaultValue=1.0f) const
~QSSGGlobalRenderNodeData()
QSSGGlobalRenderNodeData()
QMatrix4x4 getGlobalTransform(QSSGRenderNodeHandle h) const
QMatrix4x4 getGlobalTransform(const QSSGRenderNode &node) const
LayerNodeView getLayerNodeView(QSSGRenderLayerHandle h) const
QMatrix4x4 getGlobalTransform(QSSGRenderNodeHandle h, QMatrix4x4 defaultValue) const
InstanceTransforms getInstanceTransforms(const QSSGRenderNode &node) const
void reindex(QSSGRenderRoot *rootNode)
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(QSSGRenderItem2DHandle h) const
ModelViewProjections getModelViewProjection(const QSSGRenderItem2D &item) const
void updateModelData(QSSGModelsView &models, QSSGRenderer *renderer, const QSSGRenderCameraDataList &renderCameraData)
QMatrix3x3 getNormalMatrix(QSSGRenderModelHandle h, QMatrix3x3 defaultValue) const
QSSGRenderMesh * getMesh(QSSGRenderModelHandle h) const
MaterialList getMaterials(QSSGRenderModelHandle h) const
ModelViewProjections getModelViewProjection(QSSGRenderModelHandle h) const
#define qssgTryWaitForDone()
static bool calcGlobalNodeDataIndexedImpl(QSSGRenderNode *node, const quint32 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)
std::shared_ptr< QSSGGlobalRenderNodeData > QSSGGlobalRenderNodeDataPtr