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