Revision control
++ // Copyright (C) 2008-2012 NVIDIA Corporation.
++ // Copyright (C) 2022 The Qt Company Ltd.
++ // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
++ // Qt-Security score:significant reason:default
++
++
++ #ifndef QSSG_LAYER_RENDER_DATA_H
++ #define QSSG_LAYER_RENDER_DATA_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 <QtQuick3DRuntimeRender/private/qssgrenderitem2d_p.h>
++ #include <QtQuick3DRuntimeRender/private/qssgrenderer_p.h>
++ #include <QtQuick3DRuntimeRender/private/qssgrendershadercache_p.h>
++ #include <QtQuick3DRuntimeRender/private/qssgrenderableobjects_p.h>
++ #include <QtQuick3DRuntimeRender/private/qssgrendershadowmap_p.h>
++ #include <QtQuick3DRuntimeRender/private/qssgrendereffect_p.h>
++ #include <QtQuick3DRuntimeRender/private/qssgrenderresourceloader_p.h>
++ #include <QtQuick3DRuntimeRender/private/qssgrenderreflectionmap_p.h>
++ #include <QtQuick3DRuntimeRender/private/qssgrenderskymaterialmanager_p.h>
++ #include <QtQuick3DRuntimeRender/private/qssgrendercamera_p.h>
++ #include <QtQuick3DRuntimeRender/private/qssgrhicontext_p.h>
++ #include <QtQuick3DRuntimeRender/private/qssgperframeallocator_p.h>
++ #include <QtQuick3DRuntimeRender/private/qssgshadermapkey_p.h>
++ #include <QtQuick3DRuntimeRender/private/qssglightmapbaker_p.h>
++ #include <QtQuick3DRuntimeRender/private/qssguserrenderpassmanager_p.h>
++ #include <ssg/qssgrenderextensions.h>
++
++ #include <QtQuick3DUtils/private/qssgrenderbasetypes_p.h>
++
++ #include <optional>
++ #include <unordered_map>
++
++ #include "qssgrenderpass_p.h"
++ #include "qssgrenderdata_p.h"
++
++ QT_BEGIN_NAMESPACE
++
++ class QSSGRenderableObject;
++
++ class QSGRenderer;
++
++ namespace QSSGRenderResult
++ {
++
++ enum class Key : quint32
++ {
++ // Public
++ AoTexture,
++ DepthTexture,
++ ScreenTexture,
++ NormalTexture,
++ MotionVectorTexture,
++ // Internal
++ AccumTexture,
++ RevealageTexture,
++ ABufferImage = AccumTexture,
++ AuxiliaryImage = RevealageTexture,
++ DepthTextureMS,
++ CounterImage,
++ SkyMaterialBackgroundTexture,
++ RenderResultCount,
++ };
++
++ inline QSSGRenderResult::Key toInternalRenderResultKey(QSSGFrameData::RenderResult id)
++ {
++ switch (id) {
++ case QSSGFrameData::RenderResult::AoTexture:
++ return QSSGRenderResult::Key::AoTexture;
++ case QSSGFrameData::RenderResult::DepthTexture:
++ return QSSGRenderResult::Key::DepthTexture;
++ case QSSGFrameData::RenderResult::ScreenTexture:
++ return QSSGRenderResult::Key::ScreenTexture;
++ case QSSGFrameData::RenderResult::NormalTexture:
++ return QSSGRenderResult::Key::NormalTexture;
++ case QSSGFrameData::RenderResult::MotionVectorTexture:
++ return QSSGRenderResult::Key::MotionVectorTexture;
++ }
++
++ if (size_t(QSSGRenderResult::Key::CounterImage) > size_t(id))
++ return static_cast<QSSGRenderResult::Key>(size_t(id));
++
++ return QSSGRenderResult::Key::RenderResultCount;
++ }
++
++ } // namespace QSSGRenderResult
++
++ enum class QSSGLayerRenderPreparationResultFlag
++ {
++ // Was the data in this layer dirty (meaning re-render to texture, possibly)
++ WasLayerDataDirty = 1 << 0,
++
++ // Was the data in this layer dirty *or* this layer *or* any effect dirty.
++ WasDirty = 1 << 1,
++
++ RequiresDepthTexture = 1 << 2,
++
++ // SSAO should be done in a separate pass
++ // Note that having an AO pass necessitates a DepthTexture so this flag should
++ // never be set without the RequiresDepthTexture flag as well.
++ RequiresSsaoPass = 1 << 3,
++
++ // if some light cause shadow
++ // we need a separate per light shadow map pass
++ RequiresShadowMapPass = 1 << 4,
++
++ RequiresScreenTexture = 1 << 5,
++
++ // set together with RequiresScreenTexture when SCREEN_MIP_TEXTURE is used
++ RequiresMipmapsForScreenTexture = 1 << 6,
++
++ // Set when material has custom blend mode(not SourceOver)
++ MaterialHasCustomBlendMode = 1 << 7,
++
++ // Set when multisampled depth texture is required
++ RequiresDepthTextureMS = 1 << 8,
++
++ RequiresNormalTexture = 1 << 9,
++
++ RequiresMotionVectorPass = 1 << 10,
++
++ RequiresSkyMaterialPass = 1 << 11
++ };
++
++ struct QSSGLayerRenderPreparationResultFlags : public QFlags<QSSGLayerRenderPreparationResultFlag>
++ {
++ bool wasLayerDataDirty() const
++ {
++ return this->operator&(QSSGLayerRenderPreparationResultFlag::WasLayerDataDirty);
++ }
++ void setLayerDataDirty(bool inValue)
++ {
++ setFlag(QSSGLayerRenderPreparationResultFlag::WasLayerDataDirty, inValue);
++ }
++
++ bool wasDirty() const { return this->operator&(QSSGLayerRenderPreparationResultFlag::WasDirty); }
++ void setWasDirty(bool inValue) { setFlag(QSSGLayerRenderPreparationResultFlag::WasDirty, inValue); }
++
++ bool requiresDepthTexture() const
++ {
++ return this->operator&(QSSGLayerRenderPreparationResultFlag::RequiresDepthTexture);
++ }
++ void setRequiresDepthTexture(bool inValue)
++ {
++ setFlag(QSSGLayerRenderPreparationResultFlag::RequiresDepthTexture, inValue);
++ }
++
++ bool requiresDepthTextureMS() const
++ {
++ return this->operator&(QSSGLayerRenderPreparationResultFlag::RequiresDepthTextureMS);
++ }
++ void setRequiresDepthTextureMS(bool inValue)
++ {
++ setFlag(QSSGLayerRenderPreparationResultFlag::RequiresDepthTextureMS, inValue);
++ }
++
++ bool requiresSsaoPass() const { return this->operator&(QSSGLayerRenderPreparationResultFlag::RequiresSsaoPass); }
++ void setRequiresSsaoPass(bool inValue)
++ {
++ setFlag(QSSGLayerRenderPreparationResultFlag::RequiresSsaoPass, inValue);
++ }
++
++ bool requiresShadowMapPass() const
++ {
++ return this->operator&(QSSGLayerRenderPreparationResultFlag::RequiresShadowMapPass);
++ }
++ void setRequiresShadowMapPass(bool inValue)
++ {
++ setFlag(QSSGLayerRenderPreparationResultFlag::RequiresShadowMapPass, inValue);
++ }
++
++ bool requiresScreenTexture() const
++ {
++ return this->operator&(QSSGLayerRenderPreparationResultFlag::RequiresScreenTexture);
++ }
++ void setRequiresScreenTexture(bool inValue)
++ {
++ setFlag(QSSGLayerRenderPreparationResultFlag::RequiresScreenTexture, inValue);
++ }
++
++ bool requiresMipmapsForScreenTexture() const
++ {
++ return this->operator&(QSSGLayerRenderPreparationResultFlag::RequiresMipmapsForScreenTexture);
++ }
++ void setRequiresMipmapsForScreenTexture(bool inValue)
++ {
++ setFlag(QSSGLayerRenderPreparationResultFlag::RequiresMipmapsForScreenTexture, inValue);
++ }
++
++ bool hasCustomBlendMode() const
++ {
++ return this->operator&(QSSGLayerRenderPreparationResultFlag::MaterialHasCustomBlendMode);
++ }
++ void setHasCustomBlendMode(bool inValue)
++ {
++ setFlag(QSSGLayerRenderPreparationResultFlag::MaterialHasCustomBlendMode, inValue);
++ }
++
++ bool requiresNormalTexture() const
++ {
++ return this->operator&(QSSGLayerRenderPreparationResultFlag::RequiresNormalTexture);
++ }
++ void setRequiresNormalTexture(bool inValue)
++ {
++ setFlag(QSSGLayerRenderPreparationResultFlag::RequiresNormalTexture, inValue);
++ }
++
++ bool requiresMotionVectorPass() const
++ {
++ return this->operator&(QSSGLayerRenderPreparationResultFlag::RequiresMotionVectorPass);
++ }
++ void setRequiresMotionVectorPass(bool inValue)
++ {
++ setFlag(QSSGLayerRenderPreparationResultFlag::RequiresMotionVectorPass, inValue);
++ }
++
++ bool requiresSkyMaterialPass() const
++ {
++ return this->operator&(QSSGLayerRenderPreparationResultFlag::RequiresSkyMaterialPass);
++ }
++ void setRequiresSkyMaterialPass(bool inValue)
++ {
++ setFlag(QSSGLayerRenderPreparationResultFlag::RequiresSkyMaterialPass, inValue);
++ }
++ };
++
++ class QSSGLayerRenderPreparationResult
++ {
++ public:
++ enum class State : quint8
++ {
++ Null = 0,
++ DataPrep,
++ Done,
++ };
++
++ QSSGLayerRenderPreparationResult() = default;
++ QSSGLayerRenderPreparationResult(const QRectF &inViewport, QSSGRenderLayer &inLayer);
++
++ void setState(State state) { m_state = state; }
++ State getState() const { return m_state; }
++ bool isNull() const { return !layer && m_state == State::Null; }
++ bool isLayerVisible() const;
++ QSize textureDimensions() const;
++ QRectF getViewport() const { return viewport; }
++ const QSSGLayerRenderPreparationResultFlags &getFlags() const { return flags; }
++ QSSGRenderLayer *getLayer() const { return layer; }
++
++ private:
++ friend class QSSGLayerRenderData;
++ friend class QSSGFrameData;
++
++ QSSGLayerRenderPreparationResultFlags flags;
++ QRectF viewport;
++ QSSGRenderLayer *layer = nullptr;
++ State m_state = State::Null;
++ };
++
++ struct QSSGDefaultMaterialPreparationResult
++ {
++ QSSGRenderableImage *firstImage;
++ float opacity;
++ QSSGRenderableObjectFlags renderableFlags;
++ QSSGShaderDefaultMaterialKey materialKey;
++ bool dirty;
++
++ explicit QSSGDefaultMaterialPreparationResult(QSSGShaderDefaultMaterialKey inMaterialKey);
++ };
++
++ struct QSSGBakedLightingModel
++ {
++ QSSGBakedLightingModel(const QSSGRenderModel *model, const QVector<QSSGRenderableObjectHandle> &renderables)
++ : model(model),
++ renderables(renderables)
++ { }
++
++ const QSSGRenderModel *model;
++ QVector<QSSGRenderableObjectHandle> renderables;
++ };
++
++ struct QSSGOITRenderContext
++ {
++ QRhiTextureRenderTarget *oitRenderTarget = nullptr;
++ QRhiRenderPassDescriptor *renderPassDescriptor = nullptr;
++ QRhiTexture *copyTexture = nullptr;
++ QRhiBuffer *aBuffer = nullptr;
++ QRhiBuffer *auxBuffer = nullptr;
++ QRhiBuffer *counterBuffer = nullptr;
++ QRhiResourceUpdateBatch *rub = nullptr;
++ QList<QRhiReadbackResult *> completedResults;
++ QRhiReadbackResult *pendingResult = nullptr;
++ void reset()
++ {
++ delete oitRenderTarget;
++ delete renderPassDescriptor;
++ delete copyTexture;
++ delete aBuffer;
++ delete auxBuffer;
++ delete counterBuffer;
++ for (const auto *result : std::as_const(completedResults))
++ delete result;
++ completedResults.clear();
++ if (rub) {
++ rub->release();
++ rub = nullptr;
++ delete pendingResult;
++ } else if (pendingResult) {
++ // Still in flight: QRhi writes to it later, so the completion callback takes ownership.
++ pendingResult->completed = [result = pendingResult] { delete result; };
++ }
++ oitRenderTarget = nullptr;
++ renderPassDescriptor = nullptr;
++ copyTexture = nullptr;
++ aBuffer = nullptr;
++ auxBuffer = nullptr;
++ counterBuffer = nullptr;
++ pendingResult = nullptr;
++ }
++ };
++
++ class Q_QUICK3DRUNTIMERENDER_EXPORT QSSGLayerRenderData
++ {
++ public:
++ enum Enum {
++ MAX_AA_LEVELS = 8,
++ MAX_TEMPORAL_AA_LEVELS = 2,
++ };
++
++ using InstanceTransforms = QSSGGlobalRenderNodeData::InstanceTransforms;
++ using ModelViewProjections = QSSGRenderModelData::ModelViewProjections;
++
++ using QSSGModelsView = QSSGDataView<QSSGRenderModel *>;
++ using QSSGParticlesView = QSSGDataView<QSSGRenderParticles *>;
++ using QSSGItem2DsView = QSSGDataView<QSSGRenderItem2D *>;
++ using QSSGCamerasView = QSSGDataView<QSSGRenderCamera *>;
++ using QSSGLightsView = QSSGDataView<QSSGRenderLight *>;
++ using QSSGReflectionProbesView = QSSGDataView<QSSGRenderReflectionProbe *>;
++ using QSSGNonCategorizedView = QSSGDataView<QSSGRenderNode *>;
++
++ using RenderableFilter = std::function<bool(QSSGModelContext *)>;
++
++ QSSGLayerRenderData(QSSGRenderLayer &inLayer, QSSGRenderer &inRenderer);
++ ~QSSGLayerRenderData();
++
++ typedef QVector<QSSGModelContext *> TModelContextPtrList;
++ using RenderableNodeEntries = QVector<QSSGRenderableNodeEntry>;
++ using RenderableItem2DEntries = QVector<QSSGRenderItem2D *>;
++
++ void prepareImageForRender(QSSGRenderImage &inImage,
++ QSSGRenderableImage::Type inMapType,
++ QSSGRenderableImage *&ioFirstImage,
++ QSSGRenderableImage *&ioNextImage,
++ QSSGRenderableObjectFlags &ioFlags,
++ QSSGShaderDefaultMaterialKey &ioGeneratedShaderKey,
++ quint32 inImageIndex, QSSGRenderDefaultMaterial *inMaterial = nullptr);
++
++ void setVertexInputPresence(const QSSGRenderableObjectFlags &renderableFlags,
++ QSSGShaderDefaultMaterialKey &key);
++
++ static void prepareModelBoneTextures(const QSSGRenderContextInterface &contextInterface,
++ const RenderableNodeEntries &renderableModels);
++
++ // Helper functions used during PrepareForRender and PrepareAndRender
++ // Updates lights with model receivesShadows. Do not pass globalLights.
++ bool prepareModelsForRender(QSSGRenderContextInterface &ctx,
++ const RenderableNodeEntries &renderableModels,
++ QSSGLayerRenderPreparationResultFlags &ioFlags,
++ const QSSGRenderCameraList &allCameras,
++ const QSSGRenderCameraDataList &allCameraData,
++ TModelContextPtrList &modelContexts,
++ QSSGRenderableObjectList &opaqueObjects,
++ QSSGRenderableObjectList &transparentObjects,
++ QSSGRenderableObjectList &screenTextureObjects,
++ float lodThreshold = 0.0f);
++ bool prepareParticlesForRender(const RenderableNodeEntries &renderableParticles, const QSSGRenderCameraData &cameraData, QSSGLayerRenderPreparationResultFlags &ioFlags);
++
++ void prepareResourceLoaders();
++
++ void prepareForRender();
++ // Helper function used during prepareForRender
++ void prepareReflectionProbesForRender();
++
++ [[nodiscard]] static qsizetype frustumCullingInline(const QSSGClippingFrustum &clipFrustum, QSSGRenderableObjectList &renderables);
++
++ [[nodiscard]] static qsizetype filterLayerMaskInline(quint32 layerMask, QSSGRenderableObjectList &renderables);
++
++
++ // Per-frame cache of renderable objects post-sort (for the MAIN rendering camera, i.e., don't use these lists for rendering from a different camera).
++ const QSSGRenderableObjectList &getSortedOpaqueRenderableObjects(const QSSGRenderCamera &camera, size_t index = 0, quint32 layerMask = 0xFFFFFFFF);
++ // If layer depth test is false, this may also contain opaque objects.
++ const QSSGRenderableObjectList &getSortedTransparentRenderableObjects(const QSSGRenderCamera &camera, size_t index = 0, quint32 layerMask = 0xFFFFFFFF);
++ const QSSGRenderableObjectList &getSortedScreenTextureRenderableObjects(const QSSGRenderCamera &camera, size_t index = 0);
++ const QVector<QSSGBakedLightingModel> &getSortedBakedLightingModels();
++ const RenderableItem2DEntries &getRenderableItem2Ds();
++ const QSSGRenderableObjectList &getSortedRenderedDepthWriteObjects(const QSSGRenderCamera &camera, size_t index = 0);
++ const QSSGRenderableObjectList &getSortedrenderedOpaqueDepthPrepassObjects(const QSSGRenderCamera &camera, size_t index = 0);
++ void getShadowCastingObjects(const QSSGRenderCamera &camera,
++ QSSGRenderableObjectList &outObjects,
++ QSSGBounds3 &outBoundsCasting,
++ QSSGBounds3 &outBoundsReceiving);
++
++ void resetForFrame();
++
++ QSSGFrameData &getFrameData();
++
++ ShadowMapPass shadowMapPass;
++ ReflectionMapPass reflectionMapPass;
++ ZPrePassPass zPrePassPass;
++ SSAOMapPass ssaoMapPass;
++ DepthMapPass depthMapPass;
++ DepthMapPass depthMapPassMS;
++ SkyMaterialPass skyMaterialPass;
++ ScreenMapPass screenMapPass;
++ ScreenReflectionPass reflectionPass;
++ Item2DPass item2DPass;
++ SkyboxPass skyboxPass;
++ SkyMaterialBackgroundPass skyMaterialBackgroundPass;
++ SkyboxCubeMapPass skyboxCubeMapPass;
++ UserRenderPass userRenderPasses;
++ static constexpr size_t USERPASSES = 3; // See QSSGRenderLayer::RenderExtensionMode::Count
++ UserExtensionPass userPasses[USERPASSES];
++ OpaquePass opaquePass;
++ TransparentPass transparentPass;
++ OITRenderPass oitRenderPass;
++ OITCompositePass oitCompositePass;
++ InfiniteGridPass infiniteGridPass;
++ DebugDrawPass debugDrawPass;
++ NormalPass normalPass;
++ MotionVectorMapPass motionVectorMapPass;
++
++ // Built-in passes
++ QVarLengthArray<QSSGRenderPass *, 16> activePasses;
++
++ QSSGRenderLayer &layer;
++ QSSGRenderer *renderer = nullptr;
++ // List of nodes we can render, not all may be active. Found by doing a depth-first
++ // search through m_FirstChild if length is zero.
++
++ using LayerNodes = std::vector<QSSGRenderNode *>;
++ QSSGGlobalRenderNodeData::LayerNodeView layerNodes;
++
++ // renderableNodes have all lights, but properties configured for specific node
++ RenderableNodeEntries renderableModels;
++ RenderableNodeEntries renderableParticles;
++
++ // Views into the collected nodes (unsorted)
++ class NodeCollection
++ {
++ Q_DISABLE_COPY(NodeCollection)
++ public:
++ NodeCollection() = default;
++
++ QSSGModelsView modelsView;
++ QSSGParticlesView particlesView;
++ QSSGItem2DsView item2DsView;
++ QSSGCamerasView camerasView;
++ QSSGLightsView lightsView;
++ QSSGReflectionProbesView reflectionProbesView;
++ QSSGNonCategorizedView nonCategorizedView;
++ LayerNodes layerNodesCategorized;
++ };
++ NodeCollection nodeCollection;
++ // FIXME: Convenience for now
++ QSSGModelsView &modelsView = nodeCollection.modelsView;
++ QSSGParticlesView &particlesView = nodeCollection.particlesView;
++ QSSGItem2DsView &item2DsView = nodeCollection.item2DsView;
++ QSSGCamerasView &camerasView = nodeCollection.camerasView;
++ QSSGLightsView &lightsView = nodeCollection.lightsView;
++ QSSGReflectionProbesView &reflectionProbesView = nodeCollection.reflectionProbesView;
++ QSSGNonCategorizedView &nonCategorizedView = nodeCollection.nonCategorizedView;
++
++ // Results of prepare for render.
++ QSSGRenderCameraList renderedCameras; // multiple items with multiview, one otherwise (or zero if no cameras at all)
++ QSSGShaderLightList globalLights; // All non-scoped lights
++
++ QVector<QSSGBakedLightingModel> bakedLightingModels;
++ // Sorted lists of the rendered objects. There may be other transforms applied so
++ // it is simplest to duplicate the lists.
++ QVector<QSSGBakedLightingModel> renderedBakedLightingModels;
++ RenderableItem2DEntries renderedItem2Ds;
++
++ QSSGLayerRenderPreparationResult layerPrepResult;
++ std::optional<QSSGRenderCameraDataList> renderedCameraData;
++
++ TModelContextPtrList modelContexts;
++
++ QQsbCollection::EntryMap m_particleShaderEntries;
++
++ bool nonExplicitCameraWithLayerMaskWarningShown = false;
++ bool tooManyLightsWarningShown = false;
++ bool tooManyDirectionalLightsWarningShown = false;
++ bool oitWarningUnsupportedShown = false;
++ bool oitWarningInvalidBlendModeShown = false;
++ bool orderIndependentTransparencyEnabled = false;
++ bool disableMainPasses = true;
++
++ std::unique_ptr<QSSGLightmapBaker> lightmapBaker = nullptr;
++
++ QSSGShaderFeatures getShaderFeatures() const { return features; }
++ QSSGRhiGraphicsPipelineState getPipelineState() const { return ps; }
++
++ void initializeLightmapBaking(QSSGLightmapBaker::Context &ctx);
++ void maybeProcessLightmapBaking();
++
++ [[nodiscard]] QSSGRenderGraphObject *getCamera(QSSGCameraId id) const;
++ [[nodiscard]] QSSGRenderCamera *activeCamera() const { return !renderedCameras.isEmpty() ? renderedCameras[0] : nullptr; }
++
++ [[nodiscard]] QSSGRenderCameraData getCameraRenderData(const QSSGRenderCamera *camera);
++ [[nodiscard]] QSSGRenderCameraData getCameraRenderData(const QSSGRenderCamera *camera) const;
++
++ void setLightmapTexture(const QSSGModelContext &modelContext, QRhiTexture *lightmapTexture);
++ [[nodiscard]] QRhiTexture *getLightmapTexture(const QSSGModelContext &modelContext) const;
++
++ void setBonemapTexture(const QSSGModelContext &modelContext, QRhiTexture *bonemapTexture);
++ [[nodiscard]] QRhiTexture *getBonemapTexture(const QSSGModelContext &modelContext) const;
++
++ [[nodiscard]] QSSGRenderContextInterface *contextInterface() const;
++ // Note: temp. API to report the state of the z-prepass step
++ [[nodiscard]] bool isZPrePassActive() const { return zPrePassActive; }
++ void setZPrePassPrepResult(bool res) { zPrePassActive = res; }
++
++ // Exposed as const, as we often need to use this to look-up values from a specific key.
++ [[nodiscard]] const QSSGShaderDefaultMaterialKeyProperties &getDefaultMaterialPropertyTable() const
++ {
++ return defaultMaterialShaderKeyProperties;
++ }
++ [[nodiscard]] const QSSGShaderParticleMaterialKeyProperties &getParticleMaterialPropertyTable() const
++ {
++ return particleMaterialShaderKeyProperties;
++ }
++
++ struct GlobalRenderProperties
++ {
++ bool isYUpInFramebuffer = true;
++ bool isYUpInNDC = true;
++ bool isClipDepthZeroToOne = true;
++ };
++
++ [[nodiscard]] static GlobalRenderProperties globalRenderProperties(const QSSGRenderContextInterface &ctx);
++
++ // Temp. API. Ideally there shouldn't be a reason for anyone to hold onto these,
++ // but we follow the existing pattern for now.
++ const QSSGRenderShadowMapPtr &requestShadowMapManager();
++ const QSSGRenderReflectionMapPtr &requestReflectionMapManager();
++ const QSSGRenderSkyMaterialManagerPtr &requestSkyMaterialManager();
++ const QSSGUserRenderPassManagerPtr &requestUserRenderPassManager();
++ const QSSGRenderMotionVectorMapPtr &requestMotionVectorMapManager();
++ const QSSGRenderShadowMapPtr &getShadowMapManager() const { return shadowMapManager; }
++ const QSSGRenderReflectionMapPtr &getReflectionMapManager() const { return reflectionMapManager; }
++ const QSSGRenderSkyMaterialManagerPtr &getSkyMaterialManager() const { return skyMaterialManager; }
++ const QSSGUserRenderPassManagerPtr &getUserRenderPassManager() const { return userRenderPassManager; }
++ const QSSGRenderMotionVectorMapPtr &getMotionvectorMapManager() const { return motionVectorMapManager; }
++
++ QSSGOITRenderContext &getOitRenderContext() { return oitRenderContext; }
++ const QSSGOITRenderContext &getOitRenderContextConst() const { return oitRenderContext; }
++
++ static bool prepareInstancing(QSSGRhiContext *rhiCtx,
++ QSSGSubsetRenderable *renderable,
++ const QVector3D &cameraDirection,
++ const QVector3D &cameraPosition,
++ float minThreshold,
++ float maxThreshold);
++
++ [[nodiscard]] QSSGRhiRenderableTexture *getRenderResult(QSSGRenderResult::Key id) { return &renderResults[size_t(id)]; }
++ [[nodiscard]] const QSSGRhiRenderableTexture *getRenderResult(QSSGRenderResult::Key id) const { return &renderResults[size_t(id)]; }
++
++ QSSGRenderImageTexture skyMaterialTexture; // Always a cubemap, might be IBL
++ void resolveLayerIblTexture(QSSGPassKey passKey);
++
++ [[nodiscard]] static inline const std::unique_ptr<QSSGPerFrameAllocator> &perFrameAllocator(QSSGRenderContextInterface &ctx);
++ [[nodiscard]] static inline QSSGLayerRenderData *getCurrent(const QSSGRenderer &renderer) { return renderer.m_currentLayer; }
++ void saveRenderState(const QSSGRenderer &renderer);
++ void restoreRenderState(QSSGRenderer &renderer);
++
++ static void setTonemapFeatures(QSSGShaderFeatures &features, QSSGRenderLayer::TonemapMode tonemapMode)
++ {
++ features.set(QSSGShaderFeatures::Feature::LinearTonemapping,
++ tonemapMode == QSSGRenderLayer::TonemapMode::Linear);
++ features.set(QSSGShaderFeatures::Feature::AcesTonemapping,
++ tonemapMode == QSSGRenderLayer::TonemapMode::Aces);
++ features.set(QSSGShaderFeatures::Feature::HejlDawsonTonemapping,
++ tonemapMode == QSSGRenderLayer::TonemapMode::HejlDawson);
++ features.set(QSSGShaderFeatures::Feature::FilmicTonemapping,
++ tonemapMode == QSSGRenderLayer::TonemapMode::Filmic);
++ features.set(QSSGShaderFeatures::Feature::ForceIblExposure,
++ tonemapMode == QSSGRenderLayer::TonemapMode::Custom);
++ }
++
++ QSSGPrepContextId getOrCreateExtensionContext(const QSSGRenderExtension &ext,
++ QSSGRenderCamera *camera = nullptr,
++ quint32 slot = 0);
++
++ // Model API
++ QSSGRenderablesId createRenderables(QSSGPrepContextId prepId, const QSSGNodeIdList &nodes, QSSGRenderHelpers::CreateFlags createFlags);
++ void setGlobalTransform(QSSGRenderablesId renderablesId, const QSSGRenderModel &model, const QMatrix4x4 &mvp);
++ QMatrix4x4 getGlobalTransform(QSSGPrepContextId prepId, const QSSGRenderModel &model);
++ void setGlobalOpacity(QSSGRenderablesId renderablesId, const QSSGRenderModel &model, float opacity);
++ float getGlobalOpacity(QSSGPrepContextId prepId, const QSSGRenderModel &model);
++ [[nodiscard]] QMatrix4x4 getModelMvps(QSSGPrepContextId prepId, const QSSGRenderModel &model) const;
++ void setModelMaterials(QSSGRenderablesId renderablesId, const QSSGRenderModel &model, const QList<QSSGResourceId> &materials);
++ void setModelMaterials(const QSSGRenderablesId renderablesId, const QList<QSSGResourceId> &materials);
++ [[nodiscard]] QSSGPrepResultId prepareModelsForRender(QSSGRenderContextInterface &contextInterface,
++ QSSGPrepContextId prepId,
++ QSSGRenderablesId renderablesId,
++ float lodThreshold);
++
++ // Convenience wrappers for getting values from the node, model store.
++ [[nodiscard]] QMatrix4x4 getGlobalTransform(QSSGRenderNodeHandle h, const QMatrix4x4 &defaultValue) const
++ {
++ return nodeData->getGlobalTransform(h, defaultValue);
++ }
++ [[nodiscard]] QMatrix4x4 getGlobalTransform(QSSGRenderNodeHandle h) const
++ {
++ return nodeData->getGlobalTransform(h, QMatrix4x4());
++ }
++ [[nodiscard]] QMatrix4x4 getGlobalTransform(const QSSGRenderNode &node) const
++ {
++ return nodeData->getGlobalTransform(node.h, node.localTransform);
++ }
++ [[nodiscard]] QMatrix3x3 getNormalMatrix(QSSGRenderNodeHandle h) const
++ {
++ return modelData->getNormalMatrix(h, QMatrix3x3(Qt::Uninitialized));
++ }
++ [[nodiscard]] QMatrix3x3 getNormalMatrix(const QSSGRenderModel &model) const
++ {
++ return modelData->getNormalMatrix(model);
++ }
++ [[nodiscard]] ModelViewProjections getModelMvps(QSSGRenderNodeHandle h) const
++ {
++ return modelData->getModelViewProjection(h);
++ }
++ [[nodiscard]] ModelViewProjections getModelMvps(const QSSGRenderModel &model) const
++ {
++ return modelData->getModelViewProjection(model);
++ }
++ [[nodiscard]] InstanceTransforms getInstanceTransforms(QSSGRenderNodeHandle h) const
++ {
++ return nodeData->getInstanceTransforms(h);
++ }
++ [[nodiscard]] InstanceTransforms getInstanceTransforms(const QSSGRenderNode &node) const
++ {
++ return nodeData->getInstanceTransforms(node.h);
++ }
++ [[nodiscard]] float getGlobalOpacity(QSSGRenderNodeHandle h, float defaultValue = 1.0f) const
++ {
++ return nodeData->getGlobalOpacity(h, defaultValue);
++ }
++ [[nodiscard]] float getGlobalOpacity(const QSSGRenderNode &node) const
++ {
++ return nodeData->getGlobalOpacity(node.h);
++ }
++
++ //
++ [[nodiscard]] QSSGRenderItem2DData::Item2DRenderer getItem2DRenderer(const QSSGRenderItem2D &item) const
++ {
++ return item2DData->getItem2DRenderer(item);
++ }
++
++ [[nodiscard]] ModelViewProjections getItem2DMvps(QSSGRenderNodeHandle h) const
++ {
++ return item2DData->getModelViewProjection(h);
++ }
++
++ [[nodiscard]] ModelViewProjections getItem2DMvps(const QSSGRenderItem2D &item) const
++ {
++ return item2DData->getModelViewProjection(item);
++ }
++
++
++ //
++ void prepareRenderables(QSSGRenderContextInterface &ctx,
++ QSSGPrepResultId prepId,
++ QRhiRenderPassDescriptor *renderPassDescriptor,
++ const QSSGRhiGraphicsPipelineState &ps,
++ QSSGRenderablesFilters filter);
++ void renderRenderables(QSSGRenderContextInterface &ctx,
++ QSSGPrepResultId prepId);
++
++ static bool calculateGlobalVariables(QSSGRenderNode &node,
++ std::vector<QMatrix4x4> &globalTransforms,
++ std::vector<float> &globalOpacities);
++
++ QSSGRenderCameraData getCameraDataImpl(const QSSGRenderCamera *camera) const;
++
++ static QSSGNodeIdList filter(const QSSGGlobalRenderNodeData::LayerNodeView &layerNodes,
++ quint32 layerMask,
++ quint32 typeMask);
++
++ [[nodiscard]] static QSSGLayerRenderData *getCurrent(const QSSGFrameData &data) { return data.getCurrent(); }
++
++ QSSGDefaultMaterialPreparationResult prepareDefaultMaterialForRender(QSSGRenderDefaultMaterial &inMaterial,
++ QSSGRenderableObjectFlags &inExistingFlags,
++ float inOpacity,
++ bool hasAnyLights,
++ bool anyLightHasShadows,
++ QSSGLayerRenderPreparationResultFlags &ioFlags);
++
++ QSSGDefaultMaterialPreparationResult prepareCustomMaterialForRender(QSSGRenderCustomMaterial &inMaterial,
++ QSSGRenderableObjectFlags &inExistingFlags,
++ float inOpacity, bool alreadyDirty,
++ bool hasAnyLights,
++ bool anyLightHasShadows,
++ QSSGLayerRenderPreparationResultFlags &ioFlags);
++
++ static void categorizeAndFilterNodes(const QSSGGlobalRenderNodeData::LayerNodeView &layerNodes,
++ QSSGLayerRenderData::NodeCollection &nodeCollection,
++ quint32 layerMask);
++ void updateFilteredLayerNodes(quint32 layerMask);
++ private:
++ friend class QSSGRenderer;
++ friend class QSSGRendererPrivate;
++ friend class QSSGFrameData;
++ friend class QSSGModelHelpers;
++ friend class QSSGRenderHelpers;
++ friend class QSSGParticleRenderer;
++
++ class ExtensionContext
++ {
++ public:
++ explicit ExtensionContext() = default;
++ explicit ExtensionContext(const QSSGRenderExtension &ownerExt, QSSGRenderCamera *cam, size_t idx, quint32 slot)
++ : owner(&ownerExt), camera(cam), ps{}, filter{0}, index(idx), slot(slot)
++ { }
++ const QSSGRenderExtension *owner = nullptr;
++ QSSGRenderCamera *camera = nullptr;
++ QSSGRhiGraphicsPipelineState ps[3] {};
++ QSSGRenderablesFilters filter { 0 };
++ size_t index = 0; // index into the model store
++ quint32 slot = 0;
++ };
++
++ std::vector<ExtensionContext> extContexts { ExtensionContext{ /* 0 - Always available */ } };
++ std::vector<RenderableNodeEntries> renderableModelStore { RenderableNodeEntries{ /* 0 - Always available */ } };
++ std::vector<TModelContextPtrList> modelContextStore { TModelContextPtrList{ /* 0 - Always available */ }};
++ std::vector<QSSGRenderableObjectList> renderableObjectStore { QSSGRenderableObjectList{ /* 0 - Always available */ }};
++ std::vector<QSSGRenderableObjectList> opaqueObjectStore { QSSGRenderableObjectList{ /* 0 - Always available */ }};
++ std::vector<QSSGRenderableObjectList> transparentObjectStore { QSSGRenderableObjectList{ /* 0 - Always available */ }};
++ std::vector<QSSGRenderableObjectList> screenTextureObjectStore { QSSGRenderableObjectList{ /* 0 - Always available */ }};
++
++ std::shared_ptr<QSSGGlobalRenderNodeData> nodeData;
++ std::unique_ptr<QSSGRenderModelData> modelData;
++ std::unique_ptr<QSSGRenderItem2DData> item2DData;
++
++ // Soreted cache (per camera and extension)
++ using CameraKey = std::pair<const QSSGRenderCamera*, uint32_t>;
++
++ struct CameraKeyHash {
++ std::size_t operator()(const CameraKey& k) const noexcept {
++ // Hash combine: pointer hash ^ (uint hash shifted)
++ return std::hash<const QSSGRenderCamera*>()(k.first) ^
++ (std::hash<uint32_t>()(k.second) << 1);
++ }
++ };
++
++ struct CameraKeyEq {
++ bool operator()(const CameraKey& a, const CameraKey& b) const noexcept {
++ return a.first == b.first && a.second == b.second;
++ }
++ };
++
++ using PerCameraCache = std::unordered_map<CameraKey, QSSGRenderableObjectList, CameraKeyHash, CameraKeyEq>;
++ std::vector<PerCameraCache> sortedOpaqueObjectCache { PerCameraCache{ /* 0 - Always available */ } };
++ std::vector<PerCameraCache> sortedTransparentObjectCache { PerCameraCache{ /* 0 - Always available */ } };
++ std::vector<PerCameraCache> sortedScreenTextureObjectCache { PerCameraCache{ /* 0 - Always available */ } };
++ std::vector<PerCameraCache> sortedOpaqueDepthPrepassCache { PerCameraCache{ /* 0 - Always available */ } };
++ std::vector<PerCameraCache> sortedDepthWriteCache { PerCameraCache{ /* 0 - Always available */ } };
++
++ [[nodiscard]] const QSSGRenderCameraDataList &getCachedCameraDatas();
++ void ensureCachedCameraDatas();
++ void updateSortedDepthObjectsListImp(const QSSGRenderCamera &camera, size_t index);
++
++ static void prepareModelMaterials(RenderableNodeEntries &renderableModels, bool cullUnrenderables);
++ static void prepareModelMaterials(const RenderableNodeEntries::ConstIterator &begin,
++ const RenderableNodeEntries::ConstIterator &end);
++
++ // Persistent data
++ QHash<QSSGShaderMapKey, QSSGRhiShaderPipelinePtr> shaderMap;
++ QHash<QSSGParticleShaderMapKey, QSSGRhiShaderPipelinePtr> particleShaderMap;
++
++ // Cached buffer.
++ QByteArray generatedShaderString;
++
++ // Saved render state (for sublayers)
++ struct SavedRenderState
++ {
++ QRect viewport;
++ QRect scissorRect;
++ float dpr = 1.0;
++ // The main pass state on the shared rhi context. A sublayer can contain a
++ // nested 3D scene, e.g. a View3D inside an Item2D, which renders to its own
++ // target and leaves its own state behind on the context. The passes of this
++ // layer that come after would otherwise build their graphics pipelines for
++ // that target's render pass and sample count instead of their own.
++ QRhiRenderPassDescriptor *mainRenderPassDescriptor = nullptr;
++ QRhiRenderTarget *renderTarget = nullptr;
++ QRhiCommandBuffer *commandBuffer = nullptr;
++ int mainPassSampleCount = 1;
++ int mainPassViewCount = 1;
++ };
++
++ std::optional<SavedRenderState> savedRenderState;
++
++ // Note: Re-used to avoid expensive initialization.
++ // - Should be revisit, as we can do better.
++ QSSGShaderDefaultMaterialKeyProperties defaultMaterialShaderKeyProperties;
++ QSSGShaderParticleMaterialKeyProperties particleMaterialShaderKeyProperties;
++ QSSGFrameData frameData;
++ QSSGRhiGraphicsPipelineState ps; // Base pipleline state
++ QSSGShaderFeatures features; // Base feature set
++ QSSGRenderNodeVersionType version = 0;
++ QSSGRenderNodeVersionType stateGeneration = 0; // Last seen QSSGGlobalRenderNodeData::stateGeneration()
++ bool particlesEnabled = true;
++ bool hasDepthWriteObjects = false;
++ bool zPrePassActive = false;
++ // NOTE: For the time being we need to keep track of extensions modifying the renderables
++ // because then we need to reset the lists.
++ // FIXME: This should be revisited, as we can do better (hence the verbose name).
++ bool renderablesModifiedByExtension = false;
++ enum class DepthPrepassObject : quint8
++ {
++ None = 0x0,
++ ScreenTexture = 0x1,
++ Transparent = 0x2,
++ Opaque = 0x4
++ };
++ using DepthPrepassObjectStateT = std::underlying_type_t<DepthPrepassObject>;
++ DepthPrepassObjectStateT depthPrepassObjectsState { DepthPrepassObjectStateT(DepthPrepassObject::None) };
++ QSSGRenderShadowMapPtr shadowMapManager;
++ QSSGRenderReflectionMapPtr reflectionMapManager;
++ QSSGRenderSkyMaterialManagerPtr skyMaterialManager;
++ QSSGUserRenderPassManagerPtr userRenderPassManager;
++ QSSGRenderMotionVectorMapPtr motionVectorMapManager;
++ QHash<const QSSGModelContext *, QRhiTexture *> lightmapTextures;
++ QHash<const QSSGModelContext *, QRhiTexture *> bonemapTextures;
++ QSSGRhiRenderableTexture renderResults[size_t(QSSGRenderResult::Key::RenderResultCount)] {};
++ QSSGOITRenderContext oitRenderContext;
++ };
++
++ QT_END_NAMESPACE
++
++ #endif // QSSG_LAYER_RENDER_DATA_H
++