1 /****************************************************************************
2 **
3 ** Copyright (C) 2008-2012 NVIDIA Corporation.
4 ** Copyright (C) 2019 The Qt Company Ltd.
5 ** Contact: https://www.qt.io/licensing/
6 **
7 ** This file is part of Qt Quick 3D.
8 **
9 ** $QT_BEGIN_LICENSE:GPL$
10 ** Commercial License Usage
11 ** Licensees holding valid commercial Qt licenses may use this file in
12 ** accordance with the commercial license agreement provided with the
13 ** Software or, alternatively, in accordance with the terms contained in
14 ** a written agreement between you and The Qt Company. For licensing terms
15 ** and conditions see https://www.qt.io/terms-conditions. For further
16 ** information use the contact form at https://www.qt.io/contact-us.
17 **
18 ** GNU General Public License Usage
19 ** Alternatively, this file may be used under the terms of the GNU
20 ** General Public License version 3 or (at your option) any later version
21 ** approved by the KDE Free Qt Foundation. The licenses are as published by
22 ** the Free Software Foundation and appearing in the file LICENSE.GPL3
23 ** included in the packaging of this file. Please review the following
24 ** information to ensure the GNU General Public License requirements will
25 ** be met: https://www.gnu.org/licenses/gpl-3.0.html.
26 **
27 ** $QT_END_LICENSE$
28 **
29 ****************************************************************************/
30 
31 
32 #include <QtQuick3DRuntimeRender/private/qssgrenderer_p.h>
33 #include <QtQuick3DRuntimeRender/private/qssgrendererimpl_p.h>
34 #include <QtQuick3DRuntimeRender/private/qssgrenderlayer_p.h>
35 #include <QtQuick3DRuntimeRender/private/qssgrendereffect_p.h>
36 #include <QtQuick3DRuntimeRender/private/qssgrenderlight_p.h>
37 #include <QtQuick3DRuntimeRender/private/qssgrendercamera_p.h>
38 #include <QtQuick3DRuntimeRender/private/qssgrendercontextcore_p.h>
39 #include <QtQuick3DRuntimeRender/private/qssgrenderresourcemanager_p.h>
40 #include <QtQuick3DRuntimeRender/private/qssgrendereffectsystem_p.h>
41 #include <QtQuick3DRender/private/qssgrenderframebuffer_p.h>
42 #include <QtQuick3DRender/private/qssgrenderrenderbuffer_p.h>
43 #include <QtQuick3DRuntimeRender/private/qssgrenderresourcebufferobjects_p.h>
44 #include <QtQuick3DUtils/private/qssgperftimer_p.h>
45 #include <QtQuick3DRuntimeRender/private/qssgrenderbuffermanager_p.h>
46 #include <QtQuick3DRuntimeRender/private/qssgrendercustommaterialsystem_p.h>
47 #include <QtQuick3DRuntimeRender/private/qssgrendershadercache_p.h>
48 #include <QtQuick3DRuntimeRender/private/qssgperframeallocator_p.h>
49 #include <QtQuick3DUtils/private/qssgutils_p.h>
50 #include <QtQuick3DRuntimeRender/private/qssgruntimerenderlogging_p.h>
51 
52 #ifdef Q_CC_MSVC
53 #pragma warning(disable : 4355)
54 #endif
55 
56 QT_BEGIN_NAMESPACE
57 
maybeQueueNodeForRender(QSSGRenderNode & inNode,QVector<QSSGRenderableNodeEntry> & outRenderables,QVector<QSSGRenderCamera * > & outCameras,QVector<QSSGRenderLight * > & outLights,quint32 & ioDFSIndex)58 static void maybeQueueNodeForRender(QSSGRenderNode &inNode,
59                                     QVector<QSSGRenderableNodeEntry> &outRenderables,
60                                     QVector<QSSGRenderCamera *> &outCameras,
61                                     QVector<QSSGRenderLight *> &outLights,
62                                     quint32 &ioDFSIndex)
63 {
64     ++ioDFSIndex;
65     inNode.dfsIndex = ioDFSIndex;
66     if (inNode.isRenderableType())
67         outRenderables.push_back(inNode);
68     else if (inNode.type == QSSGRenderGraphObject::Type::Camera)
69         outCameras.push_back(static_cast<QSSGRenderCamera *>(&inNode));
70     else if (inNode.type == QSSGRenderGraphObject::Type::Light)
71         outLights.push_back(static_cast<QSSGRenderLight *>(&inNode));
72 
73     for (QSSGRenderNode *theChild = inNode.firstChild; theChild != nullptr; theChild = theChild->nextSibling)
74         maybeQueueNodeForRender(*theChild, outRenderables, outCameras, outLights, ioDFSIndex);
75 }
76 
hasValidLightProbe(QSSGRenderImage * inLightProbeImage)77 static inline bool hasValidLightProbe(QSSGRenderImage *inLightProbeImage)
78 {
79     return inLightProbeImage && inLightProbeImage->m_textureData.m_texture;
80 }
81 
QSSGDefaultMaterialPreparationResult(QSSGShaderDefaultMaterialKey inKey)82 QSSGDefaultMaterialPreparationResult::QSSGDefaultMaterialPreparationResult(QSSGShaderDefaultMaterialKey inKey)
83     : firstImage(nullptr), opacity(1.0f), materialKey(inKey), dirty(false)
84 {
85 }
86 
87 #define MAX_AA_LEVELS 8
88 
QSSGLayerRenderPreparationData(QSSGRenderLayer & inLayer,const QSSGRef<QSSGRendererImpl> & inRenderer)89 QSSGLayerRenderPreparationData::QSSGLayerRenderPreparationData(QSSGRenderLayer &inLayer,
90                                                                    const QSSGRef<QSSGRendererImpl> &inRenderer)
91     : layer(inLayer)
92     , renderer(inRenderer)
93     , camera(nullptr)
94     , featuresDirty(true)
95     , featureSetHash(0)
96     , tooManyLightsError(false)
97 {
98 }
99 
100 QSSGLayerRenderPreparationData::~QSSGLayerRenderPreparationData() = default;
101 
setShaderFeature(const char * theStr,bool inValue)102 void QSSGLayerRenderPreparationData::setShaderFeature(const char *theStr, bool inValue)
103 {
104     auto iter = features.cbegin();
105     const auto end = features.cend();
106 
107     while (iter != end && iter->name != theStr)
108         ++iter;
109 
110     if (iter != end) {
111         if (iter->enabled != inValue) {
112             iter->enabled = inValue;
113             featuresDirty = true;
114             featureSetHash = 0;
115         }
116     } else {
117         features.push_back(QSSGShaderPreprocessorFeature{theStr, inValue});
118         featuresDirty = true;
119         featureSetHash = 0;
120     }
121 }
122 
getShaderFeatureSet()123 ShaderFeatureSetList QSSGLayerRenderPreparationData::getShaderFeatureSet()
124 {
125     if (featuresDirty) {
126         std::sort(features.begin(), features.end());
127         featuresDirty = false;
128     }
129     return features;
130 }
131 
getShaderFeatureSetHash()132 size_t QSSGLayerRenderPreparationData::getShaderFeatureSetHash()
133 {
134     if (!featureSetHash)
135         featureSetHash = hashShaderFeatureSet(getShaderFeatureSet());
136     return featureSetHash;
137 }
138 
createShadowMapManager()139 void QSSGLayerRenderPreparationData::createShadowMapManager()
140 {
141     shadowMapManager = QSSGRenderShadowMap::create(renderer->contextInterface());
142 }
143 
getCameraDirection()144 QVector3D QSSGLayerRenderPreparationData::getCameraDirection()
145 {
146     if (!cameraDirection.hasValue()) {
147         if (camera)
148             cameraDirection = camera->getScalingCorrectDirection();
149         else
150             cameraDirection = QVector3D(0, 0, -1);
151     }
152     return *cameraDirection;
153 }
154 
155 // Per-frame cache of renderable objects post-sort.
getOpaqueRenderableObjects(bool performSort)156 const QVector<QSSGRenderableObjectHandle> &QSSGLayerRenderPreparationData::getOpaqueRenderableObjects(bool performSort)
157 {
158     if (!renderedOpaqueObjects.empty() || camera == nullptr)
159         return renderedOpaqueObjects;
160     if (layer.flags.testFlag(QSSGRenderLayer::Flag::LayerEnableDepthTest) && !opaqueObjects.empty()) {
161         QVector3D theCameraDirection(getCameraDirection());
162         QVector3D theCameraPosition = camera->getGlobalPos();
163         renderedOpaqueObjects = opaqueObjects;
164         // Setup the object's sorting information
165         for (int idx = 0, end = renderedOpaqueObjects.size(); idx < end; ++idx) {
166             QSSGRenderableObjectHandle &theInfo = renderedOpaqueObjects[idx];
167             const QVector3D difference = theInfo.obj->worldCenterPoint - theCameraPosition;
168             theInfo.cameraDistanceSq = QVector3D::dotProduct(difference, theCameraDirection);
169         }
170 
171         static const auto isRenderObjectPtrLessThan = [](const QSSGRenderableObjectHandle &lhs, const QSSGRenderableObjectHandle &rhs) {
172             return lhs.cameraDistanceSq < rhs.cameraDistanceSq;
173         };
174         // Render nearest to furthest objects
175         if (performSort)
176             std::sort(renderedOpaqueObjects.begin(), renderedOpaqueObjects.end(), isRenderObjectPtrLessThan);
177     }
178     return renderedOpaqueObjects;
179 }
180 
181 // If layer depth test is false, this may also contain opaque objects.
getTransparentRenderableObjects()182 const QVector<QSSGRenderableObjectHandle> &QSSGLayerRenderPreparationData::getTransparentRenderableObjects()
183 {
184     if (!renderedTransparentObjects.empty() || camera == nullptr)
185         return renderedTransparentObjects;
186 
187     renderedTransparentObjects = transparentObjects;
188 
189     if (!layer.flags.testFlag(QSSGRenderLayer::Flag::LayerEnableDepthTest))
190         renderedTransparentObjects.append(opaqueObjects);
191 
192     if (!renderedTransparentObjects.empty()) {
193         QVector3D theCameraDirection(getCameraDirection());
194         QVector3D theCameraPosition = camera->getGlobalPos();
195 
196         // Setup the object's sorting information
197         for (quint32 idx = 0, end = renderedTransparentObjects.size(); idx < end; ++idx) {
198             QSSGRenderableObjectHandle &theInfo = renderedTransparentObjects[idx];
199             const QVector3D difference = theInfo.obj->worldCenterPoint - theCameraPosition;
200             theInfo.cameraDistanceSq = QVector3D::dotProduct(difference, theCameraDirection);
201         }
202 
203         static const auto iSRenderObjectPtrGreatThan = [](const QSSGRenderableObjectHandle &lhs, const QSSGRenderableObjectHandle &rhs) {
204             return lhs.cameraDistanceSq > rhs.cameraDistanceSq;
205         };
206         // render furthest to nearest.
207         std::sort(renderedTransparentObjects.begin(), renderedTransparentObjects.end(), iSRenderObjectPtrGreatThan);
208     }
209 
210     return renderedTransparentObjects;
211 }
212 
getRenderableItem2Ds()213 const QVector<QSSGRenderableNodeEntry> &QSSGLayerRenderPreparationData::getRenderableItem2Ds()
214 {
215 
216     if (!renderedItem2Ds.isEmpty() || camera == nullptr)
217         return renderedItem2Ds;
218 
219     renderedItem2Ds = renderableItem2Ds;
220 
221     const QVector3D cameraDirection(getCameraDirection());
222     const QVector3D cameraPosition = camera->getGlobalPos();
223 
224     const auto isItemNodeDistanceGreatThan = [cameraDirection, cameraPosition]
225             (const QSSGRenderableNodeEntry &lhs, const QSSGRenderableNodeEntry &rhs) {
226         if (!lhs.node->parent || !rhs.node->parent)
227             return false;
228         const QVector3D lhsDifference = lhs.node->parent->position - cameraPosition;
229         const float lhsCameraDistanceSq = QVector3D::dotProduct(lhsDifference, cameraDirection);
230         const QVector3D rhsDifference = rhs.node->parent->position - cameraPosition;
231         const float rhsCameraDistanceSq = QVector3D::dotProduct(rhsDifference, cameraDirection);
232         return lhsCameraDistanceSq > rhsCameraDistanceSq;
233     };
234 
235     const auto isItemZOrderLessThan = []
236             (const QSSGRenderableNodeEntry &lhs, const QSSGRenderableNodeEntry &rhs) {
237         if (lhs.node->parent && rhs.node->parent && lhs.node->parent == rhs.node->parent) {
238             // Same parent nodes, so sort with item z-ordering
239             QSSGRenderItem2D *lhsItem = static_cast<QSSGRenderItem2D *>(lhs.node);
240             QSSGRenderItem2D *rhsItem = static_cast<QSSGRenderItem2D *>(rhs.node);
241             return lhsItem->zOrder < rhsItem->zOrder;
242         }
243         return false;
244     };
245 
246     // Render furthest to nearest items (parent nodes).
247     std::stable_sort(renderedItem2Ds.begin(), renderedItem2Ds.end(), isItemNodeDistanceGreatThan);
248     // Render items inside same node by item z-order.
249     // Note: stable_sort so item order in QML file is respected.
250     std::stable_sort(renderedItem2Ds.begin(), renderedItem2Ds.end(), isItemZOrderLessThan);
251 
252     return renderedItem2Ds;
253 }
254 
255 /**
256  * Usage: T *ptr = RENDER_FRAME_NEW<T>(context, arg0, arg1, ...); is equivalent to: T *ptr = new T(arg0, arg1, ...);
257  * so RENDER_FRAME_NEW() takes the RCI + T's arguments
258  */
259 template <typename T, typename... Args>
RENDER_FRAME_NEW(const QSSGRef<QSSGRenderContextInterface> & ctx,const Args &...args)260 inline T *RENDER_FRAME_NEW(const QSSGRef<QSSGRenderContextInterface> &ctx, const Args&... args)
261 {
262     return new (ctx->perFrameAllocator().allocate(sizeof(T)))T(const_cast<Args &>(args)...);
263 }
264 
generateLightingKey(QSSGRenderDefaultMaterial::MaterialLighting inLightingType,bool receivesShadows)265 QSSGShaderDefaultMaterialKey QSSGLayerRenderPreparationData::generateLightingKey(QSSGRenderDefaultMaterial::MaterialLighting inLightingType, bool receivesShadows)
266 {
267     const uint features = uint(getShaderFeatureSetHash());
268     QSSGShaderDefaultMaterialKey theGeneratedKey(features);
269     const bool lighting = inLightingType != QSSGRenderDefaultMaterial::MaterialLighting::NoLighting;
270     renderer->defaultMaterialShaderKeyProperties().m_hasLighting.setValue(theGeneratedKey, lighting);
271     if (lighting) {
272         const bool lightProbe = layer.lightProbe && layer.lightProbe->m_textureData.m_texture;
273         renderer->defaultMaterialShaderKeyProperties().m_hasIbl.setValue(theGeneratedKey, lightProbe);
274 
275         quint32 numLights = quint32(globalLights.size());
276         if (Q_UNLIKELY(numLights > QSSGShaderDefaultMaterialKeyProperties::LightCount && !tooManyLightsError)) {
277             tooManyLightsError = true;
278             numLights = QSSGShaderDefaultMaterialKeyProperties::LightCount;
279             qCCritical(INVALID_OPERATION, "Too many lights on layer, max is %d", QSSGShaderDefaultMaterialKeyProperties::LightCount);
280             Q_ASSERT(false);
281         }
282         renderer->defaultMaterialShaderKeyProperties().m_lightCount.setValue(theGeneratedKey, numLights);
283 
284         for (qint32 lightIdx = 0, lightEnd = globalLights.size(); lightIdx < lightEnd; ++lightIdx) {
285             QSSGRenderLight *theLight(globalLights[lightIdx]);
286             const bool isDirectional = theLight->m_lightType == QSSGRenderLight::Type::Directional;
287             const bool isArea = theLight->m_lightType == QSSGRenderLight::Type::Area;
288             const bool isSpot = theLight->m_lightType == QSSGRenderLight::Type::Spot;
289             const bool castShadowsArea = (theLight->m_lightType != QSSGRenderLight::Type::Area) && (theLight->m_castShadow) && receivesShadows;
290 
291             renderer->defaultMaterialShaderKeyProperties().m_lightFlags[lightIdx].setValue(theGeneratedKey, !isDirectional);
292             renderer->defaultMaterialShaderKeyProperties().m_lightAreaFlags[lightIdx].setValue(theGeneratedKey, isArea);
293             renderer->defaultMaterialShaderKeyProperties().m_lightSpotFlags[lightIdx].setValue(theGeneratedKey, isSpot);
294             renderer->defaultMaterialShaderKeyProperties().m_lightShadowFlags[lightIdx].setValue(theGeneratedKey, castShadowsArea);
295         }
296     }
297     return theGeneratedKey;
298 }
299 
prepareImageForRender(QSSGRenderImage & inImage,QSSGImageMapTypes inMapType,QSSGRenderableImage * & ioFirstImage,QSSGRenderableImage * & ioNextImage,QSSGRenderableObjectFlags & ioFlags,QSSGShaderDefaultMaterialKey & inShaderKey,quint32 inImageIndex,QSSGRenderDefaultMaterial * inMaterial)300 void QSSGLayerRenderPreparationData::prepareImageForRender(QSSGRenderImage &inImage,
301                                                            QSSGImageMapTypes inMapType,
302                                                            QSSGRenderableImage *&ioFirstImage,
303                                                            QSSGRenderableImage *&ioNextImage,
304                                                            QSSGRenderableObjectFlags &ioFlags,
305                                                            QSSGShaderDefaultMaterialKey &inShaderKey,
306                                                            quint32 inImageIndex,
307                                                            QSSGRenderDefaultMaterial *inMaterial)
308 {
309     const QSSGRef<QSSGRenderContextInterface> &contextInterface(renderer->contextInterface());
310     const QSSGRef<QSSGBufferManager> &bufferManager = contextInterface->bufferManager();
311 
312     if (inImage.clearDirty(bufferManager))
313         ioFlags |= QSSGRenderableObjectFlag::Dirty;
314 
315     if (inImage.m_textureData.m_texture) {
316         if (inImage.m_textureData.m_textureFlags.hasTransparency()
317             && (inMapType == QSSGImageMapTypes::Diffuse || inMapType == QSSGImageMapTypes::Opacity
318                 || inMapType == QSSGImageMapTypes::Translucency)) {
319             ioFlags |= QSSGRenderableObjectFlag::HasTransparency;
320         }
321         // Textures used in general have linear characteristics.
322         // PKC -- The filters are properly set already.  Setting them here only overrides what
323         // would
324         // otherwise be a correct setting.
325         // inImage.m_TextureData.m_Texture->SetMinFilter( QSSGRenderTextureMinifyingOp::Linear );
326         // inImage.m_TextureData.m_Texture->SetMagFilter( QSSGRenderTextureMagnifyingOp::Linear );
327 
328         QSSGRenderableImage *theImage = RENDER_FRAME_NEW<QSSGRenderableImage>(renderer->contextInterface(), inMapType, inImage);
329         QSSGShaderKeyImageMap &theKeyProp = renderer->defaultMaterialShaderKeyProperties().m_imageMaps[inImageIndex];
330 
331         theKeyProp.setEnabled(inShaderKey, true);
332         switch (inImage.m_mappingMode) {
333         default:
334             Q_ASSERT(false);
335             // fallthrough intentional
336         case QSSGRenderImage::MappingModes::Normal:
337             break;
338         case QSSGRenderImage::MappingModes::Environment:
339             theKeyProp.setEnvMap(inShaderKey, true);
340             break;
341         case QSSGRenderImage::MappingModes::LightProbe:
342             theKeyProp.setLightProbe(inShaderKey, true);
343             break;
344         }
345         bool hasA = false;
346         bool hasG = false;
347         bool hasB = false;
348         switch (inImage.m_textureData.m_texture->textureDetails().format.format) {
349         case QSSGRenderTextureFormat::RG8:
350         case QSSGRenderTextureFormat::RG16F:
351         case QSSGRenderTextureFormat::RG32F:
352             hasG = true;
353             break;
354         case QSSGRenderTextureFormat::RGB8:
355         case QSSGRenderTextureFormat::RGB16F:
356         case QSSGRenderTextureFormat::RGB32F:
357             hasG = true;
358             hasB = true;
359             break;
360         case QSSGRenderTextureFormat::Alpha8:
361             hasA = true;
362             break;
363         case QSSGRenderTextureFormat::LuminanceAlpha8:
364             hasA = true;
365             hasG = true;
366             break;
367         default:
368             hasA = true;
369             hasG = true;
370             hasB = true;
371             break;
372         }
373 
374         if (inImage.m_textureData.m_textureFlags.isInvertUVCoords())
375             theKeyProp.setInvertUVMap(inShaderKey, true);
376 
377         if (inImage.isImageTransformIdentity())
378             theKeyProp.setIdentityTransform(inShaderKey, true);
379 
380         if (ioFirstImage == nullptr)
381             ioFirstImage = theImage;
382         else
383             ioNextImage->m_nextImage = theImage;
384 
385         // assume offscreen renderer produces non-premultiplied image
386         if (inImage.m_textureData.m_textureFlags.isPreMultiplied())
387             theKeyProp.setPremultiplied(inShaderKey, true);
388 
389         QSSGShaderKeyTextureSwizzle &theSwizzleKeyProp = renderer->defaultMaterialShaderKeyProperties().m_textureSwizzle[inImageIndex];
390         theSwizzleKeyProp.setSwizzleMode(inShaderKey, inImage.m_textureData.m_texture->textureSwizzleMode(), true);
391 
392         ioNextImage = theImage;
393 
394         if (inMaterial && inImageIndex >= QSSGShaderDefaultMaterialKeyProperties::SingleChannelImagesFirst) {
395             QSSGRenderDefaultMaterial::TextureChannelMapping value = QSSGRenderDefaultMaterial::R;
396             QSSGRenderDefaultMaterial::TextureChannelMapping defaultValues[5] = {QSSGRenderDefaultMaterial::R, QSSGRenderDefaultMaterial::G, QSSGRenderDefaultMaterial::B, QSSGRenderDefaultMaterial::R, QSSGRenderDefaultMaterial::A};
397             if (inMaterial->type == QSSGRenderGraphObject::Type::DefaultMaterial)
398                 defaultValues[1] = defaultValues[2] = QSSGRenderDefaultMaterial::R;
399 
400             const quint32 scIndex = inImageIndex - QSSGShaderDefaultMaterialKeyProperties::SingleChannelImagesFirst;
401             QSSGShaderKeyTextureChannel &channelKey = renderer->defaultMaterialShaderKeyProperties().m_textureChannels[scIndex];
402             switch (inImageIndex) {
403             case QSSGShaderDefaultMaterialKeyProperties::OpacityMap:
404                 value = inMaterial->opacityChannel;
405                 break;
406             case QSSGShaderDefaultMaterialKeyProperties::RoughnessMap:
407                 value = inMaterial->roughnessChannel;
408                 break;
409             case QSSGShaderDefaultMaterialKeyProperties::MetalnessMap:
410                 value = inMaterial->metalnessChannel;
411                 break;
412             case QSSGShaderDefaultMaterialKeyProperties::OcclusionMap:
413                 value = inMaterial->occlusionChannel;
414                 break;
415             case QSSGShaderDefaultMaterialKeyProperties::TranslucencyMap:
416                 value = inMaterial->translucencyChannel;
417                 break;
418             default:
419                 break;
420             }
421             bool useDefault = false;
422             switch (value) {
423             case QSSGRenderDefaultMaterial::TextureChannelMapping::G:
424                 useDefault = !hasG;
425                 break;
426             case QSSGRenderDefaultMaterial::TextureChannelMapping::B:
427                 useDefault = !hasB;
428                 break;
429             case QSSGRenderDefaultMaterial::TextureChannelMapping::A:
430                 useDefault = !hasA;
431                 break;
432             default:
433                 break;
434             }
435             if (useDefault)
436                 value = defaultValues[scIndex];
437             channelKey.setTextureChannel(QSSGShaderKeyTextureChannel::TexturChannelBits(value), inShaderKey);
438         }
439     }
440 }
441 
setVertexInputPresence(const QSSGRenderableObjectFlags & renderableFlags,QSSGShaderDefaultMaterialKey & key)442 void QSSGLayerRenderPreparationData::setVertexInputPresence(const QSSGRenderableObjectFlags &renderableFlags,
443                                                             QSSGShaderDefaultMaterialKey &key)
444 {
445     quint32 vertexAttribs = 0;
446     if (renderableFlags.hasAttributePosition())
447         vertexAttribs |= QSSGShaderKeyVertexAttribute::Position;
448     if (renderableFlags.hasAttributeNormal())
449         vertexAttribs |= QSSGShaderKeyVertexAttribute::Normal;
450     if (renderableFlags.hasAttributeTexCoord0())
451         vertexAttribs |= QSSGShaderKeyVertexAttribute::TexCoord0;
452     if (renderableFlags.hasAttributeTexCoord1())
453         vertexAttribs |= QSSGShaderKeyVertexAttribute::TexCoord1;
454     if (renderableFlags.hasAttributeTangent())
455         vertexAttribs |= QSSGShaderKeyVertexAttribute::Tangent;
456     if (renderableFlags.hasAttributeBinormal())
457         vertexAttribs |= QSSGShaderKeyVertexAttribute::Binormal;
458     if (renderableFlags.hasAttributeColor())
459         vertexAttribs |= QSSGShaderKeyVertexAttribute::Color;
460     renderer->defaultMaterialShaderKeyProperties().m_vertexAttributes.setValue(key, vertexAttribs);
461 }
462 
prepareDefaultMaterialForRender(QSSGRenderDefaultMaterial & inMaterial,QSSGRenderableObjectFlags & inExistingFlags,float inOpacity)463 QSSGDefaultMaterialPreparationResult QSSGLayerRenderPreparationData::prepareDefaultMaterialForRender(
464         QSSGRenderDefaultMaterial &inMaterial,
465         QSSGRenderableObjectFlags &inExistingFlags,
466         float inOpacity)
467 {
468     QSSGRenderDefaultMaterial *theMaterial = &inMaterial;
469     QSSGDefaultMaterialPreparationResult retval(generateLightingKey(theMaterial->lighting, inExistingFlags.receivesShadows()));
470     retval.renderableFlags = inExistingFlags;
471     QSSGRenderableObjectFlags &renderableFlags(retval.renderableFlags);
472     QSSGShaderDefaultMaterialKey &theGeneratedKey(retval.materialKey);
473     retval.opacity = inOpacity;
474     float &subsetOpacity(retval.opacity);
475 
476     if (theMaterial->dirty.isDirty()) {
477         renderableFlags |= QSSGRenderableObjectFlag::Dirty;
478     }
479     subsetOpacity *= theMaterial->opacity;
480 
481     QSSGRenderableImage *firstImage = nullptr;
482 
483     // set wireframe mode
484     renderer->defaultMaterialShaderKeyProperties().m_wireframeMode.setValue(theGeneratedKey,
485                                                                             renderer->contextInterface()->wireframeMode());
486     // isDoubleSided
487     renderer->defaultMaterialShaderKeyProperties().m_isDoubleSided.setValue(theGeneratedKey, theMaterial->cullMode == QSSGCullFaceMode::Disabled);
488 
489     // alpha Mode
490     renderer->defaultMaterialShaderKeyProperties().m_alphaMode.setValue(theGeneratedKey, theMaterial->alphaMode);
491 
492     // vertex attribute presence flags
493     quint32 vertexAttribs = 0;
494     if (renderableFlags.hasAttributePosition())
495         vertexAttribs |= QSSGShaderKeyVertexAttribute::Position;
496     if (renderableFlags.hasAttributeNormal())
497         vertexAttribs |= QSSGShaderKeyVertexAttribute::Normal;
498     if (renderableFlags.hasAttributeTexCoord0())
499         vertexAttribs |= QSSGShaderKeyVertexAttribute::TexCoord0;
500     if (renderableFlags.hasAttributeTexCoord1())
501         vertexAttribs |= QSSGShaderKeyVertexAttribute::TexCoord1;
502     if (renderableFlags.hasAttributeTangent())
503         vertexAttribs |= QSSGShaderKeyVertexAttribute::Tangent;
504     if (renderableFlags.hasAttributeBinormal())
505         vertexAttribs |= QSSGShaderKeyVertexAttribute::Binormal;
506     if (renderableFlags.hasAttributeColor())
507         vertexAttribs |= QSSGShaderKeyVertexAttribute::Color;
508     renderer->defaultMaterialShaderKeyProperties().m_vertexAttributes.setValue(theGeneratedKey, vertexAttribs);
509 
510     if (theMaterial->iblProbe && checkLightProbeDirty(*theMaterial->iblProbe)) {
511         renderer->prepareImageForIbl(*theMaterial->iblProbe);
512     }
513 
514     if (!renderer->defaultMaterialShaderKeyProperties().m_hasIbl.getValue(theGeneratedKey)) {
515         bool lightProbeValid = hasValidLightProbe(theMaterial->iblProbe);
516         setShaderFeature(QSSGShaderDefines::asString(QSSGShaderDefines::LightProbe), lightProbeValid);
517         renderer->defaultMaterialShaderKeyProperties().m_hasIbl.setValue(theGeneratedKey, lightProbeValid);
518         // setShaderFeature(ShaderFeatureDefines::enableIblFov(),
519         // m_Renderer.GetLayerRenderData()->m_Layer.m_ProbeFov < 180.0f );
520     }
521 
522     if (subsetOpacity >= QSSG_RENDER_MINIMUM_RENDER_OPACITY) {
523 
524         if (theMaterial->blendMode != QSSGRenderDefaultMaterial::MaterialBlendMode::SourceOver ||
525             theMaterial->opacityMap ||
526             theMaterial->alphaMode == QSSGRenderDefaultMaterial::Blend ||
527             theMaterial->alphaMode == QSSGRenderDefaultMaterial::Mask) {
528             renderableFlags |= QSSGRenderableObjectFlag::HasTransparency;
529         }
530 
531         const bool specularEnabled = theMaterial->isSpecularEnabled();
532         const bool metalnessEnabled = theMaterial->isMetalnessEnabled();
533         renderer->defaultMaterialShaderKeyProperties().m_specularEnabled.setValue(theGeneratedKey, (specularEnabled || metalnessEnabled));
534         if (specularEnabled || metalnessEnabled)
535             renderer->defaultMaterialShaderKeyProperties().m_specularModel.setSpecularModel(theGeneratedKey, theMaterial->specularModel);
536 
537         renderer->defaultMaterialShaderKeyProperties().m_fresnelEnabled.setValue(theGeneratedKey, theMaterial->isFresnelEnabled());
538 
539         renderer->defaultMaterialShaderKeyProperties().m_vertexColorsEnabled.setValue(theGeneratedKey,
540                                                                                       theMaterial->isVertexColorsEnabled());
541 
542         // Run through the material's images and prepare them for render.
543         // this may in fact set pickable on the renderable flags if one of the images
544         // links to a sub presentation or any offscreen rendered object.
545         QSSGRenderableImage *nextImage = nullptr;
546 #define CHECK_IMAGE_AND_PREPARE(img, imgtype, shadercomponent)                          \
547     if ((img))                                                                          \
548         prepareImageForRender(*(img), imgtype, firstImage, nextImage, renderableFlags,  \
549                               theGeneratedKey, shadercomponent, &inMaterial)
550 
551         if (theMaterial->type == QSSGRenderGraphObject::Type::PrincipledMaterial) {
552             CHECK_IMAGE_AND_PREPARE(theMaterial->colorMap,
553                                     QSSGImageMapTypes::BaseColor,
554                                     QSSGShaderDefaultMaterialKeyProperties::BaseColorMap);
555             CHECK_IMAGE_AND_PREPARE(theMaterial->metalnessMap,
556                                     QSSGImageMapTypes::Metalness,
557                                     QSSGShaderDefaultMaterialKeyProperties::MetalnessMap);
558             CHECK_IMAGE_AND_PREPARE(theMaterial->occlusionMap,
559                                     QSSGImageMapTypes::Occlusion,
560                                     QSSGShaderDefaultMaterialKeyProperties::OcclusionMap);
561         } else {
562             CHECK_IMAGE_AND_PREPARE(theMaterial->colorMap,
563                                     QSSGImageMapTypes::Diffuse,
564                                     QSSGShaderDefaultMaterialKeyProperties::DiffuseMap);
565         }
566         CHECK_IMAGE_AND_PREPARE(theMaterial->emissiveMap, QSSGImageMapTypes::Emissive, QSSGShaderDefaultMaterialKeyProperties::EmissiveMap);
567         CHECK_IMAGE_AND_PREPARE(theMaterial->specularReflection,
568                                 QSSGImageMapTypes::Specular,
569                                 QSSGShaderDefaultMaterialKeyProperties::SpecularMap);
570         CHECK_IMAGE_AND_PREPARE(theMaterial->roughnessMap,
571                                 QSSGImageMapTypes::Roughness,
572                                 QSSGShaderDefaultMaterialKeyProperties::RoughnessMap);
573         CHECK_IMAGE_AND_PREPARE(theMaterial->opacityMap, QSSGImageMapTypes::Opacity, QSSGShaderDefaultMaterialKeyProperties::OpacityMap);
574         CHECK_IMAGE_AND_PREPARE(theMaterial->bumpMap, QSSGImageMapTypes::Bump, QSSGShaderDefaultMaterialKeyProperties::BumpMap);
575         CHECK_IMAGE_AND_PREPARE(theMaterial->specularMap,
576                                 QSSGImageMapTypes::SpecularAmountMap,
577                                 QSSGShaderDefaultMaterialKeyProperties::SpecularAmountMap);
578         CHECK_IMAGE_AND_PREPARE(theMaterial->normalMap, QSSGImageMapTypes::Normal, QSSGShaderDefaultMaterialKeyProperties::NormalMap);
579         CHECK_IMAGE_AND_PREPARE(theMaterial->displacementMap,
580                                 QSSGImageMapTypes::Displacement,
581                                 QSSGShaderDefaultMaterialKeyProperties::DisplacementMap);
582         CHECK_IMAGE_AND_PREPARE(theMaterial->translucencyMap,
583                                 QSSGImageMapTypes::Translucency,
584                                 QSSGShaderDefaultMaterialKeyProperties::TranslucencyMap);
585         CHECK_IMAGE_AND_PREPARE(theMaterial->lightmaps.m_lightmapIndirect,
586                                 QSSGImageMapTypes::LightmapIndirect,
587                                 QSSGShaderDefaultMaterialKeyProperties::LightmapIndirect);
588         CHECK_IMAGE_AND_PREPARE(theMaterial->lightmaps.m_lightmapRadiosity,
589                                 QSSGImageMapTypes::LightmapRadiosity,
590                                 QSSGShaderDefaultMaterialKeyProperties::LightmapRadiosity);
591         CHECK_IMAGE_AND_PREPARE(theMaterial->lightmaps.m_lightmapShadow,
592                                 QSSGImageMapTypes::LightmapShadow,
593                                 QSSGShaderDefaultMaterialKeyProperties::LightmapShadow);
594     }
595 #undef CHECK_IMAGE_AND_PREPARE
596 
597     if (subsetOpacity < QSSG_RENDER_MINIMUM_RENDER_OPACITY) {
598         subsetOpacity = 0.0f;
599         // You can still pick against completely transparent objects(or rather their bounding
600         // box)
601         // you just don't render them.
602         renderableFlags |= QSSGRenderableObjectFlag::HasTransparency;
603         renderableFlags |= QSSGRenderableObjectFlag::CompletelyTransparent;
604     }
605 
606     if (subsetOpacity > 1.f - QSSG_RENDER_MINIMUM_RENDER_OPACITY)
607         subsetOpacity = 1.f;
608     else
609         renderableFlags |= QSSGRenderableObjectFlag::HasTransparency;
610 
611     retval.firstImage = firstImage;
612     if (retval.renderableFlags.isDirty())
613         retval.dirty = true;
614     if (retval.dirty)
615         renderer->addMaterialDirtyClear(&inMaterial);
616     return retval;
617 }
618 
prepareCustomMaterialForRender(QSSGRenderCustomMaterial & inMaterial,QSSGRenderableObjectFlags & inExistingFlags,float inOpacity,bool alreadyDirty)619 QSSGDefaultMaterialPreparationResult QSSGLayerRenderPreparationData::prepareCustomMaterialForRender(QSSGRenderCustomMaterial &inMaterial,
620                                                                                                     QSSGRenderableObjectFlags &inExistingFlags,
621                                                                                                     float inOpacity, bool alreadyDirty)
622 {
623     QSSGDefaultMaterialPreparationResult retval(generateLightingKey(QSSGRenderDefaultMaterial::MaterialLighting::FragmentLighting, inExistingFlags.receivesShadows())); // always fragment lighting
624     retval.renderableFlags = inExistingFlags;
625     QSSGRenderableObjectFlags &renderableFlags(retval.renderableFlags);
626     QSSGShaderDefaultMaterialKey &theGeneratedKey(retval.materialKey);
627     retval.opacity = inOpacity;
628     float &subsetOpacity(retval.opacity);
629 
630     // set wireframe mode
631     renderer->defaultMaterialShaderKeyProperties().m_wireframeMode.setValue(theGeneratedKey,
632                                                                             renderer->contextInterface()->wireframeMode());
633 
634     if (subsetOpacity < QSSG_RENDER_MINIMUM_RENDER_OPACITY) {
635         subsetOpacity = 0.0f;
636         // You can still pick against completely transparent objects(or rather their bounding
637         // box)
638         // you just don't render them.
639         renderableFlags |= QSSGRenderableObjectFlag::HasTransparency;
640         renderableFlags |= QSSGRenderableObjectFlag::CompletelyTransparent;
641     }
642 
643     if (subsetOpacity > 1.f - QSSG_RENDER_MINIMUM_RENDER_OPACITY)
644         subsetOpacity = 1.f;
645     else
646         renderableFlags |= QSSGRenderableObjectFlag::HasTransparency;
647 
648     QSSGRenderableImage *firstImage = nullptr;
649     QSSGRenderableImage *nextImage = nullptr;
650 
651 #define CHECK_IMAGE_AND_PREPARE(img, imgtype, shadercomponent)                          \
652     if ((img))                                                                          \
653         prepareImageForRender(*(img), imgtype, firstImage, nextImage, renderableFlags,  \
654                               theGeneratedKey, shadercomponent, nullptr)
655 
656     CHECK_IMAGE_AND_PREPARE(inMaterial.m_displacementMap,
657                             QSSGImageMapTypes::Displacement,
658                             QSSGShaderDefaultMaterialKeyProperties::DisplacementMap);
659     CHECK_IMAGE_AND_PREPARE(inMaterial.m_lightmaps.m_lightmapIndirect,
660                             QSSGImageMapTypes::LightmapIndirect,
661                             QSSGShaderDefaultMaterialKeyProperties::LightmapIndirect);
662     CHECK_IMAGE_AND_PREPARE(inMaterial.m_lightmaps.m_lightmapRadiosity,
663                             QSSGImageMapTypes::LightmapRadiosity,
664                             QSSGShaderDefaultMaterialKeyProperties::LightmapRadiosity);
665     CHECK_IMAGE_AND_PREPARE(inMaterial.m_lightmaps.m_lightmapShadow,
666                             QSSGImageMapTypes::LightmapShadow,
667                             QSSGShaderDefaultMaterialKeyProperties::LightmapShadow);
668 #undef CHECK_IMAGE_AND_PREPARE
669 
670     retval.firstImage = firstImage;
671     if (retval.dirty || alreadyDirty)
672         renderer->addMaterialDirtyClear(&inMaterial);
673 
674     // register the custom material shaders with the dynamic object system if they
675     // are not registered already
676     const QSSGRef<QSSGDynamicObjectSystem> &theDynamicSystem(renderer->contextInterface()->dynamicObjectSystem());
677     for (auto shaderPath : inMaterial.shaders.keys())
678         theDynamicSystem->setShaderData(shaderPath,
679                                         inMaterial.shaders[shaderPath],
680                                         inMaterial.shaderInfo.type,
681                                         inMaterial.shaderInfo.version,
682                                         false,
683                                         false);
684 
685     return retval;
686 }
687 
prepareModelForRender(QSSGRenderModel & inModel,const QMatrix4x4 & inViewProjection,const QSSGOption<QSSGClippingFrustum> & inClipFrustum,QSSGNodeLightEntryList & inScopedLights)688 bool QSSGLayerRenderPreparationData::prepareModelForRender(QSSGRenderModel &inModel,
689                                                              const QMatrix4x4 &inViewProjection,
690                                                              const QSSGOption<QSSGClippingFrustum> &inClipFrustum,
691                                                              QSSGNodeLightEntryList &inScopedLights)
692 {
693     const QSSGRef<QSSGRenderContextInterface> &contextInterface(renderer->contextInterface());
694     const QSSGRef<QSSGBufferManager> &bufferManager = contextInterface->bufferManager();
695 
696     QSSGRenderMesh *theMesh = nullptr;
697     // create custom mesh if set
698     if (inModel.meshPath.isNull() && inModel.geometry)
699         theMesh = inModel.geometry->createOrUpdate(bufferManager);
700     else
701         theMesh = bufferManager->loadMesh(inModel.meshPath);
702 
703     if (theMesh == nullptr)
704         return false;
705 
706     QSSGModelContext &theModelContext = *RENDER_FRAME_NEW<QSSGModelContext>(renderer->contextInterface(), inModel, inViewProjection);
707     modelContexts.push_back(&theModelContext);
708 
709     bool subsetDirty = false;
710 
711     // Completely transparent models cannot be pickable.  But models with completely
712     // transparent materials still are.  This allows the artist to control pickability
713     // in a somewhat fine-grained style.
714     const bool canModelBePickable = (inModel.globalOpacity > QSSG_RENDER_MINIMUM_RENDER_OPACITY)
715                                     && (theModelContext.model.flags.testFlag(QSSGRenderModel::Flag::GloballyPickable));
716     if (canModelBePickable) {
717         // Check if there is BVH data, if not generate it
718         if (!theMesh->bvh && !inModel.meshPath.isNull()) {
719             theMesh->bvh = bufferManager->loadMeshBVH(inModel.meshPath);
720             if (theMesh->bvh) {
721                 for (int i = 0; i < theMesh->bvh->roots.count(); ++i)
722                     theMesh->subsets[i].bvhRoot = theMesh->bvh->roots.at(i);
723             }
724         }
725     }
726 
727     const QSSGScopedLightsListScope lightsScope(globalLights, lightDirections, sourceLightDirections, inScopedLights);
728     setShaderFeature(QSSGShaderDefines::asString(QSSGShaderDefines::CgLighting), !globalLights.empty());
729     for (int idx = 0; idx < theMesh->subsets.size(); ++idx) {
730         // If the materials list < size of subsets, then use the last material for the rest
731         QSSGRenderGraphObject *theSourceMaterialObject = nullptr;
732         if (inModel.materials.isEmpty())
733             break;
734         if (idx + 1 > inModel.materials.count())
735             theSourceMaterialObject = inModel.materials.last();
736         else
737             theSourceMaterialObject = inModel.materials.at(idx);
738         QSSGRenderSubset &theOuterSubset(theMesh->subsets[idx]);
739         {
740             QSSGRenderSubset &theSubset(theOuterSubset);
741             QSSGRenderableObjectFlags renderableFlags;
742             float subsetOpacity = inModel.globalOpacity;
743             QVector3D theModelCenter(theSubset.bounds.center());
744             theModelCenter = mat44::transform(inModel.globalTransform, theModelCenter);
745 
746             if (subsetOpacity >= QSSG_RENDER_MINIMUM_RENDER_OPACITY && inClipFrustum.hasValue()) {
747                 // Check bounding box against the clipping planes
748                 QSSGBounds3 theGlobalBounds = theSubset.bounds;
749                 theGlobalBounds.transform(theModelContext.model.globalTransform);
750                 if (!inClipFrustum->intersectsWith(theGlobalBounds))
751                     subsetOpacity = 0.0f;
752             }
753 
754             renderableFlags.setPickable(canModelBePickable);
755 
756             // Casting and Receiving Shadows
757             renderableFlags.setCastsShadows(inModel.castsShadows);
758             renderableFlags.setReceivesShadows(inModel.receivesShadows);
759 
760             for (const QByteArray &attr : qAsConst(theMesh->inputLayoutInputNames)) {
761                 using namespace QSSGMeshUtilities;
762                 if (attr == Mesh::getPositionAttrName())
763                     renderableFlags.setHasAttributePosition(true);
764                 else if (attr == Mesh::getNormalAttrName())
765                     renderableFlags.setHasAttributeNormal(true);
766                 else if (attr == Mesh::getUVAttrName())
767                     renderableFlags.setHasAttributeTexCoord0(true);
768                 else if (attr == Mesh::getUV2AttrName())
769                     renderableFlags.setHasAttributeTexCoord1(true);
770                 else if (attr == Mesh::getTexTanAttrName())
771                     renderableFlags.setHasAttributeTangent(true);
772                 else if (attr == Mesh::getTexBinormalAttrName())
773                     renderableFlags.setHasAttributeBinormal(true);
774                 else if (attr == Mesh::getColorAttrName())
775                     renderableFlags.setHasAttributeColor(true);
776             }
777 
778             QSSGRenderableObject *theRenderableObject = nullptr;
779             QSSGRenderGraphObject *theMaterialObject = theSourceMaterialObject;
780 
781             if (theMaterialObject == nullptr)
782                 continue;
783 
784             // set tessellation
785             if (inModel.tessellationMode != TessellationModeValues::NoTessellation) {
786                 theSubset.primitiveType = QSSGRenderDrawMode::Patches;
787                 // set tessellation factor
788                 theSubset.edgeTessFactor = inModel.edgeTessellation;
789                 theSubset.innerTessFactor = inModel.innerTessellation;
790                 // update the vertex ver patch count in the input assembler
791                 // currently we only support triangle patches so count is always 3
792                 theSubset.inputAssembler->setPatchVertexCount(3);
793                 theSubset.inputAssemblerDepth->setPatchVertexCount(3);
794                 // check wireframe mode
795                 theSubset.wireframeMode = contextInterface->wireframeMode();
796 
797                 subsetDirty = subsetDirty | (theSubset.wireframeMode != inModel.wireframeMode);
798                 inModel.wireframeMode = contextInterface->wireframeMode();
799             } else {
800                 theSubset.primitiveType = theSubset.inputAssembler->drawMode();
801                 theSubset.inputAssembler->setPatchVertexCount(1);
802                 theSubset.inputAssemblerDepth->setPatchVertexCount(1);
803                 // currently we allow wirframe mode only if tessellation is on
804                 theSubset.wireframeMode = false;
805 
806                 subsetDirty = subsetDirty | (theSubset.wireframeMode != inModel.wireframeMode);
807                 inModel.wireframeMode = false;
808             }
809 
810             if (theMaterialObject == nullptr)
811                 continue;
812 
813             if (theMaterialObject->type == QSSGRenderGraphObject::Type::DefaultMaterial || theMaterialObject->type == QSSGRenderGraphObject::Type::PrincipledMaterial) {
814                 QSSGRenderDefaultMaterial &theMaterial(static_cast<QSSGRenderDefaultMaterial &>(*theMaterialObject));
815                 // vertexColor should be supported in both DefaultMaterial and PrincipleMaterial
816                 // if the mesh has it.
817                 theMaterial.vertexColorsEnabled = renderableFlags.hasAttributeColor();
818                 QSSGDefaultMaterialPreparationResult theMaterialPrepResult(
819                         prepareDefaultMaterialForRender(theMaterial, renderableFlags, subsetOpacity));
820                 QSSGShaderDefaultMaterialKey theGeneratedKey = theMaterialPrepResult.materialKey;
821                 subsetOpacity = theMaterialPrepResult.opacity;
822                 QSSGRenderableImage *firstImage(theMaterialPrepResult.firstImage);
823                 subsetDirty |= theMaterialPrepResult.dirty;
824                 renderableFlags = theMaterialPrepResult.renderableFlags;
825 
826                 renderer->defaultMaterialShaderKeyProperties().m_tessellationMode.setTessellationMode(theGeneratedKey,
827                                                                                                       inModel.tessellationMode,
828                                                                                                       true);
829 
830                 QSSGDataView<QMatrix4x4> boneGlobals;
831                 if (theSubset.joints.size()) {
832                     Q_ASSERT(false);
833                 }
834 
835                 theRenderableObject = RENDER_FRAME_NEW<QSSGSubsetRenderable>(renderer->contextInterface(),
836                                                                              renderableFlags,
837                                                                              theModelCenter,
838                                                                              renderer,
839                                                                              theSubset,
840                                                                              theMaterial,
841                                                                              theModelContext,
842                                                                              subsetOpacity,
843                                                                              firstImage,
844                                                                              theGeneratedKey,
845                                                                              boneGlobals);
846                 subsetDirty = subsetDirty || renderableFlags.isDirty();
847             } else if (theMaterialObject->type == QSSGRenderGraphObject::Type::CustomMaterial) {
848                 QSSGRenderCustomMaterial &theMaterial(static_cast<QSSGRenderCustomMaterial &>(*theMaterialObject));
849 
850                 const QSSGRef<QSSGMaterialSystem> &theMaterialSystem(contextInterface->customMaterialSystem());
851                 subsetDirty |= theMaterialSystem->prepareForRender(theModelContext.model, theSubset, theMaterial);
852 
853                 QSSGDefaultMaterialPreparationResult theMaterialPrepResult(
854                         prepareCustomMaterialForRender(theMaterial, renderableFlags, subsetOpacity, subsetDirty));
855                 QSSGShaderDefaultMaterialKey theGeneratedKey = theMaterialPrepResult.materialKey;
856                 subsetOpacity = theMaterialPrepResult.opacity;
857                 QSSGRenderableImage *firstImage(theMaterialPrepResult.firstImage);
858                 renderableFlags = theMaterialPrepResult.renderableFlags;
859 
860                 // prepare for render tells us if the object is transparent
861                 if (theMaterial.m_hasTransparency)
862                     renderableFlags |= QSSGRenderableObjectFlag::HasTransparency;
863                 // prepare for render tells us if the object is transparent
864                 if (theMaterial.m_hasRefraction)
865                     renderableFlags |= QSSGRenderableObjectFlag::HasRefraction;
866 
867                 renderer->defaultMaterialShaderKeyProperties().m_tessellationMode.setTessellationMode(theGeneratedKey,
868                                                                                                       inModel.tessellationMode,
869                                                                                                       true);
870 
871                 if (theMaterial.m_iblProbe && checkLightProbeDirty(*theMaterial.m_iblProbe)) {
872                     renderer->prepareImageForIbl(*theMaterial.m_iblProbe);
873                 }
874 
875                 theRenderableObject = RENDER_FRAME_NEW<QSSGCustomMaterialRenderable>(renderer->contextInterface(),
876                                                                                      renderableFlags,
877                                                                                      theModelCenter,
878                                                                                      renderer,
879                                                                                      theSubset,
880                                                                                      theMaterial,
881                                                                                      theModelContext,
882                                                                                      subsetOpacity,
883                                                                                      firstImage,
884                                                                                      theGeneratedKey);
885             }
886             if (theRenderableObject) {
887                 theRenderableObject->scopedLights = inScopedLights;
888                 // set tessellation
889                 theRenderableObject->tessellationMode = inModel.tessellationMode;
890 
891                 if (theRenderableObject->renderableFlags.hasTransparency() || theRenderableObject->renderableFlags.hasRefraction()) {
892                     transparentObjects.push_back(QSSGRenderableObjectHandle::create(theRenderableObject));
893                 } else {
894                     opaqueObjects.push_back(QSSGRenderableObjectHandle::create(theRenderableObject));
895                 }
896             }
897         }
898     }
899     return subsetDirty;
900 }
901 
prepareRenderablesForRender(const QMatrix4x4 & inViewProjection,const QSSGOption<QSSGClippingFrustum> & inClipFrustum,QSSGLayerRenderPreparationResultFlags & ioFlags)902 bool QSSGLayerRenderPreparationData::prepareRenderablesForRender(const QMatrix4x4 &inViewProjection,
903                                                                    const QSSGOption<QSSGClippingFrustum> &inClipFrustum,
904                                                                    QSSGLayerRenderPreparationResultFlags &ioFlags)
905 {
906     Q_UNUSED(ioFlags)
907     QSSGStackPerfTimer perfTimer(renderer->contextInterface()->performanceTimer(), Q_FUNC_INFO);
908     viewProjection = inViewProjection;
909     bool wasDataDirty = false;
910     for (qint32 idx = 0, end = renderableNodes.size(); idx < end; ++idx) {
911         QSSGRenderableNodeEntry &theNodeEntry(renderableNodes[idx]);
912         QSSGRenderNode *theNode = theNodeEntry.node;
913         wasDataDirty = wasDataDirty || theNode->flags.testFlag(QSSGRenderNode::Flag::Dirty);
914         switch (theNode->type) {
915         case QSSGRenderGraphObject::Type::Model: {
916             QSSGRenderModel *theModel = static_cast<QSSGRenderModel *>(theNode);
917             theModel->calculateGlobalVariables();
918             if (theModel->flags.testFlag(QSSGRenderModel::Flag::GloballyActive)) {
919                 bool wasModelDirty = prepareModelForRender(*theModel, inViewProjection, inClipFrustum, theNodeEntry.lights);
920                 wasDataDirty = wasDataDirty || wasModelDirty;
921             }
922         } break;
923         case QSSGRenderGraphObject::Type::Item2D: {
924             QSSGRenderItem2D *theItem2D = static_cast<QSSGRenderItem2D *>(theNode);
925             theItem2D->calculateGlobalVariables();
926             if (theItem2D->flags.testFlag(QSSGRenderModel::Flag::GloballyActive)) {
927                 theItem2D->MVP = inViewProjection * theItem2D->globalTransform;
928                 // Pushing front to keep item order inside QML file
929                 renderableItem2Ds.push_front(theNodeEntry);
930             }
931         } break;
932         default:
933             Q_ASSERT(false);
934             break;
935         }
936     }
937     return wasDataDirty;
938 }
939 
checkLightProbeDirty(QSSGRenderImage & inLightProbe)940 bool QSSGLayerRenderPreparationData::checkLightProbeDirty(QSSGRenderImage &inLightProbe)
941 {
942     const QSSGRef<QSSGRenderContextInterface> &theContext(renderer->contextInterface());
943     const QSSGRef<QSSGBufferManager> &bufferManager = theContext->bufferManager();
944     return inLightProbe.clearDirty(bufferManager, true);
945 }
946 
947 struct QSSGLightNodeMarker
948 {
949     QSSGRenderLight *light = nullptr;
950     quint32 lightIndex = 0;
951     quint32 firstValidIndex = 0;
952     quint32 justPastLastValidIndex = 0;
953     bool addOrRemove = false;
954     QSSGLightNodeMarker() = default;
QSSGLightNodeMarkerQSSGLightNodeMarker955     QSSGLightNodeMarker(QSSGRenderLight &inLight, quint32 inLightIndex, QSSGRenderNode &inNode, bool aorm)
956         : light(&inLight), lightIndex(inLightIndex), addOrRemove(aorm)
957     {
958         if (inNode.type == QSSGRenderGraphObject::Type::Layer) {
959             firstValidIndex = 0;
960             justPastLastValidIndex = std::numeric_limits<quint32>::max();
961         } else {
962             firstValidIndex = inNode.dfsIndex;
963             QSSGRenderNode *lastChild = nullptr;
964             QSSGRenderNode *firstChild = inNode.firstChild;
965             // find deepest last child
966             while (firstChild) {
967                 for (QSSGRenderNode *childNode = firstChild; childNode; childNode = childNode->nextSibling)
968                     lastChild = childNode;
969 
970                 if (lastChild)
971                     firstChild = lastChild->firstChild;
972                 else
973                     firstChild = nullptr;
974             }
975             if (lastChild)
976                 // last valid index would be the last child index + 1
977                 justPastLastValidIndex = lastChild->dfsIndex + 1;
978             else // no children.
979                 justPastLastValidIndex = firstValidIndex + 1;
980         }
981     }
982 };
983 
prepareForRender(const QSize & inViewportDimensions)984 void QSSGLayerRenderPreparationData::prepareForRender(const QSize &inViewportDimensions)
985 {
986     QSSGStackPerfTimer perfTimer(renderer->contextInterface()->performanceTimer(), Q_FUNC_INFO);
987     if (layerPrepResult.hasValue())
988         return;
989 
990     features.clear();
991     featureSetHash = 0;
992     QVector2D thePresentationDimensions((float)inViewportDimensions.width(), (float)inViewportDimensions.height());
993     QRect theViewport(renderer->contextInterface()->viewport());
994     QRect theScissor(renderer->contextInterface()->scissorRect());
995     if (theScissor.isNull() || (theScissor == theViewport)) {
996         theScissor = theViewport;
997         renderer->contextInterface()->renderContext()->setScissorTestEnabled(false);
998     } else {
999         renderer->contextInterface()->renderContext()->setScissorTestEnabled(true);
1000     }
1001 
1002     bool wasDirty = false;
1003     bool wasDataDirty = false;
1004     wasDirty = layer.flags.testFlag(QSSGRenderLayer::Flag::Dirty);
1005     // The first pass is just to render the data.
1006     quint32 maxNumAAPasses = layer.antialiasingMode == QSSGRenderLayer::AAMode::NoAA ? (quint32)0 : (quint32)(layer.antialiasingQuality) + 1;
1007     maxNumAAPasses = qMin((quint32)(MAX_AA_LEVELS + 1), maxNumAAPasses);
1008     QSSGRenderEffect *theLastEffect = nullptr;
1009     // Uncomment the line below to disable all progressive AA.
1010     // maxNumAAPasses = 0;
1011 
1012     QSSGLayerRenderPreparationResult thePrepResult;
1013 
1014     bool SSAOEnabled = (layer.aoStrength > 0.0f && layer.aoDistance > 0.0f);
1015     bool SSDOEnabled = (layer.shadowStrength > 0.0f && layer.shadowDist > 0.0f);
1016     setShaderFeature(QSSGShaderDefines::asString(QSSGShaderDefines::Ssao), SSAOEnabled);
1017     setShaderFeature(QSSGShaderDefines::asString(QSSGShaderDefines::Ssdo), SSDOEnabled);
1018     bool requiresDepthPrepass = (SSAOEnabled || SSDOEnabled);
1019     setShaderFeature(QSSGShaderDefines::asString(QSSGShaderDefines::Ssm), false); // by default no shadow map generation
1020 
1021     if (layer.flags.testFlag(QSSGRenderLayer::Flag::Active)) {
1022         // Get the layer's width and height.
1023         for (QSSGRenderEffect *theEffect = layer.firstEffect; theEffect; theEffect = theEffect->m_nextEffect) {
1024             if (theEffect->flags.testFlag(QSSGRenderEffect::Flag::Dirty)) {
1025                 wasDirty = true;
1026                 theEffect->flags.setFlag(QSSGRenderEffect::Flag::Dirty, false);
1027             }
1028             if (theEffect->flags.testFlag(QSSGRenderEffect::Flag::Active)) {
1029                 theLastEffect = theEffect;
1030                 if (theEffect->requiresDepthTexture)
1031                     requiresDepthPrepass = true;
1032             }
1033         }
1034         if (layer.flags.testFlag(QSSGRenderLayer::Flag::Dirty)) {
1035             wasDirty = true;
1036             layer.calculateGlobalVariables();
1037         }
1038 
1039         thePrepResult = QSSGLayerRenderPreparationResult(
1040                 QSSGLayerRenderHelper(theViewport,
1041                                         theScissor,
1042                                         layer));
1043 
1044         thePrepResult.lastEffect = theLastEffect;
1045         thePrepResult.maxAAPassIndex = maxNumAAPasses;
1046         thePrepResult.flags.setRequiresDepthTexture(requiresDepthPrepass);
1047         if (renderer->context()->renderContextType() != QSSGRenderContextType::GLES2)
1048             thePrepResult.flags.setRequiresSsaoPass(SSAOEnabled);
1049 
1050         if (thePrepResult.isLayerVisible()) {
1051             if (layer.lightProbe && checkLightProbeDirty(*layer.lightProbe)) {
1052                 renderer->prepareImageForIbl(*layer.lightProbe);
1053                 wasDataDirty = true;
1054             }
1055 
1056             bool lightProbeValid = hasValidLightProbe(layer.lightProbe);
1057 
1058             setShaderFeature(QSSGShaderDefines::asString(QSSGShaderDefines::LightProbe), lightProbeValid);
1059             setShaderFeature(QSSGShaderDefines::asString(QSSGShaderDefines::IblFov), layer.probeFov < 180.0f);
1060 
1061             if (lightProbeValid && layer.lightProbe2 && checkLightProbeDirty(*layer.lightProbe2)) {
1062                 renderer->prepareImageForIbl(*layer.lightProbe2);
1063                 wasDataDirty = true;
1064             }
1065 
1066             setShaderFeature(QSSGShaderDefines::asString(QSSGShaderDefines::LightProbe2), lightProbeValid && hasValidLightProbe(layer.lightProbe2));
1067 
1068             // Push nodes in reverse depth first order
1069 //            if (renderableNodes.empty()) {
1070 //                camerasAndLights.clear();
1071 //                quint32 dfsIndex = 0;
1072 //                for (QSSGRenderNode *theChild = layer.firstChild; theChild; theChild = theChild->nextSibling)
1073 //                    MaybeQueueNodeForRender(*theChild, renderableNodes, camerasAndLights, dfsIndex);
1074 //                std::reverse(camerasAndLights.begin(), camerasAndLights.end());
1075 //                std::reverse(renderableNodes.begin(), renderableNodes.end());
1076 //                lightToNodeMap.clear();
1077 //            }
1078             // ### TODO: Really this should only be done if renderableNodes is empty or dirty
1079             // but we don't have a way to say it's dirty yet (new renderables added to the tree)
1080             cameras.clear();
1081             lights.clear();
1082             renderableNodes.clear();
1083             renderableItem2Ds.clear();
1084             quint32 dfsIndex = 0;
1085             for (QSSGRenderNode *theChild = layer.firstChild; theChild; theChild = theChild->nextSibling)
1086                 maybeQueueNodeForRender(*theChild, renderableNodes, cameras, lights, dfsIndex);
1087             lightToNodeMap.clear();
1088 
1089             globalLights.clear();
1090             for (const auto &oo : qAsConst(opaqueObjects))
1091                 delete oo.obj;
1092             opaqueObjects.clear();
1093             for (const auto &to : qAsConst(transparentObjects))
1094                 delete to.obj;
1095             transparentObjects.clear();
1096             QVector<QSSGLightNodeMarker> theLightNodeMarkers;
1097             sourceLightDirections.clear();
1098 
1099             // Cameras
1100             // First, check the activeCamera is GloballyActive
1101             // and then if not, seek a GloballyActive one from the first
1102             camera = layer.activeCamera;
1103             if (camera != nullptr) {
1104                 wasDataDirty = wasDataDirty
1105                     || camera->flags.testFlag(QSSGRenderNode::Flag::Dirty);
1106                 QSSGCameraGlobalCalculationResult theResult = thePrepResult.setupCameraForRender(*camera);
1107                 wasDataDirty = wasDataDirty || theResult.m_wasDirty;
1108                 if (!theResult.m_computeFrustumSucceeded)
1109                     qCCritical(INTERNAL_ERROR, "Failed to calculate camera frustum");
1110                 if (!camera->flags.testFlag(QSSGRenderCamera::Flag::GloballyActive))
1111                     camera = nullptr;
1112 
1113             }
1114             for (auto iter = cameras.cbegin();
1115                     (camera == nullptr) && (iter != cameras.cend()); iter++) {
1116                 QSSGRenderCamera *theCamera = *iter;
1117                 wasDataDirty = wasDataDirty
1118                     || theCamera->flags.testFlag(QSSGRenderNode::Flag::Dirty);
1119                 QSSGCameraGlobalCalculationResult theResult = thePrepResult.setupCameraForRender(*theCamera);
1120                 wasDataDirty = wasDataDirty || theResult.m_wasDirty;
1121                 if (!theResult.m_computeFrustumSucceeded)
1122                     qCCritical(INTERNAL_ERROR, "Failed to calculate camera frustum");
1123                 if (theCamera->flags.testFlag(QSSGRenderCamera::Flag::GloballyActive))
1124                     camera = theCamera;
1125             }
1126             layer.renderedCamera = camera;
1127 
1128             // Lights
1129             for (auto rIt = lights.crbegin(); rIt != lights.crend(); rIt++) {
1130                 QSSGRenderLight *theLight = *rIt;
1131                 wasDataDirty = wasDataDirty || theLight->flags.testFlag(QSSGRenderNode::Flag::Dirty);
1132                 bool lightResult = theLight->calculateGlobalVariables();
1133                 wasDataDirty = lightResult || wasDataDirty;
1134                 // Note we setup the light index such that it is completely invariant of if
1135                 // the
1136                 // light is active or scoped.
1137                 quint32 lightIndex = (quint32)sourceLightDirections.size();
1138                 sourceLightDirections.push_back(QVector3D(0.0, 0.0, 0.0));
1139                 // Note we still need a light check when building the renderable light list.
1140                 // We also cannot cache shader-light bindings based on layers any more
1141                 // because
1142                 // the number of lights for a given renderable does not depend on the layer
1143                 // as it used to but
1144                 // additional perhaps on the light's scoping rules.
1145                 if (theLight->flags.testFlag(QSSGRenderLight::Flag::GloballyActive)) {
1146                     if (theLight->m_scope == nullptr) {
1147                         globalLights.push_back(theLight);
1148                         if (renderer->context()->renderContextType() != QSSGRenderContextType::GLES2
1149                                 && theLight->m_castShadow) {
1150                             if (!shadowMapManager)
1151                                 createShadowMapManager();
1152 
1153                             // PKC -- use of "res" as an exponent of two is an annoying
1154                             // artifact of the XML interface
1155                             // I'll change this with an enum interface later on, but that's
1156                             // less important right now.
1157                             quint32 mapSize = 1 << theLight->m_shadowMapRes;
1158                             ShadowMapModes mapMode = (theLight->m_lightType != QSSGRenderLight::Type::Directional)
1159                                     ? ShadowMapModes::CUBE
1160                                     : ShadowMapModes::VSM;
1161                             shadowMapManager->addShadowMapEntry(globalLights.size() - 1,
1162                                                                 mapSize,
1163                                                                 mapSize,
1164                                                                 QSSGRenderTextureFormat::R16F,
1165                                                                 1,
1166                                                                 mapMode,
1167                                                                 ShadowFilterValues::NONE);
1168                             thePrepResult.flags.setRequiresShadowMapPass(true);
1169                             setShaderFeature(QSSGShaderDefines::asString(QSSGShaderDefines::Ssm), true);
1170                         }
1171                     }
1172                     TLightToNodeMap::iterator iter = lightToNodeMap.insert(theLight, (QSSGRenderNode *)nullptr);
1173                     QSSGRenderNode *oldLightScope = iter.value();
1174                     QSSGRenderNode *newLightScope = theLight->m_scope;
1175 
1176                     if (oldLightScope != newLightScope) {
1177                         iter.value() = newLightScope;
1178                         if (oldLightScope)
1179                             theLightNodeMarkers.push_back(QSSGLightNodeMarker(*theLight, lightIndex, *oldLightScope, false));
1180                         if (newLightScope)
1181                             theLightNodeMarkers.push_back(QSSGLightNodeMarker(*theLight, lightIndex, *newLightScope, true));
1182                     }
1183                     if (newLightScope) {
1184                         sourceLightDirections.back() = theLight->getScalingCorrectDirection();
1185                     }
1186                 }
1187             }
1188             if (!theLightNodeMarkers.empty()) {
1189                 for (auto rIt = renderableNodes.rbegin();
1190                         rIt != renderableNodes.rend(); rIt++) {
1191                     QSSGRenderableNodeEntry &theNodeEntry(*rIt);
1192                     quint32 nodeDFSIndex = theNodeEntry.node->dfsIndex;
1193                     for (quint32 markerIdx = 0, markerEnd = theLightNodeMarkers.size(); markerIdx < markerEnd; ++markerIdx) {
1194                         QSSGLightNodeMarker &theMarker = theLightNodeMarkers[markerIdx];
1195                         if (nodeDFSIndex >= theMarker.firstValidIndex && nodeDFSIndex < theMarker.justPastLastValidIndex) {
1196                             if (theMarker.addOrRemove) {
1197                                 QSSGNodeLightEntry *theNewEntry = new QSSGNodeLightEntry(theMarker.light, theMarker.lightIndex);
1198                                 theNodeEntry.lights.push_back(*theNewEntry);
1199                             } else {
1200                                 for (QSSGNodeLightEntryList::iterator lightIter = theNodeEntry.lights.begin(),
1201                                                                         lightEnd = theNodeEntry.lights.end();
1202                                      lightIter != lightEnd;
1203                                      ++lightIter) {
1204                                     if (lightIter->light == theMarker.light) {
1205                                         QSSGNodeLightEntry &theEntry = *lightIter;
1206                                         theNodeEntry.lights.remove(theEntry);
1207                                         delete &theEntry;
1208                                         break;
1209                                     }
1210                                 }
1211                             }
1212                         }
1213                     }
1214                 }
1215             }
1216 
1217             if (camera) {
1218                 camera->calculateViewProjectionMatrix(viewProjection);
1219                 if (camera->enableFrustumClipping) {
1220                     QSSGClipPlane nearPlane;
1221                     QMatrix3x3 theUpper33(camera->globalTransform.normalMatrix());
1222 
1223                     QVector3D dir(mat33::transform(theUpper33, QVector3D(0, 0, -1)));
1224                     dir.normalize();
1225                     nearPlane.normal = dir;
1226                     QVector3D theGlobalPos = camera->getGlobalPos() + camera->clipNear * dir;
1227                     nearPlane.d = -(QVector3D::dotProduct(dir, theGlobalPos));
1228                     // the near plane's bbox edges are calculated in the clipping frustum's
1229                     // constructor.
1230                     clippingFrustum = QSSGClippingFrustum(viewProjection, nearPlane);
1231                 } else if (clippingFrustum.hasValue()) {
1232                     clippingFrustum.setEmpty();
1233                 }
1234             } else
1235                 viewProjection = QMatrix4x4();
1236 
1237             // Setup the light directions here.
1238 
1239             for (qint32 lightIdx = 0, lightEnd = globalLights.size(); lightIdx < lightEnd; ++lightIdx) {
1240                 lightDirections.push_back(globalLights.at(lightIdx)->getScalingCorrectDirection());
1241             }
1242 
1243             modelContexts.clear();
1244 
1245             bool renderablesDirty = prepareRenderablesForRender(viewProjection,
1246                                                                 clippingFrustum,
1247                                                                 thePrepResult.flags);
1248             wasDataDirty = wasDataDirty || renderablesDirty;
1249         }
1250     }
1251     wasDirty = wasDirty || wasDataDirty;
1252     thePrepResult.flags.setWasDirty(wasDirty);
1253     thePrepResult.flags.setLayerDataDirty(wasDataDirty);
1254 
1255     layerPrepResult = thePrepResult;
1256 
1257     // Per-frame cache of renderable objects post-sort.
1258     getOpaqueRenderableObjects();
1259     // If layer depth test is false, this may also contain opaque objects.
1260     getTransparentRenderableObjects();
1261 
1262     getCameraDirection();
1263 }
1264 
resetForFrame()1265 void QSSGLayerRenderPreparationData::resetForFrame()
1266 {
1267     transparentObjects.clear();
1268     opaqueObjects.clear();
1269     layerPrepResult.setEmpty();
1270     // The check for if the camera is or is not null is used
1271     // to figure out if this layer was rendered at all.
1272     camera = nullptr;
1273     cameraDirection.setEmpty();
1274     lightDirections.clear();
1275     renderedOpaqueObjects.clear();
1276     renderedTransparentObjects.clear();
1277     renderedItem2Ds.clear();
1278 }
1279 
1280 QT_END_NAMESPACE
1281