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) && (node->tag.isSet(layerMask))) {
111 if (node->type == QSSGRenderGraphObject::Type::Model)
112 ++stat.modelCount;
113 else if (node->type == QSSGRenderGraphObject::Type::Particles)
114 ++stat.particlesCount;
115 else if (node->type == QSSGRenderGraphObject::Type::Item2D)
116 ++stat.item2DCount;
117 else if (node->type == QSSGRenderGraphObject::Type::ReflectionProbe)
118 ++stat.reflectionProbeCount;
119 else if (QSSGRenderGraphObject::isCamera(node->type))
120 ++stat.cameraCount;
121 else if (QSSGRenderGraphObject::isLight(node->type))
122 ++stat.lightCount;
123 else
124 ++stat.otherCount;
125 }
126 }
127
128 return stat;
129}
130
131// These are meant to be pixel offsets, so you need to divide them by the width/height
132// of the layer respectively.
134 QVector2D(-0.170840f, -0.553840f), // 1x
135 QVector2D(0.162960f, -0.319340f), // 2x
136 QVector2D(0.360260f, -0.245840f), // 3x
137 QVector2D(-0.561340f, -0.149540f), // 4x
138 QVector2D(0.249460f, 0.453460f), // 5x
139 QVector2D(-0.336340f, 0.378260f), // 6x
140 QVector2D(0.340000f, 0.166260f), // 7x
141 QVector2D(0.235760f, 0.527760f), // 8x
142};
143
144qsizetype QSSGLayerRenderData::frustumCulling(const QSSGClippingFrustum &clipFrustum, const QSSGRenderableObjectList &renderables, QSSGRenderableObjectList &visibleRenderables)
145{
146 QSSG_ASSERT(visibleRenderables.isEmpty(), visibleRenderables.clear());
147 visibleRenderables.reserve(renderables.size());
148 for (quint32 end = renderables.size(), idx = quint32(0); idx != end; ++idx) {
149 auto handle = renderables.at(idx);
150 const auto &b = handle.obj->globalBounds;
151 if (clipFrustum.intersectsWith(b))
152 visibleRenderables.push_back(handle);
153 }
154
155 return visibleRenderables.size();
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 auto &b = renderables.at(front).obj->globalBounds;
166 if (clipFrustum.intersectsWith(b))
167 ++front;
168 else
169 renderables.swapItemsAt(front, back--);
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
879/**
880 * Usage: T *ptr = RENDER_FRAME_NEW<T>(context, arg0, arg1, ...); is equivalent to: T *ptr = new T(arg0, arg1, ...);
881 * so RENDER_FRAME_NEW() takes the RCI + T's arguments
882 */
883template <typename T, typename... Args>
884[[nodiscard]] inline T *RENDER_FRAME_NEW(QSSGRenderContextInterface &ctx, Args&&... args)
885{
886 static_assert(std::is_trivially_destructible_v<T>, "Objects allocated using the per-frame allocator needs to be trivially destructible!");
887 return new (QSSGLayerRenderData::perFrameAllocator(ctx)->allocate(sizeof(T)))T(std::forward<Args>(args)...);
888}
889
890template <typename T>
891[[nodiscard]] inline QSSGDataRef<T> RENDER_FRAME_NEW_BUFFER(QSSGRenderContextInterface &ctx, size_t count)
892{
893 static_assert(std::is_trivially_destructible_v<T>, "Objects allocated using the per-frame allocator needs to be trivially destructible!");
894 const size_t asize = sizeof(T) * count;
895 return { reinterpret_cast<T *>(QSSGLayerRenderData::perFrameAllocator(ctx)->allocate(asize)), qsizetype(count) };
896}
897
898void QSSGLayerRenderData::prepareImageForRender(QSSGRenderImage &inImage,
899 QSSGRenderableImage::Type inMapType,
900 QSSGRenderableImage *&ioFirstImage,
901 QSSGRenderableImage *&ioNextImage,
902 QSSGRenderableObjectFlags &ioFlags,
903 QSSGShaderDefaultMaterialKey &inShaderKey,
904 quint32 inImageIndex,
905 QSSGRenderDefaultMaterial *inMaterial)
906{
907 QSSGRenderContextInterface &contextInterface = *renderer->contextInterface();
908 const auto &bufferManager = contextInterface.bufferManager();
909
910 if (inImage.clearDirty())
911 ioFlags |= QSSGRenderableObjectFlag::Dirty;
912
913 // This is where the QRhiTexture gets created, if not already done. Note
914 // that the bufferManager is per-QQuickWindow, and so per-render-thread.
915 // Hence using the same Texture (backed by inImage as the backend node) in
916 // multiple windows will work by each scene in each window getting its own
917 // QRhiTexture. And that's why the QSSGRenderImageTexture cannot be a
918 // member of the QSSGRenderImage. Conceptually this matches what we do for
919 // models (QSSGRenderModel -> QSSGRenderMesh retrieved from the
920 // bufferManager in each prepareModelForRender, etc.).
921
922 const QSSGRenderImageTexture texture = bufferManager->loadRenderImage(&inImage);
923
924 if (texture.m_texture) {
925 if (texture.m_flags.hasTransparency()
926 && (inMapType == QSSGRenderableImage::Type::Diffuse // note: Type::BaseColor is skipped here intentionally
927 || inMapType == QSSGRenderableImage::Type::Opacity
928 || inMapType == QSSGRenderableImage::Type::Translucency))
929 {
930 ioFlags |= QSSGRenderableObjectFlag::HasTransparency;
931 }
932
933 QSSGRenderableImage *theImage = RENDER_FRAME_NEW<QSSGRenderableImage>(contextInterface, inMapType, inImage, texture);
934 QSSGShaderKeyImageMap &theKeyProp = defaultMaterialShaderKeyProperties.m_imageMaps[inImageIndex];
935
936 theKeyProp.setEnabled(inShaderKey, true);
937 switch (inImage.m_mappingMode) {
938 case QSSGRenderImage::MappingModes::Normal:
939 break;
940 case QSSGRenderImage::MappingModes::Environment:
941 theKeyProp.setEnvMap(inShaderKey, true);
942 break;
943 case QSSGRenderImage::MappingModes::LightProbe:
944 theKeyProp.setLightProbe(inShaderKey, true);
945 break;
946 }
947
948 bool hasA = false;
949 bool hasG = false;
950 bool hasB = false;
951
952
953 //### TODO: More formats
954 switch (texture.m_texture->format()) {
955 case QRhiTexture::Format::RED_OR_ALPHA8:
956 hasA = !renderer->contextInterface()->rhiContext()->rhi()->isFeatureSupported(QRhi::RedOrAlpha8IsRed);
957 break;
958 case QRhiTexture::Format::R8:
959 // Leave BGA as false
960 break;
961 default:
962 hasA = true;
963 hasG = true;
964 hasB = true;
965 break;
966 }
967
968 if (inImage.isImageTransformIdentity())
969 theKeyProp.setIdentityTransform(inShaderKey, true);
970
971 if (inImage.m_indexUV == 1)
972 theKeyProp.setUsesUV1(inShaderKey, true);
973
974 if (texture.m_flags.isLinear())
975 theKeyProp.setLinear(inShaderKey, true);
976
977 if (texture.m_flags.isPreMultipliedAlpha())
978 theKeyProp.setPreMultipliedAlpha(inShaderKey, true);
979
980 if (ioFirstImage == nullptr)
981 ioFirstImage = theImage;
982 else
983 ioNextImage->m_nextImage = theImage;
984
985 ioNextImage = theImage;
986
987 if (inMaterial && inImageIndex >= QSSGShaderDefaultMaterialKeyProperties::SingleChannelImagesFirst) {
988 QSSGRenderDefaultMaterial::TextureChannelMapping value = QSSGRenderDefaultMaterial::R;
989
990 const quint32 scIndex = inImageIndex - QSSGShaderDefaultMaterialKeyProperties::SingleChannelImagesFirst;
991 QSSGShaderKeyTextureChannel &channelKey = defaultMaterialShaderKeyProperties.m_textureChannels[scIndex];
992 switch (inImageIndex) {
993 case QSSGShaderDefaultMaterialKeyProperties::OpacityMap:
994 value = inMaterial->opacityChannel;
995 break;
996 case QSSGShaderDefaultMaterialKeyProperties::RoughnessMap:
997 value = inMaterial->roughnessChannel;
998 break;
999 case QSSGShaderDefaultMaterialKeyProperties::MetalnessMap:
1000 value = inMaterial->metalnessChannel;
1001 break;
1002 case QSSGShaderDefaultMaterialKeyProperties::OcclusionMap:
1003 value = inMaterial->occlusionChannel;
1004 break;
1005 case QSSGShaderDefaultMaterialKeyProperties::TranslucencyMap:
1006 value = inMaterial->translucencyChannel;
1007 break;
1008 case QSSGShaderDefaultMaterialKeyProperties::HeightMap:
1009 value = inMaterial->heightChannel;
1010 break;
1011 case QSSGShaderDefaultMaterialKeyProperties::ClearcoatMap:
1012 value = inMaterial->clearcoatChannel;
1013 break;
1014 case QSSGShaderDefaultMaterialKeyProperties::ClearcoatRoughnessMap:
1015 value = inMaterial->clearcoatRoughnessChannel;
1016 break;
1017 case QSSGShaderDefaultMaterialKeyProperties::TransmissionMap:
1018 value = inMaterial->transmissionChannel;
1019 break;
1020 case QSSGShaderDefaultMaterialKeyProperties::ThicknessMap:
1021 value = inMaterial->thicknessChannel;
1022 break;
1023 case QSSGShaderDefaultMaterialKeyProperties::BaseColorMap:
1024 value = inMaterial->baseColorChannel;
1025 break;
1026 case QSSGShaderDefaultMaterialKeyProperties::SpecularAmountMap:
1027 value = inMaterial->specularAmountChannel;
1028 break;
1029 case QSSGShaderDefaultMaterialKeyProperties::EmissiveMap:
1030 value = inMaterial->emissiveChannel;
1031 break;
1032 default:
1033 break;
1034 }
1035 bool useDefault = false;
1036 switch (value) {
1037 case QSSGRenderDefaultMaterial::TextureChannelMapping::G:
1038 useDefault = !hasG;
1039 break;
1040 case QSSGRenderDefaultMaterial::TextureChannelMapping::B:
1041 useDefault = !hasB;
1042 break;
1043 case QSSGRenderDefaultMaterial::TextureChannelMapping::A:
1044 useDefault = !hasA;
1045 break;
1046 default:
1047 break;
1048 }
1049 if (useDefault)
1050 value = QSSGRenderDefaultMaterial::R; // Always Fallback to Red
1051 channelKey.setTextureChannel(QSSGShaderKeyTextureChannel::TexturChannelBits(value), inShaderKey);
1052 }
1053 }
1054}
1055
1056void QSSGLayerRenderData::setVertexInputPresence(const QSSGRenderableObjectFlags &renderableFlags,
1057 QSSGShaderDefaultMaterialKey &key)
1058{
1059 quint32 vertexAttribs = 0;
1060 if (renderableFlags.hasAttributePosition())
1061 vertexAttribs |= QSSGShaderKeyVertexAttribute::Position;
1062 if (renderableFlags.hasAttributeNormal())
1063 vertexAttribs |= QSSGShaderKeyVertexAttribute::Normal;
1064 if (renderableFlags.hasAttributeTexCoord0())
1065 vertexAttribs |= QSSGShaderKeyVertexAttribute::TexCoord0;
1066 if (renderableFlags.hasAttributeTexCoord1())
1067 vertexAttribs |= QSSGShaderKeyVertexAttribute::TexCoord1;
1068 if (renderableFlags.hasAttributeTexCoordLightmap())
1069 vertexAttribs |= QSSGShaderKeyVertexAttribute::TexCoordLightmap;
1070 if (renderableFlags.hasAttributeTangent())
1071 vertexAttribs |= QSSGShaderKeyVertexAttribute::Tangent;
1072 if (renderableFlags.hasAttributeBinormal())
1073 vertexAttribs |= QSSGShaderKeyVertexAttribute::Binormal;
1074 if (renderableFlags.hasAttributeColor())
1075 vertexAttribs |= QSSGShaderKeyVertexAttribute::Color;
1076 if (renderableFlags.hasAttributeJointAndWeight())
1077 vertexAttribs |= QSSGShaderKeyVertexAttribute::JointAndWeight;
1078 defaultMaterialShaderKeyProperties.m_vertexAttributes.setValue(key, vertexAttribs);
1079}
1080
1081QSSGDefaultMaterialPreparationResult QSSGLayerRenderData::prepareDefaultMaterialForRender(
1082 QSSGRenderDefaultMaterial &inMaterial,
1083 QSSGRenderableObjectFlags &inExistingFlags,
1084 float inOpacity, bool hasAnyLights,
1085 bool anyLightHasShadows,
1086 QSSGLayerRenderPreparationResultFlags &ioFlags)
1087{
1088 QSSGRenderDefaultMaterial *theMaterial = &inMaterial;
1089 QSSGDefaultMaterialPreparationResult retval(QSSGShaderDefaultMaterialKey(qHash(features)));
1090 retval.renderableFlags = inExistingFlags;
1091 QSSGRenderableObjectFlags &renderableFlags(retval.renderableFlags);
1092 QSSGShaderDefaultMaterialKey &theGeneratedKey(retval.materialKey);
1093 retval.opacity = inOpacity;
1094 float &subsetOpacity(retval.opacity);
1095
1096 if (theMaterial->isDirty())
1097 renderableFlags |= QSSGRenderableObjectFlag::Dirty;
1098
1099 subsetOpacity *= theMaterial->opacity;
1100
1101 QSSGRenderableImage *firstImage = nullptr;
1102
1103 const bool lighting = theMaterial->lighting != QSSGRenderDefaultMaterial::MaterialLighting::NoLighting;
1104 defaultMaterialShaderKeyProperties.m_hasLighting.setValue(theGeneratedKey, lighting);
1105 if (lighting) {
1106 defaultMaterialShaderKeyProperties.m_hasPunctualLights.setValue(theGeneratedKey, hasAnyLights);
1107 defaultMaterialShaderKeyProperties.m_hasShadows.setValue(theGeneratedKey, anyLightHasShadows);
1108 defaultMaterialShaderKeyProperties.m_hasIbl.setValue(theGeneratedKey, layer.lightProbe != nullptr);
1109 }
1110
1111 defaultMaterialShaderKeyProperties.m_specularAAEnabled.setValue(theGeneratedKey, layer.specularAAEnabled);
1112
1113 // isDoubleSided
1114 defaultMaterialShaderKeyProperties.m_isDoubleSided.setValue(theGeneratedKey, theMaterial->cullMode == QSSGCullFaceMode::Disabled);
1115
1116 // default materials never define their on position
1117 defaultMaterialShaderKeyProperties.m_overridesPosition.setValue(theGeneratedKey, false);
1118
1119 // default materials dont make use of raw projection or inverse projection matrices
1120 defaultMaterialShaderKeyProperties.m_usesProjectionMatrix.setValue(theGeneratedKey, false);
1121 defaultMaterialShaderKeyProperties.m_usesInverseProjectionMatrix.setValue(theGeneratedKey, false);
1122 // nor they do rely on VAR_COLOR
1123 defaultMaterialShaderKeyProperties.m_usesVarColor.setValue(theGeneratedKey, false);
1124
1125 // alpha Mode
1126 defaultMaterialShaderKeyProperties.m_alphaMode.setValue(theGeneratedKey, theMaterial->alphaMode);
1127
1128 // vertex attribute presence flags
1129 setVertexInputPresence(renderableFlags, theGeneratedKey);
1130
1131 // set the flag indicating the need for gl_PointSize
1132 defaultMaterialShaderKeyProperties.m_usesPointsTopology.setValue(theGeneratedKey, renderableFlags.isPointsTopology());
1133
1134 // propagate the flag indicating the presence of a lightmap
1135 defaultMaterialShaderKeyProperties.m_lightmapEnabled.setValue(theGeneratedKey, renderableFlags.rendersWithLightmap());
1136 defaultMaterialShaderKeyProperties.m_metallicRoughnessEnabled.setValue(theGeneratedKey, theMaterial->type == QSSGRenderDefaultMaterial::Type::PrincipledMaterial);
1137 defaultMaterialShaderKeyProperties.m_specularGlossyEnabled.setValue(theGeneratedKey, theMaterial->type == QSSGRenderGraphObject::Type::SpecularGlossyMaterial);
1138
1139
1140 // debug modes
1141 defaultMaterialShaderKeyProperties.m_debugMode.setValue(theGeneratedKey, int(layer.debugMode));
1142
1143 // fog
1144 defaultMaterialShaderKeyProperties.m_fogEnabled.setValue(theGeneratedKey, layer.fog.enabled);
1145
1146 // multiview
1147 defaultMaterialShaderKeyProperties.m_viewCount.setValue(theGeneratedKey, layer.viewCount);
1148 defaultMaterialShaderKeyProperties.m_usesViewIndex.setValue(theGeneratedKey, layer.viewCount >= 2);
1149
1150 if (!defaultMaterialShaderKeyProperties.m_hasIbl.getValue(theGeneratedKey) && theMaterial->iblProbe) {
1151 features.set(QSSGShaderFeatures::Feature::LightProbe, true);
1152 defaultMaterialShaderKeyProperties.m_hasIbl.setValue(theGeneratedKey, true);
1153 // features.set(ShaderFeatureDefines::enableIblFov(),
1154 // m_Renderer.GetLayerRenderData()->m_Layer.m_ProbeFov < 180.0f );
1155 }
1156
1157 if (subsetOpacity >= QSSGRendererPrivate::minimumRenderOpacity) {
1158
1159 // Set the semi-transparency flag as specified in PrincipledMaterial's
1160 // blendMode and alphaMode:
1161 // - the default SourceOver blendMode does not imply alpha blending on
1162 // its own,
1163 // - but other blendMode values do,
1164 // - an alphaMode of Blend guarantees blending to be enabled regardless
1165 // of anything else.
1166 // Additionally:
1167 // - Opacity and texture map alpha are handled elsewhere (that's when a
1168 // blendMode of SourceOver or an alphaMode of Default/Opaque can in the
1169 // end still result in HasTransparency),
1170 // - the presence of an opacityMap guarantees alpha blending regardless
1171 // of its content.
1172
1173 if (theMaterial->blendMode != QSSGRenderDefaultMaterial::MaterialBlendMode::SourceOver
1174 || theMaterial->opacityMap
1175 || theMaterial->alphaMode == QSSGRenderDefaultMaterial::Blend)
1176 {
1177 renderableFlags |= QSSGRenderableObjectFlag::HasTransparency;
1178 }
1179
1180 const bool specularEnabled = theMaterial->isSpecularEnabled();
1181 const bool metalnessEnabled = theMaterial->isMetalnessEnabled();
1182 defaultMaterialShaderKeyProperties.m_specularEnabled.setValue(theGeneratedKey, specularEnabled || metalnessEnabled);
1183 defaultMaterialShaderKeyProperties.m_specularModel.setSpecularModel(theGeneratedKey, theMaterial->specularModel);
1184 defaultMaterialShaderKeyProperties.m_diffuseModel.setDiffuseModel(theGeneratedKey, theMaterial->diffuseModel);
1185 defaultMaterialShaderKeyProperties.m_fresnelScaleBiasEnabled.setValue(theGeneratedKey, theMaterial->isFresnelScaleBiasEnabled());
1186 defaultMaterialShaderKeyProperties.m_clearcoatFresnelScaleBiasEnabled.setValue(theGeneratedKey, theMaterial->isClearcoatFresnelScaleBiasEnabled());
1187 defaultMaterialShaderKeyProperties.m_fresnelEnabled.setValue(theGeneratedKey, theMaterial->isFresnelEnabled());
1188 defaultMaterialShaderKeyProperties.m_baseColorSingleChannelEnabled.setValue(theGeneratedKey, theMaterial->isBaseColorSingleChannelEnabled());
1189 defaultMaterialShaderKeyProperties.m_specularSingleChannelEnabled.setValue(theGeneratedKey, theMaterial->isSpecularAmountSingleChannelEnabled());
1190 defaultMaterialShaderKeyProperties.m_emissiveSingleChannelEnabled.setValue(theGeneratedKey, theMaterial->isEmissiveSingleChannelEnabled());
1191 defaultMaterialShaderKeyProperties.m_invertOpacityMapValue.setValue(theGeneratedKey, theMaterial->isInvertOpacityMapValue());
1192 defaultMaterialShaderKeyProperties.m_vertexColorsEnabled.setValue(theGeneratedKey, theMaterial->isVertexColorsEnabled());
1193 defaultMaterialShaderKeyProperties.m_vertexColorsMaskEnabled.setValue(theGeneratedKey, theMaterial->isVertexColorsMaskEnabled());
1194 defaultMaterialShaderKeyProperties.m_vertexColorRedMask.setValue(theGeneratedKey, quint16(theMaterial->vertexColorRedMask.toInt()));
1195 defaultMaterialShaderKeyProperties.m_vertexColorGreenMask.setValue(theGeneratedKey, quint16(theMaterial->vertexColorGreenMask.toInt()));
1196 defaultMaterialShaderKeyProperties.m_vertexColorBlueMask.setValue(theGeneratedKey, quint16(theMaterial->vertexColorBlueMask.toInt()));
1197 defaultMaterialShaderKeyProperties.m_vertexColorAlphaMask.setValue(theGeneratedKey, quint16(theMaterial->vertexColorAlphaMask.toInt()));
1198 defaultMaterialShaderKeyProperties.m_clearcoatEnabled.setValue(theGeneratedKey, theMaterial->isClearcoatEnabled());
1199 defaultMaterialShaderKeyProperties.m_transmissionEnabled.setValue(theGeneratedKey, theMaterial->isTransmissionEnabled());
1200
1201 // Run through the material's images and prepare them for render.
1202 // this may in fact set pickable on the renderable flags if one of the images
1203 // links to a sub presentation or any offscreen rendered object.
1204 QSSGRenderableImage *nextImage = nullptr;
1205#define CHECK_IMAGE_AND_PREPARE(img, imgtype, shadercomponent)
1206 if ((img))
1207 prepareImageForRender(*(img), imgtype, firstImage, nextImage, renderableFlags,
1208 theGeneratedKey, shadercomponent, &inMaterial)
1209
1210 if (theMaterial->type == QSSGRenderGraphObject::Type::PrincipledMaterial ||
1211 theMaterial->type == QSSGRenderGraphObject::Type::SpecularGlossyMaterial) {
1212 CHECK_IMAGE_AND_PREPARE(theMaterial->colorMap,
1213 QSSGRenderableImage::Type::BaseColor,
1214 QSSGShaderDefaultMaterialKeyProperties::BaseColorMap);
1215 CHECK_IMAGE_AND_PREPARE(theMaterial->occlusionMap,
1216 QSSGRenderableImage::Type::Occlusion,
1217 QSSGShaderDefaultMaterialKeyProperties::OcclusionMap);
1218 CHECK_IMAGE_AND_PREPARE(theMaterial->heightMap,
1219 QSSGRenderableImage::Type::Height,
1220 QSSGShaderDefaultMaterialKeyProperties::HeightMap);
1221 CHECK_IMAGE_AND_PREPARE(theMaterial->clearcoatMap,
1222 QSSGRenderableImage::Type::Clearcoat,
1223 QSSGShaderDefaultMaterialKeyProperties::ClearcoatMap);
1224 CHECK_IMAGE_AND_PREPARE(theMaterial->clearcoatRoughnessMap,
1225 QSSGRenderableImage::Type::ClearcoatRoughness,
1226 QSSGShaderDefaultMaterialKeyProperties::ClearcoatRoughnessMap);
1227 CHECK_IMAGE_AND_PREPARE(theMaterial->clearcoatNormalMap,
1228 QSSGRenderableImage::Type::ClearcoatNormal,
1229 QSSGShaderDefaultMaterialKeyProperties::ClearcoatNormalMap);
1230 CHECK_IMAGE_AND_PREPARE(theMaterial->transmissionMap,
1231 QSSGRenderableImage::Type::Transmission,
1232 QSSGShaderDefaultMaterialKeyProperties::TransmissionMap);
1233 CHECK_IMAGE_AND_PREPARE(theMaterial->thicknessMap,
1234 QSSGRenderableImage::Type::Thickness,
1235 QSSGShaderDefaultMaterialKeyProperties::ThicknessMap);
1236 if (theMaterial->type == QSSGRenderGraphObject::Type::PrincipledMaterial) {
1237 CHECK_IMAGE_AND_PREPARE(theMaterial->metalnessMap,
1238 QSSGRenderableImage::Type::Metalness,
1239 QSSGShaderDefaultMaterialKeyProperties::MetalnessMap);
1240 }
1241 } else {
1242 CHECK_IMAGE_AND_PREPARE(theMaterial->colorMap,
1243 QSSGRenderableImage::Type::Diffuse,
1244 QSSGShaderDefaultMaterialKeyProperties::DiffuseMap);
1245 }
1246 CHECK_IMAGE_AND_PREPARE(theMaterial->emissiveMap, QSSGRenderableImage::Type::Emissive, QSSGShaderDefaultMaterialKeyProperties::EmissiveMap);
1247 CHECK_IMAGE_AND_PREPARE(theMaterial->specularReflection,
1248 QSSGRenderableImage::Type::Specular,
1249 QSSGShaderDefaultMaterialKeyProperties::SpecularMap);
1250 CHECK_IMAGE_AND_PREPARE(theMaterial->roughnessMap,
1251 QSSGRenderableImage::Type::Roughness,
1252 QSSGShaderDefaultMaterialKeyProperties::RoughnessMap);
1253 CHECK_IMAGE_AND_PREPARE(theMaterial->opacityMap, QSSGRenderableImage::Type::Opacity, QSSGShaderDefaultMaterialKeyProperties::OpacityMap);
1254 CHECK_IMAGE_AND_PREPARE(theMaterial->bumpMap, QSSGRenderableImage::Type::Bump, QSSGShaderDefaultMaterialKeyProperties::BumpMap);
1255 CHECK_IMAGE_AND_PREPARE(theMaterial->specularMap,
1256 QSSGRenderableImage::Type::SpecularAmountMap,
1257 QSSGShaderDefaultMaterialKeyProperties::SpecularAmountMap);
1258 CHECK_IMAGE_AND_PREPARE(theMaterial->normalMap, QSSGRenderableImage::Type::Normal, QSSGShaderDefaultMaterialKeyProperties::NormalMap);
1259 CHECK_IMAGE_AND_PREPARE(theMaterial->translucencyMap,
1260 QSSGRenderableImage::Type::Translucency,
1261 QSSGShaderDefaultMaterialKeyProperties::TranslucencyMap);
1262 }
1263#undef CHECK_IMAGE_AND_PREPARE
1264
1265 if (subsetOpacity < QSSGRendererPrivate::minimumRenderOpacity) {
1266 subsetOpacity = 0.0f;
1267 // You can still pick against completely transparent objects(or rather their bounding
1268 // box)
1269 // you just don't render them.
1270 renderableFlags |= QSSGRenderableObjectFlag::HasTransparency;
1271 renderableFlags |= QSSGRenderableObjectFlag::CompletelyTransparent;
1272 }
1273
1274 if (subsetOpacity > 1.f - QSSGRendererPrivate::minimumRenderOpacity)
1275 subsetOpacity = 1.f;
1276 else
1277 renderableFlags |= QSSGRenderableObjectFlag::HasTransparency;
1278
1279 if (inMaterial.isTransmissionEnabled()) {
1280 ioFlags.setRequiresScreenTexture(true);
1281 ioFlags.setRequiresMipmapsForScreenTexture(true);
1282 renderableFlags |= QSSGRenderableObjectFlag::RequiresScreenTexture;
1283 }
1284
1285 if (renderableFlags.hasTransparency()) {
1286 if (orderIndependentTransparencyEnabled)
1287 defaultMaterialShaderKeyProperties.m_orderIndependentTransparency.setValue(theGeneratedKey, int(layer.oitMethod));
1288 if (layer.oitMethodDirty)
1289 renderableFlags |= QSSGRenderableObjectFlag::Dirty;
1290 }
1291
1292 retval.firstImage = firstImage;
1293 if (retval.renderableFlags.isDirty())
1294 retval.dirty = true;
1295 if (retval.dirty)
1296 renderer->addMaterialDirtyClear(&inMaterial);
1297 return retval;
1298}
1299
1300QSSGDefaultMaterialPreparationResult QSSGLayerRenderData::prepareCustomMaterialForRender(
1301 QSSGRenderCustomMaterial &inMaterial, QSSGRenderableObjectFlags &inExistingFlags,
1302 float inOpacity, bool alreadyDirty, bool hasAnyLights, bool anyLightHasShadows,
1303 QSSGLayerRenderPreparationResultFlags &ioFlags)
1304{
1305 QSSGDefaultMaterialPreparationResult retval(QSSGShaderDefaultMaterialKey(qHash(features)));
1306 retval.renderableFlags = inExistingFlags;
1307 QSSGRenderableObjectFlags &renderableFlags(retval.renderableFlags);
1308 QSSGShaderDefaultMaterialKey &theGeneratedKey(retval.materialKey);
1309 retval.opacity = inOpacity;
1310 float &subsetOpacity(retval.opacity);
1311
1312 if (subsetOpacity < QSSGRendererPrivate::minimumRenderOpacity) {
1313 subsetOpacity = 0.0f;
1314 // You can still pick against completely transparent objects(or rather their bounding
1315 // box)
1316 // you just don't render them.
1317 renderableFlags |= QSSGRenderableObjectFlag::HasTransparency;
1318 renderableFlags |= QSSGRenderableObjectFlag::CompletelyTransparent;
1319 }
1320
1321 if (subsetOpacity > 1.f - QSSGRendererPrivate::minimumRenderOpacity)
1322 subsetOpacity = 1.f;
1323 else
1324 renderableFlags |= QSSGRenderableObjectFlag::HasTransparency;
1325
1326 defaultMaterialShaderKeyProperties.m_hasLighting.setValue(theGeneratedKey, true);
1327 defaultMaterialShaderKeyProperties.m_hasPunctualLights.setValue(theGeneratedKey, hasAnyLights);
1328 defaultMaterialShaderKeyProperties.m_hasShadows.setValue(theGeneratedKey, anyLightHasShadows);
1329 defaultMaterialShaderKeyProperties.m_hasIbl.setValue(theGeneratedKey, layer.lightProbe != nullptr);
1330 defaultMaterialShaderKeyProperties.m_specularEnabled.setValue(theGeneratedKey, true);
1331
1332 defaultMaterialShaderKeyProperties.m_specularAAEnabled.setValue(theGeneratedKey, layer.specularAAEnabled);
1333
1334 // isDoubleSided
1335 defaultMaterialShaderKeyProperties.m_isDoubleSided.setValue(theGeneratedKey, inMaterial.m_cullMode == QSSGCullFaceMode::Disabled);
1336
1337 // Custom Materials are always Metallic Roughness Workflow
1338 defaultMaterialShaderKeyProperties.m_metallicRoughnessEnabled.setValue(theGeneratedKey, true);
1339
1340 // Does the material override the position output
1341 const bool overridesPosition = inMaterial.m_renderFlags.testFlag(QSSGRenderCustomMaterial::RenderFlag::OverridesPosition);
1342 defaultMaterialShaderKeyProperties.m_overridesPosition.setValue(theGeneratedKey, overridesPosition);
1343
1344 // Optional usage of PROJECTION_MATRIX and/or INVERSE_PROJECTION_MATRIX
1345 const bool usesProjectionMatrix = inMaterial.m_renderFlags.testFlag(QSSGRenderCustomMaterial::RenderFlag::ProjectionMatrix);
1346 defaultMaterialShaderKeyProperties.m_usesProjectionMatrix.setValue(theGeneratedKey, usesProjectionMatrix);
1347 const bool usesInvProjectionMatrix = inMaterial.m_renderFlags.testFlag(QSSGRenderCustomMaterial::RenderFlag::InverseProjectionMatrix);
1348 defaultMaterialShaderKeyProperties.m_usesInverseProjectionMatrix.setValue(theGeneratedKey, usesInvProjectionMatrix);
1349
1350 // vertex attribute presence flags
1351 setVertexInputPresence(renderableFlags, theGeneratedKey);
1352
1353 // set the flag indicating the need for gl_PointSize
1354 defaultMaterialShaderKeyProperties.m_usesPointsTopology.setValue(theGeneratedKey, renderableFlags.isPointsTopology());
1355
1356 // propagate the flag indicating the presence of a lightmap
1357 defaultMaterialShaderKeyProperties.m_lightmapEnabled.setValue(theGeneratedKey, renderableFlags.rendersWithLightmap());
1358
1359 // debug modes
1360 defaultMaterialShaderKeyProperties.m_debugMode.setValue(theGeneratedKey, int(layer.debugMode));
1361
1362 // fog
1363 defaultMaterialShaderKeyProperties.m_fogEnabled.setValue(theGeneratedKey, layer.fog.enabled);
1364
1365 // multiview
1366 defaultMaterialShaderKeyProperties.m_viewCount.setValue(theGeneratedKey, layer.viewCount);
1367 defaultMaterialShaderKeyProperties.m_usesViewIndex.setValue(theGeneratedKey,
1368 inMaterial.m_renderFlags.testFlag(QSSGRenderCustomMaterial::RenderFlag::ViewIndex));
1369
1370 // Knowing whether VAR_COLOR is used becomes relevant when there is no
1371 // custom vertex shader, but VAR_COLOR is present in the custom fragment
1372 // snippet, because that case needs special care.
1373 const bool usesVarColor = inMaterial.m_renderFlags.testFlag(QSSGRenderCustomMaterial::RenderFlag::VarColor);
1374 defaultMaterialShaderKeyProperties.m_usesVarColor.setValue(theGeneratedKey, usesVarColor);
1375
1376 const bool usesClearcoat = inMaterial.m_renderFlags.testFlag(QSSGRenderCustomMaterial::RenderFlag::Clearcoat);
1377 defaultMaterialShaderKeyProperties.m_clearcoatEnabled.setValue(theGeneratedKey, usesClearcoat);
1378
1379 const bool usesClearcoatFresnelScaleBias = inMaterial.m_renderFlags.testFlag(QSSGRenderCustomMaterial::RenderFlag::ClearcoatFresnelScaleBias);
1380 defaultMaterialShaderKeyProperties.m_clearcoatFresnelScaleBiasEnabled.setValue(theGeneratedKey, usesClearcoatFresnelScaleBias);
1381
1382 const bool usesFresnelScaleBias = inMaterial.m_renderFlags.testFlag(QSSGRenderCustomMaterial::RenderFlag::FresnelScaleBias);
1383 defaultMaterialShaderKeyProperties.m_fresnelScaleBiasEnabled.setValue(theGeneratedKey, usesFresnelScaleBias);
1384
1385 const bool usesTransmission = inMaterial.m_renderFlags.testFlag(QSSGRenderCustomMaterial::RenderFlag::Transmission);
1386 defaultMaterialShaderKeyProperties.m_transmissionEnabled.setValue(theGeneratedKey, usesTransmission);
1387
1388 if (inMaterial.m_renderFlags.testFlag(QSSGRenderCustomMaterial::RenderFlag::Blending))
1389 renderableFlags |= QSSGRenderableObjectFlag::HasTransparency;
1390
1391 if (inMaterial.m_renderFlags.testFlag(QSSGRenderCustomMaterial::RenderFlag::ScreenTexture)) {
1392 ioFlags.setRequiresScreenTexture(true);
1393 renderableFlags |= QSSGRenderableObjectFlag::RequiresScreenTexture;
1394 }
1395
1396 if (inMaterial.m_renderFlags.testFlag(QSSGRenderCustomMaterial::RenderFlag::ScreenMipTexture)) {
1397 ioFlags.setRequiresScreenTexture(true);
1398 ioFlags.setRequiresMipmapsForScreenTexture(true);
1399 renderableFlags |= QSSGRenderableObjectFlag::RequiresScreenTexture;
1400 }
1401
1402 if (inMaterial.m_renderFlags.testFlag(QSSGRenderCustomMaterial::RenderFlag::DepthTexture))
1403 ioFlags.setRequiresDepthTexture(true);
1404
1405 if (inMaterial.m_renderFlags.testFlag(QSSGRenderCustomMaterial::RenderFlag::NormalTexture)) {
1406 ioFlags.setRequiresNormalTexture(true);
1407 renderableFlags |= QSSGRenderableObjectFlag::RequiresNormalTexture;
1408 }
1409
1410 if (inMaterial.m_renderFlags.testFlag(QSSGRenderCustomMaterial::RenderFlag::AoTexture)) {
1411 ioFlags.setRequiresDepthTexture(true);
1412 ioFlags.setRequiresSsaoPass(true);
1413 }
1414 if (orderIndependentTransparencyEnabled && renderableFlags.hasTransparency())
1415 defaultMaterialShaderKeyProperties.m_orderIndependentTransparency.setValue(theGeneratedKey, int(layer.oitMethod));
1416
1417 retval.firstImage = nullptr;
1418
1419 if (inMaterial.m_renderFlags.testFlag(QSSGRenderCustomMaterial::RenderFlag::MotionVectorTexture))
1420 ioFlags.setRequiresMotionVectorPass(true);
1421
1422 if (retval.dirty || alreadyDirty)
1423 renderer->addMaterialDirtyClear(&inMaterial);
1424 return retval;
1425}
1426
1427void QSSGLayerRenderData::setLightmapTexture(const QSSGModelContext &modelContext, QRhiTexture *lightmapTexture)
1428{
1429 lightmapTextures[&modelContext] = lightmapTexture;
1430}
1431
1432QRhiTexture *QSSGLayerRenderData::getLightmapTexture(const QSSGModelContext &modelContext) const
1433{
1434 QRhiTexture *ret = nullptr;
1435 if (modelContext.model.hasLightmap()) {
1436 const auto it = lightmapTextures.constFind(&modelContext);
1437 ret = (it != lightmapTextures.cend()) ? *it : nullptr;
1438 }
1439
1440 return ret;
1441}
1442
1443void QSSGLayerRenderData::setBonemapTexture(const QSSGModelContext &modelContext, QRhiTexture *bonemapTexture)
1444{
1445 bonemapTextures[&modelContext] = bonemapTexture;
1446}
1447
1448QRhiTexture *QSSGLayerRenderData::getBonemapTexture(const QSSGModelContext &modelContext) const
1449{
1450 QRhiTexture *ret = nullptr;
1451 if (modelContext.model.usesBoneTexture()) {
1452 const auto it = bonemapTextures.constFind(&modelContext);
1453 ret = (it != bonemapTextures.cend()) ? *it : nullptr;
1454 }
1455
1456 return ret;
1457}
1458
1459static bool hasCustomBlendMode(const QSSGRenderCustomMaterial &material)
1460{
1461 // Check SrcOver
1462
1463 // srcAlpha is same for all
1464 if (material.m_srcAlphaBlend != QRhiGraphicsPipeline::One)
1465 return true;
1466
1467 // SrcOver srcColor is SrcAlpha
1468 if (material.m_srcBlend != QRhiGraphicsPipeline::SrcAlpha)
1469 return true;
1470
1471 if (material.m_dstBlend == QRhiGraphicsPipeline::OneMinusSrcAlpha
1472 && material.m_dstAlphaBlend == QRhiGraphicsPipeline::OneMinusSrcAlpha)
1473 return false;
1474 return true;
1475}
1476
1477// inModel is const to emphasize the fact that its members cannot be written
1478// here: in case there is a scene shared between multiple View3Ds in different
1479// QQuickWindows, each window may run this in their own render thread, while
1480// inModel is the same.
1481bool QSSGLayerRenderData::prepareModelsForRender(QSSGRenderContextInterface &contextInterface,
1482 const RenderableNodeEntries &renderableModels,
1483 QSSGLayerRenderPreparationResultFlags &ioFlags,
1484 const QSSGRenderCameraList &allCameras,
1485 const QSSGRenderCameraDataList &allCameraData,
1486 TModelContextPtrList &modelContexts,
1487 QSSGRenderableObjectList &opaqueObjects,
1488 QSSGRenderableObjectList &transparentObjects,
1489 QSSGRenderableObjectList &screenTextureObjects,
1490 float lodThreshold)
1491{
1492 const auto &rhiCtx = contextInterface.rhiContext();
1493 const auto &bufferManager = contextInterface.bufferManager();
1494
1495 const auto &debugDrawSystem = contextInterface.debugDrawSystem();
1496 const bool maybeDebugDraw = debugDrawSystem && debugDrawSystem->isEnabled();
1497
1498 bool wasDirty = false;
1499
1500 for (const QSSGRenderableNodeEntry &renderable : renderableModels) {
1501 if ((renderable.overridden & QSSGRenderableNodeEntry::Overridden::Disabled) != 0)
1502 continue;
1503
1504 const QSSGRenderModel &model = *static_cast<QSSGRenderModel *>(renderable.node);
1505 const auto &lights = renderable.lights;
1506 QSSGRenderMesh *theMesh = modelData->getMesh(model);
1507 if (!theMesh)
1508 continue;
1509
1510 const bool altGlobalTransform = ((renderable.overridden & QSSGRenderableNodeEntry::Overridden::GlobalTransform) != 0);
1511 const auto &globalTransform = altGlobalTransform ? renderable.extOverrides.globalTransform : getGlobalTransform(model);
1512 QMatrix3x3 normalMatrix { Qt::Uninitialized };
1513 QSSGLayerRenderData::ModelViewProjections mvps;
1514 if (altGlobalTransform) {
1515 QSSGRenderNode::calculateNormalMatrix(globalTransform, normalMatrix);
1516 size_t mvpCount = 0;
1517 for (const auto &cameraData : allCameraData) {
1518 QSSGRenderNode::calculateMVPAndNormalMatrix(globalTransform, cameraData.viewProjection, mvps[mvpCount++], normalMatrix);
1519 }
1520 } else {
1521 if (model.usesBoneTexture()) {
1522 // FIXME:
1523 // For skinning, node's global transformation will be ignored and
1524 // an identity matrix will be used for the normalMatrix
1525 size_t mvpCount = 0;
1526 for (const QSSGRenderCameraData &cameraData : allCameraData) {
1527 mvps[mvpCount++] = cameraData.viewProjection;
1528 normalMatrix = QMatrix3x3();
1529 }
1530 } else {
1531 normalMatrix = getNormalMatrix(model);
1532 mvps = getModelMvps(model);
1533 }
1534 }
1535 const bool altModelOpacity = ((renderable.overridden & QSSGRenderableNodeEntry::Overridden::GlobalOpacity) != 0);
1536 const float modelGlobalOpacity = altModelOpacity ? renderable.extOverrides.globalOpacity : getGlobalOpacity(model);
1537 QSSGModelContext &theModelContext = *RENDER_FRAME_NEW<QSSGModelContext>(contextInterface, model, globalTransform, normalMatrix, mvps);
1538 modelContexts.push_back(&theModelContext);
1539 // We might over-allocate here, as the material list technically can contain an invalid (nullptr) material.
1540 // We'll fix that by adjusting the size at the end for now...
1541 const auto &meshSubsets = theMesh->subsets;
1542 const auto meshSubsetCount = meshSubsets.size();
1543 theModelContext.subsets = RENDER_FRAME_NEW_BUFFER<QSSGSubsetRenderable>(contextInterface, meshSubsetCount);
1544
1545 // Prepare boneTexture for skinning
1546 if (model.skin) {
1547 auto boneTexture = bufferManager->loadSkinmap(model.skin);
1548 setBonemapTexture(theModelContext, boneTexture.m_texture);
1549 } else if (model.skeleton) {
1550 auto boneTexture = bufferManager->loadSkinmap(&(model.skeleton->boneTexData));
1551 setBonemapTexture(theModelContext, boneTexture.m_texture);
1552 }
1553
1554 // many renderableFlags are the same for all the subsets
1555 QSSGRenderableObjectFlags renderableFlagsForModel;
1556
1557 if (meshSubsetCount > 0) {
1558 const QSSGRenderSubset &theSubset = meshSubsets.at(0);
1559
1560 renderableFlagsForModel.setMotionVectorParticipant(model.motionVectorEnabled);
1561
1562 renderableFlagsForModel.setCastsShadows(model.castsShadows);
1563 renderableFlagsForModel.setReceivesShadows(model.receivesShadows);
1564 renderableFlagsForModel.setReceivesReflections(model.receivesReflections);
1565 renderableFlagsForModel.setCastsReflections(model.castsReflections);
1566
1567 renderableFlagsForModel.setUsedInBakedLighting(model.usedInBakedLighting);
1568 if (model.hasLightmap()) {
1569 QSSGRenderImageTexture lmImageTexture = bufferManager->loadLightmap(model);
1570 if (lmImageTexture.m_texture) {
1571 renderableFlagsForModel.setRendersWithLightmap(true);
1572 setLightmapTexture(theModelContext, lmImageTexture.m_texture);
1573 }
1574 }
1575
1576 // TODO: This should be a oneshot thing, move the flags over!
1577 // With the RHI we need to be able to tell the material shader
1578 // generator to not generate vertex input attributes that are not
1579 // provided by the mesh. (because unlike OpenGL, other graphics
1580 // APIs may treat unbound vertex inputs as a fatal error)
1581 bool hasJoint = false;
1582 bool hasWeight = false;
1583 bool hasMorphTarget = theSubset.rhi.targetsTexture != nullptr;
1584 for (const QSSGRhiInputAssemblerState::InputSemantic &sem : std::as_const(theSubset.rhi.ia.inputs)) {
1585 if (sem == QSSGRhiInputAssemblerState::PositionSemantic) {
1586 renderableFlagsForModel.setHasAttributePosition(true);
1587 } else if (sem == QSSGRhiInputAssemblerState::NormalSemantic) {
1588 renderableFlagsForModel.setHasAttributeNormal(true);
1589 } else if (sem == QSSGRhiInputAssemblerState::TexCoord0Semantic) {
1590 renderableFlagsForModel.setHasAttributeTexCoord0(true);
1591 } else if (sem == QSSGRhiInputAssemblerState::TexCoord1Semantic) {
1592 renderableFlagsForModel.setHasAttributeTexCoord1(true);
1593 } else if (sem == QSSGRhiInputAssemblerState::TexCoordLightmapSemantic) {
1594 renderableFlagsForModel.setHasAttributeTexCoordLightmap(true);
1595 } else if (sem == QSSGRhiInputAssemblerState::TangentSemantic) {
1596 renderableFlagsForModel.setHasAttributeTangent(true);
1597 } else if (sem == QSSGRhiInputAssemblerState::BinormalSemantic) {
1598 renderableFlagsForModel.setHasAttributeBinormal(true);
1599 } else if (sem == QSSGRhiInputAssemblerState::ColorSemantic) {
1600 renderableFlagsForModel.setHasAttributeColor(true);
1601 // For skinning, we will set the HasAttribute only
1602 // if the mesh has both joint and weight
1603 } else if (sem == QSSGRhiInputAssemblerState::JointSemantic) {
1604 hasJoint = true;
1605 } else if (sem == QSSGRhiInputAssemblerState::WeightSemantic) {
1606 hasWeight = true;
1607 }
1608 }
1609 renderableFlagsForModel.setHasAttributeJointAndWeight(hasJoint && hasWeight);
1610 renderableFlagsForModel.setHasAttributeMorphTarget(hasMorphTarget);
1611 }
1612
1613 QSSGRenderableObjectList bakedLightingObjects;
1614 bool usesBlendParticles = particlesEnabled && theModelContext.model.particleBuffer != nullptr
1615 && model.particleBuffer->particleCount();
1616 const bool anyLightHasShadows = std::find_if(lights.begin(),
1617 lights.end(),
1618 [](const QSSGShaderLight &light) { return light.shadows; })
1619 != lights.end();
1620 const bool hasAnyLights = !lights.isEmpty();
1621 QSSGRenderLight::SoftShadowQuality maxSoftShadowQuality = QSSGRenderLight::SoftShadowQuality::Hard;
1622 if (anyLightHasShadows) {
1623 // Iterate the light list to find the maximum shadow quality of lights that cast shadows
1624 for (const QSSGShaderLight &light : lights) {
1625 if (light.shadows && light.light->m_softShadowQuality > maxSoftShadowQuality)
1626 maxSoftShadowQuality = light.light->m_softShadowQuality;
1627 }
1628 }
1629
1630
1631 // Subset(s)
1632 auto &renderableSubsets = theModelContext.subsets;
1633 const bool hasMaterialOverrides = ((renderable.overridden & QSSGRenderableNodeEntry::Overridden::Materials) != 0);
1634 const auto &materials = hasMaterialOverrides ? renderable.extOverrides.materials : modelData->getMaterials(model);
1635 const auto materialCount = materials.size();
1636 QSSGRenderGraphObject *lastMaterial = !materials.isEmpty() ? materials.last() : nullptr;
1637 int idx = 0, subsetIdx = 0;
1638 for (; idx < meshSubsetCount; ++idx) {
1639 // If the materials list < size of subsets, then use the last material for the rest
1640 QSSGRenderGraphObject *theMaterialObject = (idx >= materialCount) ? lastMaterial : materials[idx];
1641 if (!theMaterialObject)
1642 continue;
1643
1644 const QSSGRenderSubset &theSubset = meshSubsets.at(idx);
1645 QSSGRenderableObjectFlags renderableFlags = renderableFlagsForModel;
1646 float subsetOpacity = modelGlobalOpacity;
1647
1648 renderableFlags.setPointsTopology(theSubset.rhi.ia.topology == QRhiGraphicsPipeline::Points);
1649 QSSGRenderableObject *theRenderableObject = &renderableSubsets[subsetIdx++];
1650
1651 const bool usesInstancing = theModelContext.model.instancing()
1652 && rhiCtx->rhi()->isFeatureSupported(QRhi::Instancing);
1653 if (usesInstancing && theModelContext.model.instanceTable->hasTransparency())
1654 renderableFlags |= QSSGRenderableObjectFlag::HasTransparency;
1655 if (theModelContext.model.hasTransparency)
1656 renderableFlags |= QSSGRenderableObjectFlag::HasTransparency;
1657
1658 // Level Of Detail
1659 quint32 subsetLevelOfDetail = 0;
1660 if (!theSubset.lods.isEmpty() && lodThreshold > 0.0f) {
1661 // Accounts for FOV
1662 float lodDistanceMultiplier = camerasView[0]->getLevelOfDetailMultiplier();
1663 float distanceThreshold = 0.0f;
1664 const auto scale = QSSGUtils::mat44::getScale(globalTransform);
1665 float modelScale = qMax(scale.x(), qMax(scale.y(), scale.z()));
1666 QSSGBounds3 transformedBounds = theSubset.bounds;
1667 if (camerasView[0]->type != QSSGRenderGraphObject::Type::OrthographicCamera) {
1668 transformedBounds.transform(globalTransform);
1669 if (maybeDebugDraw && debugDrawSystem->isEnabled(QSSGDebugDrawSystem::Mode::MeshLod))
1670 debugDrawSystem->drawBounds(transformedBounds, QColor(Qt::red));
1671 const QMatrix4x4 cameraGlobalTranform = getGlobalTransform(*camerasView[0]);
1672 const QVector3D cameraNormal = QSSGRenderNode::getScalingCorrectDirection(cameraGlobalTranform);
1673 const QVector3D cameraPosition = QSSGRenderNode::getGlobalPos(cameraGlobalTranform);
1674 const QSSGPlane cameraPlane = QSSGPlane(cameraPosition, cameraNormal);
1675 const QVector3D lodSupportMin = transformedBounds.getSupport(-cameraNormal);
1676 const QVector3D lodSupportMax = transformedBounds.getSupport(cameraNormal);
1677 if (maybeDebugDraw && debugDrawSystem->isEnabled(QSSGDebugDrawSystem::Mode::MeshLod))
1678 debugDrawSystem->drawPoint(lodSupportMin, QColor("orange"));
1679
1680 const float distanceMin = cameraPlane.distance(lodSupportMin);
1681 const float distanceMax = cameraPlane.distance(lodSupportMax);
1682
1683 if (distanceMin * distanceMax < 0.0)
1684 distanceThreshold = 0.0;
1685 else if (distanceMin >= 0.0)
1686 distanceThreshold = distanceMin;
1687 else if (distanceMax <= 0.0)
1688 distanceThreshold = -distanceMax;
1689
1690 } else {
1691 // Orthographic Projection
1692 distanceThreshold = 1.0;
1693 }
1694
1695 int currentLod = -1;
1696 if (model.levelOfDetailBias > 0.0f) {
1697 const float threshold = distanceThreshold * lodDistanceMultiplier;
1698 const float modelBias = 1 / model.levelOfDetailBias;
1699 for (qsizetype i = 0; i < theSubset.lods.count(); ++i) {
1700 float subsetDistance = theSubset.lods[i].distance * modelScale * modelBias;
1701 float screenSize = subsetDistance / threshold;
1702 if (screenSize > lodThreshold)
1703 break;
1704 currentLod = i;
1705 }
1706 }
1707 if (currentLod == -1)
1708 subsetLevelOfDetail = 0;
1709 else
1710 subsetLevelOfDetail = currentLod + 1;
1711 if (maybeDebugDraw && debugDrawSystem->isEnabled(QSSGDebugDrawSystem::Mode::MeshLod))
1712 debugDrawSystem->drawBounds(transformedBounds, QSSGDebugDrawSystem::levelOfDetailColor(subsetLevelOfDetail));
1713 }
1714
1715 QVector3D theModelCenter(theSubset.bounds.center());
1716 theModelCenter = QSSGUtils::mat44::transform(globalTransform, theModelCenter);
1717 if (maybeDebugDraw && debugDrawSystem->isEnabled(QSSGDebugDrawSystem::Mode::MeshLodNormal)) {
1718 const QMatrix4x4 allCamera0GlobalTransform = getGlobalTransform(*allCameras[0]);
1719 debugDrawSystem->debugNormals(*bufferManager, theModelContext, theSubset, subsetLevelOfDetail, (theModelCenter - QSSGRenderNode::getGlobalPos(allCamera0GlobalTransform)).length() * 0.01);
1720 }
1721
1722 auto checkF32TypeIndex = [&rhiCtx](QRhiVertexInputAttribute::Format f) {
1723 if ((f == QRhiVertexInputAttribute::Format::Float4)
1724 || (f == QRhiVertexInputAttribute::Format::Float3)
1725 || (f == QRhiVertexInputAttribute::Format::Float2)
1726 || (f == QRhiVertexInputAttribute::Format::Float)) {
1727 return true;
1728 }
1729 if (!rhiCtx->rhi()->isFeatureSupported(QRhi::IntAttributes))
1730 qWarning() << "WARN: Model has non-integer type indices for skinning but current RHI backend doesn't support it!";
1731 return false;
1732 };
1733
1734 if (theMaterialObject->type == QSSGRenderGraphObject::Type::DefaultMaterial ||
1735 theMaterialObject->type == QSSGRenderGraphObject::Type::PrincipledMaterial ||
1736 theMaterialObject->type == QSSGRenderGraphObject::Type::SpecularGlossyMaterial) {
1737 QSSGRenderDefaultMaterial &theMaterial(static_cast<QSSGRenderDefaultMaterial &>(*theMaterialObject));
1738 QSSGDefaultMaterialPreparationResult theMaterialPrepResult(prepareDefaultMaterialForRender(theMaterial, renderableFlags, subsetOpacity, hasAnyLights, anyLightHasShadows, ioFlags));
1739 QSSGShaderDefaultMaterialKey &theGeneratedKey(theMaterialPrepResult.materialKey);
1740 subsetOpacity = theMaterialPrepResult.opacity;
1741 QSSGRenderableImage *firstImage(theMaterialPrepResult.firstImage);
1742 wasDirty |= theMaterialPrepResult.dirty;
1743 renderableFlags = theMaterialPrepResult.renderableFlags;
1744 if (renderableFlags.hasTransparency())
1745 ioFlags.setHasCustomBlendMode(theMaterial.blendMode != QSSGRenderDefaultMaterial::MaterialBlendMode::SourceOver);
1746
1747 // Blend particles
1748 defaultMaterialShaderKeyProperties.m_blendParticles.setValue(theGeneratedKey, usesBlendParticles);
1749
1750 if (defaultMaterialShaderKeyProperties.m_orderIndependentTransparency.getValue(theGeneratedKey) == int(QSSGRenderLayer::OITMethod::LinkedList))
1751 defaultMaterialShaderKeyProperties.m_oitMSAA.setValue(theGeneratedKey, rhiCtx->mainPassSampleCount() > 1);
1752
1753 // Skin
1754 const auto boneCount = model.skin ? model.skin->boneCount :
1755 model.skeleton ? model.skeleton->boneCount : 0;
1756 defaultMaterialShaderKeyProperties.m_boneCount.setValue(theGeneratedKey, boneCount);
1757 if (auto idJoint = theSubset.rhi.ia.inputs.indexOf(QSSGRhiInputAssemblerState::JointSemantic); idJoint != -1) {
1758 const auto attr = theSubset.rhi.ia.inputLayout.attributeAt(idJoint);
1759 defaultMaterialShaderKeyProperties.m_usesFloatJointIndices.setValue(theGeneratedKey, checkF32TypeIndex(attr->format()));
1760 }
1761
1762 // SoftShadow quality
1763 defaultMaterialShaderKeyProperties.m_shadowSoftness.setShadowSoftness(theGeneratedKey, maxSoftShadowQuality);
1764
1765 // Instancing
1766 defaultMaterialShaderKeyProperties.m_usesInstancing.setValue(theGeneratedKey, usesInstancing);
1767 // Morphing
1768 defaultMaterialShaderKeyProperties.m_targetCount.setValue(theGeneratedKey,
1769 theSubset.rhi.ia.targetCount);
1770 defaultMaterialShaderKeyProperties.m_targetPositionOffset.setValue(theGeneratedKey,
1771 theSubset.rhi.ia.targetOffsets[QSSGRhiInputAssemblerState::PositionSemantic]);
1772 defaultMaterialShaderKeyProperties.m_targetNormalOffset.setValue(theGeneratedKey,
1773 theSubset.rhi.ia.targetOffsets[QSSGRhiInputAssemblerState::NormalSemantic]);
1774 defaultMaterialShaderKeyProperties.m_targetTangentOffset.setValue(theGeneratedKey,
1775 theSubset.rhi.ia.targetOffsets[QSSGRhiInputAssemblerState::TangentSemantic]);
1776 defaultMaterialShaderKeyProperties.m_targetBinormalOffset.setValue(theGeneratedKey,
1777 theSubset.rhi.ia.targetOffsets[QSSGRhiInputAssemblerState::BinormalSemantic]);
1778 defaultMaterialShaderKeyProperties.m_targetTexCoord0Offset.setValue(theGeneratedKey,
1779 theSubset.rhi.ia.targetOffsets[QSSGRhiInputAssemblerState::TexCoord0Semantic]);
1780 defaultMaterialShaderKeyProperties.m_targetTexCoord1Offset.setValue(theGeneratedKey,
1781 theSubset.rhi.ia.targetOffsets[QSSGRhiInputAssemblerState::TexCoord1Semantic]);
1782 defaultMaterialShaderKeyProperties.m_targetColorOffset.setValue(theGeneratedKey,
1783 theSubset.rhi.ia.targetOffsets[QSSGRhiInputAssemblerState::ColorSemantic]);
1784
1785 new (theRenderableObject) QSSGSubsetRenderable(QSSGSubsetRenderable::Type::DefaultMaterialMeshSubset,
1786 renderableFlags,
1787 theModelCenter,
1788 renderer,
1789 theSubset,
1790 theModelContext,
1791 subsetOpacity,
1792 subsetLevelOfDetail,
1793 theMaterial,
1794 firstImage,
1795 theGeneratedKey,
1796 lights,
1797 anyLightHasShadows);
1798 wasDirty = wasDirty || renderableFlags.isDirty();
1799 } else if (theMaterialObject->type == QSSGRenderGraphObject::Type::CustomMaterial) {
1800 QSSGRenderCustomMaterial &theMaterial(static_cast<QSSGRenderCustomMaterial &>(*theMaterialObject));
1801
1802 const auto &theMaterialSystem(contextInterface.customMaterialSystem());
1803 wasDirty |= theMaterialSystem->prepareForRender(theModelContext.model, theSubset, theMaterial);
1804
1805 if (theMaterial.m_renderFlags.testFlag(QSSGRenderCustomMaterial::RenderFlag::Blending))
1806 ioFlags.setHasCustomBlendMode(!hasCustomBlendMode(theMaterial));
1807
1808 QSSGDefaultMaterialPreparationResult theMaterialPrepResult(
1809 prepareCustomMaterialForRender(theMaterial, renderableFlags, subsetOpacity, wasDirty,
1810 hasAnyLights, anyLightHasShadows, ioFlags));
1811 QSSGShaderDefaultMaterialKey &theGeneratedKey(theMaterialPrepResult.materialKey);
1812 subsetOpacity = theMaterialPrepResult.opacity;
1813 QSSGRenderableImage *firstImage(theMaterialPrepResult.firstImage);
1814 renderableFlags = theMaterialPrepResult.renderableFlags;
1815
1816 if (model.particleBuffer && model.particleBuffer->particleCount())
1817 defaultMaterialShaderKeyProperties.m_blendParticles.setValue(theGeneratedKey, true);
1818 else
1819 defaultMaterialShaderKeyProperties.m_blendParticles.setValue(theGeneratedKey, false);
1820
1821 if (defaultMaterialShaderKeyProperties.m_orderIndependentTransparency.getValue(theGeneratedKey) == int(QSSGRenderLayer::OITMethod::LinkedList))
1822 defaultMaterialShaderKeyProperties.m_oitMSAA.setValue(theGeneratedKey, rhiCtx->mainPassSampleCount() > 1);
1823
1824 // SoftShadow quality
1825 defaultMaterialShaderKeyProperties.m_shadowSoftness.setShadowSoftness(theGeneratedKey, maxSoftShadowQuality);
1826
1827 // Skin
1828 const auto boneCount = model.skin ? model.skin->boneCount :
1829 model.skeleton ? model.skeleton->boneCount : 0;
1830 defaultMaterialShaderKeyProperties.m_boneCount.setValue(theGeneratedKey, boneCount);
1831 if (auto idJoint = theSubset.rhi.ia.inputs.indexOf(QSSGRhiInputAssemblerState::JointSemantic); idJoint != -1) {
1832 const auto attr = theSubset.rhi.ia.inputLayout.attributeAt(idJoint);
1833 defaultMaterialShaderKeyProperties.m_usesFloatJointIndices.setValue(theGeneratedKey, checkF32TypeIndex(attr->format()));
1834 }
1835
1836 // Instancing
1837 bool usesInstancing = theModelContext.model.instancing()
1838 && rhiCtx->rhi()->isFeatureSupported(QRhi::Instancing);
1839 defaultMaterialShaderKeyProperties.m_usesInstancing.setValue(theGeneratedKey, usesInstancing);
1840 // Morphing
1841 defaultMaterialShaderKeyProperties.m_targetCount.setValue(theGeneratedKey,
1842 theSubset.rhi.ia.targetCount);
1843 defaultMaterialShaderKeyProperties.m_targetPositionOffset.setValue(theGeneratedKey,
1844 theSubset.rhi.ia.targetOffsets[QSSGRhiInputAssemblerState::PositionSemantic]);
1845 defaultMaterialShaderKeyProperties.m_targetNormalOffset.setValue(theGeneratedKey,
1846 theSubset.rhi.ia.targetOffsets[QSSGRhiInputAssemblerState::NormalSemantic]);
1847 defaultMaterialShaderKeyProperties.m_targetTangentOffset.setValue(theGeneratedKey,
1848 theSubset.rhi.ia.targetOffsets[QSSGRhiInputAssemblerState::TangentSemantic]);
1849 defaultMaterialShaderKeyProperties.m_targetBinormalOffset.setValue(theGeneratedKey,
1850 theSubset.rhi.ia.targetOffsets[QSSGRhiInputAssemblerState::BinormalSemantic]);
1851 defaultMaterialShaderKeyProperties.m_targetTexCoord0Offset.setValue(theGeneratedKey,
1852 theSubset.rhi.ia.targetOffsets[QSSGRhiInputAssemblerState::TexCoord0Semantic]);
1853 defaultMaterialShaderKeyProperties.m_targetTexCoord1Offset.setValue(theGeneratedKey,
1854 theSubset.rhi.ia.targetOffsets[QSSGRhiInputAssemblerState::TexCoord1Semantic]);
1855 defaultMaterialShaderKeyProperties.m_targetColorOffset.setValue(theGeneratedKey,
1856 theSubset.rhi.ia.targetOffsets[QSSGRhiInputAssemblerState::ColorSemantic]);
1857
1858 if (theMaterial.m_iblProbe)
1859 theMaterial.m_iblProbe->clearDirty();
1860
1861 new (theRenderableObject) QSSGSubsetRenderable(QSSGSubsetRenderable::Type::CustomMaterialMeshSubset,
1862 renderableFlags,
1863 theModelCenter,
1864 renderer,
1865 theSubset,
1866 theModelContext,
1867 subsetOpacity,
1868 subsetLevelOfDetail,
1869 theMaterial,
1870 firstImage,
1871 theGeneratedKey,
1872 lights,
1873 anyLightHasShadows);
1874 }
1875 if (theRenderableObject) // NOTE: Should just go in with the ctor args
1876 theRenderableObject->camdistSq = getCameraDistanceSq(*theRenderableObject, allCameraData[0]);
1877 }
1878
1879 // If the indices don't match then something's off and we need to adjust the subset renderable list size.
1880 if (Q_UNLIKELY(idx != subsetIdx))
1881 renderableSubsets.mSize = subsetIdx; // subsetIdx == next_subsetIdx == size
1882
1883 for (auto &ro : renderableSubsets) {
1884 const auto depthMode = ro.depthWriteMode;
1885 hasDepthWriteObjects |= (depthMode == QSSGDepthDrawMode::Always || depthMode == QSSGDepthDrawMode::OpaqueOnly);
1886 enum ObjectType : quint8 { ScreenTexture, Transparent, Opaque };
1887 static constexpr DepthPrepassObject ppState[][2] = { {DepthPrepassObject::None, DepthPrepassObject::ScreenTexture},
1888 {DepthPrepassObject::None, DepthPrepassObject::Transparent},
1889 {DepthPrepassObject::None, DepthPrepassObject::Opaque} };
1890
1891 if (ro.renderableFlags.requiresScreenTexture()) {
1892 depthPrepassObjectsState |= DepthPrepassObjectStateT(ppState[ObjectType::ScreenTexture][size_t(depthMode == QSSGDepthDrawMode::OpaquePrePass)]);
1893 screenTextureObjects.push_back({&ro, ro.camdistSq, model.tag});
1894 } else if (ro.renderableFlags.hasTransparency()) {
1895 depthPrepassObjectsState |= DepthPrepassObjectStateT(ppState[ObjectType::Transparent][size_t(depthMode == QSSGDepthDrawMode::OpaquePrePass)]);
1896 transparentObjects.push_back({&ro, ro.camdistSq, model.tag});
1897 } else {
1898 depthPrepassObjectsState |= DepthPrepassObjectStateT(ppState[ObjectType::Opaque][size_t(depthMode == QSSGDepthDrawMode::OpaquePrePass)]);
1899 opaqueObjects.push_back({&ro, ro.camdistSq, model.tag});
1900 }
1901
1902 if (ro.renderableFlags.usedInBakedLighting())
1903 bakedLightingObjects.push_back({&ro, ro.camdistSq, model.tag});
1904 }
1905
1906 if (!bakedLightingObjects.isEmpty())
1907 bakedLightingModels.push_back(QSSGBakedLightingModel(&model, bakedLightingObjects));
1908 }
1909
1910 return wasDirty;
1911}
1912
1913bool QSSGLayerRenderData::prepareParticlesForRender(const RenderableNodeEntries &renderableParticles, const QSSGRenderCameraData &cameraData, QSSGLayerRenderPreparationResultFlags &ioFlags)
1914{
1915 QSSG_ASSERT(particlesEnabled, return false);
1916
1917 QSSGRenderContextInterface &contextInterface = *renderer->contextInterface();
1918
1919 bool dirty = false;
1920
1921 //
1922 auto &opaqueObjects = opaqueObjectStore[0];
1923 auto &transparentObjects = transparentObjectStore[0];
1924 auto &screenTextureObjects = screenTextureObjectStore[0];
1925
1926 for (auto &renderable : renderableParticles) {
1927 QSSGShaderParticleMaterialKeyProperties &properties = particleMaterialShaderKeyProperties;
1928
1929 QSSGRenderParticles &particles = *static_cast<QSSGRenderParticles *>(renderable.node);
1930 QSSGShaderParticleMaterialKey &theGeneratedKey(particles.materialKey);
1931 const auto &lights = renderable.lights;
1932
1933 QSSGRenderableObjectFlags renderableFlags;
1934 renderableFlags.setCastsShadows(false);
1935 renderableFlags.setReceivesShadows(false);
1936 renderableFlags.setHasAttributePosition(true);
1937 renderableFlags.setHasAttributeNormal(true);
1938 renderableFlags.setHasAttributeTexCoord0(true);
1939 renderableFlags.setHasAttributeColor(true);
1940 renderableFlags.setHasTransparency(particles.m_hasTransparency);
1941 renderableFlags.setCastsReflections(particles.m_castsReflections);
1942 if (particles.m_hasTransparency && particles.m_blendMode != QSSGRenderParticles::BlendMode::SourceOver)
1943 ioFlags.setHasCustomBlendMode(true);
1944
1945 properties.m_isLineParticle.setValue(theGeneratedKey, particles.m_featureLevel >= QSSGRenderParticles::FeatureLevel::Line);
1946 const bool animated = particles.m_featureLevel == QSSGRenderParticles::FeatureLevel::LineAnimated
1947 || particles.m_featureLevel == QSSGRenderParticles::FeatureLevel::Animated
1948 || particles.m_featureLevel == QSSGRenderParticles::FeatureLevel::AnimatedVLight
1949 || particles.m_featureLevel == QSSGRenderParticles::FeatureLevel::LineAnimatedVLight;
1950 const bool mapped = particles.m_featureLevel == QSSGRenderParticles::FeatureLevel::LineMapped
1951 || particles.m_featureLevel == QSSGRenderParticles::FeatureLevel::Mapped
1952 || particles.m_featureLevel == QSSGRenderParticles::FeatureLevel::MappedVLight
1953 || particles.m_featureLevel == QSSGRenderParticles::FeatureLevel::LineMappedVLight;
1954 const bool lit = particles.m_featureLevel == QSSGRenderParticles::FeatureLevel::LineVLight
1955 || particles.m_featureLevel == QSSGRenderParticles::FeatureLevel::AnimatedVLight
1956 || particles.m_featureLevel == QSSGRenderParticles::FeatureLevel::MappedVLight
1957 || particles.m_featureLevel == QSSGRenderParticles::FeatureLevel::LineMappedVLight
1958 || particles.m_featureLevel == QSSGRenderParticles::FeatureLevel::LineAnimatedVLight
1959 || particles.m_featureLevel == QSSGRenderParticles::FeatureLevel::SimpleVLight;
1960 properties.m_isAnimated.setValue(theGeneratedKey, animated);
1961 properties.m_isMapped.setValue(theGeneratedKey, mapped);
1962 properties.m_hasLighting.setValue(theGeneratedKey, lit);
1963 properties.m_viewCount.setValue(theGeneratedKey, layer.viewCount);
1964 if (renderableFlags.hasTransparency() && orderIndependentTransparencyEnabled) {
1965 properties.m_orderIndependentTransparency.setValue(theGeneratedKey, int(layer.oitMethod));
1966 if (layer.oitMethod == QSSGRenderLayer::OITMethod::LinkedList)
1967 properties.m_oitMSAA.setValue(theGeneratedKey, contextInterface.rhiContext()->mainPassSampleCount() > 1);
1968 } else {
1969 properties.m_orderIndependentTransparency.setValue(theGeneratedKey, int(0));
1970 }
1971
1972 float opacity = getGlobalOpacity(particles);
1973 QVector3D center(particles.m_particleBuffer.bounds().center());
1974 center = QSSGUtils::mat44::transform(getGlobalTransform(particles), center);
1975
1976 QSSGRenderableImage *firstImage = nullptr;
1977 if (particles.m_sprite) {
1978 const auto &bufferManager = contextInterface.bufferManager();
1979
1980 if (particles.m_sprite->clearDirty())
1981 dirty = true;
1982
1983 const QSSGRenderImageTexture texture = bufferManager->loadRenderImage(particles.m_sprite);
1984 QSSGRenderableImage *theImage = RENDER_FRAME_NEW<QSSGRenderableImage>(contextInterface, QSSGRenderableImage::Type::Diffuse, *particles.m_sprite, texture);
1985 firstImage = theImage;
1986 properties.m_isSpriteLinear.setValue(theGeneratedKey, texture.m_flags.isLinear());
1987 }
1988
1989 QSSGRenderableImage *colorTable = nullptr;
1990 if (particles.m_colorTable) {
1991 const auto &bufferManager = contextInterface.bufferManager();
1992
1993 if (particles.m_colorTable->clearDirty())
1994 dirty = true;
1995
1996 const QSSGRenderImageTexture texture = bufferManager->loadRenderImage(particles.m_colorTable);
1997
1998 QSSGRenderableImage *theImage = RENDER_FRAME_NEW<QSSGRenderableImage>(contextInterface, QSSGRenderableImage::Type::Diffuse, *particles.m_colorTable, texture);
1999 colorTable = theImage;
2000 properties.m_isColorTableLinear.setValue(theGeneratedKey, texture.m_flags.isLinear());
2001 }
2002
2003 if (opacity > 0.0f && particles.m_particleBuffer.particleCount()) {
2004 const auto globalTransform = getGlobalTransform(particles);
2005 auto *theRenderableObject = RENDER_FRAME_NEW<QSSGParticlesRenderable>(contextInterface,
2006 renderableFlags,
2007 center,
2008 renderer,
2009 globalTransform,
2010 particles,
2011 firstImage,
2012 colorTable,
2013 lights,
2014 opacity,
2015 theGeneratedKey);
2016 if (theRenderableObject) {
2017 if (theRenderableObject->renderableFlags.requiresScreenTexture())
2018 screenTextureObjects.push_back({theRenderableObject, getCameraDistanceSq(*theRenderableObject, cameraData), particles.tag});
2019 else if (theRenderableObject->renderableFlags.hasTransparency())
2020 transparentObjects.push_back({theRenderableObject, getCameraDistanceSq(*theRenderableObject, cameraData), particles.tag});
2021 else
2022 opaqueObjects.push_back({theRenderableObject, getCameraDistanceSq(*theRenderableObject, cameraData), particles.tag});
2023 }
2024 }
2025 }
2026
2027 return dirty;
2028}
2029
2030void QSSGLayerRenderData::prepareResourceLoaders()
2031{
2032 QSSGRenderContextInterface &contextInterface = *renderer->contextInterface();
2033 const auto &bufferManager = contextInterface.bufferManager();
2034
2035 for (const auto resourceLoader : std::as_const(layer.resourceLoaders))
2036 bufferManager->processResourceLoader(static_cast<QSSGRenderResourceLoader *>(resourceLoader));
2037}
2038
2039void QSSGLayerRenderData::prepareReflectionProbesForRender()
2040{
2041 const auto probeCount = reflectionProbesView.size();
2042 requestReflectionMapManager(); // ensure that we have a reflection map manager
2043
2044 for (int i = 0; i < probeCount; i++) {
2045 QSSGRenderReflectionProbe* probe = reflectionProbesView[i];
2046
2047 QMatrix4x4 probeTransform = getGlobalTransform(*probe);
2048
2049 int reflectionObjectCount = 0;
2050 QVector3D probeExtent = probe->boxSize / 2;
2051 QSSGBounds3 probeBound = QSSGBounds3::centerExtents(QSSGRenderNode::getGlobalPos(probeTransform) + probe->boxOffset, probeExtent);
2052
2053 const auto injectProbe = [&](const QSSGRenderableObjectHandle &handle) {
2054 if (handle.obj->renderableFlags.testFlag(QSSGRenderableObjectFlag::ReceivesReflections)
2055 && !(handle.obj->type == QSSGRenderableObject::Type::Particles)) {
2056 QSSGSubsetRenderable* renderableObj = static_cast<QSSGSubsetRenderable*>(handle.obj);
2057 QSSGBounds3 nodeBound = renderableObj->bounds;
2058 QVector4D vmin(nodeBound.minimum, 1.0);
2059 QVector4D vmax(nodeBound.maximum, 1.0);
2060 const QMatrix4x4 &renderableTransform = renderableObj->modelContext.globalTransform;
2061 vmin = renderableTransform * vmin;
2062 vmax = renderableTransform * vmax;
2063 nodeBound.minimum = vmin.toVector3D();
2064 nodeBound.maximum = vmax.toVector3D();
2065 if (probeBound.intersects(nodeBound)) {
2066 QVector3D nodeBoundCenter = nodeBound.center();
2067 QVector3D probeBoundCenter = probeBound.center();
2068 float distance = nodeBoundCenter.distanceToPoint(probeBoundCenter);
2069 if (renderableObj->reflectionProbeIndex == -1 || distance < renderableObj->distanceFromReflectionProbe) {
2070 renderableObj->reflectionProbeIndex = i;
2071 renderableObj->distanceFromReflectionProbe = distance;
2072 renderableObj->reflectionProbe.parallaxCorrection = probe->parallaxCorrection;
2073 renderableObj->reflectionProbe.probeCubeMapCenter = QSSGRenderNode::getGlobalPos(probeTransform);
2074 renderableObj->reflectionProbe.probeBoxMax = probeBound.maximum;
2075 renderableObj->reflectionProbe.probeBoxMin = probeBound.minimum;
2076 renderableObj->reflectionProbe.enabled = true;
2077 reflectionObjectCount++;
2078 }
2079 }
2080 }
2081 };
2082
2083 const auto &transparentObjects = std::as_const(transparentObjectStore[0]);
2084 const auto &opaqueObjects = std::as_const(opaqueObjectStore[0]);
2085 const auto &screenTextureObjects = std::as_const(screenTextureObjectStore[0]);
2086
2087 for (const auto &handle : std::as_const(transparentObjects))
2088 injectProbe(handle);
2089
2090 for (const auto &handle : std::as_const(opaqueObjects))
2091 injectProbe(handle);
2092
2093 for (const auto &handle : std::as_const(screenTextureObjects))
2094 injectProbe(handle);
2095
2096 if (probe->texture)
2097 reflectionMapManager->addTexturedReflectionMapEntry(i, *probe);
2098 else if (reflectionObjectCount > 0)
2099 reflectionMapManager->addReflectionMapEntry(i, *probe);
2100 }
2101}
2102
2103static bool scopeLight(QSSGRenderNode *node, QSSGRenderNode *lightScope)
2104{
2105 // check if the node is parent of the lightScope
2106 while (node) {
2107 if (node == lightScope)
2108 return true;
2109 node = node->parent;
2110 }
2111 return false;
2112}
2113
2114static const int REDUCED_MAX_LIGHT_COUNT_THRESHOLD_BYTES = 5200; // 325 vec4s
2115
2116static inline int effectiveMaxLightCount(const QSSGShaderFeatures &features)
2117{
2118 if (features.isSet(QSSGShaderFeatures::Feature::ReduceMaxNumLights))
2119 return QSSG_REDUCED_MAX_NUM_LIGHTS;
2120
2121 return QSSG_MAX_NUM_LIGHTS;
2122}
2123
2124static inline int effectiveMaxDirectionalLightCount(const QSSGShaderFeatures &features)
2125{
2126 if (features.isSet(QSSGShaderFeatures::Feature::ReduceMaxNumLights))
2127 return QSSG_REDUCED_MAX_NUM_DIRECTIONAL_LIGHTS;
2128
2129 return QSSG_MAX_NUM_DIRECTIONAL_LIGHTS;
2130}
2131
2132static void updateDirtySkeletons(const QSSGLayerRenderData &renderData, const QSSGLayerRenderData::QSSGModelsView &renderableNodes)
2133{
2134 // First model using skeleton clears the dirty flag so we need another mechanism
2135 // to tell to the other models the skeleton is dirty.
2136 QSet<QSSGRenderSkeleton *> dirtySkeletons;
2137 for (const auto &modelNode : std::as_const(renderableNodes)) {
2138 QSSGRenderSkeleton *skeletonNode = modelNode->skeleton;
2139 bool hcj = false;
2140 if (skeletonNode) {
2141 const bool dirtySkeleton = dirtySkeletons.contains(skeletonNode);
2142 const bool hasDirtyNonJoints = (skeletonNode->containsNonJointNodes
2143 && (hasDirtyNonJointNodes(skeletonNode, hcj) || dirtySkeleton));
2144 if (skeletonNode->skinningDirty || hasDirtyNonJoints) {
2145 Q_ASSERT(!skeletonNode->isDirty(QSSGRenderNode::DirtyFlag::GlobalValuesDirty));
2146 skeletonNode->boneTransformsDirty = false;
2147 if (hasDirtyNonJoints && !dirtySkeleton)
2148 dirtySkeletons.insert(skeletonNode);
2149 skeletonNode->skinningDirty = false;
2150 const qsizetype dataSize = BONEDATASIZE4ID(skeletonNode->maxIndex);
2151 if (skeletonNode->boneData.size() < dataSize)
2152 skeletonNode->boneData.resize(dataSize);
2153 skeletonNode->containsNonJointNodes = false;
2154 for (auto &child : skeletonNode->children)
2155 collectBoneTransforms(renderData, &child, skeletonNode, modelNode->inverseBindPoses);
2156 }
2157 skeletonNode->boneCount = skeletonNode->boneData.size() / 2 / 4 / 16;
2158 const int boneTexWidth = qCeil(qSqrt(skeletonNode->boneCount * 4 * 2));
2159 skeletonNode->boneTexData.setSize(QSize(boneTexWidth, boneTexWidth));
2160 skeletonNode->boneData.resize(boneTexWidth * boneTexWidth * 16);
2161 skeletonNode->boneTexData.setTextureData(skeletonNode->boneData);
2162 }
2163 const int numMorphTarget = modelNode->morphTargets.size();
2164 for (int i = 0; i < numMorphTarget; ++i) {
2165 auto morphTarget = static_cast<const QSSGRenderMorphTarget *>(modelNode->morphTargets.at(i));
2166 modelNode->morphWeights[i] = morphTarget->weight;
2167 modelNode->morphAttributes[i] = morphTarget->attributes;
2169 modelNode->morphAttributes[i] &= 0x1; // MorphTarget.Position
2171 modelNode->morphAttributes[i] &= 0x3; // MorphTarget.Position | MorphTarget.Normal
2172 }
2173 }
2174
2175 dirtySkeletons.clear();
2176}
2177
2178QSSGNodeIdList QSSGLayerRenderData::filter(const QSSGGlobalRenderNodeData::LayerNodeView &layerNodes,
2179 quint32 layerMask,
2180 quint32 typeMask)
2181{
2182 QSSGNodeIdList res;
2183 for (auto *n : layerNodes) {
2184 // Check mask
2185 if (((quint32(n->type) & typeMask) == typeMask) && n->tag.isSet(layerMask))
2186 res.push_back(QSSGNodeId(reinterpret_cast<quintptr>(n)));
2187 }
2188
2189 return res;
2190}
2191
2192void QSSGLayerRenderData::prepareForRender()
2193{
2194 QSSG_ASSERT_X(layerPrepResult.isNull(), "Prep-result was not reset for render!", layerPrepResult = {});
2195
2196 QRect theViewport(renderer->viewport());
2197
2198 // NOTE: The renderer won't change in practice (after being set the first time), but just update
2199 // it anyways.
2200 frameData.m_ctx = renderer->contextInterface();
2201 frameData.clear();
2202
2203 // Create base pipeline state
2204 ps = {}; // Reset
2205 ps.viewport = { float(theViewport.x()), float(theViewport.y()), float(theViewport.width()), float(theViewport.height()), 0.0f, 1.0f };
2206 if (layer.scissorRect.isValid()) {
2207 ps.flags |= QSSGRhiGraphicsPipelineState::Flag::UsesScissor;
2208 ps.scissor = { layer.scissorRect.x(),
2209 theViewport.height() - (layer.scissorRect.y() + layer.scissorRect.height()),
2210 layer.scissorRect.width(),
2211 layer.scissorRect.height() };
2212 }
2213
2214 ps.depthFunc = QRhiGraphicsPipeline::LessOrEqual;
2215 ps.flags.setFlag(QSSGRhiGraphicsPipelineState::Flag::BlendEnabled, false);
2216
2217 // Enable Wireframe mode
2218 ps.polygonMode = layer.wireframeMode ? QRhiGraphicsPipeline::Line : QRhiGraphicsPipeline::Fill;
2219
2220 bool wasDirty = false;
2221 bool wasDataDirty = false;
2222 wasDirty = layer.isDirty();
2223
2224 const bool shouldDisableInternalPasses = (layer.renderOverrides & size_t(QSSGRenderLayer::RenderOverrides::DisableInternalPasses)) != 0;
2225 wasDirty |= (shouldDisableInternalPasses != disableMainPasses);
2226 disableMainPasses = shouldDisableInternalPasses;
2227
2228 // NOTE: Prep-state is implicitly set to "DataPrep"
2229 layerPrepResult = { theViewport, layer };
2230
2231 // SSAO
2232 const bool SSAOEnabled = layer.ssaoEnabled();
2233 layerPrepResult.flags.setRequiresSsaoPass(SSAOEnabled);
2234 features.set(QSSGShaderFeatures::Feature::Ssao, SSAOEnabled);
2235
2236 // Effects
2237 bool requiresDepthTexture = SSAOEnabled;
2238 bool requiresNormalTexture = false;
2239 bool requiresMotionVectorTexture = false;
2240 for (QSSGRenderEffect *theEffect = layer.firstEffect; theEffect; theEffect = theEffect->m_nextEffect) {
2241 if (theEffect->isDirty()) {
2242 wasDirty = true;
2243 theEffect->clearDirty();
2244 }
2245 if (theEffect->testFlag(QSSGRenderEffect::Flags::UsesDepthTexture))
2246 requiresDepthTexture = true;
2247 if (theEffect->testFlag(QSSGRenderEffect::Flags::UsesNormalTexture))
2248 requiresNormalTexture = true;
2249 if (theEffect->testFlag(QSSGRenderEffect::Flags::UsesMotionVectorTexture))
2250 requiresMotionVectorTexture = true;
2251 }
2252
2253 const auto &rhiCtx = renderer->contextInterface()->rhiContext();
2254 orderIndependentTransparencyEnabled = (layer.oitMethod != QSSGRenderLayer::OITMethod::None);
2255 if (layer.oitMethod == QSSGRenderLayer::OITMethod::WeightedBlended) {
2256 orderIndependentTransparencyEnabled = rhiCtx->rhi()->isFeatureSupported(QRhi::PerRenderTargetBlending);
2257 if (rhiCtx->mainPassSampleCount() > 1)
2258 orderIndependentTransparencyEnabled |= rhiCtx->rhi()->isFeatureSupported(QRhi::TexelFetch) && rhiCtx->rhi()->isFeatureSupported(QRhi::SampleVariables);
2259 if (!orderIndependentTransparencyEnabled && !oitWarningUnsupportedShown) {
2260 qCWarning(lcQuick3DRender) << "WeightedBlended OIT is requested, but it is not supported.";
2261 oitWarningUnsupportedShown = true;
2262 }
2263 } else if (layer.oitMethod == QSSGRenderLayer::OITMethod::LinkedList) {
2264 if (rhiCtx->mainPassSampleCount() > 1)
2265 orderIndependentTransparencyEnabled |= rhiCtx->rhi()->isFeatureSupported(QRhi::SampleVariables);
2266 if (!orderIndependentTransparencyEnabled && !oitWarningUnsupportedShown) {
2267 qCWarning(lcQuick3DRender) << "LinkedList OIT is requested, but it is not supported.";
2268 oitWarningUnsupportedShown = true;
2269 }
2270 }
2271 if (layer.oitMethodDirty) {
2272 oitRenderContext.reset();
2273 for (auto &renderResult : renderResults)
2274 renderResult.reset();
2275 }
2276
2277 layerPrepResult.flags.setRequiresDepthTexture(requiresDepthTexture);
2278
2279 layerPrepResult.flags.setRequiresNormalTexture(requiresNormalTexture);
2280
2281 layerPrepResult.flags.setRequiresMotionVectorPass(requiresMotionVectorTexture);
2282
2283 // Tonemapping. Except when there are effects, then it is up to the
2284 // last pass of the last effect to perform tonemapping.
2285 if (!layer.firstEffect)
2286 QSSGLayerRenderData::setTonemapFeatures(features, layer.tonemapMode);
2287
2288 // We may not be able to have an array of 15 light struct elements in
2289 // the shaders. Switch on the reduced-max-number-of-lights feature
2290 // if necessary. In practice this is relevant with OpenGL ES 3.0 or
2291 // 2.0, because there are still implementations in use that only
2292 // support the spec mandated minimum of 224 vec4s (so 3584 bytes).
2293 if (rhiCtx->rhi()->resourceLimit(QRhi::MaxUniformBufferRange) < REDUCED_MAX_LIGHT_COUNT_THRESHOLD_BYTES) {
2294 features.set(QSSGShaderFeatures::Feature::ReduceMaxNumLights, true);
2295 static bool notified = false;
2296 if (!notified) {
2297 notified = true;
2298 qCDebug(lcQuick3DRender, "Qt Quick 3D maximum number of lights has been reduced from %d to %d due to the graphics driver's limitations",
2299 QSSG_MAX_NUM_LIGHTS, QSSG_REDUCED_MAX_NUM_LIGHTS);
2300 }
2301 }
2302
2303 // IBL Lightprobe Image
2304 QSSGRenderImageTexture lightProbeTexture;
2305 if (layer.lightProbe) {
2306 const auto &lightProbeSettings = layer.lightProbeSettings;
2307 if (layer.lightProbe->m_format == QSSGRenderTextureFormat::Unknown) {
2308 // Choose on a format that makes sense for a light probe
2309 // At this point it's just a suggestion
2310 if (renderer->contextInterface()->rhiContext()->rhi()->isTextureFormatSupported(QRhiTexture::RGBA16F))
2311 layer.lightProbe->m_format = QSSGRenderTextureFormat::RGBA16F;
2312 else
2313 layer.lightProbe->m_format = QSSGRenderTextureFormat::RGBE8;
2314 }
2315
2316 if (layer.lightProbe->clearDirty())
2317 wasDataDirty = true;
2318
2319 // NOTE: This call can lead to rendering (of envmap) and a texture upload
2320 lightProbeTexture = renderer->contextInterface()->bufferManager()->loadRenderImage(layer.lightProbe, QSSGBufferManager::MipModeBsdf);
2321 if (lightProbeTexture.m_texture) {
2322
2323 features.set(QSSGShaderFeatures::Feature::LightProbe, true);
2324 features.set(QSSGShaderFeatures::Feature::IblOrientation, !lightProbeSettings.probeOrientation.isIdentity());
2325
2326 // By this point we will know what the actual texture format of the light probe is
2327 // Check if using RGBE format light probe texture (the Rhi format will be RGBA8)
2328 if (lightProbeTexture.m_flags.isRgbe8())
2329 features.set(QSSGShaderFeatures::Feature::RGBELightProbe, true);
2330 } else {
2331 layer.lightProbe = nullptr;
2332 }
2333
2334 const bool forceIblExposureValues = (features.isSet(QSSGShaderFeatures::Feature::LightProbe) && layer.tonemapMode == QSSGRenderLayer::TonemapMode::Custom);
2335 features.set(QSSGShaderFeatures::Feature::ForceIblExposure, forceIblExposureValues);
2336 }
2337
2338 frameData.m_ctx->bufferManager()->setLightmapSource(layer.lightmapSource);
2339
2340 // Update the node data version for this layer.
2341 // This version should only change if the world tree was re-indexed.
2342 version = nodeData->version();
2343
2344 // We're using/updating the node data directly.
2345 // NOTE: These are the transforms and opacities for all nodes for all layers/views.
2346 // We'll just use or update the ones the one for this layer.
2347 auto &globalTransforms = nodeData->globalTransforms;
2348 auto &globalOpacities = nodeData->globalOpacities;
2349 auto &instanceTransforms = nodeData->instanceTransforms;
2350
2351 ///////// 2 - START LAYER
2352 QSSGRenderDataHelpers::GlobalStateResultT globalStateResult = QSSGRenderDataHelpers::GlobalStateResult::None;
2353
2354 const bool layerTreeWasDirty = layer.isDirty(QSSGRenderLayer::DirtyFlag::TreeDirty);
2355 layer.clearDirty(QSSGRenderLayer::DirtyFlag::TreeDirty);
2356 if (layerTreeWasDirty) {
2357 wasDataDirty = true;
2358 layerNodes = nodeData->getLayerNodeView(layer);
2359 } else {
2360 for (auto &node : layerNodes)
2361 globalStateResult |= QSSGRenderDataHelpers::updateGlobalNodeState(node, version);
2362
2363 bool transformAndOpacityDirty = false;
2364 for (auto &node : layerNodes)
2365 transformAndOpacityDirty |= QSSGRenderDataHelpers::calcGlobalNodeData<QSSGRenderDataHelpers::Strategy::Update>(node, version, globalTransforms, globalOpacities);
2366
2367 // FIXME: We shouldn't need to re-create all the instance transforms even when instancing isn't used...
2368 if (transformAndOpacityDirty) {
2369 for (const auto &node : layerNodes)
2370 wasDataDirty |= QSSGRenderDataHelpers::calcInstanceTransforms(node, version, globalTransforms, instanceTransforms);
2371 }
2372
2373 wasDataDirty |= transformAndOpacityDirty;
2374 }
2375
2376 // Check if we have an explicit camera!
2377 // NOTE: We only do layering if we have an explicit camera!!!
2378
2379 const bool hasExplicitCamera = (layer.explicitCameras.size() != 0);
2380 bool cameraLayerMaskDirty = false;
2381 quint32 layerMask = QSSGRenderCamera::LayerMaskAll;
2382 if (hasExplicitCamera) {
2383 QSSGRenderCamera *explicitCamera = layer.explicitCameras[0];
2384 layerMask = explicitCamera->tag.value();
2385 cameraLayerMaskDirty = explicitCamera->isDirty(QSSGRenderCamera::DirtyFlag::LayerMaskDirty);
2386 explicitCamera->clearDirty(QSSGRenderCamera::DirtyFlag::LayerMaskDirty);
2387 }
2388
2389 const bool restatNodes = (layerTreeWasDirty || (globalStateResult & QSSGRenderDataHelpers::GlobalStateResult::ActiveChanged) || cameraLayerMaskDirty);
2390
2391 if (restatNodes) {
2392 modelsView.clear();
2393 particlesView.clear();
2394 item2DsView.clear();
2395 camerasView.clear();
2396 lightsView.clear();
2397 reflectionProbesView.clear();
2398 nonCategorizedView.clear();
2399
2400 enum NodeType : size_t { Model = 0, Particles, Item2D, Camera, ImportedCamera, Light, ReflectionProbe, Other, Inactive };
2401 const auto nodeType = [layerMask](QSSGRenderNode *node) -> NodeType {
2402 if (!(node->getGlobalState(QSSGRenderNode::GlobalState::Active) && (node->tag.isSet(layerMask))))
2403 return NodeType::Inactive;
2404 switch (node->type) {
2405 case QSSGRenderGraphObject::Type::Model: return NodeType::Model;
2406 case QSSGRenderGraphObject::Type::Particles: return NodeType::Particles;
2407 case QSSGRenderGraphObject::Type::Item2D: return NodeType::Item2D;
2408 case QSSGRenderGraphObject::Type::ReflectionProbe: return NodeType::ReflectionProbe;
2409 default: break;
2410 }
2411
2412 if (QSSGRenderGraphObject::isCamera(node->type)) {
2413 // NOTE: To keep compatibility with old code, we collect shared and non-shared cameras differently,
2414 // so that shared cameras (import scene) are picked after non-shared ones.
2415 const bool isImported = node->getGlobalState(QSSGRenderNode::GlobalState::Imported);
2416 constexpr NodeType cameraTypes[2] { NodeType::Camera, NodeType::ImportedCamera };
2417 return cameraTypes[size_t(isImported)];
2418 }
2419 if (QSSGRenderGraphObject::isLight(node->type))
2420 return NodeType::Light;
2421
2422 return NodeType::Other;
2423 };
2424 // sort nodes by type - We could do this on insert, but it's not given that it would be beneficial.
2425 // Depending on how we want to handle the nodes later it might just not give us anything
2426 // so, keep it simple for now.
2427 // We could also speed up this by having the pointer and the type in the same struct and sort without
2428 // indirection. However, that' slightly less convenient and the idea here is that we don't process
2429 // this list unless things change, which is not something that should happen often if the user is
2430 // concerned about performance, as it means we need to reevaluate the whole scene anyway.
2431 {
2432 // Sort the nodes by type (we copy the pointers to avoid sorting the original list,
2433 // which is stored based on the nodes' order in the world tree).
2434 layerNodesCategorized = { layerNodes.begin(), layerNodes.end() };
2435 // NOTE: Due to the ordering of item2ds, we need to use stable_sort.
2436 std::stable_sort(layerNodesCategorized.begin(), layerNodesCategorized.end(), [nodeType](QSSGRenderNode *a, QSSGRenderNode *b) {
2437 return nodeType(a) < nodeType(b);
2438 });
2439 }
2440
2441 // Group nodes by type inline and keep track of the individual parts using QSSGDataViews
2442 const LayerNodeStatResult stat = statLayerNodes(layerNodesCategorized, layerMask);
2443
2444 // Go through the sorted nodes and create the views
2445 size_t next = 0;
2446
2447 if (stat.modelCount > 0) {
2448 modelsView = QSSGModelsView((QSSGRenderModel **)(layerNodesCategorized.data() + next), stat.modelCount);
2449 next = modelsView.size();
2450 }
2451 if (stat.particlesCount > 0) {
2452 particlesView = QSSGParticlesView((QSSGRenderParticles **)(layerNodesCategorized.data() + next), stat.particlesCount);
2453 next += particlesView.size();
2454 }
2455 if (stat.item2DCount > 0) {
2456 item2DsView = QSSGItem2DsView((QSSGRenderItem2D **)(layerNodesCategorized.data() + next), stat.item2DCount);
2457 next += item2DsView.size();
2458 }
2459 if (stat.cameraCount > 0) {
2460 camerasView = QSSGCamerasView((QSSGRenderCamera **)(layerNodesCategorized.data() + next), stat.cameraCount);
2461 next += camerasView.size();
2462 }
2463 if (stat.lightCount > 0) {
2464 lightsView = QSSGLightsView((QSSGRenderLight **)(layerNodesCategorized.data() + next), stat.lightCount);
2465 next += lightsView.size();
2466 }
2467 if (stat.reflectionProbeCount > 0) {
2468 reflectionProbesView = QSSGReflectionProbesView((QSSGRenderReflectionProbe **)(layerNodesCategorized.data() + next), stat.reflectionProbeCount);
2469 next += reflectionProbesView.size();
2470 }
2471 if (stat.otherCount > 0) {
2472 nonCategorizedView = QSSGNonCategorizedView((QSSGRenderNode **)(layerNodesCategorized.data() + next), stat.otherCount);
2473 next += nonCategorizedView.size();
2474 (void)next;
2475 }
2476
2477 // FIXME: Compatability with old code (Will remove later).
2478 // NOTE: see resetForFrame() as well for extensions usage
2479 renderableModels.clear();
2480 renderableParticles.clear();
2481 renderableModels.reserve(modelsView.size());
2482 renderableParticles.reserve(particlesView.size());
2483
2484 renderableModels = {modelsView.begin(), modelsView.end()};
2485 renderableParticles = {particlesView.begin(), particlesView.end()};
2486 }
2487
2488 // Cameras
2489 // 1. If there's an explicit camera set and it's active (visible) we'll use that.
2490 // 2. ... if the explicitly set camera is not visible, no further attempts will be done.
2491 // 3. If no explicit camera is set, we'll search and pick the first active camera.
2492 QSSGRenderCamera::Configuration cameraConfig { renderer->dpr(), layer.isSsaaEnabled() ? layer.ssaaMultiplier : 1.0f };
2493 renderedCameras.clear();
2494 if (!layer.explicitCameras.isEmpty()) {
2495 for (QSSGRenderCamera *cam : std::as_const(layer.explicitCameras)) {
2496 // 1.
2497 if (cam->getGlobalState(QSSGRenderCamera::GlobalState::Active)) {
2498 const bool computeFrustumSucceeded = cam->calculateProjection(theViewport, cameraConfig);
2499 if (Q_LIKELY(computeFrustumSucceeded))
2500 renderedCameras.append(cam);
2501 else
2502 qCCritical(INTERNAL_ERROR, "Failed to calculate camera frustum");
2503 }
2504 }
2505 // 2.
2506 } else if (QSSG_GUARD_X(layer.viewCount == 1, "Multiview rendering requires explicit cameras to be set!.")) {
2507 // NOTE: This path can never be hit with multiview, hence the guard.
2508 // (Multiview will always have explicit cameras set.)
2509
2510 // 3.
2511 for (auto iter = camerasView.begin(); renderedCameras.isEmpty() && iter != camerasView.end(); iter++) {
2512 QSSGRenderCamera *theCamera = *iter;
2513 if (theCamera->getGlobalState(QSSGRenderCamera::GlobalState::Active)) {
2514 const bool computeFrustumSucceeded = theCamera->calculateProjection(theViewport, cameraConfig);
2515 if (Q_LIKELY(computeFrustumSucceeded))
2516 renderedCameras.append(theCamera);
2517 else
2518 qCCritical(INTERNAL_ERROR, "Failed to calculate camera frustum");
2519 }
2520 }
2521 }
2522
2523 float meshLodThreshold = 1.0f;
2524 if (!renderedCameras.isEmpty())
2525 meshLodThreshold = renderedCameras[0]->levelOfDetailPixelThreshold / theViewport.width();
2526
2527 layer.renderedCamerasMutex.lock();
2528 layer.renderedCameras = renderedCameras;
2529 layer.renderedCamerasMutex.unlock();
2530
2531 // Meshes, materials, MVP, and normal matrices for the models
2532 const QSSGRenderCameraDataList &renderCameraData = getCachedCameraDatas();
2533 modelData->updateModelData(modelsView, renderer, renderCameraData);
2534
2535 // Item2Ds
2536 item2DData->updateItem2DData(item2DsView, renderer, renderCameraData);
2537
2538 // ResourceLoaders
2539 prepareResourceLoaders();
2540
2541 // Skeletons
2542 updateDirtySkeletons(*this, modelsView);
2543
2544 // Lights
2545 int directionalLightsCount = 0;
2546 int positionalLightsCount = 0;
2547 const int maxLightCount = effectiveMaxLightCount(features);
2548 const int maxDirectionalLights = effectiveMaxDirectionalLightCount(features);
2549 QSSGShaderLightList renderableLights;
2550 int shadowMapCount = 0;
2551 bool hasScopedLights = false;
2552
2553 // Determine which lights will actually Render
2554 // Determine how many lights will need shadow maps
2555 // NOTE: This culling is specific to our Forward renderer
2556 {
2557 auto it = std::make_reverse_iterator(lightsView.end());
2558 const auto end = it + qMin(maxLightCount, lightsView.size());
2559 for (; it != end; ++it) {
2560 QSSGRenderLight *renderLight = (*it);
2561 QMatrix4x4 renderLightTransform = getGlobalTransform(*renderLight);
2562 if (renderLight->type == QSSGRenderGraphObject::Type::DirectionalLight)
2563 directionalLightsCount++;
2564 else
2565 positionalLightsCount++;
2566
2567 if (positionalLightsCount > maxLightCount)
2568 continue;
2569 if (directionalLightsCount > maxDirectionalLights)
2570 continue;
2571
2572
2573 hasScopedLights |= (renderLight->m_scope != nullptr);
2574 const bool castShadows = renderLight->m_castShadow && !renderLight->m_fullyBaked;
2575 shadowMapCount += int(castShadows);
2576 const auto &direction = renderLight->getScalingCorrectDirection(renderLightTransform);
2577 renderableLights.push_back(QSSGShaderLight{ renderLight, castShadows, direction });
2578 }
2579 }
2580
2581 const bool showLightCountWarning = !tooManyLightsWarningShown && (positionalLightsCount > maxLightCount);
2582 if (showLightCountWarning) {
2583 qWarning("Too many lights in scene, maximum is %d", maxLightCount);
2584 tooManyLightsWarningShown = true;
2585 }
2586
2587 const bool showDirectionalLightCountWarning = !tooManyDirectionalLightsWarningShown && (directionalLightsCount > maxDirectionalLights);
2588 if (showDirectionalLightCountWarning) {
2589 qWarning("Too many directional lights in scene, maximum is %d", maxDirectionalLights);
2590 tooManyDirectionalLightsWarningShown = true;
2591 }
2592
2593 if (shadowMapCount > 0) { // Setup Shadow Maps Entries for Lights casting shadows
2594 requestShadowMapManager(); // Ensure we have a shadow map manager
2595 layerPrepResult.flags.setRequiresShadowMapPass(true);
2596 // Any light with castShadow=true triggers shadow mapping
2597 // in the generated shaders. The fact that some (or even
2598 // all) objects may opt out from receiving shadows plays no
2599 // role here whatsoever.
2600 features.set(QSSGShaderFeatures::Feature::Ssm, true);
2601 shadowMapManager->addShadowMaps(renderableLights);
2602 } else if (shadowMapManager) {
2603 // No shadows but a shadow manager so clear old resources
2604 shadowMapManager->releaseCachedResources();
2605 }
2606
2607 // Give each renderable a copy of the lights available
2608 // Also setup scoping for scoped lights
2609
2610 QSSG_ASSERT(globalLights.isEmpty(), globalLights.clear());
2611 if (hasScopedLights) { // Filter out scoped lights from the global lights list
2612 for (const auto &shaderLight : std::as_const(renderableLights)) {
2613 if (!shaderLight.light->m_scope)
2614 globalLights.push_back(shaderLight);
2615 }
2616
2617 const auto prepareLightsWithScopedLights = [&renderableLights, this](QVector<QSSGRenderableNodeEntry> &renderableNodes) {
2618 for (qint32 idx = 0, end = renderableNodes.size(); idx < end; ++idx) {
2619 QSSGRenderableNodeEntry &theNodeEntry(renderableNodes[idx]);
2620 QSSGShaderLightList filteredLights;
2621 for (const auto &light : std::as_const(renderableLights)) {
2622 if (light.light->m_scope && !scopeLight(theNodeEntry.node, light.light->m_scope))
2623 continue;
2624 filteredLights.push_back(light);
2625 }
2626
2627 if (filteredLights.isEmpty()) { // Node without scoped lights, just reference the global light list.
2628 theNodeEntry.lights = QSSGDataView(globalLights);
2629 } else {
2630 // This node has scoped lights, i.e., it's lights differ from the global list
2631 // we therefore create a bespoke light list for it. Technically this might be the same for
2632 // more then this one node, but the overhead for tracking that is not worth it.
2633 auto customLightList = RENDER_FRAME_NEW_BUFFER<QSSGShaderLight>(*renderer->contextInterface(), filteredLights.size());
2634 std::copy(filteredLights.cbegin(), filteredLights.cend(), customLightList.begin());
2635 theNodeEntry.lights = customLightList;
2636 }
2637 }
2638 };
2639
2640 prepareLightsWithScopedLights(renderableModels);
2641 prepareLightsWithScopedLights(renderableParticles);
2642 } else { // Just a simple copy
2643 globalLights = renderableLights;
2644 // No scoped lights, all nodes can just reference the global light list.
2645 const auto prepareLights = [this](QVector<QSSGRenderableNodeEntry> &renderableNodes) {
2646 for (qint32 idx = 0, end = renderableNodes.size(); idx < end; ++idx) {
2647 QSSGRenderableNodeEntry &theNodeEntry(renderableNodes[idx]);
2648 theNodeEntry.lights = QSSGDataView(globalLights);
2649 }
2650 };
2651
2652 prepareLights(renderableModels);
2653 prepareLights(renderableParticles);
2654 }
2655
2656 {
2657 // Give user provided passes a chance to modify the renderable data before starting
2658 // Note: All non-active extensions should be filtered out by now
2659 Q_STATIC_ASSERT(USERPASSES == size_t(QSSGRenderLayer::RenderExtensionStage::Count));
2660 for (size_t i = 0; i != size_t(QSSGRenderLayer::RenderExtensionStage::Count); ++i) {
2661 const auto &renderExtensions = layer.renderExtensions[i];
2662 auto &userPass = userPasses[i];
2663 for (auto rit = renderExtensions.crbegin(), rend = renderExtensions.crend(); rit != rend; ++rit) {
2664 if ((*rit)->prepareData(frameData)) {
2665 wasDirty |= true;
2666 userPass.extensions.push_back(*rit);
2667 }
2668 }
2669 }
2670 }
2671
2672 // User render passes
2673 {
2674 const auto &userRenderPassManager = requestUserRenderPassManager();
2675 userRenderPassManager->updateUserPassOrder(layerTreeWasDirty); // NOTE: If the tree was dirty we need to force an update.
2676 userRenderPasses.userPasses = userRenderPassManager->scheduledUserPasses();
2677 }
2678
2679 auto &opaqueObjects = opaqueObjectStore[0];
2680 auto &transparentObjects = transparentObjectStore[0];
2681 auto &screenTextureObjects = screenTextureObjectStore[0];
2682
2683 if (!renderedCameras.isEmpty()) { // NOTE: We shouldn't really get this far without a camera...
2684 wasDirty |= prepareModelsForRender(*renderer->contextInterface(), renderableModels, layerPrepResult.flags, renderedCameras, getCachedCameraDatas(), modelContexts, opaqueObjects, transparentObjects, screenTextureObjects, meshLodThreshold);
2685 if (particlesEnabled) {
2686 const auto &cameraDatas = getCachedCameraDatas();
2687 wasDirty |= prepareParticlesForRender(renderableParticles, cameraDatas[0], layerPrepResult.flags);
2688 }
2689 // If there's item2Ds we set wasDirty.
2690 wasDirty |= (item2DsView.size() != 0);
2691 }
2692 if (orderIndependentTransparencyEnabled) {
2693 // OIT blending mode must be SourceOver and have transparent objects
2694 if (transparentObjects.size() > 0 && !layerPrepResult.flags.hasCustomBlendMode()) {
2695 if (layer.oitMethod == QSSGRenderLayer::OITMethod::WeightedBlended) {
2696 if (rhiCtx->mainPassSampleCount() > 1)
2697 layerPrepResult.flags.setRequiresDepthTextureMS(true);
2698 else
2699 layerPrepResult.flags.setRequiresDepthTexture(true);
2700 }
2701 } else {
2702 orderIndependentTransparencyEnabled = false;
2703 if (!oitWarningInvalidBlendModeShown) {
2704 qCWarning(lcQuick3DRender) << "Order Independent Transparency requested, but disabled due to invalid blend modes.";
2705 qCWarning(lcQuick3DRender) << "Use SourceOver blend mode for Order Independent Transparency.";
2706 oitWarningInvalidBlendModeShown = true;
2707 }
2708 }
2709 }
2710 layer.oitMethodDirty = false;
2711
2712 prepareReflectionProbesForRender();
2713
2714 wasDirty = wasDirty || wasDataDirty;
2715 layerPrepResult.flags.setWasDirty(wasDirty);
2716 layerPrepResult.flags.setLayerDataDirty(wasDataDirty);
2717
2718 //
2719 const bool animating = wasDirty;
2720 if (animating)
2721 layer.progAAPassIndex = 0;
2722
2723 const bool progressiveAA = layer.isProgressiveAAEnabled() && !animating;
2724 layer.progressiveAAIsActive = progressiveAA;
2725 const bool temporalAA = layer.isTemporalAAEnabled() && !progressiveAA;
2726
2727 layer.temporalAAIsActive = temporalAA;
2728
2729 QVector2D vertexOffsetsAA;
2730
2731 if (progressiveAA && layer.progAAPassIndex > 0 && layer.progAAPassIndex < quint32(layer.antialiasingQuality)) {
2732 int idx = layer.progAAPassIndex - 1;
2733 vertexOffsetsAA = s_ProgressiveAAVertexOffsets[idx] / QVector2D{ float(theViewport.width()/2.0), float(theViewport.height()/2.0) };
2734 }
2735
2736 if (temporalAA) {
2737 if (layer.temporalAAMode == QSSGRenderLayer::TAAMode::MotionVector && layer.tempAAPassIndex > 0) {
2738 if (layer.tempAAPassIndex >= quint32(MAX_AA_LEVELS) + 1)
2739 layer.tempAAPassIndex = 1;
2740 int idx = layer.tempAAPassIndex - 1;
2741 vertexOffsetsAA = s_ProgressiveAAVertexOffsets[idx] / QVector2D{ float(theViewport.width()/2.0), float(theViewport.height()/2.0) };
2742 layer.currentAndLastJitter = QVector4D(vertexOffsetsAA, layer.currentAndLastJitter.x(), layer.currentAndLastJitter.y());
2743 layerPrepResult.flags.setRequiresMotionVectorPass(true);
2744 layerPrepResult.flags.setRequiresDepthTexture(true);
2745 }else {
2746 const int t = 1 - 2 * (layer.tempAAPassIndex % 2);
2747 const float f = t * layer.temporalAAStrength;
2748 vertexOffsetsAA = { f / float(theViewport.width()/2.0), f / float(theViewport.height()/2.0) };
2749 }
2750 }
2751
2752 if (!renderedCameras.isEmpty()) {
2753 if (temporalAA || progressiveAA /*&& !vertexOffsetsAA.isNull()*/) {
2754 QMatrix4x4 offsetProjection = renderedCameras[0]->projection;
2755 QMatrix4x4 invProjection = renderedCameras[0]->projection.inverted();
2756 if (renderedCameras[0]->type == QSSGRenderCamera::Type::OrthographicCamera) {
2757 offsetProjection(0, 3) -= vertexOffsetsAA.x();
2758 offsetProjection(1, 3) -= vertexOffsetsAA.y();
2759 } else if (renderedCameras[0]->type == QSSGRenderCamera::Type::PerspectiveCamera) {
2760 offsetProjection(0, 2) += vertexOffsetsAA.x();
2761 offsetProjection(1, 2) += vertexOffsetsAA.y();
2762 }
2763 for (auto &modelContext : std::as_const(modelContexts)) {
2764 for (int mvpIdx = 0; mvpIdx < renderedCameras.count(); ++mvpIdx)
2765 modelContext->modelViewProjections[mvpIdx] = offsetProjection * invProjection * modelContext->modelViewProjections[mvpIdx];
2766 }
2767 }
2768 }
2769
2770 const bool hasItem2Ds = (item2DsView.size() > 0);
2771 const bool layerEnableDepthTest = layer.layerFlags.testFlag(QSSGRenderLayer::LayerFlag::EnableDepthTest);
2772 const bool layerEnabledDepthPrePass = layer.layerFlags.testFlag(QSSGRenderLayer::LayerFlag::EnableDepthPrePass);
2773 const bool depthTestEnableDefault = layerEnableDepthTest && (!opaqueObjects.isEmpty() || depthPrepassObjectsState || hasDepthWriteObjects);
2774 const bool zPrePassForced = (depthPrepassObjectsState != 0);
2775 zPrePassActive = zPrePassForced || (layerEnabledDepthPrePass && layerEnableDepthTest && (hasDepthWriteObjects || hasItem2Ds));
2776 const bool depthWriteEnableDefault = depthTestEnableDefault && (!layerEnabledDepthPrePass || !zPrePassActive);
2777
2778 ps.flags.setFlag(QSSGRhiGraphicsPipelineState::Flag::DepthTestEnabled, depthTestEnableDefault);
2779 ps.flags.setFlag(QSSGRhiGraphicsPipelineState::Flag::DepthWriteEnabled, depthWriteEnableDefault);
2780
2781
2782 // All data prep is done, from now on the layer content shouldn't change.
2783 layerPrepResult.setState(QSSGLayerRenderPreparationResult::State::Done);
2784
2785 // Prepare passes
2786 QSSG_ASSERT(activePasses.isEmpty(), activePasses.clear());
2787 // If needed, generate a depth texture with the opaque objects. This
2788 // and normal and the SSAO texture must come first since other passes may want to
2789 // expose these textures to their shaders.
2790 if (layerPrepResult.flags.requiresDepthTexture())
2791 activePasses.push_back(&depthMapPass);
2792 if (layerPrepResult.flags.requiresDepthTextureMS())
2793 activePasses.push_back(&depthMapPassMS);
2794
2795 if (layerPrepResult.flags.requiresNormalTexture())
2796 activePasses.push_back(&normalPass);
2797
2798 // Screen space ambient occlusion. Relies on the depth texture and generates an AO map.
2799 if (layerPrepResult.flags.requiresSsaoPass())
2800 activePasses.push_back(&ssaoMapPass);
2801
2802 // Shadows. Generates a 2D or cube shadow map. (opaque + pre-pass transparent objects)
2803 if (layerPrepResult.flags.requiresShadowMapPass())
2804 activePasses.push_back(&shadowMapPass);
2805
2806 if (zPrePassActive && !disableMainPasses)
2807 activePasses.push_back(&zPrePassPass);
2808
2809 // Screen texture with opaque objects.
2810 if (layerPrepResult.flags.requiresScreenTexture())
2811 activePasses.push_back(&screenMapPass);
2812
2813 // Reflection pass
2814 activePasses.push_back(&reflectionMapPass);
2815
2816 auto &textureExtensionPass = userPasses[size_t(QSSGRenderLayer::RenderExtensionStage::TextureProviders)];
2817 if (textureExtensionPass.hasData())
2818 activePasses.push_back(&textureExtensionPass);
2819
2820 auto &underlayPass = userPasses[size_t(QSSGRenderLayer::RenderExtensionStage::Underlay)];
2821 if (underlayPass.hasData())
2822 activePasses.push_back(&underlayPass);
2823
2824 // Generated User render passes (QML)
2825 if (userRenderPasses.hasData())
2826 activePasses.push_back(&userRenderPasses);
2827
2828 const bool hasOpaqueObjects = (opaqueObjects.size() > 0);
2829
2830 if (hasOpaqueObjects && !disableMainPasses)
2831 activePasses.push_back(&opaquePass);
2832
2833 // MotionVector Pass
2834 if (layerPrepResult.flags.requiresMotionVectorPass())
2835 activePasses.push_back(&motionVectorMapPass);
2836
2837 // NOTE: When the a screen texture is used, the skybox pass will be called twice. First from
2838 // the screen texture pass and later as part of the normal run through the list.
2839 if (renderer->contextInterface()->rhiContext()->rhi()->isFeatureSupported(QRhi::TexelFetch)) {
2840 if (layer.background == QSSGRenderLayer::Background::SkyBoxCubeMap && layer.skyBoxCubeMap && !disableMainPasses)
2841 activePasses.push_back(&skyboxCubeMapPass);
2842 else if (layer.background == QSSGRenderLayer::Background::SkyBox && layer.lightProbe && !disableMainPasses)
2843 activePasses.push_back(&skyboxPass);
2844 }
2845
2846 if (hasItem2Ds && !disableMainPasses)
2847 activePasses.push_back(&item2DPass);
2848
2849 if (layerPrepResult.flags.requiresScreenTexture())
2850 activePasses.push_back(&reflectionPass);
2851
2852 // Note: Transparent pass includeds opaque objects when layerEnableDepthTest is false.
2853 if ((transparentObjects.size() > 0 || (!layerEnableDepthTest && hasOpaqueObjects)) && !disableMainPasses) {
2854 if (orderIndependentTransparencyEnabled) {
2855 activePasses.push_back(&oitRenderPass);
2856 activePasses.push_back(&oitCompositePass);
2857 oitRenderPass.setMethod(layer.oitMethod);
2858 oitCompositePass.setMethod(layer.oitMethod);
2859 } else {
2860 activePasses.push_back(&transparentPass);
2861 }
2862 }
2863
2864 auto &overlayPass = userPasses[size_t(QSSGRenderLayer::RenderExtensionStage::Overlay)];
2865 if (overlayPass.hasData())
2866 activePasses.push_back(&overlayPass);
2867
2868 if (layer.gridEnabled)
2869 activePasses.push_back(&infiniteGridPass);
2870
2871 if (const auto &dbgDrawSystem = renderer->contextInterface()->debugDrawSystem(); dbgDrawSystem && dbgDrawSystem->isEnabled() && !disableMainPasses)
2872 activePasses.push_back(&debugDrawPass);
2873}
2874
2875template<typename T>
2876static void clearTable(std::vector<T> &entry)
2877{
2878 for (auto &e : entry)
2879 e.clear();
2880}
2881
2882void QSSGLayerRenderData::resetForFrame()
2883{
2884 for (const auto &pass : activePasses)
2885 pass->resetForFrame();
2886 activePasses.clear();
2887 bakedLightingModels.clear();
2888 layerPrepResult = {};
2889 renderedCameraData.reset();
2890 renderedItem2Ds.clear();
2891 renderedBakedLightingModels.clear();
2892 lightmapTextures.clear();
2893 bonemapTextures.clear();
2894 globalLights.clear();
2895 modelContexts.clear();
2896 features = QSSGShaderFeatures();
2897 hasDepthWriteObjects = false;
2898 depthPrepassObjectsState = { DepthPrepassObjectStateT(DepthPrepassObject::None) };
2899 zPrePassActive = false;
2900 savedRenderState.reset();
2901
2902 clearTable(renderableModelStore);
2903 clearTable(modelContextStore);
2904 clearTable(renderableObjectStore);
2905 clearTable(opaqueObjectStore);
2906 clearTable(transparentObjectStore);
2907 clearTable(screenTextureObjectStore);
2908
2909 clearTable(sortedOpaqueObjectCache);
2910 clearTable(sortedTransparentObjectCache);
2911 clearTable(sortedScreenTextureObjectCache);
2912 clearTable(sortedOpaqueDepthPrepassCache);
2913 clearTable(sortedDepthWriteCache);
2914
2915 // Until we have a better solution for extensions...
2916 if (renderablesModifiedByExtension) {
2917 renderableModels.clear();
2918 renderableParticles.clear();
2919 renderableModels.reserve(modelsView.size());
2920 renderableParticles.reserve(particlesView.size());
2921
2922 renderableModels = {modelsView.begin(), modelsView.end()};
2923 renderableParticles = {particlesView.begin(), particlesView.end()};
2924
2925 renderablesModifiedByExtension = false;
2926 }
2927
2928 if (userRenderPassManager)
2929 userRenderPassManager->resetForFrame();
2930}
2931
2932QSSGLayerRenderPreparationResult::QSSGLayerRenderPreparationResult(const QRectF &inViewport, QSSGRenderLayer &inLayer)
2933 : layer(&inLayer)
2934 , m_state(State::DataPrep)
2935{
2936 viewport = inViewport;
2937}
2938
2940{
2941 return viewport.height() >= 2.0f && viewport.width() >= 2.0f;
2942}
2943
2945{
2946 const auto size = viewport.size().toSize();
2947 return QSize(QSSGRendererUtil::nextMultipleOf4(size.width()), QSSGRendererUtil::nextMultipleOf4(size.height()));
2948}
2949
2950QSSGLayerRenderData::QSSGLayerRenderData(QSSGRenderLayer &inLayer, QSSGRenderer &inRenderer)
2951 : layer(inLayer)
2952 , renderer(&inRenderer)
2953 , orderIndependentTransparencyEnabled(false)
2954 , particlesEnabled(checkParticleSupport(inRenderer.contextInterface()->rhi()))
2955{
2956 depthMapPassMS.setMultisamplingEnabled(true);
2957 Q_ASSERT(extContexts.size() == 1);
2958
2959 // Set-up the world root node and create the data store for the models.
2960 auto *root = layer.rootNode;
2961 nodeData = root->globalNodeData();
2962 modelData = std::make_unique<QSSGRenderModelData>(nodeData);
2963 item2DData = std::make_unique<QSSGRenderItem2DData>(nodeData);
2964
2965 inRenderer.registerItem2DData(*item2DData);
2966}
2967
2968QSSGLayerRenderData::~QSSGLayerRenderData()
2969{
2970 for (auto &renderResult : renderResults)
2971 renderResult.reset();
2972 oitRenderContext.reset();
2973
2974 if (userRenderPassManager)
2975 userRenderPassManager->releaseAll();
2976
2977 renderer->unregisterItem2DData(*item2DData);
2978}
2979
2980static void sortInstances(QByteArray &sortedData, QList<QSSGRhiSortData> &sortData, const void *instances,
2981 int stride, int count, const QVector3D &cameraDirection)
2982{
2983 sortData.resize(count);
2984 Q_ASSERT(stride == sizeof(QSSGRenderInstanceTableEntry));
2985 // create sort data
2986 {
2987 const QSSGRenderInstanceTableEntry *instance = reinterpret_cast<const QSSGRenderInstanceTableEntry *>(instances);
2988 for (int i = 0; i < count; i++) {
2989 const QVector3D pos = QVector3D(instance->row0.w(), instance->row1.w(), instance->row2.w());
2990 sortData[i] = {QVector3D::dotProduct(pos, cameraDirection), i};
2991 instance++;
2992 }
2993 }
2994
2995 // sort
2996 std::sort(sortData.begin(), sortData.end(), [](const QSSGRhiSortData &a, const QSSGRhiSortData &b){
2997 return a.d > b.d;
2998 });
2999
3000 // copy instances
3001 {
3002 const QSSGRenderInstanceTableEntry *instance = reinterpret_cast<const QSSGRenderInstanceTableEntry *>(instances);
3003 QSSGRenderInstanceTableEntry *dest = reinterpret_cast<QSSGRenderInstanceTableEntry *>(sortedData.data());
3004 for (auto &s : sortData)
3005 *dest++ = instance[s.indexOrOffset];
3006 }
3007}
3008
3009static void cullLodInstances(QByteArray &lodData, const void *instances, int count,
3010 const QVector3D &cameraPosition, float minThreshold, float maxThreshold)
3011{
3012 const QSSGRenderInstanceTableEntry *instance = reinterpret_cast<const QSSGRenderInstanceTableEntry *>(instances);
3013 QSSGRenderInstanceTableEntry *dest = reinterpret_cast<QSSGRenderInstanceTableEntry *>(lodData.data());
3014 for (int i = 0; i < count; ++i) {
3015 const float x = cameraPosition.x() - instance->row0.w();
3016 const float y = cameraPosition.y() - instance->row1.w();
3017 const float z = cameraPosition.z() - instance->row2.w();
3018 const float distanceSq = x * x + y * y + z * z;
3019 if (distanceSq >= minThreshold * minThreshold && (maxThreshold < 0 || distanceSq < maxThreshold * maxThreshold))
3020 *dest = *instance;
3021 else
3022 *dest= {};
3023 dest++;
3024 instance++;
3025 }
3026}
3027
3028bool QSSGLayerRenderData::prepareInstancing(QSSGRhiContext *rhiCtx,
3029 QSSGSubsetRenderable *renderable,
3030 const QVector3D &cameraDirection,
3031 const QVector3D &cameraPosition,
3032 float minThreshold,
3033 float maxThreshold)
3034{
3035 QSSGRhiContextPrivate *rhiCtxD = QSSGRhiContextPrivate::get(rhiCtx);
3036 auto &modelContext = renderable->modelContext;
3037 auto &instanceBuffer = renderable->instanceBuffer; // intentional ref2ptr
3038 const QMatrix4x4 &renderableGlobalTransform = renderable->modelContext.globalTransform;
3039 if (!modelContext.model.instancing() || instanceBuffer)
3040 return instanceBuffer;
3041 auto *table = modelContext.model.instanceTable;
3042 bool usesLod = minThreshold >= 0 || maxThreshold >= 0;
3043 QSSGRhiInstanceBufferData &instanceData(usesLod ? rhiCtxD->instanceBufferData(&modelContext.model) : rhiCtxD->instanceBufferData(table));
3044 quint32 instanceBufferSize = table->dataSize();
3045 // Create or resize the instance buffer ### if (instanceData.owned)
3046 bool sortingChanged = table->isDepthSortingEnabled() != instanceData.sorting;
3047 bool cameraDirectionChanged = !qFuzzyCompare(instanceData.sortedCameraDirection, cameraDirection);
3048 bool cameraPositionChanged = !qFuzzyCompare(instanceData.cameraPosition, cameraPosition);
3049 bool updateInstanceBuffer = table->serial() != instanceData.serial || sortingChanged || (cameraDirectionChanged && table->isDepthSortingEnabled());
3050 bool instanceBufferSizeChanged = instanceData.buffer && instanceBufferSize != instanceData.buffer->size();
3051 bool updateForLod = (cameraPositionChanged || instanceBufferSizeChanged) && usesLod;
3052 if (sortingChanged && !table->isDepthSortingEnabled()) {
3053 instanceData.sortedData.clear();
3054 instanceData.sortData.clear();
3055 instanceData.sortedCameraDirection = {};
3056 }
3057 instanceData.sorting = table->isDepthSortingEnabled();
3058 if (instanceData.buffer && instanceData.buffer->size() < instanceBufferSize) {
3059 updateInstanceBuffer = true;
3060 // qDebug() << "Resizing instance buffer";
3061 instanceData.buffer->setSize(instanceBufferSize);
3062 instanceData.buffer->create();
3063 }
3064 if (!instanceData.buffer) {
3065 // qDebug() << "Creating instance buffer";
3066 updateInstanceBuffer = true;
3067 instanceData.buffer = rhiCtx->rhi()->newBuffer(QRhiBuffer::Dynamic, QRhiBuffer::VertexBuffer, instanceBufferSize);
3068 instanceData.buffer->create();
3069 }
3070 if (updateInstanceBuffer || updateForLod) {
3071 const void *data = nullptr;
3072 if (table->isDepthSortingEnabled()) {
3073 if (updateInstanceBuffer) {
3074 QMatrix4x4 invGlobalTransform = renderableGlobalTransform.inverted();
3075 instanceData.sortedData.resize(table->dataSize());
3076 sortInstances(instanceData.sortedData,
3077 instanceData.sortData,
3078 table->constData(),
3079 table->stride(),
3080 table->count(),
3081 invGlobalTransform.map(cameraDirection).normalized());
3082 }
3083 data = instanceData.sortedData.constData();
3084 instanceData.sortedCameraDirection = cameraDirection;
3085 } else {
3086 data = table->constData();
3087 }
3088 if (data) {
3089 if (updateForLod) {
3090 if (table->isDepthSortingEnabled()) {
3091 instanceData.lodData.resize(table->dataSize());
3092 cullLodInstances(instanceData.lodData, instanceData.sortedData.constData(), instanceData.sortedData.size(), cameraPosition, minThreshold, maxThreshold);
3093 data = instanceData.lodData.constData();
3094 } else {
3095 instanceData.lodData.resize(table->dataSize());
3096 cullLodInstances(instanceData.lodData, table->constData(), table->count(), cameraPosition, minThreshold, maxThreshold);
3097 data = instanceData.lodData.constData();
3098 }
3099 }
3100 QRhiResourceUpdateBatch *rub = rhiCtx->rhi()->nextResourceUpdateBatch();
3101 rub->updateDynamicBuffer(instanceData.buffer, 0, instanceBufferSize, data);
3102 rhiCtx->commandBuffer()->resourceUpdate(rub);
3103 //qDebug() << "****** UPDATING INST BUFFER. Size" << instanceBufferSize;
3104 } else {
3105 qWarning() << "NO DATA IN INSTANCE TABLE";
3106 }
3107 instanceData.serial = table->serial();
3108 instanceData.cameraPosition = cameraPosition;
3109 }
3110 instanceBuffer = instanceData.buffer;
3111 return instanceBuffer;
3112}
3113
3114QSSGFrameData &QSSGLayerRenderData::getFrameData()
3115{
3116 return frameData;
3117}
3118
3119void QSSGLayerRenderData::initializeLightmapBaking(QSSGLightmapBaker::Context &ctx)
3120{
3121 ctx.callbacks.modelsToBake = [this]() { return getSortedBakedLightingModels(); };
3122
3123 lightmapBaker = std::make_unique<QSSGLightmapBaker>(ctx);
3124}
3125
3126void QSSGLayerRenderData::maybeProcessLightmapBaking()
3127{
3128 if (lightmapBaker) {
3129 const QSSGLightmapBaker::Status status = lightmapBaker->process();
3130 if (status == QSSGLightmapBaker::Status::Finished)
3131 lightmapBaker.reset();
3132 }
3133}
3134
3135QSSGRenderGraphObject *QSSGLayerRenderData::getCamera(QSSGCameraId id) const
3136{
3137 QSSGRenderGraphObject *ret = nullptr;
3138 if (auto res = reinterpret_cast<QSSGRenderGraphObject *>(id))
3139 ret = res;
3140
3141 return ret;
3142}
3143
3144QSSGRenderCameraData QSSGLayerRenderData::getCameraRenderData(const QSSGRenderCamera *camera_)
3145{
3146 if ((!camera_ || camera_ == renderedCameras[0]) && renderedCameraData.has_value())
3147 return renderedCameraData.value()[0];
3148 if (camera_)
3149 return getCameraDataImpl(camera_);
3150 return {};
3151}
3152
3153QSSGRenderCameraData QSSGLayerRenderData::getCameraRenderData(const QSSGRenderCamera *camera_) const
3154{
3155 if ((!camera_ || camera_ == renderedCameras[0]) && renderedCameraData.has_value())
3156 return renderedCameraData.value()[0];
3157 if (camera_)
3158 return getCameraDataImpl(camera_);
3159 return {};
3160}
3161
3162QSSGRenderContextInterface *QSSGLayerRenderData::contextInterface() const
3163{
3164 return renderer ? renderer->contextInterface() : nullptr;
3165}
3166
3167QSSGLayerRenderData::GlobalRenderProperties QSSGLayerRenderData::globalRenderProperties(const QSSGRenderContextInterface &ctx)
3168{
3169 GlobalRenderProperties props {};
3170 if (const auto &rhiCtx = ctx.rhiContext(); rhiCtx->isValid()) {
3171 QRhi *rhi = rhiCtx->rhi();
3172 props.isYUpInFramebuffer = rhi->isYUpInFramebuffer();
3173 props.isYUpInNDC = rhi->isYUpInNDC();
3174 props.isClipDepthZeroToOne = rhi->isClipDepthZeroToOne();
3175 }
3176
3177 return props;
3178}
3179
3180const QSSGRenderShadowMapPtr &QSSGLayerRenderData::requestShadowMapManager()
3181{
3182 if (!shadowMapManager && QSSG_GUARD(renderer && renderer->contextInterface()))
3183 shadowMapManager.reset(new QSSGRenderShadowMap(*renderer->contextInterface()));
3184 return shadowMapManager;
3185}
3186
3187const QSSGRenderReflectionMapPtr &QSSGLayerRenderData::requestReflectionMapManager()
3188{
3189 if (!reflectionMapManager && QSSG_GUARD(renderer && renderer->contextInterface()))
3190 reflectionMapManager.reset(new QSSGRenderReflectionMap(*renderer->contextInterface()));
3191 return reflectionMapManager;
3192}
3193
3194
3195const QSSGUserRenderPassManagerPtr &QSSGLayerRenderData::requestUserRenderPassManager()
3196{
3197 if (!userRenderPassManager) {
3198 userRenderPassManager = QSSGUserRenderPassManager::create();
3199 renderer->contextInterface()->bufferManager()->registerUserRenderPassManager(userRenderPassManager);
3200 }
3201 return userRenderPassManager;
3202}
3203
3204const QSSGRenderMotionVectorMapPtr &QSSGLayerRenderData::requestMotionVectorMapManager()
3205{
3206 if (!motionVectorMapManager && QSSG_GUARD(renderer && renderer->contextInterface()))
3207 motionVectorMapManager.reset(new QSSGRenderMotionVectorMap(*renderer->contextInterface()));
3208 return motionVectorMapManager;
3209}
3210
3211QT_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 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)
static void cullLodInstances(QByteArray &lodData, const void *instances, int count, const QVector3D &cameraPosition, float minThreshold, float maxThreshold)
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)