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