1// Copyright (C) 2008-2012 NVIDIA Corporation.
2// Copyright (C) 2022 The Qt Company Ltd.
3// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
4
5#include "qssglayerrenderdata_p.h"
6
7#include <QtQuick3DRuntimeRender/private/qssgrenderer_p.h>
8#include <QtQuick3DRuntimeRender/private/qssgrenderlight_p.h>
9#include <QtQuick3DRuntimeRender/private/qssgrhicustommaterialsystem_p.h>
10#include <QtQuick3DRuntimeRender/private/qssgrhiquadrenderer_p.h>
11#include <QtQuick3DRuntimeRender/private/qssgrhiparticles_p.h>
12#include <QtQuick3DRuntimeRender/private/qssgrenderlayer_p.h>
13#include <QtQuick3DRuntimeRender/private/qssgrendereffect_p.h>
14#include <QtQuick3DRuntimeRender/private/qssgrendercamera_p.h>
15#include <QtQuick3DRuntimeRender/private/qssgrenderskeleton_p.h>
16#include <QtQuick3DRuntimeRender/private/qssgrenderjoint_p.h>
17#include <QtQuick3DRuntimeRender/private/qssgrendermorphtarget_p.h>
18#include <QtQuick3DRuntimeRender/private/qssgrenderparticles_p.h>
19#include "../graphobjects/qssgrenderroot_p.h"
20#include "../qssgrendercontextcore.h"
21#include <QtQuick3DRuntimeRender/private/qssgrenderbuffermanager_p.h>
22#include <QtQuick3DRuntimeRender/private/qssgrendershadercache_p.h>
23#include <QtQuick3DRuntimeRender/private/qssgperframeallocator_p.h>
24#include <QtQuick3DRuntimeRender/private/qssgruntimerenderlogging_p.h>
25#include <QtQuick3DRuntimeRender/private/qssglightmapper_p.h>
26#include <QtQuick3DRuntimeRender/private/qssgdebugdrawsystem_p.h>
27
28#include <QtQuick3DUtils/private/qssgutils_p.h>
29#include <QtQuick3DUtils/private/qssgassert_p.h>
30
31#include <QtQuick/private/qsgtexture_p.h>
32#include <QtQuick/private/qsgrenderer_p.h>
33
34#include <array>
35
36#include "qssgrenderpass_p.h"
37#include "rendererimpl/qssgrenderhelpers_p.h"
38
39QT_BEGIN_NAMESPACE
40
41Q_STATIC_LOGGING_CATEGORY(lcQuick3DRender, "qt.quick3d.render");
42
43#define POS4BONETRANS(x) (sizeof(float) * 16 * (x) * 2)
44#define POS4BONENORM(x) (sizeof(float) * 16 * ((x) * 2 + 1))
45#define BONEDATASIZE4ID(x) POS4BONETRANS(x + 1)
46
47static bool checkParticleSupport(QRhi *rhi)
48{
49 QSSG_ASSERT(rhi, return false);
50
51 bool ret = true;
52 const bool supportRgba32f = rhi->isTextureFormatSupported(format: QRhiTexture::RGBA32F);
53 const bool supportRgba16f = rhi->isTextureFormatSupported(format: QRhiTexture::RGBA16F);
54 if (!supportRgba32f && !supportRgba16f) {
55 static bool warningShown = false;
56 if (!warningShown) {
57 qWarning () << "Particles not supported due to missing RGBA32F and RGBA16F texture format support";
58 warningShown = true;
59 }
60 ret = false;
61 }
62
63 return ret;
64}
65
66struct LayerNodeStatResult
67{
68 qsizetype modelCount = 0;
69 qsizetype particlesCount = 0;
70 qsizetype item2DCount = 0;
71 qsizetype cameraCount = 0;
72 qsizetype lightCount = 0;
73 qsizetype reflectionProbeCount = 0;
74 qsizetype otherCount = 0;
75
76 friend LayerNodeStatResult &operator+=(LayerNodeStatResult &lhs, const LayerNodeStatResult &rhs)
77 {
78 lhs.modelCount += rhs.modelCount;
79 lhs.particlesCount += rhs.particlesCount;
80 lhs.item2DCount += rhs.item2DCount;
81 lhs.cameraCount += rhs.cameraCount;
82 lhs.lightCount += rhs.lightCount;
83 lhs.reflectionProbeCount += rhs.reflectionProbeCount;
84 lhs.otherCount += rhs.otherCount;
85 return lhs;
86 }
87
88 friend QDebug operator<<(QDebug dbg, const LayerNodeStatResult &stat)
89 {
90 dbg.nospace() << "LayerNodeStatResult(modelCount: " << stat.modelCount
91 << ", particlesCount: " << stat.particlesCount
92 << ", item2DCount: " << stat.item2DCount
93 << ", cameraCount: " << stat.cameraCount
94 << ", lightCount: " << stat.lightCount
95 << ", reflectionProbeCount: " << stat.reflectionProbeCount
96 << ", otherCount: " << stat.otherCount
97 << ")";
98 return dbg.space();
99 }
100};
101
102static LayerNodeStatResult statLayerNodes(const QSSGLayerRenderData::LayerNodes &layerNodes) {
103
104 LayerNodeStatResult stat;
105
106 for (auto *node : layerNodes) {
107 if (node->getGlobalState(stateFlag: QSSGRenderNode::GlobalState::Active)) {
108 if (node->type == QSSGRenderGraphObject::Type::Model)
109 ++stat.modelCount;
110 else if (node->type == QSSGRenderGraphObject::Type::Particles)
111 ++stat.particlesCount;
112 else if (node->type == QSSGRenderGraphObject::Type::Item2D)
113 ++stat.item2DCount;
114 else if (node->type == QSSGRenderGraphObject::Type::ReflectionProbe)
115 ++stat.reflectionProbeCount;
116 else if (QSSGRenderGraphObject::isCamera(type: node->type))
117 ++stat.cameraCount;
118 else if (QSSGRenderGraphObject::isLight(type: node->type))
119 ++stat.lightCount;
120 else
121 ++stat.otherCount;
122 }
123 }
124
125 return stat;
126}
127
128// These are meant to be pixel offsets, so you need to divide them by the width/height
129// of the layer respectively.
130static const QVector2D s_ProgressiveAAVertexOffsets[QSSGLayerRenderData::MAX_AA_LEVELS] = {
131 QVector2D(-0.170840f, -0.553840f), // 1x
132 QVector2D(0.162960f, -0.319340f), // 2x
133 QVector2D(0.360260f, -0.245840f), // 3x
134 QVector2D(-0.561340f, -0.149540f), // 4x
135 QVector2D(0.249460f, 0.453460f), // 5x
136 QVector2D(-0.336340f, 0.378260f), // 6x
137 QVector2D(0.340000f, 0.166260f), // 7x
138 QVector2D(0.235760f, 0.527760f), // 8x
139};
140
141qsizetype QSSGLayerRenderData::frustumCulling(const QSSGClippingFrustum &clipFrustum, const QSSGRenderableObjectList &renderables, QSSGRenderableObjectList &visibleRenderables)
142{
143 QSSG_ASSERT(visibleRenderables.isEmpty(), visibleRenderables.clear());
144 visibleRenderables.reserve(asize: renderables.size());
145 for (quint32 end = renderables.size(), idx = quint32(0); idx != end; ++idx) {
146 auto handle = renderables.at(i: idx);
147 const auto &b = handle.obj->globalBounds;
148 if (clipFrustum.intersectsWith(bounds: b))
149 visibleRenderables.push_back(t: handle);
150 }
151
152 return visibleRenderables.size();
153}
154
155qsizetype QSSGLayerRenderData::frustumCullingInline(const QSSGClippingFrustum &clipFrustum, QSSGRenderableObjectList &renderables)
156{
157 const qint32 end = renderables.size();
158 qint32 front = 0;
159 qint32 back = end - 1;
160
161 while (front <= back) {
162 const auto &b = renderables.at(i: front).obj->globalBounds;
163 if (clipFrustum.intersectsWith(bounds: b))
164 ++front;
165 else
166 renderables.swapItemsAt(i: front, j: back--);
167 }
168
169 return back + 1;
170}
171
172[[nodiscard]] constexpr static inline bool nearestToFurthestCompare(const QSSGRenderableObjectHandle &lhs, const QSSGRenderableObjectHandle &rhs) noexcept
173{
174 return lhs.cameraDistanceSq < rhs.cameraDistanceSq;
175}
176
177[[nodiscard]] constexpr static inline bool furthestToNearestCompare(const QSSGRenderableObjectHandle &lhs, const QSSGRenderableObjectHandle &rhs) noexcept
178{
179 return lhs.cameraDistanceSq > rhs.cameraDistanceSq;
180}
181
182static void collectBoneTransforms(const QSSGLayerRenderData &renderData, QSSGRenderNode *node, QSSGRenderSkeleton *skeletonNode, const QVector<QMatrix4x4> &poses)
183{
184 if (node->type == QSSGRenderGraphObject::Type::Joint) {
185 QSSGRenderJoint *jointNode = static_cast<QSSGRenderJoint *>(node);
186 Q_ASSERT(!jointNode->isDirty(QSSGRenderNode::DirtyFlag::GlobalValuesDirty));
187 QMatrix4x4 globalTrans = renderData.getGlobalTransform(node: *jointNode);
188 // if user doesn't give the inverseBindPose, identity matrices are used.
189 if (poses.size() > jointNode->index)
190 globalTrans *= poses[jointNode->index];
191 memcpy(dest: skeletonNode->boneData.data() + POS4BONETRANS(jointNode->index),
192 src: reinterpret_cast<const void *>(globalTrans.constData()),
193 n: sizeof(float) * 16);
194 // only upper 3x3 is meaningful
195 memcpy(dest: skeletonNode->boneData.data() + POS4BONENORM(jointNode->index),
196 src: reinterpret_cast<const void *>(QMatrix4x4(globalTrans.normalMatrix()).constData()),
197 n: sizeof(float) * 11);
198 } else {
199 skeletonNode->containsNonJointNodes = true;
200 }
201 for (auto &child : node->children)
202 collectBoneTransforms(renderData, node: &child, skeletonNode, poses);
203}
204
205static bool hasDirtyNonJointNodes(QSSGRenderNode *node, bool &hasChildJoints)
206{
207 if (!node)
208 return false;
209 // we might be non-joint dirty node, but if we do not have child joints we need to return false
210 // Note! The frontend clears TransformDirty. Use dirty instead.
211 bool dirtyNonJoint = ((node->type != QSSGRenderGraphObject::Type::Joint)
212 && node->isDirty());
213
214 // Tell our parent we are joint
215 if (node->type == QSSGRenderGraphObject::Type::Joint)
216 hasChildJoints = true;
217 bool nodeHasChildJoints = false;
218 for (auto &child : node->children) {
219 bool ret = hasDirtyNonJointNodes(node: &child, hasChildJoints&: nodeHasChildJoints);
220 // return if we have child joints and non-joint dirty nodes, else check other children
221 hasChildJoints |= nodeHasChildJoints;
222 if (ret && nodeHasChildJoints)
223 return true;
224 }
225 // return true if we have child joints and we are dirty non-joint
226 hasChildJoints |= nodeHasChildJoints;
227 return dirtyNonJoint && nodeHasChildJoints;
228}
229
230#define MAX_MORPH_TARGET 8
231#define MAX_MORPH_TARGET_INDEX_SUPPORTS_NORMALS 3
232#define MAX_MORPH_TARGET_INDEX_SUPPORTS_TANGENTS 1
233
234QSSGDefaultMaterialPreparationResult::QSSGDefaultMaterialPreparationResult(QSSGShaderDefaultMaterialKey inKey)
235 : firstImage(nullptr), opacity(1.0f), materialKey(inKey), dirty(false)
236{
237}
238
239QSSGRenderCameraData QSSGLayerRenderData::getCameraDataImpl(const QSSGRenderCamera *camera) const
240{
241 QSSGRenderCameraData ret;
242 if (camera) {
243 // Calculate viewProjection and clippingFrustum for Render Camera
244 QMatrix4x4 viewProjection(Qt::Uninitialized);
245 QMatrix4x4 cameraGlobalTransform = getGlobalTransform(node: *camera);
246 camera->calculateViewProjectionMatrix(globalTransform: cameraGlobalTransform, outMatrix&: viewProjection);
247 std::optional<QSSGClippingFrustum> clippingFrustum;
248 const QMatrix4x4 camGlobalTransform = getGlobalTransform(node: *camera);
249 const QVector3D camGlobalPos = QSSGRenderNode::getGlobalPos(globalTransform: camGlobalTransform);
250 if (camera->enableFrustumClipping) {
251 QSSGClipPlane nearPlane;
252 QMatrix3x3 theUpper33(camGlobalTransform.normalMatrix());
253 QVector3D dir(QSSGUtils::mat33::transform(m: theUpper33, v: QVector3D(0, 0, -1)));
254 dir.normalize();
255 nearPlane.normal = dir;
256 QVector3D theGlobalPos = camGlobalPos + camera->clipPlanes.clipNear() * dir;
257 nearPlane.d = -(QVector3D::dotProduct(v1: dir, v2: theGlobalPos));
258 // the near plane's bbox edges are calculated in the clipping frustum's
259 // constructor.
260 clippingFrustum = QSSGClippingFrustum{viewProjection, nearPlane};
261 }
262 QMatrix4x4 globalTransform = getGlobalTransform(node: *camera);
263 ret = { .viewProjection: viewProjection, .clippingFrustum: clippingFrustum, .direction: camera->getScalingCorrectDirection(globalTransform), .position: camGlobalPos };
264 }
265
266 return ret;
267}
268
269// Returns the cached data for the active render camera(s) (if any)
270const QSSGRenderCameraDataList &QSSGLayerRenderData::getCachedCameraDatas()
271{
272 ensureCachedCameraDatas();
273 return *renderedCameraData;
274}
275
276void QSSGLayerRenderData::ensureCachedCameraDatas()
277{
278 if (renderedCameraData.has_value())
279 return;
280
281 QSSGRenderCameraDataList cameraData;
282 for (QSSGRenderCamera *cam : std::as_const(t&: renderedCameras))
283 cameraData.append(t: getCameraDataImpl(camera: cam));
284 renderedCameraData = std::move(cameraData);
285}
286
287[[nodiscard]] static inline float getCameraDistanceSq(const QSSGRenderableObject &obj,
288 const QSSGRenderCameraData &camera) noexcept
289{
290 const QVector3D difference = obj.worldCenterPoint - camera.position;
291 return QVector3D::dotProduct(v1: difference, v2: camera.direction) + obj.depthBiasSq;
292}
293
294// Per-frame cache of renderable objects post-sort.
295const QVector<QSSGRenderableObjectHandle> &QSSGLayerRenderData::getSortedOpaqueRenderableObjects(const QSSGRenderCamera &camera, size_t index)
296{
297 index = index * size_t(index < opaqueObjectStore.size());
298 auto &sortedOpaqueObjects = sortedOpaqueObjectCache[index][&camera];
299 if (!sortedOpaqueObjects.empty())
300 return sortedOpaqueObjects;
301
302 if (layer.layerFlags.testFlag(flag: QSSGRenderLayer::LayerFlag::EnableDepthTest))
303 sortedOpaqueObjects = std::as_const(t&: opaqueObjectStore)[index];
304
305 const auto &clippingFrustum = getCameraRenderData(camera: &camera).clippingFrustum;
306 if (clippingFrustum.has_value()) { // Frustum culling
307 const auto visibleObjects = QSSGLayerRenderData::frustumCullingInline(clipFrustum: clippingFrustum.value(), renderables&: sortedOpaqueObjects);
308 sortedOpaqueObjects.resize(size: visibleObjects);
309 }
310
311 // Render nearest to furthest objects
312 std::sort(first: sortedOpaqueObjects.begin(), last: sortedOpaqueObjects.end(), comp: nearestToFurthestCompare);
313
314 return sortedOpaqueObjects;
315}
316
317// If layer depth test is false, this may also contain opaque objects.
318const QVector<QSSGRenderableObjectHandle> &QSSGLayerRenderData::getSortedTransparentRenderableObjects(const QSSGRenderCamera &camera, size_t index)
319{
320 index = index * size_t(index < transparentObjectStore.size());
321 auto &sortedTransparentObjects = sortedTransparentObjectCache[index][&camera];
322
323 if (!sortedTransparentObjects.empty())
324 return sortedTransparentObjects;
325
326 sortedTransparentObjects = std::as_const(t&: transparentObjectStore)[index];
327
328 if (!layer.layerFlags.testFlag(flag: QSSGRenderLayer::LayerFlag::EnableDepthTest)) {
329 const auto &opaqueObjects = std::as_const(t&: opaqueObjectStore)[index];
330 sortedTransparentObjects.append(l: opaqueObjects);
331 }
332
333 const auto &clippingFrustum = getCameraRenderData(camera: &camera).clippingFrustum;
334 if (clippingFrustum.has_value()) { // Frustum culling
335 const auto visibleObjects = QSSGLayerRenderData::frustumCullingInline(clipFrustum: clippingFrustum.value(), renderables&: sortedTransparentObjects);
336 sortedTransparentObjects.resize(size: visibleObjects);
337 }
338
339 // render furthest to nearest.
340 std::sort(first: sortedTransparentObjects.begin(), last: sortedTransparentObjects.end(), comp: furthestToNearestCompare);
341
342 return sortedTransparentObjects;
343}
344
345const QVector<QSSGRenderableObjectHandle> &QSSGLayerRenderData::getSortedScreenTextureRenderableObjects(const QSSGRenderCamera &camera, size_t index)
346{
347 index = index * size_t(index < screenTextureObjectStore.size());
348 const auto &screenTextureObjects = std::as_const(t&: screenTextureObjectStore)[index];
349 auto &renderedScreenTextureObjects = sortedScreenTextureObjectCache[index][&camera];
350
351 if (!renderedScreenTextureObjects.empty())
352 return renderedScreenTextureObjects;
353 renderedScreenTextureObjects = screenTextureObjects;
354 if (!renderedScreenTextureObjects.empty()) {
355 // render furthest to nearest.
356 std::sort(first: renderedScreenTextureObjects.begin(), last: renderedScreenTextureObjects.end(), comp: furthestToNearestCompare);
357 }
358 return renderedScreenTextureObjects;
359}
360
361const QVector<QSSGBakedLightingModel> &QSSGLayerRenderData::getSortedBakedLightingModels()
362{
363 if (!renderedBakedLightingModels.empty() || renderedCameras.isEmpty())
364 return renderedBakedLightingModels;
365 if (layer.layerFlags.testFlag(flag: QSSGRenderLayer::LayerFlag::EnableDepthTest) && !bakedLightingModels.empty()) {
366 renderedBakedLightingModels = bakedLightingModels;
367 for (QSSGBakedLightingModel &lm : renderedBakedLightingModels) {
368 // sort nearest to furthest (front to back)
369 std::sort(first: lm.renderables.begin(), last: lm.renderables.end(), comp: nearestToFurthestCompare);
370 }
371 }
372 return renderedBakedLightingModels;
373}
374
375const QSSGLayerRenderData::RenderableItem2DEntries &QSSGLayerRenderData::getRenderableItem2Ds()
376{
377 if (!renderedItem2Ds.isEmpty() || renderedCameras.isEmpty())
378 return renderedItem2Ds;
379
380 // Maintain QML item order
381 renderedItem2Ds = { std::make_reverse_iterator(i: item2DsView.end()),
382 std::make_reverse_iterator(i: item2DsView.begin()) };
383
384 if (!renderedItem2Ds.isEmpty()) {
385 const QSSGRenderCameraDataList &cameraDatas(getCachedCameraDatas());
386 // with multiview this means using the left eye camera
387 const QSSGRenderCameraData &cameraDirectionAndPosition(cameraDatas[0]);
388 const QVector3D &cameraDirection = cameraDirectionAndPosition.direction;
389 const QVector3D &cameraPosition = cameraDirectionAndPosition.position;
390
391 const auto isItemNodeDistanceGreatThan = [this, cameraDirection, cameraPosition]
392 (const QSSGRenderItem2D *lhs, const QSSGRenderItem2D *rhs) {
393 if (!lhs->parent || !rhs->parent)
394 return false;
395
396 auto lhsGlobalTransform = getGlobalTransform(node: *lhs->parent);
397 auto rhsGlobalTransform = getGlobalTransform(node: *rhs->parent);
398
399 const QVector3D lhsDifference = QSSGRenderNode::getGlobalPos(globalTransform: lhsGlobalTransform) - cameraPosition;
400 const float lhsCameraDistanceSq = QVector3D::dotProduct(v1: lhsDifference, v2: cameraDirection);
401 const QVector3D rhsDifference = QSSGRenderNode::getGlobalPos(globalTransform: rhsGlobalTransform) - cameraPosition;
402 const float rhsCameraDistanceSq = QVector3D::dotProduct(v1: rhsDifference, v2: cameraDirection);
403 return lhsCameraDistanceSq > rhsCameraDistanceSq;
404 };
405
406 // Render furthest to nearest items (parent nodes).
407 std::stable_sort(first: renderedItem2Ds.begin(), last: renderedItem2Ds.end(), comp: isItemNodeDistanceGreatThan);
408 }
409
410 return renderedItem2Ds;
411}
412
413// Depth Write List
414void QSSGLayerRenderData::updateSortedDepthObjectsListImp(const QSSGRenderCamera &camera, size_t index)
415{
416 auto &depthWriteObjects = sortedDepthWriteCache[index][&camera];
417 auto &depthPrepassObjects = sortedOpaqueDepthPrepassCache[index][&camera];
418
419 if (!depthWriteObjects.isEmpty() || !depthPrepassObjects.isEmpty())
420 return;
421
422 if (layer.layerFlags.testFlag(flag: QSSGRenderLayer::LayerFlag::EnableDepthTest)) {
423 if (hasDepthWriteObjects || (depthPrepassObjectsState & DepthPrepassObjectStateT(DepthPrepassObject::Opaque)) != 0) {
424 const auto &sortedOpaqueObjects = getSortedOpaqueRenderableObjects(camera, index); // front to back
425 for (const auto &opaqueObject : sortedOpaqueObjects) {
426 const auto depthMode = opaqueObject.obj->depthWriteMode;
427 if (depthMode == QSSGDepthDrawMode::Always || depthMode == QSSGDepthDrawMode::OpaqueOnly)
428 depthWriteObjects.append(t: opaqueObject);
429 else if (depthMode == QSSGDepthDrawMode::OpaquePrePass)
430 depthPrepassObjects.append(t: opaqueObject);
431 }
432 }
433 if (hasDepthWriteObjects || (depthPrepassObjectsState & DepthPrepassObjectStateT(DepthPrepassObject::Transparent)) != 0) {
434 const auto &sortedTransparentObjects = getSortedTransparentRenderableObjects(camera, index); // back to front
435 for (const auto &transparentObject : sortedTransparentObjects) {
436 const auto depthMode = transparentObject.obj->depthWriteMode;
437 if (depthMode == QSSGDepthDrawMode::Always)
438 depthWriteObjects.append(t: transparentObject);
439 else if (depthMode == QSSGDepthDrawMode::OpaquePrePass)
440 depthPrepassObjects.append(t: transparentObject);
441 }
442 }
443 if (hasDepthWriteObjects || (depthPrepassObjectsState & DepthPrepassObjectStateT(DepthPrepassObject::ScreenTexture)) != 0) {
444 const auto &sortedScreenTextureObjects = getSortedScreenTextureRenderableObjects(camera, index); // back to front
445 for (const auto &screenTextureObject : sortedScreenTextureObjects) {
446 const auto depthMode = screenTextureObject.obj->depthWriteMode;
447 if (depthMode == QSSGDepthDrawMode::Always || depthMode == QSSGDepthDrawMode::OpaqueOnly)
448 depthWriteObjects.append(t: screenTextureObject);
449 else if (depthMode == QSSGDepthDrawMode::OpaquePrePass)
450 depthPrepassObjects.append(t: screenTextureObject);
451 }
452 }
453 }
454}
455
456const std::unique_ptr<QSSGPerFrameAllocator> &QSSGLayerRenderData::perFrameAllocator(QSSGRenderContextInterface &ctx)
457{
458 return ctx.perFrameAllocator();
459}
460
461void QSSGLayerRenderData::saveRenderState(const QSSGRenderer &renderer)
462{
463 QSSG_CHECK(!savedRenderState.has_value());
464 savedRenderState = std::make_optional<SavedRenderState>(t: { .viewport: renderer.m_viewport, .scissorRect: renderer.m_scissorRect, .dpr: renderer.m_dpr });
465}
466
467void QSSGLayerRenderData::restoreRenderState(QSSGRenderer &renderer)
468{
469 QSSG_ASSERT(savedRenderState.has_value(), return);
470
471 renderer.m_viewport = savedRenderState->viewport;
472 renderer.m_scissorRect = savedRenderState->scissorRect;
473 renderer.m_dpr = savedRenderState->dpr;
474 savedRenderState.reset();
475}
476
477static constexpr quint16 PREP_CTX_INDEX_MASK = 0xffff;
478static constexpr QSSGPrepContextId createPrepId(size_t index, quint32 frame) { return QSSGPrepContextId { ((quint64(frame) << 32) | index ) * quint64(index <= std::numeric_limits<quint16>::max()) }; }
479static constexpr size_t getPrepContextIndex(QSSGPrepContextId id) { return (static_cast<quint64>(id) & PREP_CTX_INDEX_MASK); }
480static constexpr bool verifyPrepContext(QSSGPrepContextId id, const QSSGRenderer &renderer) { return (getPrepContextIndex(id) > 0) && ((static_cast<quint64>(id) >> 32) == renderer.frameCount()); }
481
482QSSGPrepContextId QSSGLayerRenderData::getOrCreateExtensionContext(const QSSGRenderExtension &ext, QSSGRenderCamera *camera, quint32 slot)
483{
484 const auto frame = renderer->frameCount();
485 const auto index = extContexts.size();
486 // Sanity check... Shouldn't get anywhere close to the max in real world usage (unless somethings broken).
487 QSSG_ASSERT_X(index < PREP_CTX_INDEX_MASK - 1, "Reached maximum entries!", return QSSGPrepContextId::Invalid);
488 auto it = std::find_if(first: extContexts.cbegin(), last: extContexts.cend(), pred: [&ext, slot](const ExtensionContext &e){ return (e.owner == &ext) && (e.slot == slot); });
489 if (it == extContexts.cend()) {
490 extContexts.push_back(x: ExtensionContext{ ext, camera, index, slot });
491 it = extContexts.cbegin() + index;
492 renderableModelStore.emplace_back();
493 modelContextStore.emplace_back();
494 renderableObjectStore.emplace_back();
495 screenTextureObjectStore.emplace_back();
496 opaqueObjectStore.emplace_back();
497 transparentObjectStore.emplace_back();
498 sortedOpaqueObjectCache.emplace_back();
499 sortedTransparentObjectCache.emplace_back();
500 sortedScreenTextureObjectCache.emplace_back();
501 sortedOpaqueDepthPrepassCache.emplace_back();
502 sortedDepthWriteCache.emplace_back();
503 QSSG_ASSERT(renderableModelStore.size() == extContexts.size(), renderableModelStore.resize(extContexts.size()));
504 QSSG_ASSERT(modelContextStore.size() == extContexts.size(), modelContextStore.resize(extContexts.size()));
505 QSSG_ASSERT(renderableObjectStore.size() == extContexts.size(), renderableObjectStore.resize(extContexts.size()));
506 QSSG_ASSERT(screenTextureObjectStore.size() == extContexts.size(), screenTextureObjectStore.resize(extContexts.size()));
507 QSSG_ASSERT(opaqueObjectStore.size() == extContexts.size(), opaqueObjectStore.resize(extContexts.size()));
508 QSSG_ASSERT(transparentObjectStore.size() == extContexts.size(), transparentObjectStore.resize(extContexts.size()));
509 QSSG_ASSERT(sortedOpaqueObjectCache.size() == extContexts.size(), sortedOpaqueObjectCache.resize(extContexts.size()));
510 QSSG_ASSERT(sortedTransparentObjectCache.size() == extContexts.size(), sortedTransparentObjectCache.resize(extContexts.size()));
511 QSSG_ASSERT(sortedScreenTextureObjectCache.size() == extContexts.size(), sortedScreenTextureObjectCache.resize(extContexts.size()));
512 QSSG_ASSERT(sortedOpaqueDepthPrepassCache.size() == extContexts.size(), sortedOpaqueDepthPrepassCache.resize(extContexts.size()));
513 QSSG_ASSERT(sortedDepthWriteCache.size() == extContexts.size(), sortedDepthWriteCache.resize(extContexts.size()));
514 }
515
516 return createPrepId(index: it->index, frame);
517}
518
519static void createRenderablesHelper(QSSGLayerRenderData &layer, const QSSGRenderNode::ChildList &children, QSSGLayerRenderData::RenderableNodeEntries &renderables, QSSGRenderHelpers::CreateFlags createFlags)
520{
521 const bool steal = ((createFlags & QSSGRenderHelpers::CreateFlag::Steal) != 0);
522 for (auto &chld : children) {
523 if (chld.type == QSSGRenderGraphObject::Type::Model) {
524 const auto &renderModel = static_cast<const QSSGRenderModel &>(chld);
525 auto &renderableModels = layer.renderableModels;
526 if (auto it = std::find_if(first: renderableModels.cbegin(), last: renderableModels.cend(), pred: [&renderModel](const QSSGRenderableNodeEntry &e) { return (e.node == &renderModel); }); it != renderableModels.cend()) {
527 renderables.emplace_back(args: *it);
528 if (steal)
529 renderableModels.erase(pos: it);
530 }
531 }
532
533 createRenderablesHelper(layer, children: chld.children, renderables, createFlags);
534 }
535}
536
537QSSGRenderablesId QSSGLayerRenderData::createRenderables(QSSGPrepContextId prepId, const QList<QSSGNodeId> &nodes, QSSGRenderHelpers::CreateFlags createFlags)
538{
539 QSSG_ASSERT_X(verifyPrepContext(prepId, *renderer), "Expired or invalid prep id", return {});
540
541 const size_t index = getPrepContextIndex(id: prepId);
542 QSSG_ASSERT(index < renderableModelStore.size(), return {});
543
544 auto &renderables = renderableModelStore[index];
545 if (renderables.size() != 0) {
546 qWarning() << "Renderables already created for this context - Previous renderables will be overwritten";
547 renderables.clear();
548 }
549
550 renderables.reserve(asize: nodes.size());
551
552 // We now create the renderable node entries for all the models.
553 // NOTE: The nodes are not complete at this point...
554 const bool steal = ((createFlags & QSSGRenderHelpers::CreateFlag::Steal) != 0);
555 for (const auto &nodeId : nodes) {
556 auto *node = QSSGRenderGraphObjectUtils::getNode<QSSGRenderNode>(nodeId);
557 if (node && node->type == QSSGRenderGraphObject::Type::Model) {
558 auto *renderModel = static_cast<QSSGRenderModel *>(node);
559 // NOTE: Not ideal.
560 if (auto it = std::find_if(first: renderableModels.cbegin(), last: renderableModels.cend(), pred: [renderModel](const QSSGRenderableNodeEntry &e) { return (e.node == renderModel); }); it != renderableModels.cend()) {
561 auto &inserted = renderables.emplace_back(args: *it);
562 inserted.overridden = {};
563 if (steal)
564 it->overridden |= QSSGRenderableNodeEntry::Overridden::Disabled;
565 } else {
566 renderables.emplace_back(args&: *renderModel);
567 }
568 }
569
570 if (node && ((createFlags & QSSGRenderHelpers::CreateFlag::Recurse) != 0)) {
571 const auto &children = node->children;
572 createRenderablesHelper(layer&: *this, children, renderables, createFlags);
573 }
574 }
575
576 // NOTE: Modifying the renderables list isn't ideal and should be done differently
577 // but for now this is the easiest way to get the job done.
578 // We still need to let the layer know it should reset the list once a new
579 // frame starts.
580 renderablesModifiedByExtension = true;
581
582 return (renderables.size() != 0) ? static_cast<QSSGRenderablesId>(prepId) : QSSGRenderablesId{ QSSGRenderablesId::Invalid };
583}
584
585void QSSGLayerRenderData::setGlobalTransform(QSSGRenderablesId renderablesId, const QSSGRenderModel &model, const QMatrix4x4 &globalTransform)
586{
587 const auto prepId = static_cast<QSSGPrepContextId>(renderablesId);
588 QSSG_ASSERT_X(verifyPrepContext(prepId, *renderer), "Expired or invalid renderables id", return);
589 const size_t index = getPrepContextIndex(id: prepId);
590 QSSG_ASSERT_X(index < renderableModelStore.size(), "Missing call to createRenderables()?", return);
591
592 auto &renderables = renderableModelStore[index];
593 auto it = std::find_if(first: renderables.cbegin(), last: renderables.cend(), pred: [&model](const QSSGRenderableNodeEntry &e) { return e.node == &model; });
594 if (it != renderables.cend()) {
595 it->extOverrides.globalTransform = globalTransform;
596 it->overridden |= QSSGRenderableNodeEntry::Overridden::GlobalTransform;
597 }
598}
599
600QMatrix4x4 QSSGLayerRenderData::getGlobalTransform(QSSGPrepContextId prepId, const QSSGRenderModel &model)
601{
602 QSSG_ASSERT_X(verifyPrepContext(prepId, *renderer), "Expired or invalid prep id", return {});
603 const size_t index = getPrepContextIndex(id: prepId);
604 QSSG_ASSERT_X(index < renderableModelStore.size(), "Missing call to createRenderables()?", return {});
605
606 QMatrix4x4 ret = getGlobalTransform(node: model);
607 auto &renderables = renderableModelStore[index];
608 auto it = std::find_if(first: renderables.cbegin(), last: renderables.cend(), pred: [&model](const QSSGRenderableNodeEntry &e) { return e.node == &model; });
609 if (it != renderables.cend() && (it->overridden & QSSGRenderableNodeEntry::Overridden::GlobalTransform))
610 ret = it->extOverrides.globalTransform;
611
612 return ret;
613}
614
615void QSSGLayerRenderData::setGlobalOpacity(QSSGRenderablesId renderablesId, const QSSGRenderModel &model, float opacity)
616{
617 const auto prepId = static_cast<QSSGPrepContextId>(renderablesId);
618 QSSG_ASSERT_X(verifyPrepContext(prepId, *renderer), "Expired or invalid renderables id", return);
619 const size_t index = getPrepContextIndex(id: prepId);
620 QSSG_ASSERT_X(index < renderableModelStore.size(), "Missing call to createRenderables()?", return);
621
622 auto &renderables = renderableModelStore[index];
623 auto it = std::find_if(first: renderables.cbegin(), last: renderables.cend(), pred: [&model](const QSSGRenderableNodeEntry &e) { return e.node == &model; });
624 if (it != renderables.cend()) {
625 it->extOverrides.globalOpacity = opacity;
626 it->overridden |= QSSGRenderableNodeEntry::Overridden::GlobalOpacity;
627 }
628}
629
630float QSSGLayerRenderData::getGlobalOpacity(QSSGPrepContextId prepId, const QSSGRenderModel &model)
631{
632 QSSG_ASSERT_X(verifyPrepContext(prepId, *renderer), "Expired or invalid prep id", return {});
633 const size_t index = getPrepContextIndex(id: prepId);
634 QSSG_ASSERT_X(index < renderableModelStore.size(), "Missing call to createRenderables()?", return {});
635
636 float ret = getGlobalOpacity(node: model);
637 auto &renderables = renderableModelStore[index];
638 auto it = std::find_if(first: renderables.cbegin(), last: renderables.cend(), pred: [&model](const QSSGRenderableNodeEntry &e) { return e.node == &model; });
639 if (it != renderables.cend() && (it->overridden & QSSGRenderableNodeEntry::Overridden::GlobalOpacity))
640 ret = it->extOverrides.globalOpacity;
641
642 return ret;
643}
644
645void QSSGLayerRenderData::setModelMaterials(QSSGRenderablesId renderablesId, const QSSGRenderModel &model, const QList<QSSGResourceId> &materials)
646{
647 const auto prepId = static_cast<QSSGPrepContextId>(renderablesId);
648 QSSG_ASSERT_X(verifyPrepContext(prepId, *renderer), "Expired or invalid renderable id", return);
649 const size_t index = getPrepContextIndex(id: prepId);
650 QSSG_ASSERT(index < renderableModelStore.size(), return);
651
652 // Sanity check
653 if (materials.size() > 0 && !QSSG_GUARD(QSSGRenderGraphObject::isMaterial(QSSGRenderGraphObjectUtils::getResource(materials.at(0))->type)))
654 return;
655
656 auto &renderables = renderableModelStore[index];
657 auto it = std::find_if(first: renderables.cbegin(), last: renderables.cend(), pred: [&model](const QSSGRenderableNodeEntry &e) { return e.node == &model; });
658 if (it != renderables.cend()) {
659 it->extOverrides.materials.resize(size: materials.size());
660 std::memcpy(dest: it->extOverrides.materials.data(), src: materials.data(), n: it->extOverrides.materials.size() * sizeof(QSSGRenderGraphObject *));
661 it->overridden |= QSSGRenderableNodeEntry::Overridden::Materials;
662 }
663}
664
665void QSSGLayerRenderData::setModelMaterials(const QSSGRenderablesId renderablesId, const QList<QSSGResourceId> &materials)
666{
667 const auto prepId = static_cast<QSSGPrepContextId>(renderablesId);
668 QSSG_ASSERT_X(verifyPrepContext(prepId, *renderer), "Expired or invalid renderablesId or renderables id", return);
669
670 const size_t index = getPrepContextIndex(id: prepId);
671 QSSG_ASSERT(index < renderableModelStore.size(), return);
672
673 auto &renderables = renderableModelStore[index];
674 for (auto &renderable : renderables) {
675 auto &renderableMaterials = renderable.extOverrides.materials;
676 renderableMaterials.resize(size: materials.size());
677 std::memcpy(dest: renderableMaterials.data(), src: materials.data(), n: renderableMaterials.size() * sizeof(QSSGRenderGraphObject *));
678 renderable.overridden |= QSSGRenderableNodeEntry::Overridden::Materials;
679 }
680}
681
682QSSGPrepResultId QSSGLayerRenderData::prepareModelsForRender(QSSGRenderContextInterface &contextInterface,
683 QSSGPrepContextId prepId,
684 QSSGRenderablesId renderablesId,
685 float lodThreshold)
686{
687 QSSG_ASSERT_X(renderablesId != QSSGRenderablesId::Invalid && verifyPrepContext(prepId, *renderer),
688 "Expired or invalid prep or renderables id", return QSSGPrepResultId::Invalid);
689 const size_t index = getPrepContextIndex(id: prepId);
690 QSSG_ASSERT(index < renderableModelStore.size(), return {});
691
692 const auto &extContext = extContexts.at(n: index);
693
694 QSSG_ASSERT_X(extContext.camera != nullptr, "No camera set!", return {});
695
696 const auto vp = contextInterface.renderer()->viewport();
697 const float dpr = contextInterface.renderer()->dpr();
698 const float ssaaMultiplier = layer.isSsaaEnabled() ? layer.ssaaMultiplier : 1.0f;
699
700 QSSGRenderCamera::calculateProjectionInternal(camera&: *extContext.camera, inViewport: vp, config: { .dpr: dpr, .ssaaMultiplier: ssaaMultiplier } );
701
702 auto &renderables = renderableModelStore[index];
703
704 static const auto prepareModelMaterials = [](const RenderableNodeEntries &renderables) {
705 for (auto &renderable : renderables) {
706 if ((renderable.overridden & QSSGRenderableNodeEntry::Overridden::Materials) == 0
707 && (renderable.overridden & QSSGRenderableNodeEntry::Overridden::Disabled) == 0) {
708 renderable.extOverrides.materials = static_cast<QSSGRenderModel *>(renderable.node)->materials;
709 }
710 }
711 };
712
713 prepareModelMaterials(renderables);
714
715 // ### multiview
716 QSSGRenderCameraList camera({ extContext.camera });
717 QSSGRenderCameraDataList cameraData({ getCameraRenderData(camera: extContext.camera) });
718
719 auto &modelContexts = modelContextStore[index];
720 QSSG_ASSERT(modelContexts.isEmpty(), modelContexts.clear());
721
722 auto &renderableObjects = renderableObjectStore[index];
723 QSSG_ASSERT(renderableObjects.isEmpty(), renderableObjects.clear());
724
725 auto &opaqueObjects = opaqueObjectStore[index];
726 QSSG_ASSERT(opaqueObjects.isEmpty(), opaqueObjects.clear());
727
728 auto &transparentObjects = transparentObjectStore[index];
729 QSSG_ASSERT(transparentObjects.isEmpty(), transparentObjects.clear());
730
731 auto &screenTextureObjects = screenTextureObjectStore[index];
732 QSSG_ASSERT(screenTextureObjects.isEmpty(), screenTextureObjects.clear());
733
734 bool wasDirty = prepareModelsForRender(ctx&: contextInterface,
735 renderableModels: renderables,
736 ioFlags&: layerPrepResult.flags,
737 allCameras: camera,
738 allCameraData: cameraData,
739 modelContexts,
740 opaqueObjects,
741 transparentObjects,
742 screenTextureObjects,
743 lodThreshold);
744
745 (void)wasDirty;
746
747 return static_cast<QSSGPrepResultId>(prepId);
748}
749
750QMatrix4x4 QSSGLayerRenderData::getGlobalTransform(QSSGRenderNodeHandle h, QMatrix4x4 defaultValue) const
751{
752 return nodeData->getGlobalTransform(h, defaultValue);
753}
754
755QMatrix4x4 QSSGLayerRenderData::getGlobalTransform(QSSGRenderNodeHandle h) const
756{
757 return nodeData->getGlobalTransform(h, defaultValue: QMatrix4x4());
758}
759
760QMatrix4x4 QSSGLayerRenderData::getGlobalTransform(const QSSGRenderNode &node) const
761{
762 return nodeData->getGlobalTransform(h: node.h, defaultValue: node.localTransform);
763}
764
765QMatrix3x3 QSSGLayerRenderData::getNormalMatrix(QSSGRenderModelHandle h) const
766{
767 return modelData->getNormalMatrix(h, defaultValue: QMatrix3x3(Qt::Uninitialized));
768}
769
770QMatrix3x3 QSSGLayerRenderData::getNormalMatrix(const QSSGRenderModel &model) const
771{
772 return modelData->getNormalMatrix(model);
773}
774
775QSSGLayerRenderData::ModelViewProjections QSSGLayerRenderData::getModelMvps(QSSGRenderModelHandle h) const
776{
777 return modelData->getModelViewProjection(h);
778}
779
780QSSGLayerRenderData::ModelViewProjections QSSGLayerRenderData::getModelMvps(const QSSGRenderModel &model) const
781{
782 return modelData->getModelViewProjection(model);
783}
784
785QSSGLayerRenderData::InstanceTransforms QSSGLayerRenderData::getInstanceTransforms(QSSGRenderNodeHandle h) const
786{
787 return nodeData->getInstanceTransforms(h);
788}
789
790QSSGLayerRenderData::InstanceTransforms QSSGLayerRenderData::getInstanceTransforms(const QSSGRenderNode &node) const
791{
792 return nodeData->getInstanceTransforms(h: node.h);
793}
794
795float QSSGLayerRenderData::getGlobalOpacity(QSSGRenderNodeHandle h, float defaultValue) const
796{
797 return nodeData->getGlobalOpacity(h, defaultValue);
798}
799
800float QSSGLayerRenderData::getGlobalOpacity(const QSSGRenderNode &node) const
801{
802 return nodeData->getGlobalOpacity(h: node.h);
803}
804
805static constexpr size_t pipelineStateIndex(QSSGRenderablesFilter filter)
806{
807 switch (filter) {
808 case QSSGRenderablesFilter::All:
809 return 0;
810 case QSSGRenderablesFilter::Opaque:
811 return 1;
812 case QSSGRenderablesFilter::Transparent:
813 return 2;
814 }
815
816 // GCC 8.x does not treat __builtin_unreachable() as constexpr
817# if !defined(Q_CC_GNU_ONLY) || (Q_CC_GNU >= 900)
818 // NOLINTNEXTLINE(qt-use-unreachable-return): Triggers on Clang, breaking GCC 8
819 Q_UNREACHABLE();
820# endif
821 return 0;
822}
823
824void QSSGLayerRenderData::prepareRenderables(QSSGRenderContextInterface &ctx,
825 QSSGPrepResultId prepId,
826 QRhiRenderPassDescriptor *renderPassDescriptor,
827 const QSSGRhiGraphicsPipelineState &ps,
828 QSSGRenderablesFilters filter)
829{
830 QSSG_ASSERT_X(verifyPrepContext(static_cast<QSSGPrepContextId>(prepId), *renderer), "Expired or invalid result id", return);
831 const size_t index = getPrepContextIndex(id: static_cast<QSSGPrepContextId>(prepId));
832 QSSG_ASSERT(index < renderableObjectStore.size() && index < extContexts.size(), return);
833
834 auto &extCtx = extContexts[index];
835 QSSG_ASSERT(extCtx.camera, return);
836 extCtx.filter |= filter;
837
838 QSSGShaderFeatures featureSet = getShaderFeatures();
839
840 QSSGPassKey passKey { reinterpret_cast<void *>(quintptr(extCtx.owner) ^ extCtx.slot) }; // TODO: Pass this along
841
842 if ((filter & QSSGRenderablesFilter::Opaque) != 0) {
843 auto psCpy = ps;
844 if (filter == QSSGRenderablesFilter::All) { // If 'All' we set our defaults
845 psCpy.depthFunc = QRhiGraphicsPipeline::LessOrEqual;
846 psCpy.flags.setFlag(flag: QSSGRhiGraphicsPipelineState::Flag::BlendEnabled, on: false);
847 }
848 const auto &sortedRenderables = getSortedOpaqueRenderableObjects(camera: *extCtx.camera, index);
849 OpaquePass::prep(ctx, data&: *this, passKey, ps&: psCpy, shaderFeatures: featureSet, rpDesc: renderPassDescriptor, sortedOpaqueObjects: sortedRenderables);
850 const size_t psIndex = pipelineStateIndex(filter: QSSGRenderablesFilter::Opaque);
851 extCtx.ps[psIndex] = psCpy;
852 }
853
854 if ((filter & QSSGRenderablesFilter::Transparent) != 0) {
855 auto psCpy = ps;
856 if (filter == QSSGRenderablesFilter::All) { // If 'All' we set our defaults
857 // transparent objects (or, without LayerEnableDepthTest, all objects)
858 psCpy.flags.setFlag(flag: QSSGRhiGraphicsPipelineState::Flag::BlendEnabled, on: true);
859 psCpy.flags.setFlag(flag: QSSGRhiGraphicsPipelineState::Flag::DepthWriteEnabled, on: false);
860 }
861 const auto &sortedRenderables = getSortedTransparentRenderableObjects(camera: *extCtx.camera, index);
862 TransparentPass::prep(ctx, data&: *this, passKey, ps&: psCpy, shaderFeatures: featureSet, rpDesc: renderPassDescriptor, sortedTransparentObjects: sortedRenderables);
863 const size_t psIndex = pipelineStateIndex(filter: QSSGRenderablesFilter::Transparent);
864 extCtx.ps[psIndex] = psCpy;
865 }
866}
867
868void QSSGLayerRenderData::renderRenderables(QSSGRenderContextInterface &ctx, QSSGPrepResultId prepId)
869{
870 QSSG_ASSERT_X(verifyPrepContext(static_cast<QSSGPrepContextId>(prepId), *renderer), "Expired or invalid result id", return);
871 const size_t index = getPrepContextIndex(id: static_cast<QSSGPrepContextId>(prepId));
872 QSSG_ASSERT(index < renderableObjectStore.size() && index < extContexts.size(), return);
873
874 const auto &extCtx = extContexts.at(n: index);
875 const auto filter = extCtx.filter;
876
877 if ((filter & QSSGRenderablesFilter::Opaque) != 0) {
878 const size_t psIndex = pipelineStateIndex(filter: QSSGRenderablesFilter::Opaque);
879 const auto &ps = extCtx.ps[psIndex];
880 const auto &sortedRenderables = getSortedOpaqueRenderableObjects(camera: *extCtx.camera, index);
881 OpaquePass::render(ctx, ps, sortedOpaqueObjects: sortedRenderables);
882 }
883
884 if ((filter & QSSGRenderablesFilter::Transparent) != 0) {
885 const size_t psIndex = pipelineStateIndex(filter: QSSGRenderablesFilter::Transparent);
886 const auto &ps = extCtx.ps[psIndex];
887 const auto &sortedRenderables = getSortedTransparentRenderableObjects(camera: *extCtx.camera, index);
888 TransparentPass::render(ctx, ps, sortedTransparentObjects: sortedRenderables);
889 }
890}
891
892const QSSGRenderableObjectList &QSSGLayerRenderData::getSortedRenderedDepthWriteObjects(const QSSGRenderCamera &camera, size_t index)
893{
894 updateSortedDepthObjectsListImp(camera, index);
895 return sortedDepthWriteCache[index][&camera];
896}
897
898const QSSGRenderableObjectList &QSSGLayerRenderData::getSortedrenderedOpaqueDepthPrepassObjects(const QSSGRenderCamera &camera, size_t index)
899{
900 updateSortedDepthObjectsListImp(camera, index);
901 return sortedOpaqueDepthPrepassCache[index][&camera];;
902}
903
904/**
905 * Usage: T *ptr = RENDER_FRAME_NEW<T>(context, arg0, arg1, ...); is equivalent to: T *ptr = new T(arg0, arg1, ...);
906 * so RENDER_FRAME_NEW() takes the RCI + T's arguments
907 */
908template <typename T, typename... Args>
909[[nodiscard]] inline T *RENDER_FRAME_NEW(QSSGRenderContextInterface &ctx, Args&&... args)
910{
911 static_assert(std::is_trivially_destructible_v<T>, "Objects allocated using the per-frame allocator needs to be trivially destructible!");
912 return new (QSSGLayerRenderData::perFrameAllocator(ctx)->allocate(size: sizeof(T)))T(std::forward<Args>(args)...);
913}
914
915template <typename T>
916[[nodiscard]] inline QSSGDataRef<T> RENDER_FRAME_NEW_BUFFER(QSSGRenderContextInterface &ctx, size_t count)
917{
918 static_assert(std::is_trivially_destructible_v<T>, "Objects allocated using the per-frame allocator needs to be trivially destructible!");
919 const size_t asize = sizeof(T) * count;
920 return { reinterpret_cast<T *>(QSSGLayerRenderData::perFrameAllocator(ctx)->allocate(size: asize)), qsizetype(count) };
921}
922
923QSSGShaderDefaultMaterialKey QSSGLayerRenderData::generateLightingKey(
924 QSSGRenderDefaultMaterial::MaterialLighting inLightingType, const QSSGShaderLightListView &lights, bool receivesShadows)
925{
926 QSSGShaderDefaultMaterialKey theGeneratedKey(qHash(features));
927 const bool lighting = inLightingType != QSSGRenderDefaultMaterial::MaterialLighting::NoLighting;
928 defaultMaterialShaderKeyProperties.m_hasLighting.setValue(inDataStore: theGeneratedKey, inValue: lighting);
929 if (lighting) {
930 defaultMaterialShaderKeyProperties.m_hasIbl.setValue(inDataStore: theGeneratedKey, inValue: layer.lightProbe != nullptr);
931
932 quint32 numLights = quint32(lights.size());
933 Q_ASSERT(numLights <= QSSGShaderDefaultMaterialKeyProperties::LightCount);
934 defaultMaterialShaderKeyProperties.m_lightCount.setValue(inDataStore: theGeneratedKey, inValue: numLights);
935
936 int shadowMapCount = 0;
937 for (int lightIdx = 0, lightEnd = lights.size(); lightIdx < lightEnd; ++lightIdx) {
938 QSSGRenderLight *theLight(lights[lightIdx].light);
939 const bool isDirectional = theLight->type == QSSGRenderLight::Type::DirectionalLight;
940 const bool isSpot = theLight->type == QSSGRenderLight::Type::SpotLight;
941 const bool castsShadows = theLight->m_castShadow
942 && !theLight->m_fullyBaked
943 && receivesShadows
944 && shadowMapCount < QSSG_MAX_NUM_SHADOW_MAPS;
945 if (castsShadows)
946 ++shadowMapCount;
947
948 defaultMaterialShaderKeyProperties.m_lightFlags[lightIdx].setValue(inDataStore: theGeneratedKey, inValue: !isDirectional);
949 defaultMaterialShaderKeyProperties.m_lightSpotFlags[lightIdx].setValue(inDataStore: theGeneratedKey, inValue: isSpot);
950 defaultMaterialShaderKeyProperties.m_lightShadowFlags[lightIdx].setValue(inDataStore: theGeneratedKey, inValue: castsShadows);
951 defaultMaterialShaderKeyProperties.m_lightShadowMapSize[lightIdx].setValue(inDataStore: theGeneratedKey, inValue: theLight->m_shadowMapRes);
952 defaultMaterialShaderKeyProperties.m_lightSoftShadowQuality[lightIdx].setValue(inDataStore: theGeneratedKey,
953 inValue: quint32(theLight->m_softShadowQuality));
954 }
955 }
956 return theGeneratedKey;
957}
958
959void QSSGLayerRenderData::prepareImageForRender(QSSGRenderImage &inImage,
960 QSSGRenderableImage::Type inMapType,
961 QSSGRenderableImage *&ioFirstImage,
962 QSSGRenderableImage *&ioNextImage,
963 QSSGRenderableObjectFlags &ioFlags,
964 QSSGShaderDefaultMaterialKey &inShaderKey,
965 quint32 inImageIndex,
966 QSSGRenderDefaultMaterial *inMaterial)
967{
968 QSSGRenderContextInterface &contextInterface = *renderer->contextInterface();
969 const auto &bufferManager = contextInterface.bufferManager();
970
971 if (inImage.clearDirty())
972 ioFlags |= QSSGRenderableObjectFlag::Dirty;
973
974 // This is where the QRhiTexture gets created, if not already done. Note
975 // that the bufferManager is per-QQuickWindow, and so per-render-thread.
976 // Hence using the same Texture (backed by inImage as the backend node) in
977 // multiple windows will work by each scene in each window getting its own
978 // QRhiTexture. And that's why the QSSGRenderImageTexture cannot be a
979 // member of the QSSGRenderImage. Conceptually this matches what we do for
980 // models (QSSGRenderModel -> QSSGRenderMesh retrieved from the
981 // bufferManager in each prepareModelForRender, etc.).
982
983 const QSSGRenderImageTexture texture = bufferManager->loadRenderImage(image: &inImage);
984
985 if (texture.m_texture) {
986 if (texture.m_flags.hasTransparency()
987 && (inMapType == QSSGRenderableImage::Type::Diffuse // note: Type::BaseColor is skipped here intentionally
988 || inMapType == QSSGRenderableImage::Type::Opacity
989 || inMapType == QSSGRenderableImage::Type::Translucency))
990 {
991 ioFlags |= QSSGRenderableObjectFlag::HasTransparency;
992 }
993
994 QSSGRenderableImage *theImage = RENDER_FRAME_NEW<QSSGRenderableImage>(ctx&: contextInterface, args&: inMapType, args&: inImage, args: texture);
995 QSSGShaderKeyImageMap &theKeyProp = defaultMaterialShaderKeyProperties.m_imageMaps[inImageIndex];
996
997 theKeyProp.setEnabled(inKeySet: inShaderKey, val: true);
998 switch (inImage.m_mappingMode) {
999 case QSSGRenderImage::MappingModes::Normal:
1000 break;
1001 case QSSGRenderImage::MappingModes::Environment:
1002 theKeyProp.setEnvMap(inKeySet: inShaderKey, val: true);
1003 break;
1004 case QSSGRenderImage::MappingModes::LightProbe:
1005 theKeyProp.setLightProbe(inKeySet: inShaderKey, val: true);
1006 break;
1007 }
1008
1009 bool hasA = false;
1010 bool hasG = false;
1011 bool hasB = false;
1012
1013
1014 //### TODO: More formats
1015 switch (texture.m_texture->format()) {
1016 case QRhiTexture::Format::RED_OR_ALPHA8:
1017 hasA = !renderer->contextInterface()->rhiContext()->rhi()->isFeatureSupported(feature: QRhi::RedOrAlpha8IsRed);
1018 break;
1019 case QRhiTexture::Format::R8:
1020 // Leave BGA as false
1021 break;
1022 default:
1023 hasA = true;
1024 hasG = true;
1025 hasB = true;
1026 break;
1027 }
1028
1029 if (inImage.isImageTransformIdentity())
1030 theKeyProp.setIdentityTransform(inKeySet: inShaderKey, val: true);
1031
1032 if (inImage.m_indexUV == 1)
1033 theKeyProp.setUsesUV1(inKeySet: inShaderKey, val: true);
1034
1035 if (texture.m_flags.isLinear())
1036 theKeyProp.setLinear(inKeySet: inShaderKey, val: true);
1037
1038 if (ioFirstImage == nullptr)
1039 ioFirstImage = theImage;
1040 else
1041 ioNextImage->m_nextImage = theImage;
1042
1043 ioNextImage = theImage;
1044
1045 if (inMaterial && inImageIndex >= QSSGShaderDefaultMaterialKeyProperties::SingleChannelImagesFirst) {
1046 QSSGRenderDefaultMaterial::TextureChannelMapping value = QSSGRenderDefaultMaterial::R;
1047
1048 const quint32 scIndex = inImageIndex - QSSGShaderDefaultMaterialKeyProperties::SingleChannelImagesFirst;
1049 QSSGShaderKeyTextureChannel &channelKey = defaultMaterialShaderKeyProperties.m_textureChannels[scIndex];
1050 switch (inImageIndex) {
1051 case QSSGShaderDefaultMaterialKeyProperties::OpacityMap:
1052 value = inMaterial->opacityChannel;
1053 break;
1054 case QSSGShaderDefaultMaterialKeyProperties::RoughnessMap:
1055 value = inMaterial->roughnessChannel;
1056 break;
1057 case QSSGShaderDefaultMaterialKeyProperties::MetalnessMap:
1058 value = inMaterial->metalnessChannel;
1059 break;
1060 case QSSGShaderDefaultMaterialKeyProperties::OcclusionMap:
1061 value = inMaterial->occlusionChannel;
1062 break;
1063 case QSSGShaderDefaultMaterialKeyProperties::TranslucencyMap:
1064 value = inMaterial->translucencyChannel;
1065 break;
1066 case QSSGShaderDefaultMaterialKeyProperties::HeightMap:
1067 value = inMaterial->heightChannel;
1068 break;
1069 case QSSGShaderDefaultMaterialKeyProperties::ClearcoatMap:
1070 value = inMaterial->clearcoatChannel;
1071 break;
1072 case QSSGShaderDefaultMaterialKeyProperties::ClearcoatRoughnessMap:
1073 value = inMaterial->clearcoatRoughnessChannel;
1074 break;
1075 case QSSGShaderDefaultMaterialKeyProperties::TransmissionMap:
1076 value = inMaterial->transmissionChannel;
1077 break;
1078 case QSSGShaderDefaultMaterialKeyProperties::ThicknessMap:
1079 value = inMaterial->thicknessChannel;
1080 break;
1081 case QSSGShaderDefaultMaterialKeyProperties::BaseColorMap:
1082 value = inMaterial->baseColorChannel;
1083 break;
1084 case QSSGShaderDefaultMaterialKeyProperties::SpecularAmountMap:
1085 value = inMaterial->specularAmountChannel;
1086 break;
1087 case QSSGShaderDefaultMaterialKeyProperties::EmissiveMap:
1088 value = inMaterial->emissiveChannel;
1089 break;
1090 default:
1091 break;
1092 }
1093 bool useDefault = false;
1094 switch (value) {
1095 case QSSGRenderDefaultMaterial::TextureChannelMapping::G:
1096 useDefault = !hasG;
1097 break;
1098 case QSSGRenderDefaultMaterial::TextureChannelMapping::B:
1099 useDefault = !hasB;
1100 break;
1101 case QSSGRenderDefaultMaterial::TextureChannelMapping::A:
1102 useDefault = !hasA;
1103 break;
1104 default:
1105 break;
1106 }
1107 if (useDefault)
1108 value = QSSGRenderDefaultMaterial::R; // Always Fallback to Red
1109 channelKey.setTextureChannel(channel: QSSGShaderKeyTextureChannel::TexturChannelBits(value), inKeySet: inShaderKey);
1110 }
1111 }
1112}
1113
1114void QSSGLayerRenderData::setVertexInputPresence(const QSSGRenderableObjectFlags &renderableFlags,
1115 QSSGShaderDefaultMaterialKey &key)
1116{
1117 quint32 vertexAttribs = 0;
1118 if (renderableFlags.hasAttributePosition())
1119 vertexAttribs |= QSSGShaderKeyVertexAttribute::Position;
1120 if (renderableFlags.hasAttributeNormal())
1121 vertexAttribs |= QSSGShaderKeyVertexAttribute::Normal;
1122 if (renderableFlags.hasAttributeTexCoord0())
1123 vertexAttribs |= QSSGShaderKeyVertexAttribute::TexCoord0;
1124 if (renderableFlags.hasAttributeTexCoord1())
1125 vertexAttribs |= QSSGShaderKeyVertexAttribute::TexCoord1;
1126 if (renderableFlags.hasAttributeTexCoordLightmap())
1127 vertexAttribs |= QSSGShaderKeyVertexAttribute::TexCoordLightmap;
1128 if (renderableFlags.hasAttributeTangent())
1129 vertexAttribs |= QSSGShaderKeyVertexAttribute::Tangent;
1130 if (renderableFlags.hasAttributeBinormal())
1131 vertexAttribs |= QSSGShaderKeyVertexAttribute::Binormal;
1132 if (renderableFlags.hasAttributeColor())
1133 vertexAttribs |= QSSGShaderKeyVertexAttribute::Color;
1134 if (renderableFlags.hasAttributeJointAndWeight())
1135 vertexAttribs |= QSSGShaderKeyVertexAttribute::JointAndWeight;
1136 defaultMaterialShaderKeyProperties.m_vertexAttributes.setValue(inDataStore: key, inValue: vertexAttribs);
1137}
1138
1139QSSGDefaultMaterialPreparationResult QSSGLayerRenderData::prepareDefaultMaterialForRender(
1140 QSSGRenderDefaultMaterial &inMaterial,
1141 QSSGRenderableObjectFlags &inExistingFlags,
1142 float inOpacity,
1143 const QSSGShaderLightListView &lights,
1144 QSSGLayerRenderPreparationResultFlags &ioFlags)
1145{
1146 QSSGRenderDefaultMaterial *theMaterial = &inMaterial;
1147 QSSGDefaultMaterialPreparationResult retval(generateLightingKey(inLightingType: theMaterial->lighting, lights, receivesShadows: inExistingFlags.receivesShadows()));
1148 retval.renderableFlags = inExistingFlags;
1149 QSSGRenderableObjectFlags &renderableFlags(retval.renderableFlags);
1150 QSSGShaderDefaultMaterialKey &theGeneratedKey(retval.materialKey);
1151 retval.opacity = inOpacity;
1152 float &subsetOpacity(retval.opacity);
1153
1154 if (theMaterial->isDirty())
1155 renderableFlags |= QSSGRenderableObjectFlag::Dirty;
1156
1157 subsetOpacity *= theMaterial->opacity;
1158
1159 QSSGRenderableImage *firstImage = nullptr;
1160
1161 defaultMaterialShaderKeyProperties.m_specularAAEnabled.setValue(inDataStore: theGeneratedKey, inValue: layer.specularAAEnabled);
1162
1163 // isDoubleSided
1164 defaultMaterialShaderKeyProperties.m_isDoubleSided.setValue(inDataStore: theGeneratedKey, inValue: theMaterial->cullMode == QSSGCullFaceMode::Disabled);
1165
1166 // default materials never define their on position
1167 defaultMaterialShaderKeyProperties.m_overridesPosition.setValue(inDataStore: theGeneratedKey, inValue: false);
1168
1169 // default materials dont make use of raw projection or inverse projection matrices
1170 defaultMaterialShaderKeyProperties.m_usesProjectionMatrix.setValue(inDataStore: theGeneratedKey, inValue: false);
1171 defaultMaterialShaderKeyProperties.m_usesInverseProjectionMatrix.setValue(inDataStore: theGeneratedKey, inValue: false);
1172 // nor they do rely on VAR_COLOR
1173 defaultMaterialShaderKeyProperties.m_usesVarColor.setValue(inDataStore: theGeneratedKey, inValue: false);
1174
1175 // alpha Mode
1176 defaultMaterialShaderKeyProperties.m_alphaMode.setValue(inDataStore: theGeneratedKey, inValue: theMaterial->alphaMode);
1177
1178 // vertex attribute presence flags
1179 setVertexInputPresence(renderableFlags, key&: theGeneratedKey);
1180
1181 // set the flag indicating the need for gl_PointSize
1182 defaultMaterialShaderKeyProperties.m_usesPointsTopology.setValue(inDataStore: theGeneratedKey, inValue: renderableFlags.isPointsTopology());
1183
1184 // propagate the flag indicating the presence of a lightmap
1185 defaultMaterialShaderKeyProperties.m_lightmapEnabled.setValue(inDataStore: theGeneratedKey, inValue: renderableFlags.rendersWithLightmap());
1186
1187 defaultMaterialShaderKeyProperties.m_specularGlossyEnabled.setValue(inDataStore: theGeneratedKey, inValue: theMaterial->type == QSSGRenderGraphObject::Type::SpecularGlossyMaterial);
1188
1189 // debug modes
1190 defaultMaterialShaderKeyProperties.m_debugMode.setValue(inDataStore: theGeneratedKey, inValue: int(layer.debugMode));
1191
1192 // fog
1193 defaultMaterialShaderKeyProperties.m_fogEnabled.setValue(inDataStore: theGeneratedKey, inValue: layer.fog.enabled);
1194
1195 // multiview
1196 defaultMaterialShaderKeyProperties.m_viewCount.setValue(inDataStore: theGeneratedKey, inValue: layer.viewCount);
1197 defaultMaterialShaderKeyProperties.m_usesViewIndex.setValue(inDataStore: theGeneratedKey, inValue: layer.viewCount >= 2);
1198
1199 if (!defaultMaterialShaderKeyProperties.m_hasIbl.getValue(inDataStore: theGeneratedKey) && theMaterial->iblProbe) {
1200 features.set(feature: QSSGShaderFeatures::Feature::LightProbe, val: true);
1201 defaultMaterialShaderKeyProperties.m_hasIbl.setValue(inDataStore: theGeneratedKey, inValue: true);
1202 // features.set(ShaderFeatureDefines::enableIblFov(),
1203 // m_Renderer.GetLayerRenderData()->m_Layer.m_ProbeFov < 180.0f );
1204 }
1205
1206 if (subsetOpacity >= QSSGRendererPrivate::minimumRenderOpacity) {
1207
1208 // Set the semi-transparency flag as specified in PrincipledMaterial's
1209 // blendMode and alphaMode:
1210 // - the default SourceOver blendMode does not imply alpha blending on
1211 // its own,
1212 // - but other blendMode values do,
1213 // - an alphaMode of Blend guarantees blending to be enabled regardless
1214 // of anything else.
1215 // Additionally:
1216 // - Opacity and texture map alpha are handled elsewhere (that's when a
1217 // blendMode of SourceOver or an alphaMode of Default/Opaque can in the
1218 // end still result in HasTransparency),
1219 // - the presence of an opacityMap guarantees alpha blending regardless
1220 // of its content.
1221
1222 if (theMaterial->blendMode != QSSGRenderDefaultMaterial::MaterialBlendMode::SourceOver
1223 || theMaterial->opacityMap
1224 || theMaterial->alphaMode == QSSGRenderDefaultMaterial::Blend)
1225 {
1226 renderableFlags |= QSSGRenderableObjectFlag::HasTransparency;
1227 }
1228
1229 const bool specularEnabled = theMaterial->isSpecularEnabled();
1230 const bool metalnessEnabled = theMaterial->isMetalnessEnabled();
1231 defaultMaterialShaderKeyProperties.m_specularEnabled.setValue(inDataStore: theGeneratedKey, inValue: (specularEnabled || metalnessEnabled));
1232 if (specularEnabled || metalnessEnabled)
1233 defaultMaterialShaderKeyProperties.m_specularModel.setSpecularModel(inKeySet: theGeneratedKey, inModel: theMaterial->specularModel);
1234
1235 defaultMaterialShaderKeyProperties.m_fresnelScaleBiasEnabled.setValue(inDataStore: theGeneratedKey, inValue: theMaterial->isFresnelScaleBiasEnabled());
1236
1237 defaultMaterialShaderKeyProperties.m_clearcoatFresnelScaleBiasEnabled.setValue(inDataStore: theGeneratedKey, inValue: theMaterial->isClearcoatFresnelScaleBiasEnabled());
1238
1239 defaultMaterialShaderKeyProperties.m_fresnelEnabled.setValue(inDataStore: theGeneratedKey, inValue: theMaterial->isFresnelEnabled());
1240
1241 defaultMaterialShaderKeyProperties.m_fresnelEnabled.setValue(inDataStore: theGeneratedKey, inValue: theMaterial->isFresnelEnabled());
1242
1243 defaultMaterialShaderKeyProperties.m_baseColorSingleChannelEnabled.setValue(inDataStore: theGeneratedKey,
1244 inValue: theMaterial->isBaseColorSingleChannelEnabled());
1245 defaultMaterialShaderKeyProperties.m_specularSingleChannelEnabled.setValue(inDataStore: theGeneratedKey,
1246 inValue: theMaterial->isSpecularAmountSingleChannelEnabled());
1247 defaultMaterialShaderKeyProperties.m_emissiveSingleChannelEnabled.setValue(inDataStore: theGeneratedKey,
1248 inValue: theMaterial->isEmissiveSingleChannelEnabled());
1249 defaultMaterialShaderKeyProperties.m_invertOpacityMapValue.setValue(inDataStore: theGeneratedKey,
1250 inValue: theMaterial->isInvertOpacityMapValue());
1251 defaultMaterialShaderKeyProperties.m_vertexColorsEnabled.setValue(inDataStore: theGeneratedKey,
1252 inValue: theMaterial->isVertexColorsEnabled());
1253 defaultMaterialShaderKeyProperties.m_vertexColorsMaskEnabled.setValue(inDataStore: theGeneratedKey,
1254 inValue: theMaterial->isVertexColorsMaskEnabled());
1255 defaultMaterialShaderKeyProperties.m_vertexColorRedMask.setValue(inDataStore: theGeneratedKey,
1256 inValue: theMaterial->vertexColorRedMask.toInt());
1257 defaultMaterialShaderKeyProperties.m_vertexColorGreenMask.setValue(inDataStore: theGeneratedKey,
1258 inValue: quint16(theMaterial->vertexColorGreenMask.toInt()));
1259 defaultMaterialShaderKeyProperties.m_vertexColorBlueMask.setValue(inDataStore: theGeneratedKey,
1260 inValue: quint16(theMaterial->vertexColorBlueMask.toInt()));
1261 defaultMaterialShaderKeyProperties.m_vertexColorAlphaMask.setValue(inDataStore: theGeneratedKey,
1262 inValue: quint16(theMaterial->vertexColorAlphaMask.toInt()));
1263
1264 defaultMaterialShaderKeyProperties.m_clearcoatEnabled.setValue(inDataStore: theGeneratedKey,
1265 inValue: theMaterial->isClearcoatEnabled());
1266 defaultMaterialShaderKeyProperties.m_transmissionEnabled.setValue(inDataStore: theGeneratedKey,
1267 inValue: theMaterial->isTransmissionEnabled());
1268
1269 // Run through the material's images and prepare them for render.
1270 // this may in fact set pickable on the renderable flags if one of the images
1271 // links to a sub presentation or any offscreen rendered object.
1272 QSSGRenderableImage *nextImage = nullptr;
1273#define CHECK_IMAGE_AND_PREPARE(img, imgtype, shadercomponent) \
1274 if ((img)) \
1275 prepareImageForRender(*(img), imgtype, firstImage, nextImage, renderableFlags, \
1276 theGeneratedKey, shadercomponent, &inMaterial)
1277
1278 if (theMaterial->type == QSSGRenderGraphObject::Type::PrincipledMaterial ||
1279 theMaterial->type == QSSGRenderGraphObject::Type::SpecularGlossyMaterial) {
1280 CHECK_IMAGE_AND_PREPARE(theMaterial->colorMap,
1281 QSSGRenderableImage::Type::BaseColor,
1282 QSSGShaderDefaultMaterialKeyProperties::BaseColorMap);
1283 CHECK_IMAGE_AND_PREPARE(theMaterial->occlusionMap,
1284 QSSGRenderableImage::Type::Occlusion,
1285 QSSGShaderDefaultMaterialKeyProperties::OcclusionMap);
1286 CHECK_IMAGE_AND_PREPARE(theMaterial->heightMap,
1287 QSSGRenderableImage::Type::Height,
1288 QSSGShaderDefaultMaterialKeyProperties::HeightMap);
1289 CHECK_IMAGE_AND_PREPARE(theMaterial->clearcoatMap,
1290 QSSGRenderableImage::Type::Clearcoat,
1291 QSSGShaderDefaultMaterialKeyProperties::ClearcoatMap);
1292 CHECK_IMAGE_AND_PREPARE(theMaterial->clearcoatRoughnessMap,
1293 QSSGRenderableImage::Type::ClearcoatRoughness,
1294 QSSGShaderDefaultMaterialKeyProperties::ClearcoatRoughnessMap);
1295 CHECK_IMAGE_AND_PREPARE(theMaterial->clearcoatNormalMap,
1296 QSSGRenderableImage::Type::ClearcoatNormal,
1297 QSSGShaderDefaultMaterialKeyProperties::ClearcoatNormalMap);
1298 CHECK_IMAGE_AND_PREPARE(theMaterial->transmissionMap,
1299 QSSGRenderableImage::Type::Transmission,
1300 QSSGShaderDefaultMaterialKeyProperties::TransmissionMap);
1301 CHECK_IMAGE_AND_PREPARE(theMaterial->thicknessMap,
1302 QSSGRenderableImage::Type::Thickness,
1303 QSSGShaderDefaultMaterialKeyProperties::ThicknessMap);
1304 if (theMaterial->type == QSSGRenderGraphObject::Type::PrincipledMaterial) {
1305 CHECK_IMAGE_AND_PREPARE(theMaterial->metalnessMap,
1306 QSSGRenderableImage::Type::Metalness,
1307 QSSGShaderDefaultMaterialKeyProperties::MetalnessMap);
1308 }
1309 } else {
1310 CHECK_IMAGE_AND_PREPARE(theMaterial->colorMap,
1311 QSSGRenderableImage::Type::Diffuse,
1312 QSSGShaderDefaultMaterialKeyProperties::DiffuseMap);
1313 }
1314 CHECK_IMAGE_AND_PREPARE(theMaterial->emissiveMap, QSSGRenderableImage::Type::Emissive, QSSGShaderDefaultMaterialKeyProperties::EmissiveMap);
1315 CHECK_IMAGE_AND_PREPARE(theMaterial->specularReflection,
1316 QSSGRenderableImage::Type::Specular,
1317 QSSGShaderDefaultMaterialKeyProperties::SpecularMap);
1318 CHECK_IMAGE_AND_PREPARE(theMaterial->roughnessMap,
1319 QSSGRenderableImage::Type::Roughness,
1320 QSSGShaderDefaultMaterialKeyProperties::RoughnessMap);
1321 CHECK_IMAGE_AND_PREPARE(theMaterial->opacityMap, QSSGRenderableImage::Type::Opacity, QSSGShaderDefaultMaterialKeyProperties::OpacityMap);
1322 CHECK_IMAGE_AND_PREPARE(theMaterial->bumpMap, QSSGRenderableImage::Type::Bump, QSSGShaderDefaultMaterialKeyProperties::BumpMap);
1323 CHECK_IMAGE_AND_PREPARE(theMaterial->specularMap,
1324 QSSGRenderableImage::Type::SpecularAmountMap,
1325 QSSGShaderDefaultMaterialKeyProperties::SpecularAmountMap);
1326 CHECK_IMAGE_AND_PREPARE(theMaterial->normalMap, QSSGRenderableImage::Type::Normal, QSSGShaderDefaultMaterialKeyProperties::NormalMap);
1327 CHECK_IMAGE_AND_PREPARE(theMaterial->translucencyMap,
1328 QSSGRenderableImage::Type::Translucency,
1329 QSSGShaderDefaultMaterialKeyProperties::TranslucencyMap);
1330 }
1331#undef CHECK_IMAGE_AND_PREPARE
1332
1333 if (subsetOpacity < QSSGRendererPrivate::minimumRenderOpacity) {
1334 subsetOpacity = 0.0f;
1335 // You can still pick against completely transparent objects(or rather their bounding
1336 // box)
1337 // you just don't render them.
1338 renderableFlags |= QSSGRenderableObjectFlag::HasTransparency;
1339 renderableFlags |= QSSGRenderableObjectFlag::CompletelyTransparent;
1340 }
1341
1342 if (subsetOpacity > 1.f - QSSGRendererPrivate::minimumRenderOpacity)
1343 subsetOpacity = 1.f;
1344 else
1345 renderableFlags |= QSSGRenderableObjectFlag::HasTransparency;
1346
1347 if (inMaterial.isTransmissionEnabled()) {
1348 ioFlags.setRequiresScreenTexture(true);
1349 ioFlags.setRequiresMipmapsForScreenTexture(true);
1350 renderableFlags |= QSSGRenderableObjectFlag::RequiresScreenTexture;
1351 }
1352
1353 if (renderableFlags.hasTransparency()) {
1354 if (orderIndependentTransparencyEnabled)
1355 defaultMaterialShaderKeyProperties.m_orderIndependentTransparency.setValue(inDataStore: theGeneratedKey, inValue: int(layer.oitMethod));
1356 if (layer.oitMethodDirty)
1357 renderableFlags |= QSSGRenderableObjectFlag::Dirty;
1358 }
1359
1360 retval.firstImage = firstImage;
1361 if (retval.renderableFlags.isDirty())
1362 retval.dirty = true;
1363 if (retval.dirty)
1364 renderer->addMaterialDirtyClear(material: &inMaterial);
1365 return retval;
1366}
1367
1368QSSGDefaultMaterialPreparationResult QSSGLayerRenderData::prepareCustomMaterialForRender(
1369 QSSGRenderCustomMaterial &inMaterial, QSSGRenderableObjectFlags &inExistingFlags,
1370 float inOpacity, bool alreadyDirty, const QSSGShaderLightListView &lights,
1371 QSSGLayerRenderPreparationResultFlags &ioFlags)
1372{
1373 QSSGDefaultMaterialPreparationResult retval(
1374 generateLightingKey(inLightingType: QSSGRenderDefaultMaterial::MaterialLighting::FragmentLighting,
1375 lights, receivesShadows: inExistingFlags.receivesShadows()));
1376 retval.renderableFlags = inExistingFlags;
1377 QSSGRenderableObjectFlags &renderableFlags(retval.renderableFlags);
1378 QSSGShaderDefaultMaterialKey &theGeneratedKey(retval.materialKey);
1379 retval.opacity = inOpacity;
1380 float &subsetOpacity(retval.opacity);
1381
1382 if (subsetOpacity < QSSGRendererPrivate::minimumRenderOpacity) {
1383 subsetOpacity = 0.0f;
1384 // You can still pick against completely transparent objects(or rather their bounding
1385 // box)
1386 // you just don't render them.
1387 renderableFlags |= QSSGRenderableObjectFlag::HasTransparency;
1388 renderableFlags |= QSSGRenderableObjectFlag::CompletelyTransparent;
1389 }
1390
1391 if (subsetOpacity > 1.f - QSSGRendererPrivate::minimumRenderOpacity)
1392 subsetOpacity = 1.f;
1393 else
1394 renderableFlags |= QSSGRenderableObjectFlag::HasTransparency;
1395
1396 defaultMaterialShaderKeyProperties.m_specularAAEnabled.setValue(inDataStore: theGeneratedKey, inValue: layer.specularAAEnabled);
1397
1398 // isDoubleSided
1399 defaultMaterialShaderKeyProperties.m_isDoubleSided.setValue(inDataStore: theGeneratedKey,
1400 inValue: inMaterial.m_cullMode == QSSGCullFaceMode::Disabled);
1401
1402 // Does the material override the position output
1403 const bool overridesPosition = inMaterial.m_renderFlags.testFlag(flag: QSSGRenderCustomMaterial::RenderFlag::OverridesPosition);
1404 defaultMaterialShaderKeyProperties.m_overridesPosition.setValue(inDataStore: theGeneratedKey, inValue: overridesPosition);
1405
1406 // Optional usage of PROJECTION_MATRIX and/or INVERSE_PROJECTION_MATRIX
1407 const bool usesProjectionMatrix = inMaterial.m_renderFlags.testFlag(flag: QSSGRenderCustomMaterial::RenderFlag::ProjectionMatrix);
1408 defaultMaterialShaderKeyProperties.m_usesProjectionMatrix.setValue(inDataStore: theGeneratedKey, inValue: usesProjectionMatrix);
1409 const bool usesInvProjectionMatrix = inMaterial.m_renderFlags.testFlag(flag: QSSGRenderCustomMaterial::RenderFlag::InverseProjectionMatrix);
1410 defaultMaterialShaderKeyProperties.m_usesInverseProjectionMatrix.setValue(inDataStore: theGeneratedKey, inValue: usesInvProjectionMatrix);
1411
1412 // vertex attribute presence flags
1413 setVertexInputPresence(renderableFlags, key&: theGeneratedKey);
1414
1415 // set the flag indicating the need for gl_PointSize
1416 defaultMaterialShaderKeyProperties.m_usesPointsTopology.setValue(inDataStore: theGeneratedKey, inValue: renderableFlags.isPointsTopology());
1417
1418 // propagate the flag indicating the presence of a lightmap
1419 defaultMaterialShaderKeyProperties.m_lightmapEnabled.setValue(inDataStore: theGeneratedKey, inValue: renderableFlags.rendersWithLightmap());
1420
1421 // debug modes
1422 defaultMaterialShaderKeyProperties.m_debugMode.setValue(inDataStore: theGeneratedKey, inValue: int(layer.debugMode));
1423
1424 // fog
1425 defaultMaterialShaderKeyProperties.m_fogEnabled.setValue(inDataStore: theGeneratedKey, inValue: layer.fog.enabled);
1426
1427 // multiview
1428 defaultMaterialShaderKeyProperties.m_viewCount.setValue(inDataStore: theGeneratedKey, inValue: layer.viewCount);
1429 defaultMaterialShaderKeyProperties.m_usesViewIndex.setValue(inDataStore: theGeneratedKey,
1430 inValue: inMaterial.m_renderFlags.testFlag(flag: QSSGRenderCustomMaterial::RenderFlag::ViewIndex));
1431
1432 // Knowing whether VAR_COLOR is used becomes relevant when there is no
1433 // custom vertex shader, but VAR_COLOR is present in the custom fragment
1434 // snippet, because that case needs special care.
1435 const bool usesVarColor = inMaterial.m_renderFlags.testFlag(flag: QSSGRenderCustomMaterial::RenderFlag::VarColor);
1436 defaultMaterialShaderKeyProperties.m_usesVarColor.setValue(inDataStore: theGeneratedKey, inValue: usesVarColor);
1437
1438 const bool usesClearcoat = inMaterial.m_renderFlags.testFlag(flag: QSSGRenderCustomMaterial::RenderFlag::Clearcoat);
1439 defaultMaterialShaderKeyProperties.m_clearcoatEnabled.setValue(inDataStore: theGeneratedKey, inValue: usesClearcoat);
1440
1441 const bool usesClearcoatFresnelScaleBias = inMaterial.m_renderFlags.testFlag(flag: QSSGRenderCustomMaterial::RenderFlag::ClearcoatFresnelScaleBias);
1442 defaultMaterialShaderKeyProperties.m_clearcoatFresnelScaleBiasEnabled.setValue(inDataStore: theGeneratedKey, inValue: usesClearcoatFresnelScaleBias);
1443
1444 const bool usesFresnelScaleBias = inMaterial.m_renderFlags.testFlag(flag: QSSGRenderCustomMaterial::RenderFlag::FresnelScaleBias);
1445 defaultMaterialShaderKeyProperties.m_fresnelScaleBiasEnabled.setValue(inDataStore: theGeneratedKey, inValue: usesFresnelScaleBias);
1446
1447 const bool usesTransmission = inMaterial.m_renderFlags.testFlag(flag: QSSGRenderCustomMaterial::RenderFlag::Transmission);
1448 defaultMaterialShaderKeyProperties.m_transmissionEnabled.setValue(inDataStore: theGeneratedKey, inValue: usesTransmission);
1449
1450 if (inMaterial.m_renderFlags.testFlag(flag: QSSGRenderCustomMaterial::RenderFlag::Blending))
1451 renderableFlags |= QSSGRenderableObjectFlag::HasTransparency;
1452
1453 if (inMaterial.m_renderFlags.testFlag(flag: QSSGRenderCustomMaterial::RenderFlag::ScreenTexture)) {
1454 ioFlags.setRequiresScreenTexture(true);
1455 renderableFlags |= QSSGRenderableObjectFlag::RequiresScreenTexture;
1456 }
1457
1458 if (inMaterial.m_renderFlags.testFlag(flag: QSSGRenderCustomMaterial::RenderFlag::ScreenMipTexture)) {
1459 ioFlags.setRequiresScreenTexture(true);
1460 ioFlags.setRequiresMipmapsForScreenTexture(true);
1461 renderableFlags |= QSSGRenderableObjectFlag::RequiresScreenTexture;
1462 }
1463
1464 if (inMaterial.m_renderFlags.testFlag(flag: QSSGRenderCustomMaterial::RenderFlag::DepthTexture))
1465 ioFlags.setRequiresDepthTexture(true);
1466
1467 if (inMaterial.m_renderFlags.testFlag(flag: QSSGRenderCustomMaterial::RenderFlag::AoTexture)) {
1468 ioFlags.setRequiresDepthTexture(true);
1469 ioFlags.setRequiresSsaoPass(true);
1470 }
1471 if (orderIndependentTransparencyEnabled && renderableFlags.hasTransparency())
1472 defaultMaterialShaderKeyProperties.m_orderIndependentTransparency.setValue(inDataStore: theGeneratedKey, inValue: int(layer.oitMethod));
1473 retval.firstImage = nullptr;
1474
1475 if (retval.dirty || alreadyDirty)
1476 renderer->addMaterialDirtyClear(material: &inMaterial);
1477 return retval;
1478}
1479
1480void QSSGLayerRenderData::setLightmapTexture(const QSSGModelContext &modelContext, QRhiTexture *lightmapTexture)
1481{
1482 lightmapTextures[&modelContext] = lightmapTexture;
1483}
1484
1485QRhiTexture *QSSGLayerRenderData::getLightmapTexture(const QSSGModelContext &modelContext) const
1486{
1487 QRhiTexture *ret = nullptr;
1488 if (modelContext.model.hasLightmap()) {
1489 const auto it = lightmapTextures.constFind(key: &modelContext);
1490 ret = (it != lightmapTextures.cend()) ? *it : nullptr;
1491 }
1492
1493 return ret;
1494}
1495
1496void QSSGLayerRenderData::setBonemapTexture(const QSSGModelContext &modelContext, QRhiTexture *bonemapTexture)
1497{
1498 bonemapTextures[&modelContext] = bonemapTexture;
1499}
1500
1501QRhiTexture *QSSGLayerRenderData::getBonemapTexture(const QSSGModelContext &modelContext) const
1502{
1503 QRhiTexture *ret = nullptr;
1504 if (modelContext.model.usesBoneTexture()) {
1505 const auto it = bonemapTextures.constFind(key: &modelContext);
1506 ret = (it != bonemapTextures.cend()) ? *it : nullptr;
1507 }
1508
1509 return ret;
1510}
1511
1512static bool hasCustomBlendMode(const QSSGRenderCustomMaterial &material)
1513{
1514 // Check SrcOver
1515
1516 // srcAlpha is same for all
1517 if (material.m_srcAlphaBlend != QRhiGraphicsPipeline::One)
1518 return true;
1519
1520 // SrcOver srcColor is SrcAlpha
1521 if (material.m_srcBlend != QRhiGraphicsPipeline::SrcAlpha)
1522 return true;
1523
1524 if (material.m_dstBlend == QRhiGraphicsPipeline::OneMinusSrcAlpha
1525 && material.m_dstAlphaBlend == QRhiGraphicsPipeline::OneMinusSrcAlpha)
1526 return false;
1527 return true;
1528}
1529
1530// inModel is const to emphasize the fact that its members cannot be written
1531// here: in case there is a scene shared between multiple View3Ds in different
1532// QQuickWindows, each window may run this in their own render thread, while
1533// inModel is the same.
1534bool QSSGLayerRenderData::prepareModelsForRender(QSSGRenderContextInterface &contextInterface,
1535 const RenderableNodeEntries &renderableModels,
1536 QSSGLayerRenderPreparationResultFlags &ioFlags,
1537 const QSSGRenderCameraList &allCameras,
1538 const QSSGRenderCameraDataList &allCameraData,
1539 TModelContextPtrList &modelContexts,
1540 QSSGRenderableObjectList &opaqueObjects,
1541 QSSGRenderableObjectList &transparentObjects,
1542 QSSGRenderableObjectList &screenTextureObjects,
1543 float lodThreshold)
1544{
1545 const auto &rhiCtx = contextInterface.rhiContext();
1546 const auto &bufferManager = contextInterface.bufferManager();
1547
1548 const auto &debugDrawSystem = contextInterface.debugDrawSystem();
1549 const bool maybeDebugDraw = debugDrawSystem && debugDrawSystem->isEnabled();
1550
1551 bool wasDirty = false;
1552
1553 for (const QSSGRenderableNodeEntry &renderable : renderableModels) {
1554 if ((renderable.overridden & QSSGRenderableNodeEntry::Overridden::Disabled) != 0)
1555 continue;
1556
1557 const QSSGRenderModel &model = *static_cast<QSSGRenderModel *>(renderable.node);
1558 const auto &lights = renderable.lights;
1559 QSSGRenderMesh *theMesh = modelData->getMesh(model);
1560 if (!theMesh)
1561 continue;
1562
1563 const bool altGlobalTransform = ((renderable.overridden & QSSGRenderableNodeEntry::Overridden::GlobalTransform) != 0);
1564 const auto &globalTransform = altGlobalTransform ? renderable.extOverrides.globalTransform : getGlobalTransform(node: model);
1565 QMatrix3x3 normalMatrix { Qt::Uninitialized };
1566 QSSGLayerRenderData::ModelViewProjections mvps;
1567 if (altGlobalTransform) {
1568 QSSGRenderNode::calculateNormalMatrix(globalTransform, outNormalMatrix&: normalMatrix);
1569 size_t mvpCount = 0;
1570 for (const auto &cameraData : allCameraData) {
1571 QSSGRenderNode::calculateMVPAndNormalMatrix(globalTransfor: globalTransform, inViewProjection: cameraData.viewProjection, outMVP&: mvps[mvpCount++], outNormalMatrix&: normalMatrix);
1572 }
1573 } else {
1574 if (model.usesBoneTexture()) {
1575 // FIXME:
1576 // For skinning, node's global transformation will be ignored and
1577 // an identity matrix will be used for the normalMatrix
1578 size_t mvpCount = 0;
1579 for (const QSSGRenderCameraData &cameraData : allCameraData) {
1580 mvps[mvpCount++] = cameraData.viewProjection;
1581 normalMatrix = QMatrix3x3();
1582 }
1583 } else {
1584 normalMatrix = getNormalMatrix(model);
1585 mvps = getModelMvps(model);
1586 }
1587 }
1588 const bool altModelOpacity = ((renderable.overridden & QSSGRenderableNodeEntry::Overridden::GlobalOpacity) != 0);
1589 const float modelGlobalOpacity = altModelOpacity ? renderable.extOverrides.globalOpacity : getGlobalOpacity(node: model);
1590 QSSGModelContext &theModelContext = *RENDER_FRAME_NEW<QSSGModelContext>(ctx&: contextInterface, args: model, args: globalTransform, args&: normalMatrix, args&: mvps);
1591 modelContexts.push_back(t: &theModelContext);
1592 // We might over-allocate here, as the material list technically can contain an invalid (nullptr) material.
1593 // We'll fix that by adjusting the size at the end for now...
1594 const auto &meshSubsets = theMesh->subsets;
1595 const auto meshSubsetCount = meshSubsets.size();
1596 theModelContext.subsets = RENDER_FRAME_NEW_BUFFER<QSSGSubsetRenderable>(ctx&: contextInterface, count: meshSubsetCount);
1597
1598 // Prepare boneTexture for skinning
1599 if (model.skin) {
1600 auto boneTexture = bufferManager->loadSkinmap(skin: model.skin);
1601 setBonemapTexture(modelContext: theModelContext, bonemapTexture: boneTexture.m_texture);
1602 } else if (model.skeleton) {
1603 auto boneTexture = bufferManager->loadSkinmap(skin: &(model.skeleton->boneTexData));
1604 setBonemapTexture(modelContext: theModelContext, bonemapTexture: boneTexture.m_texture);
1605 }
1606
1607 // many renderableFlags are the same for all the subsets
1608 QSSGRenderableObjectFlags renderableFlagsForModel;
1609
1610 if (meshSubsetCount > 0) {
1611 const QSSGRenderSubset &theSubset = meshSubsets.at(i: 0);
1612
1613 renderableFlagsForModel.setCastsShadows(model.castsShadows);
1614 renderableFlagsForModel.setReceivesShadows(model.receivesShadows);
1615 renderableFlagsForModel.setReceivesReflections(model.receivesReflections);
1616 renderableFlagsForModel.setCastsReflections(model.castsReflections);
1617
1618 renderableFlagsForModel.setUsedInBakedLighting(model.usedInBakedLighting);
1619 if (model.hasLightmap()) {
1620 QSSGRenderImageTexture lmImageTexture = bufferManager->loadLightmap(model);
1621 if (lmImageTexture.m_texture) {
1622 renderableFlagsForModel.setRendersWithLightmap(true);
1623 setLightmapTexture(modelContext: theModelContext, lightmapTexture: lmImageTexture.m_texture);
1624 }
1625 }
1626
1627 // TODO: This should be a oneshot thing, move the flags over!
1628 // With the RHI we need to be able to tell the material shader
1629 // generator to not generate vertex input attributes that are not
1630 // provided by the mesh. (because unlike OpenGL, other graphics
1631 // APIs may treat unbound vertex inputs as a fatal error)
1632 bool hasJoint = false;
1633 bool hasWeight = false;
1634 bool hasMorphTarget = theSubset.rhi.targetsTexture != nullptr;
1635 for (const QSSGRhiInputAssemblerState::InputSemantic &sem : std::as_const(t: theSubset.rhi.ia.inputs)) {
1636 if (sem == QSSGRhiInputAssemblerState::PositionSemantic) {
1637 renderableFlagsForModel.setHasAttributePosition(true);
1638 } else if (sem == QSSGRhiInputAssemblerState::NormalSemantic) {
1639 renderableFlagsForModel.setHasAttributeNormal(true);
1640 } else if (sem == QSSGRhiInputAssemblerState::TexCoord0Semantic) {
1641 renderableFlagsForModel.setHasAttributeTexCoord0(true);
1642 } else if (sem == QSSGRhiInputAssemblerState::TexCoord1Semantic) {
1643 renderableFlagsForModel.setHasAttributeTexCoord1(true);
1644 } else if (sem == QSSGRhiInputAssemblerState::TexCoordLightmapSemantic) {
1645 renderableFlagsForModel.setHasAttributeTexCoordLightmap(true);
1646 } else if (sem == QSSGRhiInputAssemblerState::TangentSemantic) {
1647 renderableFlagsForModel.setHasAttributeTangent(true);
1648 } else if (sem == QSSGRhiInputAssemblerState::BinormalSemantic) {
1649 renderableFlagsForModel.setHasAttributeBinormal(true);
1650 } else if (sem == QSSGRhiInputAssemblerState::ColorSemantic) {
1651 renderableFlagsForModel.setHasAttributeColor(true);
1652 // For skinning, we will set the HasAttribute only
1653 // if the mesh has both joint and weight
1654 } else if (sem == QSSGRhiInputAssemblerState::JointSemantic) {
1655 hasJoint = true;
1656 } else if (sem == QSSGRhiInputAssemblerState::WeightSemantic) {
1657 hasWeight = true;
1658 }
1659 }
1660 renderableFlagsForModel.setHasAttributeJointAndWeight(hasJoint && hasWeight);
1661 renderableFlagsForModel.setHasAttributeMorphTarget(hasMorphTarget);
1662 }
1663
1664 QSSGRenderableObjectList bakedLightingObjects;
1665 bool usesBlendParticles = particlesEnabled && theModelContext.model.particleBuffer != nullptr
1666 && model.particleBuffer->particleCount();
1667 const bool anyLightHasShadows = std::find_if(first: lights.begin(),
1668 last: lights.end(),
1669 pred: [](const QSSGShaderLight &light) { return light.shadows; })
1670 != lights.end();
1671
1672 // Subset(s)
1673 auto &renderableSubsets = theModelContext.subsets;
1674 const bool hasMaterialOverrides = ((renderable.overridden & QSSGRenderableNodeEntry::Overridden::Materials) != 0);
1675 const auto &materials = hasMaterialOverrides ? renderable.extOverrides.materials : modelData->getMaterials(model);
1676 const auto materialCount = materials.size();
1677 QSSGRenderGraphObject *lastMaterial = !materials.isEmpty() ? materials.last() : nullptr;
1678 int idx = 0, subsetIdx = 0;
1679 for (; idx < meshSubsetCount; ++idx) {
1680 // If the materials list < size of subsets, then use the last material for the rest
1681 QSSGRenderGraphObject *theMaterialObject = (idx >= materialCount) ? lastMaterial : materials[idx];
1682 if (!theMaterialObject)
1683 continue;
1684
1685 const QSSGRenderSubset &theSubset = meshSubsets.at(i: idx);
1686 QSSGRenderableObjectFlags renderableFlags = renderableFlagsForModel;
1687 float subsetOpacity = modelGlobalOpacity;
1688
1689 renderableFlags.setPointsTopology(theSubset.rhi.ia.topology == QRhiGraphicsPipeline::Points);
1690 QSSGRenderableObject *theRenderableObject = &renderableSubsets[subsetIdx++];
1691
1692 const bool usesInstancing = theModelContext.model.instancing()
1693 && rhiCtx->rhi()->isFeatureSupported(feature: QRhi::Instancing);
1694 if (usesInstancing && theModelContext.model.instanceTable->hasTransparency())
1695 renderableFlags |= QSSGRenderableObjectFlag::HasTransparency;
1696 if (theModelContext.model.hasTransparency)
1697 renderableFlags |= QSSGRenderableObjectFlag::HasTransparency;
1698
1699 // Level Of Detail
1700 quint32 subsetLevelOfDetail = 0;
1701 if (!theSubset.lods.isEmpty() && lodThreshold > 0.0f) {
1702 // Accounts for FOV
1703 float lodDistanceMultiplier = camerasView[0]->getLevelOfDetailMultiplier();
1704 float distanceThreshold = 0.0f;
1705 const auto scale = QSSGUtils::mat44::getScale(m: globalTransform);
1706 float modelScale = qMax(a: scale.x(), b: qMax(a: scale.y(), b: scale.z()));
1707 QSSGBounds3 transformedBounds = theSubset.bounds;
1708 if (camerasView[0]->type != QSSGRenderGraphObject::Type::OrthographicCamera) {
1709 transformedBounds.transform(inMatrix: globalTransform);
1710 if (maybeDebugDraw && debugDrawSystem->isEnabled(mode: QSSGDebugDrawSystem::Mode::MeshLod))
1711 debugDrawSystem->drawBounds(bounds: transformedBounds, color: QColor(Qt::red));
1712 const QMatrix4x4 cameraGlobalTranform = getGlobalTransform(node: *camerasView[0]);
1713 const QVector3D cameraNormal = QSSGRenderNode::getScalingCorrectDirection(globalTransform: cameraGlobalTranform);
1714 const QVector3D cameraPosition = QSSGRenderNode::getGlobalPos(globalTransform: cameraGlobalTranform);
1715 const QSSGPlane cameraPlane = QSSGPlane(cameraPosition, cameraNormal);
1716 const QVector3D lodSupportMin = transformedBounds.getSupport(direction: -cameraNormal);
1717 const QVector3D lodSupportMax = transformedBounds.getSupport(direction: cameraNormal);
1718 if (maybeDebugDraw && debugDrawSystem->isEnabled(mode: QSSGDebugDrawSystem::Mode::MeshLod))
1719 debugDrawSystem->drawPoint(vertex: lodSupportMin, color: QColor("orange"));
1720
1721 const float distanceMin = cameraPlane.distance(p: lodSupportMin);
1722 const float distanceMax = cameraPlane.distance(p: lodSupportMax);
1723
1724 if (distanceMin * distanceMax < 0.0)
1725 distanceThreshold = 0.0;
1726 else if (distanceMin >= 0.0)
1727 distanceThreshold = distanceMin;
1728 else if (distanceMax <= 0.0)
1729 distanceThreshold = -distanceMax;
1730
1731 } else {
1732 // Orthographic Projection
1733 distanceThreshold = 1.0;
1734 }
1735
1736 int currentLod = -1;
1737 if (model.levelOfDetailBias > 0.0f) {
1738 const float threshold = distanceThreshold * lodDistanceMultiplier;
1739 const float modelBias = 1 / model.levelOfDetailBias;
1740 for (qsizetype i = 0; i < theSubset.lods.count(); ++i) {
1741 float subsetDistance = theSubset.lods[i].distance * modelScale * modelBias;
1742 float screenSize = subsetDistance / threshold;
1743 if (screenSize > lodThreshold)
1744 break;
1745 currentLod = i;
1746 }
1747 }
1748 if (currentLod == -1)
1749 subsetLevelOfDetail = 0;
1750 else
1751 subsetLevelOfDetail = currentLod + 1;
1752 if (maybeDebugDraw && debugDrawSystem->isEnabled(mode: QSSGDebugDrawSystem::Mode::MeshLod))
1753 debugDrawSystem->drawBounds(bounds: transformedBounds, color: QSSGDebugDrawSystem::levelOfDetailColor(lod: subsetLevelOfDetail));
1754 }
1755
1756 QVector3D theModelCenter(theSubset.bounds.center());
1757 theModelCenter = QSSGUtils::mat44::transform(m: globalTransform, v: theModelCenter);
1758 if (maybeDebugDraw && debugDrawSystem->isEnabled(mode: QSSGDebugDrawSystem::Mode::MeshLodNormal)) {
1759 const QMatrix4x4 allCamera0GlobalTransform = getGlobalTransform(node: *allCameras[0]);
1760 debugDrawSystem->debugNormals(bufferManager&: *bufferManager, theModelContext, theSubset, subsetLevelOfDetail, lineLength: (theModelCenter - QSSGRenderNode::getGlobalPos(globalTransform: allCamera0GlobalTransform)).length() * 0.01);
1761 }
1762
1763 auto checkF32TypeIndex = [&rhiCtx](QRhiVertexInputAttribute::Format f) {
1764 if ((f == QRhiVertexInputAttribute::Format::Float4)
1765 || (f == QRhiVertexInputAttribute::Format::Float3)
1766 || (f == QRhiVertexInputAttribute::Format::Float2)
1767 || (f == QRhiVertexInputAttribute::Format::Float)) {
1768 return true;
1769 }
1770 if (!rhiCtx->rhi()->isFeatureSupported(feature: QRhi::IntAttributes))
1771 qWarning() << "WARN: Model has non-integer type indices for skinning but current RHI backend doesn't support it!";
1772 return false;
1773 };
1774
1775 if (theMaterialObject->type == QSSGRenderGraphObject::Type::DefaultMaterial ||
1776 theMaterialObject->type == QSSGRenderGraphObject::Type::PrincipledMaterial ||
1777 theMaterialObject->type == QSSGRenderGraphObject::Type::SpecularGlossyMaterial) {
1778 QSSGRenderDefaultMaterial &theMaterial(static_cast<QSSGRenderDefaultMaterial &>(*theMaterialObject));
1779 QSSGDefaultMaterialPreparationResult theMaterialPrepResult(prepareDefaultMaterialForRender(inMaterial&: theMaterial, inExistingFlags&: renderableFlags, inOpacity: subsetOpacity, lights, ioFlags));
1780 QSSGShaderDefaultMaterialKey &theGeneratedKey(theMaterialPrepResult.materialKey);
1781 subsetOpacity = theMaterialPrepResult.opacity;
1782 QSSGRenderableImage *firstImage(theMaterialPrepResult.firstImage);
1783 wasDirty |= theMaterialPrepResult.dirty;
1784 renderableFlags = theMaterialPrepResult.renderableFlags;
1785 if (renderableFlags.hasTransparency())
1786 ioFlags.setHasCustomBlendMode(theMaterial.blendMode != QSSGRenderDefaultMaterial::MaterialBlendMode::SourceOver);
1787
1788 // Blend particles
1789 defaultMaterialShaderKeyProperties.m_blendParticles.setValue(inDataStore: theGeneratedKey, inValue: usesBlendParticles);
1790
1791 // Skin
1792 const auto boneCount = model.skin ? model.skin->boneCount :
1793 model.skeleton ? model.skeleton->boneCount : 0;
1794 defaultMaterialShaderKeyProperties.m_boneCount.setValue(inDataStore: theGeneratedKey, inValue: boneCount);
1795 if (auto idJoint = theSubset.rhi.ia.inputs.indexOf(t: QSSGRhiInputAssemblerState::JointSemantic); idJoint != -1) {
1796 const auto attr = theSubset.rhi.ia.inputLayout.attributeAt(index: idJoint);
1797 defaultMaterialShaderKeyProperties.m_usesFloatJointIndices.setValue(inDataStore: theGeneratedKey, inValue: checkF32TypeIndex(attr->format()));
1798 }
1799
1800 // Instancing
1801 defaultMaterialShaderKeyProperties.m_usesInstancing.setValue(inDataStore: theGeneratedKey, inValue: usesInstancing);
1802 // Morphing
1803 defaultMaterialShaderKeyProperties.m_targetCount.setValue(inDataStore: theGeneratedKey,
1804 inValue: theSubset.rhi.ia.targetCount);
1805 defaultMaterialShaderKeyProperties.m_targetPositionOffset.setValue(inDataStore: theGeneratedKey,
1806 inValue: theSubset.rhi.ia.targetOffsets[QSSGRhiInputAssemblerState::PositionSemantic]);
1807 defaultMaterialShaderKeyProperties.m_targetNormalOffset.setValue(inDataStore: theGeneratedKey,
1808 inValue: theSubset.rhi.ia.targetOffsets[QSSGRhiInputAssemblerState::NormalSemantic]);
1809 defaultMaterialShaderKeyProperties.m_targetTangentOffset.setValue(inDataStore: theGeneratedKey,
1810 inValue: theSubset.rhi.ia.targetOffsets[QSSGRhiInputAssemblerState::TangentSemantic]);
1811 defaultMaterialShaderKeyProperties.m_targetBinormalOffset.setValue(inDataStore: theGeneratedKey,
1812 inValue: theSubset.rhi.ia.targetOffsets[QSSGRhiInputAssemblerState::BinormalSemantic]);
1813 defaultMaterialShaderKeyProperties.m_targetTexCoord0Offset.setValue(inDataStore: theGeneratedKey,
1814 inValue: theSubset.rhi.ia.targetOffsets[QSSGRhiInputAssemblerState::TexCoord0Semantic]);
1815 defaultMaterialShaderKeyProperties.m_targetTexCoord1Offset.setValue(inDataStore: theGeneratedKey,
1816 inValue: theSubset.rhi.ia.targetOffsets[QSSGRhiInputAssemblerState::TexCoord1Semantic]);
1817 defaultMaterialShaderKeyProperties.m_targetColorOffset.setValue(inDataStore: theGeneratedKey,
1818 inValue: theSubset.rhi.ia.targetOffsets[QSSGRhiInputAssemblerState::ColorSemantic]);
1819
1820 new (theRenderableObject) QSSGSubsetRenderable(QSSGSubsetRenderable::Type::DefaultMaterialMeshSubset,
1821 renderableFlags,
1822 theModelCenter,
1823 renderer,
1824 theSubset,
1825 theModelContext,
1826 subsetOpacity,
1827 subsetLevelOfDetail,
1828 theMaterial,
1829 firstImage,
1830 theGeneratedKey,
1831 lights,
1832 anyLightHasShadows);
1833 wasDirty = wasDirty || renderableFlags.isDirty();
1834 } else if (theMaterialObject->type == QSSGRenderGraphObject::Type::CustomMaterial) {
1835 QSSGRenderCustomMaterial &theMaterial(static_cast<QSSGRenderCustomMaterial &>(*theMaterialObject));
1836
1837 const auto &theMaterialSystem(contextInterface.customMaterialSystem());
1838 wasDirty |= theMaterialSystem->prepareForRender(inModel: theModelContext.model, inSubset: theSubset, inMaterial&: theMaterial);
1839
1840 if (theMaterial.m_renderFlags.testFlag(flag: QSSGRenderCustomMaterial::RenderFlag::Blending))
1841 ioFlags.setHasCustomBlendMode(!hasCustomBlendMode(material: theMaterial));
1842
1843 QSSGDefaultMaterialPreparationResult theMaterialPrepResult(
1844 prepareCustomMaterialForRender(inMaterial&: theMaterial, inExistingFlags&: renderableFlags, inOpacity: subsetOpacity, alreadyDirty: wasDirty,
1845 lights, ioFlags));
1846 QSSGShaderDefaultMaterialKey &theGeneratedKey(theMaterialPrepResult.materialKey);
1847 subsetOpacity = theMaterialPrepResult.opacity;
1848 QSSGRenderableImage *firstImage(theMaterialPrepResult.firstImage);
1849 renderableFlags = theMaterialPrepResult.renderableFlags;
1850
1851 if (model.particleBuffer && model.particleBuffer->particleCount())
1852 defaultMaterialShaderKeyProperties.m_blendParticles.setValue(inDataStore: theGeneratedKey, inValue: true);
1853 else
1854 defaultMaterialShaderKeyProperties.m_blendParticles.setValue(inDataStore: theGeneratedKey, inValue: false);
1855
1856 // Skin
1857 const auto boneCount = model.skin ? model.skin->boneCount :
1858 model.skeleton ? model.skeleton->boneCount : 0;
1859 defaultMaterialShaderKeyProperties.m_boneCount.setValue(inDataStore: theGeneratedKey, inValue: boneCount);
1860 if (auto idJoint = theSubset.rhi.ia.inputs.indexOf(t: QSSGRhiInputAssemblerState::JointSemantic); idJoint != -1) {
1861 const auto attr = theSubset.rhi.ia.inputLayout.attributeAt(index: idJoint);
1862 defaultMaterialShaderKeyProperties.m_usesFloatJointIndices.setValue(inDataStore: theGeneratedKey, inValue: checkF32TypeIndex(attr->format()));
1863 }
1864
1865 // Instancing
1866 bool usesInstancing = theModelContext.model.instancing()
1867 && rhiCtx->rhi()->isFeatureSupported(feature: QRhi::Instancing);
1868 defaultMaterialShaderKeyProperties.m_usesInstancing.setValue(inDataStore: theGeneratedKey, inValue: usesInstancing);
1869 // Morphing
1870 defaultMaterialShaderKeyProperties.m_targetCount.setValue(inDataStore: theGeneratedKey,
1871 inValue: theSubset.rhi.ia.targetCount);
1872 defaultMaterialShaderKeyProperties.m_targetPositionOffset.setValue(inDataStore: theGeneratedKey,
1873 inValue: theSubset.rhi.ia.targetOffsets[QSSGRhiInputAssemblerState::PositionSemantic]);
1874 defaultMaterialShaderKeyProperties.m_targetNormalOffset.setValue(inDataStore: theGeneratedKey,
1875 inValue: theSubset.rhi.ia.targetOffsets[QSSGRhiInputAssemblerState::NormalSemantic]);
1876 defaultMaterialShaderKeyProperties.m_targetTangentOffset.setValue(inDataStore: theGeneratedKey,
1877 inValue: theSubset.rhi.ia.targetOffsets[QSSGRhiInputAssemblerState::TangentSemantic]);
1878 defaultMaterialShaderKeyProperties.m_targetBinormalOffset.setValue(inDataStore: theGeneratedKey,
1879 inValue: theSubset.rhi.ia.targetOffsets[QSSGRhiInputAssemblerState::BinormalSemantic]);
1880 defaultMaterialShaderKeyProperties.m_targetTexCoord0Offset.setValue(inDataStore: theGeneratedKey,
1881 inValue: theSubset.rhi.ia.targetOffsets[QSSGRhiInputAssemblerState::TexCoord0Semantic]);
1882 defaultMaterialShaderKeyProperties.m_targetTexCoord1Offset.setValue(inDataStore: theGeneratedKey,
1883 inValue: theSubset.rhi.ia.targetOffsets[QSSGRhiInputAssemblerState::TexCoord1Semantic]);
1884 defaultMaterialShaderKeyProperties.m_targetColorOffset.setValue(inDataStore: theGeneratedKey,
1885 inValue: theSubset.rhi.ia.targetOffsets[QSSGRhiInputAssemblerState::ColorSemantic]);
1886
1887 if (theMaterial.m_iblProbe)
1888 theMaterial.m_iblProbe->clearDirty();
1889
1890 new (theRenderableObject) QSSGSubsetRenderable(QSSGSubsetRenderable::Type::CustomMaterialMeshSubset,
1891 renderableFlags,
1892 theModelCenter,
1893 renderer,
1894 theSubset,
1895 theModelContext,
1896 subsetOpacity,
1897 subsetLevelOfDetail,
1898 theMaterial,
1899 firstImage,
1900 theGeneratedKey,
1901 lights,
1902 anyLightHasShadows);
1903 }
1904 if (theRenderableObject) // NOTE: Should just go in with the ctor args
1905 theRenderableObject->camdistSq = getCameraDistanceSq(obj: *theRenderableObject, camera: allCameraData[0]);
1906 }
1907
1908 // If the indices don't match then something's off and we need to adjust the subset renderable list size.
1909 if (Q_UNLIKELY(idx != subsetIdx))
1910 renderableSubsets.mSize = subsetIdx; // subsetIdx == next_subsetIdx == size
1911
1912 for (auto &ro : renderableSubsets) {
1913 const auto depthMode = ro.depthWriteMode;
1914 hasDepthWriteObjects |= (depthMode == QSSGDepthDrawMode::Always || depthMode == QSSGDepthDrawMode::OpaqueOnly);
1915 enum ObjectType : quint8 { ScreenTexture, Transparent, Opaque };
1916 static constexpr DepthPrepassObject ppState[][2] = { {DepthPrepassObject::None, DepthPrepassObject::ScreenTexture},
1917 {DepthPrepassObject::None, DepthPrepassObject::Transparent},
1918 {DepthPrepassObject::None, DepthPrepassObject::Opaque} };
1919
1920 if (ro.renderableFlags.requiresScreenTexture()) {
1921 depthPrepassObjectsState |= DepthPrepassObjectStateT(ppState[ObjectType::ScreenTexture][size_t(depthMode == QSSGDepthDrawMode::OpaquePrePass)]);
1922 screenTextureObjects.push_back(t: {&ro, ro.camdistSq});
1923 } else if (ro.renderableFlags.hasTransparency()) {
1924 depthPrepassObjectsState |= DepthPrepassObjectStateT(ppState[ObjectType::Transparent][size_t(depthMode == QSSGDepthDrawMode::OpaquePrePass)]);
1925 transparentObjects.push_back(t: {&ro, ro.camdistSq});
1926 } else {
1927 depthPrepassObjectsState |= DepthPrepassObjectStateT(ppState[ObjectType::Opaque][size_t(depthMode == QSSGDepthDrawMode::OpaquePrePass)]);
1928 opaqueObjects.push_back(t: {&ro, ro.camdistSq});
1929 }
1930
1931 if (ro.renderableFlags.usedInBakedLighting())
1932 bakedLightingObjects.push_back(t: {&ro, ro.camdistSq});
1933 }
1934
1935 if (!bakedLightingObjects.isEmpty())
1936 bakedLightingModels.push_back(t: QSSGBakedLightingModel(&model, bakedLightingObjects));
1937 }
1938
1939 return wasDirty;
1940}
1941
1942bool QSSGLayerRenderData::prepareParticlesForRender(const RenderableNodeEntries &renderableParticles, const QSSGRenderCameraData &cameraData, QSSGLayerRenderPreparationResultFlags &ioFlags)
1943{
1944 QSSG_ASSERT(particlesEnabled, return false);
1945
1946 QSSGRenderContextInterface &contextInterface = *renderer->contextInterface();
1947
1948 bool dirty = false;
1949
1950 //
1951 auto &opaqueObjects = opaqueObjectStore[0];
1952 auto &transparentObjects = transparentObjectStore[0];
1953 auto &screenTextureObjects = screenTextureObjectStore[0];
1954
1955 for (const auto &renderable : renderableParticles) {
1956 const QSSGRenderParticles &particles = *static_cast<QSSGRenderParticles *>(renderable.node);
1957 const auto &lights = renderable.lights;
1958
1959 QSSGRenderableObjectFlags renderableFlags;
1960 renderableFlags.setCastsShadows(false);
1961 renderableFlags.setReceivesShadows(false);
1962 renderableFlags.setHasAttributePosition(true);
1963 renderableFlags.setHasAttributeNormal(true);
1964 renderableFlags.setHasAttributeTexCoord0(true);
1965 renderableFlags.setHasAttributeColor(true);
1966 renderableFlags.setHasTransparency(particles.m_hasTransparency);
1967 renderableFlags.setCastsReflections(particles.m_castsReflections);
1968 if (particles.m_hasTransparency && particles.m_blendMode != QSSGRenderParticles::BlendMode::SourceOver)
1969 ioFlags.setHasCustomBlendMode(true);
1970
1971 float opacity = getGlobalOpacity(node: particles);
1972 QVector3D center(particles.m_particleBuffer.bounds().center());
1973 center = QSSGUtils::mat44::transform(m: getGlobalTransform(node: particles), v: center);
1974
1975 QSSGRenderableImage *firstImage = nullptr;
1976 if (particles.m_sprite) {
1977 const auto &bufferManager = contextInterface.bufferManager();
1978
1979 if (particles.m_sprite->clearDirty())
1980 dirty = true;
1981
1982 const QSSGRenderImageTexture texture = bufferManager->loadRenderImage(image: particles.m_sprite);
1983 QSSGRenderableImage *theImage = RENDER_FRAME_NEW<QSSGRenderableImage>(ctx&: contextInterface, args: QSSGRenderableImage::Type::Diffuse, args&: *particles.m_sprite, args: texture);
1984 firstImage = theImage;
1985 }
1986
1987 QSSGRenderableImage *colorTable = nullptr;
1988 if (particles.m_colorTable) {
1989 const auto &bufferManager = contextInterface.bufferManager();
1990
1991 if (particles.m_colorTable->clearDirty())
1992 dirty = true;
1993
1994 const QSSGRenderImageTexture texture = bufferManager->loadRenderImage(image: particles.m_colorTable);
1995
1996 QSSGRenderableImage *theImage = RENDER_FRAME_NEW<QSSGRenderableImage>(ctx&: contextInterface, args: QSSGRenderableImage::Type::Diffuse, args&: *particles.m_colorTable, args: texture);
1997 colorTable = theImage;
1998 }
1999
2000 if (opacity > 0.0f && particles.m_particleBuffer.particleCount()) {
2001 const auto globalTransform = getGlobalTransform(node: particles);
2002 auto *theRenderableObject = RENDER_FRAME_NEW<QSSGParticlesRenderable>(ctx&: contextInterface,
2003 args&: renderableFlags,
2004 args&: center,
2005 args&: renderer,
2006 args: globalTransform,
2007 args: particles,
2008 args&: firstImage,
2009 args&: colorTable,
2010 args: lights,
2011 args&: opacity);
2012 if (theRenderableObject) {
2013 if (theRenderableObject->renderableFlags.requiresScreenTexture())
2014 screenTextureObjects.push_back(t: {theRenderableObject, getCameraDistanceSq(obj: *theRenderableObject, camera: cameraData)});
2015 else if (theRenderableObject->renderableFlags.hasTransparency())
2016 transparentObjects.push_back(t: {theRenderableObject, getCameraDistanceSq(obj: *theRenderableObject, camera: cameraData)});
2017 else
2018 opaqueObjects.push_back(t: {theRenderableObject, getCameraDistanceSq(obj: *theRenderableObject, camera: cameraData)});
2019 }
2020 }
2021 }
2022
2023 return dirty;
2024}
2025
2026bool QSSGLayerRenderData::prepareItem2DsForRender(const QSSGRenderContextInterface &ctxIfc,
2027 const QSSGItem2DsView &renderableItem2Ds)
2028{
2029 const bool hasItems = (renderableItem2Ds.size() != 0);
2030 if (hasItems) {
2031 const auto &rhiCtx = ctxIfc.rhiContext();
2032 const auto &clipSpaceCorrMatrix = ctxIfc.rhiContext()->rhi()->clipSpaceCorrMatrix();
2033 const QSSGRenderCameraDataList &cameraDatas(getCachedCameraDatas());
2034
2035 item2DDataMap.clear();
2036 item2DDataMap.reserve(n: size_t(renderableItem2Ds.size()));
2037 renderer->populateItem2DDataMapForLayer(layer, item2DDataMap);
2038 const auto getItem2DData = [&](const QSSGRenderItem2D *item) {
2039 const auto foundIt = item2DDataMap.find(x: item);
2040 return (foundIt != item2DDataMap.cend()) ? foundIt->second : QSSGRenderer::Item2DData{};
2041 };
2042
2043 for (const auto &theItem2D : renderableItem2Ds) {
2044 QSSGRenderer::Item2DData i2d = getItem2DData(theItem2D);
2045 i2d.layer = &layer;
2046 i2d.item = theItem2D;
2047 ModelViewProjections &mvps = i2d.mvps;
2048
2049 // Check that we have a renderer and that it hasn't changed (would indicate a context change)
2050 // and we need to update all the data.
2051 QSGRenderContext *sgRc = QSSGRendererPrivate::getSgRenderContext(renderer: *renderer);
2052 QSSG_ASSERT(sgRc != nullptr, continue);
2053 const bool contextChanged = (item2DRenderContext && item2DRenderContext != sgRc);
2054 item2DRenderContext = sgRc;
2055 if (contextChanged) {
2056 delete i2d.renderer;
2057 i2d.renderer = nullptr;
2058 }
2059
2060 if (!i2d.renderer)
2061 i2d.renderer = sgRc->createRenderer(renderMode: QSGRendererInterface::RenderMode3D);
2062
2063 if (i2d.renderer->rootNode() != theItem2D->m_rootNode) {
2064 i2d.renderer->setRootNode(theItem2D->m_rootNode);
2065 theItem2D->m_rootNode->markDirty(bits: QSGNode::DirtyForceUpdate); // Force matrix, clip and opacity update.
2066 i2d.renderer->nodeChanged(node: theItem2D->m_rootNode, state: QSGNode::DirtyForceUpdate); // Force render list update.
2067 }
2068
2069 if (!i2d.rpd)
2070 i2d.rpd = rhiCtx->mainRenderPassDescriptor()->newCompatibleRenderPassDescriptor();
2071
2072 for (size_t i = 0, end = qMin(a: cameraDatas.size(), b: 2); i < end; ++i) {
2073 const QSSGRenderCameraData &camData = cameraDatas[i];
2074 QMatrix4x4 mvp = camData.viewProjection * getGlobalTransform(node: *theItem2D);
2075 static const QMatrix4x4 flipMatrix(1.0f, 0.0f, 0.0f, 0.0f,
2076 0.0f, -1.0f, 0.0f, 0.0f,
2077 0.0f, 0.0f, 1.0f, 0.0f,
2078 0.0f, 0.0f, 0.0f, 1.0f);
2079 mvps[i] = clipSpaceCorrMatrix * mvp * flipMatrix;
2080 }
2081 if (i2d.isValid())
2082 renderer->registerItem2DData(data: i2d);
2083 }
2084 }
2085
2086 return hasItems;
2087}
2088
2089void QSSGLayerRenderData::prepareResourceLoaders()
2090{
2091 QSSGRenderContextInterface &contextInterface = *renderer->contextInterface();
2092 const auto &bufferManager = contextInterface.bufferManager();
2093
2094 for (const auto resourceLoader : std::as_const(t&: layer.resourceLoaders))
2095 bufferManager->processResourceLoader(loader: static_cast<QSSGRenderResourceLoader *>(resourceLoader));
2096}
2097
2098void QSSGLayerRenderData::prepareReflectionProbesForRender()
2099{
2100 const auto probeCount = reflectionProbesView.size();
2101 requestReflectionMapManager(); // ensure that we have a reflection map manager
2102
2103 for (int i = 0; i < probeCount; i++) {
2104 QSSGRenderReflectionProbe* probe = reflectionProbesView[i];
2105
2106 QMatrix4x4 probeTransform = getGlobalTransform(node: *probe);
2107
2108 int reflectionObjectCount = 0;
2109 QVector3D probeExtent = probe->boxSize / 2;
2110 QSSGBounds3 probeBound = QSSGBounds3::centerExtents(center: QSSGRenderNode::getGlobalPos(globalTransform: probeTransform) + probe->boxOffset, extent: probeExtent);
2111
2112 const auto injectProbe = [&](const QSSGRenderableObjectHandle &handle) {
2113 if (handle.obj->renderableFlags.testFlag(flag: QSSGRenderableObjectFlag::ReceivesReflections)
2114 && !(handle.obj->type == QSSGRenderableObject::Type::Particles)) {
2115 QSSGSubsetRenderable* renderableObj = static_cast<QSSGSubsetRenderable*>(handle.obj);
2116 QSSGBounds3 nodeBound = renderableObj->bounds;
2117 QVector4D vmin(nodeBound.minimum, 1.0);
2118 QVector4D vmax(nodeBound.maximum, 1.0);
2119 const QMatrix4x4 &renderableTransform = renderableObj->modelContext.globalTransform;
2120 vmin = renderableTransform * vmin;
2121 vmax = renderableTransform * vmax;
2122 nodeBound.minimum = vmin.toVector3D();
2123 nodeBound.maximum = vmax.toVector3D();
2124 if (probeBound.intersects(b: nodeBound)) {
2125 QVector3D nodeBoundCenter = nodeBound.center();
2126 QVector3D probeBoundCenter = probeBound.center();
2127 float distance = nodeBoundCenter.distanceToPoint(point: probeBoundCenter);
2128 if (renderableObj->reflectionProbeIndex == -1 || distance < renderableObj->distanceFromReflectionProbe) {
2129 renderableObj->reflectionProbeIndex = i;
2130 renderableObj->distanceFromReflectionProbe = distance;
2131 renderableObj->reflectionProbe.parallaxCorrection = probe->parallaxCorrection;
2132 renderableObj->reflectionProbe.probeCubeMapCenter = QSSGRenderNode::getGlobalPos(globalTransform: probeTransform);
2133 renderableObj->reflectionProbe.probeBoxMax = probeBound.maximum;
2134 renderableObj->reflectionProbe.probeBoxMin = probeBound.minimum;
2135 renderableObj->reflectionProbe.enabled = true;
2136 reflectionObjectCount++;
2137 }
2138 }
2139 }
2140 };
2141
2142 const auto &transparentObjects = std::as_const(t&: transparentObjectStore[0]);
2143 const auto &opaqueObjects = std::as_const(t&: opaqueObjectStore[0]);
2144 const auto &screenTextureObjects = std::as_const(t&: screenTextureObjectStore[0]);
2145
2146 for (const auto &handle : std::as_const(t: transparentObjects))
2147 injectProbe(handle);
2148
2149 for (const auto &handle : std::as_const(t: opaqueObjects))
2150 injectProbe(handle);
2151
2152 for (const auto &handle : std::as_const(t: screenTextureObjects))
2153 injectProbe(handle);
2154
2155 if (probe->texture)
2156 reflectionMapManager->addTexturedReflectionMapEntry(probeIdx: i, probe: *probe);
2157 else if (reflectionObjectCount > 0)
2158 reflectionMapManager->addReflectionMapEntry(probeIdx: i, probe: *probe);
2159 }
2160}
2161
2162static bool scopeLight(QSSGRenderNode *node, QSSGRenderNode *lightScope)
2163{
2164 // check if the node is parent of the lightScope
2165 while (node) {
2166 if (node == lightScope)
2167 return true;
2168 node = node->parent;
2169 }
2170 return false;
2171}
2172
2173static const int REDUCED_MAX_LIGHT_COUNT_THRESHOLD_BYTES = 4096; // 256 vec4
2174
2175static inline int effectiveMaxLightCount(const QSSGShaderFeatures &features)
2176{
2177 if (features.isSet(feature: QSSGShaderFeatures::Feature::ReduceMaxNumLights))
2178 return QSSG_REDUCED_MAX_NUM_LIGHTS;
2179
2180 return QSSG_MAX_NUM_LIGHTS;
2181}
2182
2183static void updateDirtySkeletons(const QSSGLayerRenderData &renderData, const QSSGLayerRenderData::QSSGModelsView &renderableNodes)
2184{
2185 // First model using skeleton clears the dirty flag so we need another mechanism
2186 // to tell to the other models the skeleton is dirty.
2187 QSet<QSSGRenderSkeleton *> dirtySkeletons;
2188 for (const auto &modelNode : std::as_const(t: renderableNodes)) {
2189 QSSGRenderSkeleton *skeletonNode = modelNode->skeleton;
2190 bool hcj = false;
2191 if (skeletonNode) {
2192 const bool dirtySkeleton = dirtySkeletons.contains(value: skeletonNode);
2193 const bool hasDirtyNonJoints = (skeletonNode->containsNonJointNodes
2194 && (hasDirtyNonJointNodes(node: skeletonNode, hasChildJoints&: hcj) || dirtySkeleton));
2195 if (skeletonNode->skinningDirty || hasDirtyNonJoints) {
2196 Q_ASSERT(!skeletonNode->isDirty(QSSGRenderNode::DirtyFlag::GlobalValuesDirty));
2197 skeletonNode->boneTransformsDirty = false;
2198 if (hasDirtyNonJoints && !dirtySkeleton)
2199 dirtySkeletons.insert(value: skeletonNode);
2200 skeletonNode->skinningDirty = false;
2201 const qsizetype dataSize = BONEDATASIZE4ID(skeletonNode->maxIndex);
2202 if (skeletonNode->boneData.size() < dataSize)
2203 skeletonNode->boneData.resize(size: dataSize);
2204 skeletonNode->containsNonJointNodes = false;
2205 for (auto &child : skeletonNode->children)
2206 collectBoneTransforms(renderData, node: &child, skeletonNode, poses: modelNode->inverseBindPoses);
2207 }
2208 skeletonNode->boneCount = skeletonNode->boneData.size() / 2 / 4 / 16;
2209 const int boneTexWidth = qCeil(v: qSqrt(v: skeletonNode->boneCount * 4 * 2));
2210 skeletonNode->boneTexData.setSize(QSize(boneTexWidth, boneTexWidth));
2211 skeletonNode->boneData.resize(size: boneTexWidth * boneTexWidth * 16);
2212 skeletonNode->boneTexData.setTextureData(skeletonNode->boneData);
2213 }
2214 const int numMorphTarget = modelNode->morphTargets.size();
2215 for (int i = 0; i < numMorphTarget; ++i) {
2216 auto morphTarget = static_cast<const QSSGRenderMorphTarget *>(modelNode->morphTargets.at(i));
2217 modelNode->morphWeights[i] = morphTarget->weight;
2218 modelNode->morphAttributes[i] = morphTarget->attributes;
2219 if (i > MAX_MORPH_TARGET_INDEX_SUPPORTS_NORMALS)
2220 modelNode->morphAttributes[i] &= 0x1; // MorphTarget.Position
2221 else if (i > MAX_MORPH_TARGET_INDEX_SUPPORTS_TANGENTS)
2222 modelNode->morphAttributes[i] &= 0x3; // MorphTarget.Position | MorphTarget.Normal
2223 }
2224 }
2225
2226 dirtySkeletons.clear();
2227}
2228
2229void QSSGLayerRenderData::prepareForRender()
2230{
2231 QSSG_ASSERT_X(layerPrepResult.isNull(), "Prep-result was not reset for render!", layerPrepResult = {});
2232
2233 QRect theViewport(renderer->viewport());
2234
2235 // NOTE: The renderer won't change in practice (after being set the first time), but just update
2236 // it anyways.
2237 frameData.m_ctx = renderer->contextInterface();
2238 frameData.clear();
2239
2240 // Create base pipeline state
2241 ps = {}; // Reset
2242 ps.viewport = { float(theViewport.x()), float(theViewport.y()), float(theViewport.width()), float(theViewport.height()), 0.0f, 1.0f };
2243 if (layer.scissorRect.isValid()) {
2244 ps.flags |= QSSGRhiGraphicsPipelineState::Flag::UsesScissor;
2245 ps.scissor = { layer.scissorRect.x(),
2246 theViewport.height() - (layer.scissorRect.y() + layer.scissorRect.height()),
2247 layer.scissorRect.width(),
2248 layer.scissorRect.height() };
2249 }
2250
2251 ps.depthFunc = QRhiGraphicsPipeline::LessOrEqual;
2252 ps.flags.setFlag(flag: QSSGRhiGraphicsPipelineState::Flag::BlendEnabled, on: false);
2253
2254 // Enable Wireframe mode
2255 ps.polygonMode = layer.wireframeMode ? QRhiGraphicsPipeline::Line : QRhiGraphicsPipeline::Fill;
2256
2257 bool wasDirty = false;
2258 bool wasDataDirty = false;
2259 wasDirty = layer.isDirty();
2260
2261 layerPrepResult = { theViewport, layer };
2262
2263 // SSAO
2264 const bool SSAOEnabled = layer.ssaoEnabled();
2265 layerPrepResult.flags.setRequiresSsaoPass(SSAOEnabled);
2266 features.set(feature: QSSGShaderFeatures::Feature::Ssao, val: SSAOEnabled);
2267
2268 // Effects
2269 bool requiresDepthTexture = SSAOEnabled;
2270 for (QSSGRenderEffect *theEffect = layer.firstEffect; theEffect; theEffect = theEffect->m_nextEffect) {
2271 if (theEffect->isDirty()) {
2272 wasDirty = true;
2273 theEffect->clearDirty();
2274 }
2275 if (theEffect->requiresDepthTexture)
2276 requiresDepthTexture = true;
2277 }
2278
2279 const auto &rhiCtx = renderer->contextInterface()->rhiContext();
2280 orderIndependentTransparencyEnabled = (layer.oitMethod != QSSGRenderLayer::OITMethod::None);
2281 if (layer.oitMethod == QSSGRenderLayer::OITMethod::WeightedBlended) {
2282 orderIndependentTransparencyEnabled = rhiCtx->rhi()->isFeatureSupported(feature: QRhi::PerRenderTargetBlending);
2283 if (rhiCtx->mainPassSampleCount() > 1)
2284 orderIndependentTransparencyEnabled |= rhiCtx->rhi()->isFeatureSupported(feature: QRhi::TexelFetch) && rhiCtx->rhi()->isFeatureSupported(feature: QRhi::SampleVariables);
2285 if (!orderIndependentTransparencyEnabled && !oitWarningUnsupportedShown) {
2286 qCWarning(lcQuick3DRender) << "Order Independent Transparency is requested, but it is not supported.";
2287 oitWarningUnsupportedShown = true;
2288 }
2289 }
2290 if (layer.oitMethodDirty) {
2291 oitRenderContext.reset();
2292 for (auto &renderResult : renderResults)
2293 renderResult.reset();
2294 }
2295
2296 layerPrepResult.flags.setRequiresDepthTexture(requiresDepthTexture);
2297
2298 // Tonemapping. Except when there are effects, then it is up to the
2299 // last pass of the last effect to perform tonemapping.
2300 if (!layer.firstEffect)
2301 QSSGLayerRenderData::setTonemapFeatures(features, tonemapMode: layer.tonemapMode);
2302
2303 // We may not be able to have an array of 15 light struct elements in
2304 // the shaders. Switch on the reduced-max-number-of-lights feature
2305 // if necessary. In practice this is relevant with OpenGL ES 3.0 or
2306 // 2.0, because there are still implementations in use that only
2307 // support the spec mandated minimum of 224 vec4s (so 3584 bytes).
2308 if (rhiCtx->rhi()->resourceLimit(limit: QRhi::MaxUniformBufferRange) < REDUCED_MAX_LIGHT_COUNT_THRESHOLD_BYTES) {
2309 features.set(feature: QSSGShaderFeatures::Feature::ReduceMaxNumLights, val: true);
2310 static bool notified = false;
2311 if (!notified) {
2312 notified = true;
2313 qCDebug(lcQuick3DRender, "Qt Quick 3D maximum number of lights has been reduced from %d to %d due to the graphics driver's limitations",
2314 QSSG_MAX_NUM_LIGHTS, QSSG_REDUCED_MAX_NUM_LIGHTS);
2315 }
2316 }
2317
2318 // IBL Lightprobe Image
2319 QSSGRenderImageTexture lightProbeTexture;
2320 if (layer.lightProbe) {
2321 const auto &lightProbeSettings = layer.lightProbeSettings;
2322 if (layer.lightProbe->m_format == QSSGRenderTextureFormat::Unknown) {
2323 // Choose on a format that makes sense for a light probe
2324 // At this point it's just a suggestion
2325 if (renderer->contextInterface()->rhiContext()->rhi()->isTextureFormatSupported(format: QRhiTexture::RGBA16F))
2326 layer.lightProbe->m_format = QSSGRenderTextureFormat::RGBA16F;
2327 else
2328 layer.lightProbe->m_format = QSSGRenderTextureFormat::RGBE8;
2329 }
2330
2331 if (layer.lightProbe->clearDirty())
2332 wasDataDirty = true;
2333
2334 // NOTE: This call can lead to rendering (of envmap) and a texture upload
2335 lightProbeTexture = renderer->contextInterface()->bufferManager()->loadRenderImage(image: layer.lightProbe, inMipMode: QSSGBufferManager::MipModeBsdf);
2336 if (lightProbeTexture.m_texture) {
2337
2338 features.set(feature: QSSGShaderFeatures::Feature::LightProbe, val: true);
2339 features.set(feature: QSSGShaderFeatures::Feature::IblOrientation, val: !lightProbeSettings.probeOrientation.isIdentity());
2340
2341 // By this point we will know what the actual texture format of the light probe is
2342 // Check if using RGBE format light probe texture (the Rhi format will be RGBA8)
2343 if (lightProbeTexture.m_flags.isRgbe8())
2344 features.set(feature: QSSGShaderFeatures::Feature::RGBELightProbe, val: true);
2345 } else {
2346 layer.lightProbe = nullptr;
2347 }
2348
2349 const bool forceIblExposureValues = (features.isSet(feature: QSSGShaderFeatures::Feature::LightProbe) && layer.tonemapMode == QSSGRenderLayer::TonemapMode::Custom);
2350 features.set(feature: QSSGShaderFeatures::Feature::ForceIblExposure, val: forceIblExposureValues);
2351 }
2352
2353 frameData.m_ctx->bufferManager()->setLightmapSource(layer.lightmapSource);
2354
2355 // Update the node data version for this layer.
2356 // This version should only change if the world tree was re-indexed.
2357 version = nodeData->version();
2358
2359 // We're using/updating the node data directly.
2360 // NOTE: These are the transforms and opacities for all nodes for all layers/views.
2361 // We'll just use or update the ones the one for this layer.
2362 auto &globalTransforms = nodeData->globalTransforms;
2363 auto &globalOpacities = nodeData->globalOpacities;
2364 auto &instanceTransforms = nodeData->instanceTransforms;
2365
2366 ///////// 2 - START LAYER
2367 QSSGRenderDataHelpers::GlobalStateResultT globalStateResult = QSSGRenderDataHelpers::GlobalStateResult::None;
2368
2369 const bool layerTreeWasDirty = layer.isDirty(dirtyFlag: QSSGRenderLayer::DirtyFlag::TreeDirty);
2370 layer.clearDirty(dirtyFlag: QSSGRenderLayer::DirtyFlag::TreeDirty);
2371 if (layerTreeWasDirty) {
2372 wasDataDirty = true;
2373 layerNodes = nodeData->getLayerNodeView(layer);
2374 } else {
2375 for (auto &node : layerNodes)
2376 globalStateResult |= QSSGRenderDataHelpers::updateGlobalNodeState(node, version);
2377
2378 bool transformAndOpacityDirty = false;
2379 for (auto &node : layerNodes)
2380 transformAndOpacityDirty |= QSSGRenderDataHelpers::calcGlobalNodeData<QSSGRenderDataHelpers::Strategy::Update>(node, version, globalTransforms, globalOpacities);
2381
2382 // FIXME: We shouldn't need to re-create all the instance transforms even when instancing isn't used...
2383 if (transformAndOpacityDirty) {
2384 for (const auto &node : layerNodes)
2385 wasDataDirty |= QSSGRenderDataHelpers::calcInstanceTransforms(node, version, globalTransforms, instanceTransforms);
2386 }
2387
2388 wasDataDirty |= transformAndOpacityDirty;
2389 }
2390
2391 const bool restatNodes = (layerTreeWasDirty || (globalStateResult & QSSGRenderDataHelpers::GlobalStateResult::ActiveChanged));
2392
2393 if (restatNodes) {
2394 modelsView.clear();
2395 particlesView.clear();
2396 item2DsView.clear();
2397 camerasView.clear();
2398 lightsView.clear();
2399 reflectionProbesView.clear();
2400
2401 enum NodeType : size_t { Model = 0, Particles, Item2D, Camera, Light, ReflectionProbe, Other, Inactive };
2402 static const auto nodeType = [](QSSGRenderNode *node) -> NodeType {
2403 if (!node->getGlobalState(stateFlag: QSSGRenderNode::GlobalState::Active))
2404 return NodeType::Inactive;
2405 switch (node->type) {
2406 case QSSGRenderGraphObject::Type::Model: return NodeType::Model;
2407 case QSSGRenderGraphObject::Type::Particles: return NodeType::Particles;
2408 case QSSGRenderGraphObject::Type::Item2D: return NodeType::Item2D;
2409 case QSSGRenderGraphObject::Type::ReflectionProbe: return NodeType::ReflectionProbe;
2410 default: break;
2411 }
2412
2413 if (QSSGRenderGraphObject::isCamera(type: node->type))
2414 return NodeType::Camera;
2415 if (QSSGRenderGraphObject::isLight(type: node->type))
2416 return NodeType::Light;
2417
2418 return NodeType::Other;
2419 };
2420 // sort nodes by type - We could do this on insert, but it's not given that it would be beneficial.
2421 // Depending on how we want to handle the nodes later it might just not give us anything
2422 // so, keep it simple for now.
2423 // We could also speed up this by having the pointer and the type in the same struct and sort without
2424 // indirection. However, that' slightly less convenient and the idea here is that we don't process
2425 // this list unless things change, which is not something that should happen often if the user is
2426 // concerned about performance, as it means we need to reevaluate the whole scene anyway.
2427 {
2428 // Sort the nodes by type (we copy the pointers to avoid sorting the original list,
2429 // which is stored based on the nodes' order in the world tree).
2430 layerNodesCategorized = { layerNodes.begin(), layerNodes.end() };
2431 // NOTE: Due to the ordering of item2ds, we need to use stable_sort.
2432 std::stable_sort(first: layerNodesCategorized.begin(), last: layerNodesCategorized.end(), comp: [](QSSGRenderNode *a, QSSGRenderNode *b) {
2433 return nodeType(a) < nodeType(b);
2434 });
2435 }
2436
2437 // Group nodes by type inline and keep track of the individual parts using QSSGDataViews
2438 const LayerNodeStatResult stat = statLayerNodes(layerNodes: layerNodesCategorized);
2439
2440 // Go through the sorted nodes and create the views
2441 size_t next = 0;
2442
2443 if (stat.modelCount > 0) {
2444 modelsView = QSSGModelsView((QSSGRenderModel **)(layerNodesCategorized.data() + next), stat.modelCount);
2445 next = modelsView.size();
2446 }
2447 if (stat.particlesCount > 0) {
2448 particlesView = QSSGParticlesView((QSSGRenderParticles **)(layerNodesCategorized.data() + next), stat.particlesCount);
2449 next += particlesView.size();
2450 }
2451 if (stat.item2DCount > 0) {
2452 item2DsView = QSSGItem2DsView((QSSGRenderItem2D **)(layerNodesCategorized.data() + next), stat.item2DCount);
2453 next += item2DsView.size();
2454 }
2455 if (stat.cameraCount > 0) {
2456 camerasView = QSSGCamerasView((QSSGRenderCamera **)(layerNodesCategorized.data() + next), stat.cameraCount);
2457 next += camerasView.size();
2458 }
2459 if (stat.lightCount > 0) {
2460 lightsView = QSSGLightsView((QSSGRenderLight **)(layerNodesCategorized.data() + next), stat.lightCount);
2461 next += lightsView.size();
2462 }
2463 if (stat.reflectionProbeCount > 0) {
2464 reflectionProbesView = QSSGReflectionProbesView((QSSGRenderReflectionProbe **)(layerNodesCategorized.data() + next), stat.reflectionProbeCount);
2465 next += reflectionProbesView.size();
2466 }
2467 if (stat.otherCount > 0) {
2468 nonCategorizedView = QSSGNonCategorizedView((QSSGRenderNode **)(layerNodesCategorized.data() + next), stat.otherCount);
2469 next += nonCategorizedView.size();
2470 (void)next;
2471 }
2472
2473 // FIXME: Compatability with old code (Will remove later).
2474 // NOTE: see resetForFrame() as well for extensions usage
2475 renderableModels.clear();
2476 renderableParticles.clear();
2477 renderableModels.reserve(asize: modelsView.size());
2478 renderableParticles.reserve(asize: particlesView.size());
2479
2480 renderableModels = {modelsView.begin(), modelsView.end()};
2481 renderableParticles = {particlesView.begin(), particlesView.end()};
2482 }
2483
2484 // Cameras
2485 // 1. If there's an explicit camera set and it's active (visible) we'll use that.
2486 // 2. ... if the explicitly set camera is not visible, no further attempts will be done.
2487 // 3. If no explicit camera is set, we'll search and pick the first active camera.
2488 QSSGRenderCamera::Configuration cameraConfig { .dpr: renderer->dpr(), .ssaaMultiplier: layer.isSsaaEnabled() ? layer.ssaaMultiplier : 1.0f };
2489 renderedCameras.clear();
2490 if (!layer.explicitCameras.isEmpty()) {
2491 for (QSSGRenderCamera *cam : std::as_const(t&: layer.explicitCameras)) {
2492 // 1.
2493 if (cam->getGlobalState(stateFlag: QSSGRenderCamera::GlobalState::Active)) {
2494 const bool computeFrustumSucceeded = cam->calculateProjection(inViewport: theViewport, config: cameraConfig);
2495 if (Q_LIKELY(computeFrustumSucceeded))
2496 renderedCameras.append(t: cam);
2497 else
2498 qCCritical(INTERNAL_ERROR, "Failed to calculate camera frustum");
2499 }
2500 }
2501 // 2.
2502 } else if (QSSG_GUARD_X(layer.viewCount == 1, "Multiview rendering requires explicit cameras to be set!.")) {
2503 // NOTE: This path can never be hit with multiview, hence the guard.
2504 // (Multiview will always have explicit cameras set.)
2505
2506 // 3.
2507 for (auto iter = camerasView.begin(); renderedCameras.isEmpty() && iter != camerasView.end(); iter++) {
2508 QSSGRenderCamera *theCamera = *iter;
2509 if (theCamera->getGlobalState(stateFlag: QSSGRenderCamera::GlobalState::Active)) {
2510 const bool computeFrustumSucceeded = theCamera->calculateProjection(inViewport: theViewport, config: cameraConfig);
2511 if (Q_LIKELY(computeFrustumSucceeded))
2512 renderedCameras.append(t: theCamera);
2513 else
2514 qCCritical(INTERNAL_ERROR, "Failed to calculate camera frustum");
2515 }
2516 }
2517 }
2518
2519 float meshLodThreshold = 1.0f;
2520 if (!renderedCameras.isEmpty())
2521 meshLodThreshold = renderedCameras[0]->levelOfDetailPixelThreshold / theViewport.width();
2522
2523 layer.renderedCamerasMutex.lock();
2524 layer.renderedCameras = renderedCameras;
2525 layer.renderedCamerasMutex.unlock();
2526
2527 // Meshes, materials, MVP, and normal matrices for the models
2528 const QSSGRenderCameraDataList &renderCameraData = getCachedCameraDatas();
2529 modelData->updateModelData(models&: modelsView, renderer, renderCameraData);
2530
2531 // ResourceLoaders
2532 prepareResourceLoaders();
2533
2534 // Skeletons
2535 updateDirtySkeletons(renderData: *this, renderableNodes: modelsView);
2536
2537 // Lights
2538 int shadowMapCount = 0;
2539 bool hasScopedLights = false;
2540 // Determine which lights will actually Render
2541 // Determine how many lights will need shadow maps
2542 // NOTE: This culling is specific to our Forward renderer
2543 const int maxLightCount = effectiveMaxLightCount(features);
2544 const bool showLightCountWarning = !tooManyLightsWarningShown && (lightsView.size() > maxLightCount);
2545 if (showLightCountWarning) {
2546 qWarning(msg: "Too many lights in scene, maximum is %d", maxLightCount);
2547 tooManyLightsWarningShown = true;
2548 }
2549
2550 QSSGShaderLightList renderableLights; // All lights (upto 'maxLightCount')
2551
2552 // List should contain only enabled lights (active && birghtness > 0).
2553 {
2554 auto it = std::make_reverse_iterator(i: lightsView.end());
2555 const auto end = it + qMin(a: maxLightCount, b: lightsView.size());
2556 for (; it != end; ++it) {
2557 QSSGRenderLight *renderLight = (*it);
2558 QMatrix4x4 renderLightTransform = getGlobalTransform(node: *renderLight);
2559 hasScopedLights |= (renderLight->m_scope != nullptr);
2560 const bool mightCastShadows = renderLight->m_castShadow && !renderLight->m_fullyBaked;
2561 const bool shadows = mightCastShadows && (shadowMapCount < QSSG_MAX_NUM_SHADOW_MAPS);
2562 shadowMapCount += int(shadows);
2563 const auto &direction = QSSGRenderNode::getScalingCorrectDirection(globalTransform: renderLightTransform);
2564 renderableLights.push_back(t: QSSGShaderLight{ .light: renderLight, .shadows: shadows, .direction: direction });
2565 }
2566
2567 if ((shadowMapCount >= QSSG_MAX_NUM_SHADOW_MAPS) && !tooManyShadowLightsWarningShown) {
2568 qWarning(msg: "Too many shadow casting lights in scene, maximum is %d", QSSG_MAX_NUM_SHADOW_MAPS);
2569 tooManyShadowLightsWarningShown = true;
2570 }
2571 }
2572
2573 if (shadowMapCount > 0) { // Setup Shadow Maps Entries for Lights casting shadows
2574 requestShadowMapManager(); // Ensure we have a shadow map manager
2575 layerPrepResult.flags.setRequiresShadowMapPass(true);
2576 // Any light with castShadow=true triggers shadow mapping
2577 // in the generated shaders. The fact that some (or even
2578 // all) objects may opt out from receiving shadows plays no
2579 // role here whatsoever.
2580 features.set(feature: QSSGShaderFeatures::Feature::Ssm, val: true);
2581 shadowMapManager->addShadowMaps(renderableLights);
2582 } else if (shadowMapManager) {
2583 // No shadows but a shadow manager so clear old resources
2584 shadowMapManager->releaseCachedResources();
2585 }
2586
2587 // Give each renderable a copy of the lights available
2588 // Also setup scoping for scoped lights
2589
2590 QSSG_ASSERT(globalLights.isEmpty(), globalLights.clear());
2591 if (hasScopedLights) { // Filter out scoped lights from the global lights list
2592 for (const auto &shaderLight : std::as_const(t&: renderableLights)) {
2593 if (!shaderLight.light->m_scope)
2594 globalLights.push_back(t: shaderLight);
2595 }
2596
2597 const auto prepareLightsWithScopedLights = [&renderableLights, this](QVector<QSSGRenderableNodeEntry> &renderableNodes) {
2598 for (qint32 idx = 0, end = renderableNodes.size(); idx < end; ++idx) {
2599 QSSGRenderableNodeEntry &theNodeEntry(renderableNodes[idx]);
2600 QSSGShaderLightList filteredLights;
2601 for (const auto &light : std::as_const(t&: renderableLights)) {
2602 if (light.light->m_scope && !scopeLight(node: theNodeEntry.node, lightScope: light.light->m_scope))
2603 continue;
2604 filteredLights.push_back(t: light);
2605 }
2606
2607 if (filteredLights.isEmpty()) { // Node without scoped lights, just reference the global light list.
2608 theNodeEntry.lights = QSSGDataView(globalLights);
2609 } else {
2610 // This node has scoped lights, i.e., it's lights differ from the global list
2611 // we therefore create a bespoke light list for it. Technically this might be the same for
2612 // more then this one node, but the overhead for tracking that is not worth it.
2613 auto customLightList = RENDER_FRAME_NEW_BUFFER<QSSGShaderLight>(ctx&: *renderer->contextInterface(), count: filteredLights.size());
2614 std::copy(first: filteredLights.cbegin(), last: filteredLights.cend(), result: customLightList.begin());
2615 theNodeEntry.lights = customLightList;
2616 }
2617 }
2618 };
2619
2620 prepareLightsWithScopedLights(renderableModels);
2621 prepareLightsWithScopedLights(renderableParticles);
2622 } else { // Just a simple copy
2623 globalLights = renderableLights;
2624 // No scoped lights, all nodes can just reference the global light list.
2625 const auto prepareLights = [this](QVector<QSSGRenderableNodeEntry> &renderableNodes) {
2626 for (qint32 idx = 0, end = renderableNodes.size(); idx < end; ++idx) {
2627 QSSGRenderableNodeEntry &theNodeEntry(renderableNodes[idx]);
2628 theNodeEntry.lights = QSSGDataView(globalLights);
2629 }
2630 };
2631
2632 prepareLights(renderableModels);
2633 prepareLights(renderableParticles);
2634 }
2635
2636 {
2637 // Give user provided passes a chance to modify the renderable data before starting
2638 // Note: All non-active extensions should be filtered out by now
2639 Q_STATIC_ASSERT(USERPASSES == size_t(QSSGRenderLayer::RenderExtensionStage::Count));
2640 for (size_t i = 0; i != size_t(QSSGRenderLayer::RenderExtensionStage::Count); ++i) {
2641 const auto &renderExtensions = layer.renderExtensions[i];
2642 auto &userPass = userPasses[i];
2643 for (auto rit = renderExtensions.crbegin(), rend = renderExtensions.crend(); rit != rend; ++rit) {
2644 if ((*rit)->prepareData(data&: frameData)) {
2645 wasDirty |= true;
2646 userPass.extensions.push_back(t: *rit);
2647 }
2648 }
2649 }
2650 }
2651
2652 auto &opaqueObjects = opaqueObjectStore[0];
2653 auto &transparentObjects = transparentObjectStore[0];
2654 auto &screenTextureObjects = screenTextureObjectStore[0];
2655
2656 if (!renderedCameras.isEmpty()) { // NOTE: We shouldn't really get this far without a camera...
2657 wasDirty |= prepareModelsForRender(contextInterface&: *renderer->contextInterface(), renderableModels, ioFlags&: layerPrepResult.flags, allCameras: renderedCameras, allCameraData: getCachedCameraDatas(), modelContexts, opaqueObjects, transparentObjects, screenTextureObjects, lodThreshold: meshLodThreshold);
2658 if (particlesEnabled) {
2659 const auto &cameraDatas = getCachedCameraDatas();
2660 wasDirty |= prepareParticlesForRender(renderableParticles, cameraData: cameraDatas[0], ioFlags&: layerPrepResult.flags);
2661 }
2662 wasDirty |= prepareItem2DsForRender(ctxIfc: *renderer->contextInterface(), renderableItem2Ds: item2DsView);
2663 }
2664 if (orderIndependentTransparencyEnabled) {
2665 // OIT blending mode must be SourceOver and have transparent objects
2666 if (transparentObjects.size() > 0 && !layerPrepResult.flags.hasCustomBlendMode()) {
2667 if (layer.oitMethod == QSSGRenderLayer::OITMethod::WeightedBlended) {
2668 if (rhiCtx->mainPassSampleCount() > 1)
2669 layerPrepResult.flags.setRequiresDepthTextureMS(true);
2670 else
2671 layerPrepResult.flags.setRequiresDepthTexture(true);
2672 }
2673 } else {
2674 orderIndependentTransparencyEnabled = false;
2675 if (!oitWarningInvalidBlendModeShown) {
2676 qCWarning(lcQuick3DRender) << "Order Independent Transparency requested, but disabled due to invalid blend modes.";
2677 qCWarning(lcQuick3DRender) << "Use SourceOver blend mode for Order Independent Transparency.";
2678 oitWarningInvalidBlendModeShown = true;
2679 }
2680 }
2681 }
2682 layer.oitMethodDirty = false;
2683
2684 prepareReflectionProbesForRender();
2685
2686 wasDirty = wasDirty || wasDataDirty;
2687 layerPrepResult.flags.setWasDirty(wasDirty);
2688 layerPrepResult.flags.setLayerDataDirty(wasDataDirty);
2689
2690 //
2691 const bool animating = wasDirty;
2692 if (animating)
2693 layer.progAAPassIndex = 0;
2694
2695 const bool progressiveAA = layer.isProgressiveAAEnabled() && !animating;
2696 layer.progressiveAAIsActive = progressiveAA;
2697 const bool temporalAA = layer.isTemporalAAEnabled() && !progressiveAA;
2698
2699 layer.temporalAAIsActive = temporalAA;
2700
2701 QVector2D vertexOffsetsAA;
2702
2703 if (progressiveAA && layer.progAAPassIndex > 0 && layer.progAAPassIndex < quint32(layer.antialiasingQuality)) {
2704 int idx = layer.progAAPassIndex - 1;
2705 vertexOffsetsAA = s_ProgressiveAAVertexOffsets[idx] / QVector2D{ float(theViewport.width()/2.0), float(theViewport.height()/2.0) };
2706 }
2707
2708 if (temporalAA) {
2709 const int t = 1 - 2 * (layer.tempAAPassIndex % 2);
2710 const float f = t * layer.temporalAAStrength;
2711 vertexOffsetsAA = { f / float(theViewport.width()/2.0), f / float(theViewport.height()/2.0) };
2712 }
2713
2714 if (!renderedCameras.isEmpty()) {
2715 if (temporalAA || progressiveAA /*&& !vertexOffsetsAA.isNull()*/) {
2716 QMatrix4x4 offsetProjection = renderedCameras[0]->projection;
2717 QMatrix4x4 invProjection = renderedCameras[0]->projection.inverted();
2718 if (renderedCameras[0]->type == QSSGRenderCamera::Type::OrthographicCamera) {
2719 offsetProjection(0, 3) -= vertexOffsetsAA.x();
2720 offsetProjection(1, 3) -= vertexOffsetsAA.y();
2721 } else if (renderedCameras[0]->type == QSSGRenderCamera::Type::PerspectiveCamera) {
2722 offsetProjection(0, 2) += vertexOffsetsAA.x();
2723 offsetProjection(1, 2) += vertexOffsetsAA.y();
2724 }
2725 for (auto &modelContext : std::as_const(t&: modelContexts)) {
2726 for (int mvpIdx = 0; mvpIdx < renderedCameras.count(); ++mvpIdx)
2727 modelContext->modelViewProjections[mvpIdx] = offsetProjection * invProjection * modelContext->modelViewProjections[mvpIdx];
2728 }
2729 }
2730 }
2731
2732 const bool hasItem2Ds = (item2DsView.size() > 0);
2733 const bool layerEnableDepthTest = layer.layerFlags.testFlag(flag: QSSGRenderLayer::LayerFlag::EnableDepthTest);
2734 const bool layerEnabledDepthPrePass = layer.layerFlags.testFlag(flag: QSSGRenderLayer::LayerFlag::EnableDepthPrePass);
2735 const bool depthTestEnableDefault = layerEnableDepthTest && (!opaqueObjects.isEmpty() || depthPrepassObjectsState || hasDepthWriteObjects);
2736 const bool zPrePassForced = (depthPrepassObjectsState != 0);
2737 zPrePassActive = zPrePassForced || (layerEnabledDepthPrePass && layerEnableDepthTest && (hasDepthWriteObjects || hasItem2Ds));
2738 const bool depthWriteEnableDefault = depthTestEnableDefault && (!layerEnabledDepthPrePass || !zPrePassActive);
2739
2740 ps.flags.setFlag(flag: QSSGRhiGraphicsPipelineState::Flag::DepthTestEnabled, on: depthTestEnableDefault);
2741 ps.flags.setFlag(flag: QSSGRhiGraphicsPipelineState::Flag::DepthWriteEnabled, on: depthWriteEnableDefault);
2742
2743 // Prepare passes
2744 QSSG_ASSERT(activePasses.isEmpty(), activePasses.clear());
2745 // If needed, generate a depth texture with the opaque objects. This
2746 // and the SSAO texture must come first since other passes may want to
2747 // expose these textures to their shaders.
2748 if (layerPrepResult.flags.requiresDepthTexture())
2749 activePasses.push_back(t: &depthMapPass);
2750 if (layerPrepResult.flags.requiresDepthTextureMS())
2751 activePasses.push_back(t: &depthMapPassMS);
2752
2753 // Screen space ambient occlusion. Relies on the depth texture and generates an AO map.
2754 if (layerPrepResult.flags.requiresSsaoPass())
2755 activePasses.push_back(t: &ssaoMapPass);
2756
2757 // Shadows. Generates a 2D or cube shadow map. (opaque + pre-pass transparent objects)
2758 if (layerPrepResult.flags.requiresShadowMapPass())
2759 activePasses.push_back(t: &shadowMapPass);
2760
2761 if (zPrePassActive)
2762 activePasses.push_back(t: &zPrePassPass);
2763
2764 // Screen texture with opaque objects.
2765 if (layerPrepResult.flags.requiresScreenTexture())
2766 activePasses.push_back(t: &screenMapPass);
2767
2768 // Reflection pass
2769 activePasses.push_back(t: &reflectionMapPass);
2770
2771 auto &underlayPass = userPasses[size_t(QSSGRenderLayer::RenderExtensionStage::Underlay)];
2772 if (underlayPass.hasData())
2773 activePasses.push_back(t: &underlayPass);
2774
2775 const bool hasOpaqueObjects = (opaqueObjects.size() > 0);
2776
2777 if (hasOpaqueObjects)
2778 activePasses.push_back(t: &opaquePass);
2779
2780 // NOTE: When the a screen texture is used, the skybox pass will be called twice. First from
2781 // the screen texture pass and later as part of the normal run through the list.
2782 if (renderer->contextInterface()->rhiContext()->rhi()->isFeatureSupported(feature: QRhi::TexelFetch)) {
2783 if (layer.background == QSSGRenderLayer::Background::SkyBoxCubeMap && layer.skyBoxCubeMap)
2784 activePasses.push_back(t: &skyboxCubeMapPass);
2785 else if (layer.background == QSSGRenderLayer::Background::SkyBox && layer.lightProbe)
2786 activePasses.push_back(t: &skyboxPass);
2787 }
2788
2789 if (hasItem2Ds)
2790 activePasses.push_back(t: &item2DPass);
2791
2792 if (layerPrepResult.flags.requiresScreenTexture())
2793 activePasses.push_back(t: &reflectionPass);
2794
2795 // Note: Transparent pass includeds opaque objects when layerEnableDepthTest is false.
2796 if (transparentObjects.size() > 0 || (!layerEnableDepthTest && hasOpaqueObjects)) {
2797 if (orderIndependentTransparencyEnabled) {
2798 activePasses.push_back(t: &oitRenderPass);
2799 activePasses.push_back(t: &oitCompositePass);
2800 oitRenderPass.setMethod(layer.oitMethod);
2801 oitCompositePass.setMethod(layer.oitMethod);
2802 } else {
2803 activePasses.push_back(t: &transparentPass);
2804 }
2805 }
2806
2807 auto &overlayPass = userPasses[size_t(QSSGRenderLayer::RenderExtensionStage::Overlay)];
2808 if (overlayPass.hasData())
2809 activePasses.push_back(t: &overlayPass);
2810
2811 if (layer.gridEnabled)
2812 activePasses.push_back(t: &infiniteGridPass);
2813
2814 if (const auto &dbgDrawSystem = renderer->contextInterface()->debugDrawSystem(); dbgDrawSystem && dbgDrawSystem->isEnabled())
2815 activePasses.push_back(t: &debugDrawPass);
2816}
2817
2818template<typename T>
2819static void clearTable(std::vector<T> &entry)
2820{
2821 for (auto &e : entry)
2822 e.clear();
2823}
2824
2825void QSSGLayerRenderData::resetForFrame()
2826{
2827 for (const auto &pass : activePasses)
2828 pass->resetForFrame();
2829 activePasses.clear();
2830 bakedLightingModels.clear();
2831 layerPrepResult = {};
2832 renderedCameraData.reset();
2833 renderedItem2Ds.clear();
2834 renderedBakedLightingModels.clear();
2835 lightmapTextures.clear();
2836 bonemapTextures.clear();
2837 globalLights.clear();
2838 modelContexts.clear();
2839 features = QSSGShaderFeatures();
2840 hasDepthWriteObjects = false;
2841 depthPrepassObjectsState = { DepthPrepassObjectStateT(DepthPrepassObject::None) };
2842 zPrePassActive = false;
2843 savedRenderState.reset();
2844
2845 clearTable(entry&: renderableModelStore);
2846 clearTable(entry&: modelContextStore);
2847 clearTable(entry&: renderableObjectStore);
2848 clearTable(entry&: opaqueObjectStore);
2849 clearTable(entry&: transparentObjectStore);
2850 clearTable(entry&: screenTextureObjectStore);
2851
2852 clearTable(entry&: sortedOpaqueObjectCache);
2853 clearTable(entry&: sortedTransparentObjectCache);
2854 clearTable(entry&: sortedScreenTextureObjectCache);
2855 clearTable(entry&: sortedOpaqueDepthPrepassCache);
2856 clearTable(entry&: sortedDepthWriteCache);
2857
2858 // Until we have a better solution for extensions...
2859 if (renderablesModifiedByExtension) {
2860 renderableModels.clear();
2861 renderableParticles.clear();
2862 renderableModels.reserve(asize: modelsView.size());
2863 renderableParticles.reserve(asize: particlesView.size());
2864
2865 renderableModels = {modelsView.begin(), modelsView.end()};
2866 renderableParticles = {particlesView.begin(), particlesView.end()};
2867
2868 renderablesModifiedByExtension = false;
2869 }
2870}
2871
2872QSSGLayerRenderPreparationResult::QSSGLayerRenderPreparationResult(const QRectF &inViewport, QSSGRenderLayer &inLayer)
2873 : layer(&inLayer)
2874{
2875 viewport = inViewport;
2876}
2877
2878bool QSSGLayerRenderPreparationResult::isLayerVisible() const
2879{
2880 return viewport.height() >= 2.0f && viewport.width() >= 2.0f;
2881}
2882
2883QSize QSSGLayerRenderPreparationResult::textureDimensions() const
2884{
2885 const auto size = viewport.size().toSize();
2886 return QSize(QSSGRendererUtil::nextMultipleOf4(value: size.width()), QSSGRendererUtil::nextMultipleOf4(value: size.height()));
2887}
2888
2889QSSGLayerRenderData::QSSGLayerRenderData(QSSGRenderLayer &inLayer, QSSGRenderer &inRenderer)
2890 : layer(inLayer)
2891 , renderer(&inRenderer)
2892 , orderIndependentTransparencyEnabled(false)
2893 , particlesEnabled(checkParticleSupport(rhi: inRenderer.contextInterface()->rhi()))
2894{
2895 depthMapPassMS.setMultisamplingEnabled(true);
2896 Q_ASSERT(extContexts.size() == 1);
2897
2898 // Set-up the world root node and create the data store for the models.
2899 auto *root = layer.rootNode;
2900 nodeData = root->globalNodeData();
2901 modelData = std::make_unique<QSSGRenderModelData>(args&: nodeData);
2902}
2903
2904QSSGLayerRenderData::~QSSGLayerRenderData()
2905{
2906 for (auto &pass : activePasses)
2907 pass->resetForFrame();
2908
2909 for (auto &renderResult : renderResults)
2910 renderResult.reset();
2911 oitRenderContext.reset();
2912}
2913
2914static void sortInstances(QByteArray &sortedData, QList<QSSGRhiSortData> &sortData, const void *instances,
2915 int stride, int count, const QVector3D &cameraDirection)
2916{
2917 sortData.resize(size: count);
2918 Q_ASSERT(stride == sizeof(QSSGRenderInstanceTableEntry));
2919 // create sort data
2920 {
2921 const QSSGRenderInstanceTableEntry *instance = reinterpret_cast<const QSSGRenderInstanceTableEntry *>(instances);
2922 for (int i = 0; i < count; i++) {
2923 const QVector3D pos = QVector3D(instance->row0.w(), instance->row1.w(), instance->row2.w());
2924 sortData[i] = {.d: QVector3D::dotProduct(v1: pos, v2: cameraDirection), .indexOrOffset: i};
2925 instance++;
2926 }
2927 }
2928
2929 // sort
2930 std::sort(first: sortData.begin(), last: sortData.end(), comp: [](const QSSGRhiSortData &a, const QSSGRhiSortData &b){
2931 return a.d > b.d;
2932 });
2933
2934 // copy instances
2935 {
2936 const QSSGRenderInstanceTableEntry *instance = reinterpret_cast<const QSSGRenderInstanceTableEntry *>(instances);
2937 QSSGRenderInstanceTableEntry *dest = reinterpret_cast<QSSGRenderInstanceTableEntry *>(sortedData.data());
2938 for (auto &s : sortData)
2939 *dest++ = instance[s.indexOrOffset];
2940 }
2941}
2942
2943static void cullLodInstances(QByteArray &lodData, const void *instances, int count,
2944 const QVector3D &cameraPosition, float minThreshold, float maxThreshold)
2945{
2946 const QSSGRenderInstanceTableEntry *instance = reinterpret_cast<const QSSGRenderInstanceTableEntry *>(instances);
2947 QSSGRenderInstanceTableEntry *dest = reinterpret_cast<QSSGRenderInstanceTableEntry *>(lodData.data());
2948 for (int i = 0; i < count; ++i) {
2949 const float x = cameraPosition.x() - instance->row0.w();
2950 const float y = cameraPosition.y() - instance->row1.w();
2951 const float z = cameraPosition.z() - instance->row2.w();
2952 const float distanceSq = x * x + y * y + z * z;
2953 if (distanceSq >= minThreshold * minThreshold && (maxThreshold < 0 || distanceSq < maxThreshold * maxThreshold))
2954 *dest = *instance;
2955 else
2956 *dest= {};
2957 dest++;
2958 instance++;
2959 }
2960}
2961
2962bool QSSGLayerRenderData::prepareInstancing(QSSGRhiContext *rhiCtx,
2963 QSSGSubsetRenderable *renderable,
2964 const QVector3D &cameraDirection,
2965 const QVector3D &cameraPosition,
2966 float minThreshold,
2967 float maxThreshold)
2968{
2969 QSSGRhiContextPrivate *rhiCtxD = QSSGRhiContextPrivate::get(q: rhiCtx);
2970 auto &modelContext = renderable->modelContext;
2971 auto &instanceBuffer = renderable->instanceBuffer; // intentional ref2ptr
2972 const QMatrix4x4 &renderableGlobalTransform = renderable->modelContext.globalTransform;
2973 if (!modelContext.model.instancing() || instanceBuffer)
2974 return instanceBuffer;
2975 auto *table = modelContext.model.instanceTable;
2976 bool usesLod = minThreshold >= 0 || maxThreshold >= 0;
2977 QSSGRhiInstanceBufferData &instanceData(usesLod ? rhiCtxD->instanceBufferData(model: &modelContext.model) : rhiCtxD->instanceBufferData(instanceTable: table));
2978 quint32 instanceBufferSize = table->dataSize();
2979 // Create or resize the instance buffer ### if (instanceData.owned)
2980 bool sortingChanged = table->isDepthSortingEnabled() != instanceData.sorting;
2981 bool cameraDirectionChanged = !qFuzzyCompare(v1: instanceData.sortedCameraDirection, v2: cameraDirection);
2982 bool cameraPositionChanged = !qFuzzyCompare(v1: instanceData.cameraPosition, v2: cameraPosition);
2983 bool updateInstanceBuffer = table->serial() != instanceData.serial || sortingChanged || (cameraDirectionChanged && table->isDepthSortingEnabled());
2984 bool updateForLod = cameraPositionChanged && usesLod;
2985 if (sortingChanged && !table->isDepthSortingEnabled()) {
2986 instanceData.sortedData.clear();
2987 instanceData.sortData.clear();
2988 instanceData.sortedCameraDirection = {};
2989 }
2990 instanceData.sorting = table->isDepthSortingEnabled();
2991 if (instanceData.buffer && instanceData.buffer->size() < instanceBufferSize) {
2992 updateInstanceBuffer = true;
2993 // qDebug() << "Resizing instance buffer";
2994 instanceData.buffer->setSize(instanceBufferSize);
2995 instanceData.buffer->create();
2996 }
2997 if (!instanceData.buffer) {
2998 // qDebug() << "Creating instance buffer";
2999 updateInstanceBuffer = true;
3000 instanceData.buffer = rhiCtx->rhi()->newBuffer(type: QRhiBuffer::Dynamic, usage: QRhiBuffer::VertexBuffer, size: instanceBufferSize);
3001 instanceData.buffer->create();
3002 }
3003 if (updateInstanceBuffer || updateForLod) {
3004 const void *data = nullptr;
3005 if (table->isDepthSortingEnabled()) {
3006 if (updateInstanceBuffer) {
3007 QMatrix4x4 invGlobalTransform = renderableGlobalTransform.inverted();
3008 instanceData.sortedData.resize(size: table->dataSize());
3009 sortInstances(sortedData&: instanceData.sortedData,
3010 sortData&: instanceData.sortData,
3011 instances: table->constData(),
3012 stride: table->stride(),
3013 count: table->count(),
3014 cameraDirection: invGlobalTransform.map(point: cameraDirection).normalized());
3015 }
3016 data = instanceData.sortedData.constData();
3017 instanceData.sortedCameraDirection = cameraDirection;
3018 } else {
3019 data = table->constData();
3020 }
3021 if (data) {
3022 if (updateForLod) {
3023 if (table->isDepthSortingEnabled()) {
3024 instanceData.lodData.resize(size: table->dataSize());
3025 cullLodInstances(lodData&: instanceData.lodData, instances: instanceData.sortedData.constData(), count: instanceData.sortedData.size(), cameraPosition, minThreshold, maxThreshold);
3026 data = instanceData.lodData.constData();
3027 } else {
3028 instanceData.lodData.resize(size: table->dataSize());
3029 cullLodInstances(lodData&: instanceData.lodData, instances: table->constData(), count: table->count(), cameraPosition, minThreshold, maxThreshold);
3030 data = instanceData.lodData.constData();
3031 }
3032 }
3033 QRhiResourceUpdateBatch *rub = rhiCtx->rhi()->nextResourceUpdateBatch();
3034 rub->updateDynamicBuffer(buf: instanceData.buffer, offset: 0, size: instanceBufferSize, data);
3035 rhiCtx->commandBuffer()->resourceUpdate(resourceUpdates: rub);
3036 //qDebug() << "****** UPDATING INST BUFFER. Size" << instanceBufferSize;
3037 } else {
3038 qWarning() << "NO DATA IN INSTANCE TABLE";
3039 }
3040 instanceData.serial = table->serial();
3041 instanceData.cameraPosition = cameraPosition;
3042 }
3043 instanceBuffer = instanceData.buffer;
3044 return instanceBuffer;
3045}
3046
3047QSSGFrameData &QSSGLayerRenderData::getFrameData()
3048{
3049 return frameData;
3050}
3051
3052void QSSGLayerRenderData::initializeLightmapBaking(QSSGLightmapBaker::Context &ctx)
3053{
3054 ctx.callbacks.modelsToBake = [this]() { return getSortedBakedLightingModels(); };
3055
3056 lightmapBaker = std::make_unique<QSSGLightmapBaker>(args&: ctx);
3057}
3058
3059void QSSGLayerRenderData::maybeProcessLightmapBaking()
3060{
3061 if (lightmapBaker) {
3062 const QSSGLightmapBaker::Status status = lightmapBaker->process();
3063 if (status == QSSGLightmapBaker::Status::Finished)
3064 lightmapBaker.reset();
3065 }
3066}
3067
3068QSSGRenderGraphObject *QSSGLayerRenderData::getCamera(QSSGCameraId id) const
3069{
3070 QSSGRenderGraphObject *ret = nullptr;
3071 if (auto res = reinterpret_cast<QSSGRenderGraphObject *>(id))
3072 ret = res;
3073
3074 return ret;
3075}
3076
3077QSSGRenderCameraData QSSGLayerRenderData::getCameraRenderData(const QSSGRenderCamera *camera_)
3078{
3079 if ((!camera_ || camera_ == renderedCameras[0]) && renderedCameraData.has_value())
3080 return renderedCameraData.value()[0];
3081 if (camera_)
3082 return getCameraDataImpl(camera: camera_);
3083 return {};
3084}
3085
3086QSSGRenderCameraData QSSGLayerRenderData::getCameraRenderData(const QSSGRenderCamera *camera_) const
3087{
3088 if ((!camera_ || camera_ == renderedCameras[0]) && renderedCameraData.has_value())
3089 return renderedCameraData.value()[0];
3090 if (camera_)
3091 return getCameraDataImpl(camera: camera_);
3092 return {};
3093}
3094
3095QSSGRenderContextInterface *QSSGLayerRenderData::contextInterface() const
3096{
3097 return renderer ? renderer->contextInterface() : nullptr;
3098}
3099
3100QSSGLayerRenderData::GlobalRenderProperties QSSGLayerRenderData::globalRenderProperties(const QSSGRenderContextInterface &ctx)
3101{
3102 GlobalRenderProperties props {};
3103 if (const auto &rhiCtx = ctx.rhiContext(); rhiCtx->isValid()) {
3104 QRhi *rhi = rhiCtx->rhi();
3105 props.isYUpInFramebuffer = rhi->isYUpInFramebuffer();
3106 props.isYUpInNDC = rhi->isYUpInNDC();
3107 props.isClipDepthZeroToOne = rhi->isClipDepthZeroToOne();
3108 }
3109
3110 return props;
3111}
3112
3113const QSSGRenderShadowMapPtr &QSSGLayerRenderData::requestShadowMapManager()
3114{
3115 if (!shadowMapManager && QSSG_GUARD(renderer && renderer->contextInterface()))
3116 shadowMapManager.reset(p: new QSSGRenderShadowMap(*renderer->contextInterface()));
3117 return shadowMapManager;
3118}
3119
3120const QSSGRenderReflectionMapPtr &QSSGLayerRenderData::requestReflectionMapManager()
3121{
3122 if (!reflectionMapManager && QSSG_GUARD(renderer && renderer->contextInterface()))
3123 reflectionMapManager.reset(p: new QSSGRenderReflectionMap(*renderer->contextInterface()));
3124 return reflectionMapManager;
3125}
3126
3127QT_END_NAMESPACE
3128

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