Revision control

++ // Copyright (C) 2025 The Qt Company Ltd.
++ // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
++ // Qt-Security score:significant reason:default
++
++
++ #ifndef QSSGRENDERDATA_P_H
++ #define QSSGRENDERDATA_P_H
++
++ //
++ // W A R N I N G
++ // -------------
++ //
++ // This file is not part of the Qt API. It exists purely as an
++ // implementation detail. This header file may change from version to
++ // version without notice, or even be removed.
++ //
++ // We mean it.
++ //
++
++ #include <QtGui/qmatrix4x4.h>
++
++ #include "qssgrenderableobjects_p.h"
++
++ #include <vector>
++ #include <unordered_map>
++ #include <memory>
++ #if QT_CONFIG(thread) && !defined(Q_OS_WASM)
++ #include <mutex>
++ #include <condition_variable>
++ #endif
++
++ class tst_NodeIndexing;
++
++ QT_BEGIN_NAMESPACE
++
++ struct QSSGRenderNode;
++ class QSSGRenderRoot;
++
++ class QThreadPool;
++
++ class QSGRenderContext;
++ class QSGRenderer;
++
++ // Per window node data
++ class Q_QUICK3DRUNTIMERENDER_EXPORT QSSGGlobalRenderNodeData
++ {
++ Q_DISABLE_COPY_MOVE(QSSGGlobalRenderNodeData)
++ public:
++ struct InstanceTransforms
++ {
++ QMatrix4x4 local;
++ QMatrix4x4 global;
++ };
++
++ struct LayerNodeSection
++ {
++ size_t offset = 0;
++ size_t size = 0;
++ };
++
++ using VersionType = QSSGRenderNodeVersionType;
++
++ using LayerNodeView = QSSGDataView<QSSGRenderNode *>;
++
++ using GlobalTransformStore = std::vector<QMatrix4x4>;
++ using GlobalOpacityStore = std::vector<float>;
++ using InstanceTransformStore = std::vector<InstanceTransforms>;
++ using NodeStore = std::vector<QSSGRenderNode *>;
++ using LayerNodeViewStore = std::vector<LayerNodeSection>;
++
++ using NormalMatrixStore = std::vector<QMatrix3x3>;
++ using MeshStore = std::vector<QSSGRenderMesh *>;
++ using MaterialList = QVector<QSSGRenderGraphObject *>;
++ using MaterialStore = std::vector<MaterialList>;
++
++ explicit QSSGGlobalRenderNodeData(QSSGRenderRoot *root);
++ ~QSSGGlobalRenderNodeData();
++
++ void reindex();
++
++ // Once this is called we expect teardown as bugn and no further
++ // updates will be made. We do however keep the data around in case
++ // anyone holding a reference to us decides to query something.
++ void invalidate();
++
++ [[nodiscard]] VersionType version() const { return m_version; }
++
++ // The state generation is bumped whenever a layer consumes an active or tag change on a
++ // node that is shared between layers (imported scene). The node's dirty flags are cleared by
++ // the first layer that sees them, so the other layers sharing the node compare the generation
++ // they last saw against this one to know that they need to redo their filtered node lists.
++ [[nodiscard]] VersionType stateGeneration() const { return m_stateGeneration; }
++ void bumpStateGeneration() { ++m_stateGeneration; }
++
++ // NOTE: The node count is the number of nodes in the "world" tree.
++ // This is not the the same as the storage size. as some nodes
++ // may not be stored in the data or use the same storage location!
++ [[nodiscard]] size_t nodeCount() const { return m_nodeCount; }
++ [[nodiscard]] size_t storageSize() const { return m_size; }
++
++ [[nodiscard]] QMatrix4x4 getGlobalTransform(QSSGRenderNodeHandle h, const QMatrix4x4 &defaultValue) const;
++ [[nodiscard]] QMatrix4x4 getGlobalTransform(QSSGRenderNodeHandle h) const;
++ [[nodiscard]] QMatrix4x4 getGlobalTransform(const QSSGRenderNode &node) const;
++ [[nodiscard]] float getGlobalOpacity(QSSGRenderNodeHandle h, float defaultValue = 1.0f) const;
++ [[nodiscard]] float getGlobalOpacity(const QSSGRenderNode &node) const;
++ [[nodiscard]] InstanceTransforms getInstanceTransforms(QSSGRenderNodeHandle h) const;
++ [[nodiscard]] InstanceTransforms getInstanceTransforms(const QSSGRenderNode &node) const;
++
++ [[nodiscard]] LayerNodeView getLayerNodeView(QSSGRenderLayerHandle h) const;
++ [[nodiscard]] LayerNodeView getLayerNodeView(const QSSGRenderLayer &layer) const;
++
++ GlobalTransformStore globalTransforms { };
++ GlobalOpacityStore globalOpacities { };
++ InstanceTransformStore instanceTransforms { };
++ NodeStore nodes { };
++ LayerNodeViewStore layerNodes { };
++ NormalMatrixStore normalMatrices { };
++ MeshStore meshes { };
++ MaterialStore materials { };
++
++ #if QT_CONFIG(thread)
++ // NOTE: Thread pool for parallel processing of render data.
++ // This thread pool is not intended for async calls.
++ // For long running tasks use the global thread pool instead.
++ // (Threads executed here are expected to be done before the sync has ended).
++ QThreadPool *threadPool() const;
++ #endif // QT_CONFIG(thread)
++
++ private:
++ friend class ::tst_NodeIndexing;
++
++ void collectNodes(QSSGRenderRoot *rootNode);
++ void updateGlobalState();
++
++ #if QT_CONFIG(thread) && !defined(Q_OS_WASM)
++ std::unique_ptr<QThreadPool> m_threadPool;
++ #endif // QT_CONFIG(thread)
++
++ QSSGRenderRoot *m_rootNode = nullptr;
++ size_t m_size = 0;
++ size_t m_nodeCount = 0;
++ VersionType m_version = 0;
++ VersionType m_stateGeneration = 0;
++ };
++
++ using QSSGGlobalRenderNodeDataPtr = std::shared_ptr<QSSGGlobalRenderNodeData>;
++
++ class QSSGRenderDataHelpers
++ {
++ QSSGRenderDataHelpers() = delete;
++ public:
++ enum class Strategy
++ {
++ Initial, // Initial calculation of ALL global variables
++ Update, // Update calculation of ONLY changed global variables
++ };
++
++ enum GlobalStateResult : quint8
++ {
++ None,
++ ActiveChanged = 0x1,
++ PickableChanged = 0x2,
++ TagChanged = 0x4, // a node's content layer tag changed; the layer filter needs a rerun
++ };
++
++ using GlobalStateResultT = std::underlying_type_t<GlobalStateResult>;
++ using VersionType = QSSGRenderNodeVersionType;
++
++ /*!
++ \brief calcGlobalNodeData
++ \param node The node to calculate the global data for.
++ \param version The version of the node current node tree.
++ \param globalTransforms The global transforms store.
++ \param globalOpacities The global opacities store.
++ \return true if the data was updated, false otherwise.
++
++ The function is used to calculate the global node data for the given node.
++ It will use the given strategy to determine if it should calculate
++ the initial values or update the existing values based on the node's dirty state.
++
++ NOTE: This function assumes the output data is already allocated and can be directly
++ indexed!!!
++ */
++ template <Strategy strategy>
++ static bool calcGlobalNodeData(QSSGRenderNode *node,
++ const VersionType version,
++ QSSGGlobalRenderNodeData::GlobalTransformStore &globalTransforms,
++ QSSGGlobalRenderNodeData::GlobalOpacityStore &globalOpacities)
++ {
++ if constexpr (strategy == Strategy::Initial)
++ return calcGlobalVariablesIndexed(node, version, globalTransforms, globalOpacities);
++ else
++ return updateGlobalNodeDataIndexed(node, version, globalTransforms, globalOpacities);
++ }
++
++ /*!
++ \brief calcInstanceTransforms
++ \param node The node to calculate the instance transforms for.
++ \param version The version of the node current node tree.
++ \param globalTransforms The global transforms store.
++ \param instanceTransforms The instance transforms store.
++ \return true if the data was updated, false otherwise.
++
++ The function is used to calculate the instance transforms for the given node.
++
++ NOTE: This function assumes the output data is already allocated and can be directly
++ indexed!!!
++ */
++ static bool calcInstanceTransforms(QSSGRenderNode *node,
++ const VersionType version,
++ QSSGGlobalRenderNodeData::GlobalTransformStore &globalTransforms,
++ QSSGGlobalRenderNodeData::InstanceTransformStore &instanceTransforms);
++
++
++ /*!
++ \brief updateGlobalNodeState
++ \param node The node to update the global state for.
++ \param version The version of the node current node tree.
++ \return The result of the update.
++
++ The function is used to update the global state flagsfor the given node,
++ meaning the global active and pickable state of the node.
++ */
++ static GlobalStateResult updateGlobalNodeState(QSSGRenderNode *node, const VersionType version);
++ private:
++ static bool updateGlobalNodeDataIndexed(QSSGRenderNode *node,
++ const VersionType version,
++ QSSGGlobalRenderNodeData::GlobalTransformStore &globalTransforms,
++ QSSGGlobalRenderNodeData::GlobalOpacityStore &globalOpacities);
++ static bool calcGlobalVariablesIndexed(QSSGRenderNode *node,
++ const VersionType version,
++ QSSGGlobalRenderNodeData::GlobalTransformStore &globalTransforms,
++ QSSGGlobalRenderNodeData::GlobalOpacityStore &globalOpacities);
++ };
++
++ // Per layer model data
++ class QSSGRenderModelData
++ {
++ Q_DISABLE_COPY_MOVE(QSSGRenderModelData)
++ public:
++ explicit QSSGRenderModelData(const QSSGGlobalRenderNodeDataPtr &globalNodeData);
++
++ using ModelViewProjections = std::array<QMatrix4x4, 2>;
++ using MaterialList = QSSGGlobalRenderNodeData::MaterialList;
++
++ using ModelViewProjectionStore = std::vector<ModelViewProjections>;
++
++ [[nodiscard]] ModelViewProjections getModelViewProjection(QSSGRenderNodeHandle h) const;
++ [[nodiscard]] ModelViewProjections getModelViewProjection(const QSSGRenderModel &model) const;
++
++ [[nodiscard]] QMatrix3x3 getNormalMatrix(QSSGRenderNodeHandle h, QMatrix3x3 defaultValue) const;
++ [[nodiscard]] QMatrix3x3 getNormalMatrix(const QSSGRenderModel &model) const;
++
++ [[nodiscard]] QSSGRenderMesh *getMesh(const QSSGRenderModel &model) const;
++
++ [[nodiscard]] MaterialList getMaterials(const QSSGRenderModel &model) const;
++
++ [[nodiscard]] const QSSGGlobalRenderNodeDataPtr &globalNodeData() const { return m_gnd; }
++
++ void updateModelData(QSSGModelsView &models, QSSGRenderer *renderer, const QSSGRenderCameraDataList &renderCameraData);
++
++ private:
++ static quint32 getThreadPoolThreshold();
++
++ ModelViewProjectionStore modelViewProjections;
++
++ void prepareMeshData(const QSSGModelsView &models, QSSGRenderer *renderer);
++ void prepareMaterials(const QSSGModelsView &models);
++
++ QSSGGlobalRenderNodeDataPtr m_gnd;
++
++ quint32 m_version = 0;
++ const quint32 m_threadPoolThreshold = getThreadPoolThreshold();
++ #if QT_CONFIG(thread) && !defined(Q_OS_WASM)
++ std::mutex m_mutex;
++ std::condition_variable m_cv;
++ #endif
++ };
++
++ class QSSGRenderItem2DData
++ {
++ Q_DISABLE_COPY_MOVE(QSSGRenderItem2DData)
++ public:
++ explicit QSSGRenderItem2DData(const QSSGGlobalRenderNodeDataPtr &globalNodeData);
++ ~QSSGRenderItem2DData();
++
++ using ModelViewProjections = std::array<QMatrix4x4, 2>;
++ using ModelViewProjectionStore = std::vector<ModelViewProjections>;
++
++ using Item2DRenderer = QPointer<QSGRenderer>;
++
++ [[nodiscard]] ModelViewProjections getModelViewProjection(QSSGRenderNodeHandle h) const;
++ [[nodiscard]] ModelViewProjections getModelViewProjection(const QSSGRenderItem2D &item) const;
++
++ [[nodiscard]] Item2DRenderer getItem2DRenderer(const QSSGRenderItem2D &item) const;
++
++ [[nodiscard]] const QSSGGlobalRenderNodeDataPtr &globalNodeData() const { return m_gnd; }
++
++ void updateItem2DData(QSSGItem2DsView &items, QSSGRenderer *renderer, const QSSGRenderCameraDataList &renderCameraData);
++
++ void releaseRenderData(const QSSGRenderItem2D &item);
++ void releaseAll();
++
++ private:
++ // The association between the 2D item and its renderer and render pass descriptor, is cached and has
++ // a strong connection to the item itself. We therefore use a map here. The other stores are
++ // indexed stores as those are pure data.
++ using Item2DRendererStore = std::unordered_map<const QSSGRenderItem2D *, Item2DRenderer>;
++
++ QSSGGlobalRenderNodeDataPtr m_gnd;
++
++ QPointer<QSGRenderContext> item2DRenderContext;
++ Item2DRendererStore item2DRenderers;
++ ModelViewProjectionStore modelViewProjections;
++
++ const QMatrix4x4 flipMatrix { 1.0f, 0.0f, 0.0f, 0.0f,
++ 0.0f, -1.0f, 0.0f, 0.0f,
++ 0.0f, 0.0f, 1.0f, 0.0f,
++ 0.0f, 0.0f, 0.0f, 1.0f };
++
++
++ quint32 m_version = 0;
++ };
++
++ QT_END_NAMESPACE
++
++ #endif // QSSGRENDERDATA_P_H