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