Qt
Internal/Contributor docs for the Qt SDK. <b>Note:</b> These are NOT official API docs; those are found <a href='https://doc.qt.io/'>here</a>.
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>
20#include <QtQuick3DRuntimeRender/private/qssgrenderbuffermanager_p.h>
21#include <QtQuick3DRuntimeRender/private/qssgrendershadercache_p.h>
22#include <QtQuick3DRuntimeRender/private/qssgperframeallocator_p.h>
23#include <QtQuick3DRuntimeRender/private/qssgruntimerenderlogging_p.h>
24#include <QtQuick3DRuntimeRender/private/qssglightmapper_p.h>
25#include <QtQuick3DRuntimeRender/private/qssgdebugdrawsystem_p.h>
26
27#include <QtQuick3DUtils/private/qssgutils_p.h>
28#include <QtQuick3DUtils/private/qssgassert_p.h>
29
30#include <QtQuick/private/qsgtexture_p.h>
31#include <QtQuick/private/qsgrenderer_p.h>
32
33#include <QtCore/QCoreApplication>
34#include <QtCore/QBitArray>
35#include <array>
36
37#include "qssgrenderpass_p.h"
39
41
42Q_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
67// These are meant to be pixel offsets, so you need to divide them by the width/height
68// of the layer respectively.
70 QVector2D(-0.170840f, -0.553840f), // 1x
71 QVector2D(0.162960f, -0.319340f), // 2x
72 QVector2D(0.360260f, -0.245840f), // 3x
73 QVector2D(-0.561340f, -0.149540f), // 4x
74 QVector2D(0.249460f, 0.453460f), // 5x
75 QVector2D(-0.336340f, 0.378260f), // 6x
76 QVector2D(0.340000f, 0.166260f), // 7x
77 QVector2D(0.235760f, 0.527760f), // 8x
78};
79
81{
82 QSSG_ASSERT(visibleRenderables.isEmpty(), visibleRenderables.clear());
83 visibleRenderables.reserve(renderables.size());
84 for (quint32 end = renderables.size(), idx = quint32(0); idx != end; ++idx) {
85 auto handle = renderables.at(idx);
86 const auto &b = handle.obj->globalBounds;
87 if (clipFrustum.intersectsWith(b))
88 visibleRenderables.push_back(handle);
89 }
90
91 return visibleRenderables.size();
92}
93
95{
96 const qint32 end = renderables.size();
97 qint32 front = 0;
98 qint32 back = end - 1;
99
100 while (front <= back) {
101 const auto &b = renderables.at(front).obj->globalBounds;
102 if (clipFrustum.intersectsWith(b))
103 ++front;
104 else
105 renderables.swapItemsAt(front, back--);
106 }
107
108 return back + 1;
109}
110
111[[nodiscard]] constexpr static inline bool nearestToFurthestCompare(const QSSGRenderableObjectHandle &lhs, const QSSGRenderableObjectHandle &rhs) noexcept
112{
113 return lhs.cameraDistanceSq < rhs.cameraDistanceSq;
114}
115
116[[nodiscard]] constexpr static inline bool furthestToNearestCompare(const QSSGRenderableObjectHandle &lhs, const QSSGRenderableObjectHandle &rhs) noexcept
117{
118 return lhs.cameraDistanceSq > rhs.cameraDistanceSq;
119}
120
121static void collectBoneTransforms(QSSGRenderNode *node, QSSGRenderSkeleton *skeletonNode, const QVector<QMatrix4x4> &poses)
122{
123 if (node->type == QSSGRenderGraphObject::Type::Joint) {
124 QSSGRenderJoint *jointNode = static_cast<QSSGRenderJoint *>(node);
125 jointNode->calculateGlobalVariables();
126 QMatrix4x4 globalTrans = jointNode->globalTransform;
127 // if user doesn't give the inverseBindPose, identity matrices are used.
128 if (poses.size() > jointNode->index)
129 globalTrans *= poses[jointNode->index];
130 memcpy(skeletonNode->boneData.data() + POS4BONETRANS(jointNode->index),
131 reinterpret_cast<const void *>(globalTrans.constData()),
132 sizeof(float) * 16);
133 // only upper 3x3 is meaningful
134 memcpy(skeletonNode->boneData.data() + POS4BONENORM(jointNode->index),
135 reinterpret_cast<const void *>(QMatrix4x4(globalTrans.normalMatrix()).constData()),
136 sizeof(float) * 11);
137 } else {
138 skeletonNode->containsNonJointNodes = true;
139 }
140 for (auto &child : node->children)
141 collectBoneTransforms(&child, skeletonNode, poses);
142}
143
144static bool hasDirtyNonJointNodes(QSSGRenderNode *node, bool &hasChildJoints)
145{
146 if (!node)
147 return false;
148 // we might be non-joint dirty node, but if we do not have child joints we need to return false
149 // Note! The frontend clears TransformDirty. Use dirty instead.
150 bool dirtyNonJoint = ((node->type != QSSGRenderGraphObject::Type::Joint)
151 && node->isDirty());
152
153 // Tell our parent we are joint
154 if (node->type == QSSGRenderGraphObject::Type::Joint)
155 hasChildJoints = true;
156 bool nodeHasChildJoints = false;
157 for (auto &child : node->children) {
158 bool ret = hasDirtyNonJointNodes(&child, nodeHasChildJoints);
159 // return if we have child joints and non-joint dirty nodes, else check other children
160 hasChildJoints |= nodeHasChildJoints;
161 if (ret && nodeHasChildJoints)
162 return true;
163 }
164 // return true if we have child joints and we are dirty non-joint
165 hasChildJoints |= nodeHasChildJoints;
166 return dirtyNonJoint && nodeHasChildJoints;
167}
168
169template<typename T, typename V>
170inline void collectNode(V node, QVector<T> &dst, int &dstPos)
171{
172 if (dstPos < dst.size())
173 dst[dstPos] = node;
174 else
175 dst.push_back(node);
176
177 ++dstPos;
178}
179template <typename T, typename V>
180static inline void collectNodeFront(V node, QVector<T> &dst, int &dstPos)
181{
182 if (dstPos < dst.size())
183 dst[dst.size() - dstPos - 1] = node;
184 else
185 dst.push_front(node);
186
187 ++dstPos;
188}
189
190#define MAX_MORPH_TARGET 8
191#define MAX_MORPH_TARGET_INDEX_SUPPORTS_NORMALS 3
192#define MAX_MORPH_TARGET_INDEX_SUPPORTS_TANGENTS 1
193
195 QVector<QSSGRenderableNodeEntry> &outRenderableModels,
196 int &ioRenderableModelsCount,
197 QVector<QSSGRenderableNodeEntry> &outRenderableParticles,
198 int &ioRenderableParticlesCount,
199 QVector<QSSGRenderItem2D *> &outRenderableItem2Ds,
200 int &ioRenderableItem2DsCount,
201 QVector<QSSGRenderCamera *> &outCameras,
202 int &ioCameraCount,
203 QVector<QSSGRenderLight *> &outLights,
204 int &ioLightCount,
205 QVector<QSSGRenderReflectionProbe *> &outReflectionProbes,
206 int &ioReflectionProbeCount,
207 quint32 &ioDFSIndex)
208{
209 bool wasDirty = inNode.isDirty(QSSGRenderNode::DirtyFlag::GlobalValuesDirty) && inNode.calculateGlobalVariables();
210 if (inNode.getGlobalState(QSSGRenderNode::GlobalState::Active)) {
211 ++ioDFSIndex;
212 inNode.dfsIndex = ioDFSIndex;
213 if (QSSGRenderGraphObject::isRenderable(inNode.type)) {
214 if (inNode.type == QSSGRenderNode::Type::Model)
215 collectNode(QSSGRenderableNodeEntry(inNode), outRenderableModels, ioRenderableModelsCount);
216 else if (inNode.type == QSSGRenderNode::Type::Particles)
217 collectNode(QSSGRenderableNodeEntry(inNode), outRenderableParticles, ioRenderableParticlesCount);
218 else if (inNode.type == QSSGRenderNode::Type::Item2D) // Pushing front to keep item order inside QML file
219 collectNodeFront(static_cast<QSSGRenderItem2D *>(&inNode), outRenderableItem2Ds, ioRenderableItem2DsCount);
220 } else if (QSSGRenderGraphObject::isCamera(inNode.type)) {
221 collectNode(static_cast<QSSGRenderCamera *>(&inNode), outCameras, ioCameraCount);
222 } else if (QSSGRenderGraphObject::isLight(inNode.type)) {
223 if (auto &light = static_cast<QSSGRenderLight &>(inNode); light.isEnabled())
224 collectNode(&light, outLights, ioLightCount);
225 } else if (inNode.type == QSSGRenderGraphObject::Type::ReflectionProbe) {
226 collectNode(static_cast<QSSGRenderReflectionProbe *>(&inNode), outReflectionProbes, ioReflectionProbeCount);
227 }
228
229 for (auto &theChild : inNode.children)
230 wasDirty |= maybeQueueNodeForRender(theChild,
231 outRenderableModels,
232 ioRenderableModelsCount,
233 outRenderableParticles,
234 ioRenderableParticlesCount,
235 outRenderableItem2Ds,
236 ioRenderableItem2DsCount,
237 outCameras,
238 ioCameraCount,
239 outLights,
240 ioLightCount,
241 outReflectionProbes,
242 ioReflectionProbeCount,
243 ioDFSIndex);
244 }
245 return wasDirty;
246}
247
249 : firstImage(nullptr), opacity(1.0f), materialKey(inKey), dirty(false)
250{
251}
252
254{
256 if (camera) {
257 // Calculate viewProjection and clippingFrustum for Render Camera
258 QMatrix4x4 viewProjection(Qt::Uninitialized);
259 camera->calculateViewProjectionMatrix(viewProjection);
260 std::optional<QSSGClippingFrustum> clippingFrustum;
261 if (camera->enableFrustumClipping) {
262 QSSGClipPlane nearPlane;
263 QMatrix3x3 theUpper33(camera->globalTransform.normalMatrix());
264 QVector3D dir(QSSGUtils::mat33::transform(theUpper33, QVector3D(0, 0, -1)));
265 dir.normalize();
266 nearPlane.normal = dir;
267 QVector3D theGlobalPos = camera->getGlobalPos() + camera->clipNear * dir;
268 nearPlane.d = -(QVector3D::dotProduct(dir, theGlobalPos));
269 // the near plane's bbox edges are calculated in the clipping frustum's
270 // constructor.
271 clippingFrustum = QSSGClippingFrustum{viewProjection, nearPlane};
272 }
273 ret = { viewProjection, clippingFrustum, camera->getScalingCorrectDirection(), camera->getGlobalPos() };
274 }
275
276 return ret;
277}
278
279// Returns the cached data for the active render camera(s) (if any)
280const QSSGRenderCameraDataList &QSSGLayerRenderData::getCachedCameraDatas()
281{
282 ensureCachedCameraDatas();
283 return *renderedCameraData;
284}
285
286void QSSGLayerRenderData::ensureCachedCameraDatas()
287{
288 if (renderedCameraData.has_value())
289 return;
290
291 QSSGRenderCameraDataList cameraData;
292 for (QSSGRenderCamera *cam : std::as_const(renderedCameras))
293 cameraData.append(getCameraDataImpl(cam));
294 renderedCameraData = std::move(cameraData);
295}
296
297[[nodiscard]] static inline float getCameraDistanceSq(const QSSGRenderableObject &obj,
298 const QSSGRenderCameraData &camera) noexcept
299{
300 const QVector3D difference = obj.worldCenterPoint - camera.position;
301 return QVector3D::dotProduct(difference, camera.direction) + obj.depthBiasSq;
302}
303
304// Per-frame cache of renderable objects post-sort.
305const QVector<QSSGRenderableObjectHandle> &QSSGLayerRenderData::getSortedOpaqueRenderableObjects(const QSSGRenderCamera &camera, size_t index)
306{
307 index = index * size_t(index < opaqueObjectStore.size());
308 auto &sortedOpaqueObjects = sortedOpaqueObjectCache[index][&camera];
309 if (!sortedOpaqueObjects.empty())
310 return sortedOpaqueObjects;
311
312 if (layer.layerFlags.testFlag(QSSGRenderLayer::LayerFlag::EnableDepthTest))
313 sortedOpaqueObjects = std::as_const(opaqueObjectStore)[index];
314
315 const auto &clippingFrustum = getCameraRenderData(&camera).clippingFrustum;
316 if (clippingFrustum.has_value()) { // Frustum culling
317 const auto visibleObjects = QSSGLayerRenderData::frustumCullingInline(clippingFrustum.value(), sortedOpaqueObjects);
318 sortedOpaqueObjects.resize(visibleObjects);
319 }
320
321 // Render nearest to furthest objects
322 std::sort(sortedOpaqueObjects.begin(), sortedOpaqueObjects.end(), nearestToFurthestCompare);
323
324 return sortedOpaqueObjects;
325}
326
327// If layer depth test is false, this may also contain opaque objects.
328const QVector<QSSGRenderableObjectHandle> &QSSGLayerRenderData::getSortedTransparentRenderableObjects(const QSSGRenderCamera &camera, size_t index)
329{
330 index = index * size_t(index < transparentObjectStore.size());
331 auto &sortedTransparentObjects = sortedTransparentObjectCache[index][&camera];
332
333 if (!sortedTransparentObjects.empty())
334 return sortedTransparentObjects;
335
336 sortedTransparentObjects = std::as_const(transparentObjectStore)[index];
337
338 if (!layer.layerFlags.testFlag(QSSGRenderLayer::LayerFlag::EnableDepthTest)) {
339 const auto &opaqueObjects = std::as_const(opaqueObjectStore)[index];
340 sortedTransparentObjects.append(opaqueObjects);
341 }
342
343 const auto &clippingFrustum = getCameraRenderData(&camera).clippingFrustum;
344 if (clippingFrustum.has_value()) { // Frustum culling
345 const auto visibleObjects = QSSGLayerRenderData::frustumCullingInline(clippingFrustum.value(), sortedTransparentObjects);
346 sortedTransparentObjects.resize(visibleObjects);
347 }
348
349 // render furthest to nearest.
350 std::sort(sortedTransparentObjects.begin(), sortedTransparentObjects.end(), furthestToNearestCompare);
351
352 return sortedTransparentObjects;
353}
354
355const QVector<QSSGRenderableObjectHandle> &QSSGLayerRenderData::getSortedScreenTextureRenderableObjects(const QSSGRenderCamera &camera, size_t index)
356{
357 index = index * size_t(index < screenTextureObjectStore.size());
358 const auto &screenTextureObjects = std::as_const(screenTextureObjectStore)[index];
359 auto &renderedScreenTextureObjects = sortedScreenTextureObjectCache[index][&camera];
360
361 if (!renderedScreenTextureObjects.empty())
362 return renderedScreenTextureObjects;
363 renderedScreenTextureObjects = screenTextureObjects;
364 if (!renderedScreenTextureObjects.empty()) {
365 // render furthest to nearest.
366 std::sort(renderedScreenTextureObjects.begin(), renderedScreenTextureObjects.end(), furthestToNearestCompare);
367 }
368 return renderedScreenTextureObjects;
369}
370
371const QVector<QSSGBakedLightingModel> &QSSGLayerRenderData::getSortedBakedLightingModels()
372{
375 if (layer.layerFlags.testFlag(QSSGRenderLayer::LayerFlag::EnableDepthTest) && !bakedLightingModels.empty()) {
378 // sort nearest to furthest (front to back)
379 std::sort(lm.renderables.begin(), lm.renderables.end(), nearestToFurthestCompare);
380 }
381 }
383}
384
386{
388 return renderedItem2Ds;
389
391
392 if (!renderedItem2Ds.isEmpty()) {
393 const QSSGRenderCameraDataList &cameraDatas(getCachedCameraDatas());
394 // with multiview this means using the left eye camera
395 const QSSGRenderCameraData &cameraDirectionAndPosition(cameraDatas[0]);
396 const QVector3D &cameraDirection = cameraDirectionAndPosition.direction;
397 const QVector3D &cameraPosition = cameraDirectionAndPosition.position;
398
399 const auto isItemNodeDistanceGreatThan = [cameraDirection, cameraPosition]
400 (const QSSGRenderItem2D *lhs, const QSSGRenderItem2D *rhs) {
401 if (!lhs->parent || !rhs->parent)
402 return false;
403 const QVector3D lhsDifference = lhs->parent->getGlobalPos() - cameraPosition;
404 const float lhsCameraDistanceSq = QVector3D::dotProduct(lhsDifference, cameraDirection);
405 const QVector3D rhsDifference = rhs->parent->getGlobalPos() - cameraPosition;
406 const float rhsCameraDistanceSq = QVector3D::dotProduct(rhsDifference, cameraDirection);
407 return lhsCameraDistanceSq > rhsCameraDistanceSq;
408 };
409
410 const auto isItemZOrderLessThan = []
411 (const QSSGRenderItem2D *lhs, const QSSGRenderItem2D *rhs) {
412 if (lhs->parent && rhs->parent && lhs->parent == rhs->parent) {
413 // Same parent nodes, so sort with item z-ordering
414 return lhs->zOrder < rhs->zOrder;
415 }
416 return false;
417 };
418
419 // Render furthest to nearest items (parent nodes).
420 std::stable_sort(renderedItem2Ds.begin(), renderedItem2Ds.end(), isItemNodeDistanceGreatThan);
421 // Render items inside same node by item z-order.
422 // Note: stable_sort so item order in QML file is respected.
423 std::stable_sort(renderedItem2Ds.begin(), renderedItem2Ds.end(), isItemZOrderLessThan);
424 }
425
426 return renderedItem2Ds;
427}
428
429// Depth Write List
430void QSSGLayerRenderData::updateSortedDepthObjectsListImp(const QSSGRenderCamera &camera, size_t index)
431{
432 auto &depthWriteObjects = sortedDepthWriteCache[index][&camera];
433 auto &depthPrepassObjects = sortedOpaqueDepthPrepassCache[index][&camera];
434
435 if (!depthWriteObjects.isEmpty() || !depthPrepassObjects.isEmpty())
436 return;
437
438 if (layer.layerFlags.testFlag(QSSGRenderLayer::LayerFlag::EnableDepthTest)) {
439 if (hasDepthWriteObjects || (depthPrepassObjectsState & DepthPrepassObjectStateT(DepthPrepassObject::Opaque)) != 0) {
440 const auto &sortedOpaqueObjects = getSortedOpaqueRenderableObjects(camera, index); // front to back
441 for (const auto &opaqueObject : sortedOpaqueObjects) {
442 const auto depthMode = opaqueObject.obj->depthWriteMode;
443 if (depthMode == QSSGDepthDrawMode::Always || depthMode == QSSGDepthDrawMode::OpaqueOnly)
444 depthWriteObjects.append(opaqueObject);
445 else if (depthMode == QSSGDepthDrawMode::OpaquePrePass)
446 depthPrepassObjects.append(opaqueObject);
447 }
448 }
449 if (hasDepthWriteObjects || (depthPrepassObjectsState & DepthPrepassObjectStateT(DepthPrepassObject::Transparent)) != 0) {
450 const auto &sortedTransparentObjects = getSortedTransparentRenderableObjects(camera, index); // back to front
451 for (const auto &transparentObject : sortedTransparentObjects) {
452 const auto depthMode = transparentObject.obj->depthWriteMode;
453 if (depthMode == QSSGDepthDrawMode::Always)
454 depthWriteObjects.append(transparentObject);
455 else if (depthMode == QSSGDepthDrawMode::OpaquePrePass)
456 depthPrepassObjects.append(transparentObject);
457 }
458 }
459 if (hasDepthWriteObjects || (depthPrepassObjectsState & DepthPrepassObjectStateT(DepthPrepassObject::ScreenTexture)) != 0) {
460 const auto &sortedScreenTextureObjects = getSortedScreenTextureRenderableObjects(camera, index); // back to front
461 for (const auto &screenTextureObject : sortedScreenTextureObjects) {
462 const auto depthMode = screenTextureObject.obj->depthWriteMode;
463 if (depthMode == QSSGDepthDrawMode::Always || depthMode == QSSGDepthDrawMode::OpaqueOnly)
464 depthWriteObjects.append(screenTextureObject);
465 else if (depthMode == QSSGDepthDrawMode::OpaquePrePass)
466 depthPrepassObjects.append(screenTextureObject);
467 }
468 }
469 }
470}
471
472const std::unique_ptr<QSSGPerFrameAllocator> &QSSGLayerRenderData::perFrameAllocator(QSSGRenderContextInterface &ctx)
473{
474 return ctx.perFrameAllocator();
475}
476
477static constexpr quint16 PREP_CTX_INDEX_MASK = 0xffff;
478static constexpr QSSGPrepContextId createPrepId(size_t index, quint32 frame) { return QSSGPrepContextId { ((quint64(frame) << 32) | index ) * quint64(index <= std::numeric_limits<quint16>::max()) }; }
479static constexpr size_t getPrepContextIndex(QSSGPrepContextId id) { return (static_cast<quint64>(id) & PREP_CTX_INDEX_MASK); }
480static constexpr bool verifyPrepContext(QSSGPrepContextId id, const QSSGRenderer &renderer) { return (getPrepContextIndex(id) > 0) && ((static_cast<quint64>(id) >> 32) == renderer.frameCount()); }
481
483{
484 const auto frame = renderer->frameCount();
485 const auto index = extContexts.size();
486 // Sanity check... Shouldn't get anywhere close to the max in real world usage (unless somethings broken).
487 QSSG_ASSERT_X(index < PREP_CTX_INDEX_MASK - 1, "Reached maximum entries!", return QSSGPrepContextId::Invalid);
488 auto it = std::find_if(extContexts.cbegin(), extContexts.cend(), [&ext, slot](const ExtensionContext &e){ return (e.owner == &ext) && (e.slot == slot); });
489 if (it == extContexts.cend()) {
490 extContexts.push_back({ &ext, camera, {/* PS */}, {/* FILTER */}, index, slot });
491 it = extContexts.cbegin() + index;
492 renderableModelStore.emplace_back();
493 modelContextStore.emplace_back();
494 renderableObjectStore.emplace_back();
495 screenTextureObjectStore.emplace_back();
496 opaqueObjectStore.emplace_back();
497 transparentObjectStore.emplace_back();
498 sortedOpaqueObjectCache.emplace_back();
499 sortedTransparentObjectCache.emplace_back();
500 sortedScreenTextureObjectCache.emplace_back();
501 sortedOpaqueDepthPrepassCache.emplace_back();
502 sortedDepthWriteCache.emplace_back();
503 QSSG_ASSERT(renderableModelStore.size() == extContexts.size(), renderableModelStore.resize(extContexts.size()));
504 QSSG_ASSERT(modelContextStore.size() == extContexts.size(), modelContextStore.resize(extContexts.size()));
505 QSSG_ASSERT(renderableObjectStore.size() == extContexts.size(), renderableObjectStore.resize(extContexts.size()));
506 QSSG_ASSERT(screenTextureObjectStore.size() == extContexts.size(), screenTextureObjectStore.resize(extContexts.size()));
507 QSSG_ASSERT(opaqueObjectStore.size() == extContexts.size(), opaqueObjectStore.resize(extContexts.size()));
508 QSSG_ASSERT(transparentObjectStore.size() == extContexts.size(), transparentObjectStore.resize(extContexts.size()));
509 QSSG_ASSERT(sortedOpaqueObjectCache.size() == extContexts.size(), sortedOpaqueObjectCache.resize(extContexts.size()));
510 QSSG_ASSERT(sortedTransparentObjectCache.size() == extContexts.size(), sortedTransparentObjectCache.resize(extContexts.size()));
511 QSSG_ASSERT(sortedScreenTextureObjectCache.size() == extContexts.size(), sortedScreenTextureObjectCache.resize(extContexts.size()));
512 QSSG_ASSERT(sortedOpaqueDepthPrepassCache.size() == extContexts.size(), sortedOpaqueDepthPrepassCache.resize(extContexts.size()));
513 QSSG_ASSERT(sortedDepthWriteCache.size() == extContexts.size(), sortedDepthWriteCache.resize(extContexts.size()));
514 }
515
516 return createPrepId(it->index, frame);
517}
518
519static void createRenderablesHelper(QSSGLayerRenderData &layer, const QSSGRenderNode::ChildList &children, QSSGLayerRenderData::RenderableNodeEntries &renderables, QSSGRenderHelpers::CreateFlags createFlags)
520{
521 const bool steal = ((createFlags & QSSGRenderHelpers::CreateFlag::Steal) != 0);
522 for (auto &chld : children) {
523 if (chld.type == QSSGRenderGraphObject::Type::Model) {
524 const auto &renderModel = static_cast<const QSSGRenderModel &>(chld);
525 auto &renderableModels = layer.renderableModels;
526 if (auto it = std::find_if(renderableModels.cbegin(), renderableModels.cend(), [&renderModel](const QSSGRenderableNodeEntry &e) { return (e.node == &renderModel); }); it != renderableModels.cend()) {
527 renderables.emplace_back(*it);
528 if (steal)
529 renderableModels.erase(it);
530 }
531 }
532
533 createRenderablesHelper(layer, chld.children, renderables, createFlags);
534 }
535}
536
537QSSGRenderablesId QSSGLayerRenderData::createRenderables(QSSGPrepContextId prepId, const QList<QSSGNodeId> &nodes, QSSGRenderHelpers::CreateFlags createFlags)
538{
539 QSSG_ASSERT_X(verifyPrepContext(prepId, *renderer), "Expired or invalid prep id", return {});
540
541 const size_t index = getPrepContextIndex(prepId);
542 QSSG_ASSERT(index < renderableModelStore.size(), return {});
543
544 auto &renderables = renderableModelStore[index];
545 if (renderables.size() != 0) {
546 qWarning() << "Renderables already created for this context - Previous renderables will be overwritten";
547 renderables.clear();
548 }
549
550 renderables.reserve(nodes.size());
551
552 // We now create the renderable node entries for all the models.
553 // NOTE: The nodes are not complete at this point...
554 const bool steal = ((createFlags & QSSGRenderHelpers::CreateFlag::Steal) != 0);
555 for (const auto &nodeId : nodes) {
556 auto *node = QSSGRenderGraphObjectUtils::getNode<QSSGRenderNode>(nodeId);
557 if (node && node->type == QSSGRenderGraphObject::Type::Model) {
558 auto *renderModel = static_cast<QSSGRenderModel *>(node);
559 // NOTE: Not ideal.
560 if (auto it = std::find_if(renderableModels.cbegin(), renderableModels.cend(), [renderModel](const QSSGRenderableNodeEntry &e) { return (e.node == renderModel); }); it != renderableModels.cend()) {
561 auto &inserted = renderables.emplace_back(*it);
562 inserted.overridden = {};
563 if (steal)
565 } else {
566 renderables.emplace_back(*renderModel);
567 }
568 }
569
570 if (node && ((createFlags & QSSGRenderHelpers::CreateFlag::Recurse) != 0)) {
571 const auto &children = node->children;
572 createRenderablesHelper(*this, children, renderables, createFlags);
573 }
574 }
575
576 return (renderables.size() != 0) ? static_cast<QSSGRenderablesId>(prepId) : QSSGRenderablesId{ QSSGRenderablesId::Invalid };
577}
578
580{
581 const auto prepId = static_cast<QSSGPrepContextId>(renderablesId);
582 QSSG_ASSERT_X(verifyPrepContext(prepId, *renderer), "Expired or invalid renderables id", return);
583 const size_t index = getPrepContextIndex(prepId);
584 QSSG_ASSERT_X(index < renderableModelStore.size(), "Missing call to createRenderables()?", return);
585
586 auto &renderables = renderableModelStore[index];
587 auto it = std::find_if(renderables.cbegin(), renderables.cend(), [&model](const QSSGRenderableNodeEntry &e) { return e.node == &model; });
588 if (it != renderables.cend()) {
589 it->globalTransform = globalTransform;
591 }
592}
593
595{
596 QSSG_ASSERT_X(verifyPrepContext(prepId, *renderer), "Expired or invalid prep id", return {});
597 const size_t index = getPrepContextIndex(prepId);
598 QSSG_ASSERT_X(index < renderableModelStore.size(), "Missing call to createRenderables()?", return {});
599
600 QMatrix4x4 ret = model.globalTransform;
601 auto &renderables = renderableModelStore[index];
602 auto it = std::find_if(renderables.cbegin(), renderables.cend(), [&model](const QSSGRenderableNodeEntry &e) { return e.node == &model; });
603 if (it != renderables.cend() && (it->overridden & QSSGRenderableNodeEntry::Overridden::GlobalTransform))
604 ret = it->globalTransform;
605
606 return ret;
607}
608
610{
611 const auto prepId = static_cast<QSSGPrepContextId>(renderablesId);
612 QSSG_ASSERT_X(verifyPrepContext(prepId, *renderer), "Expired or invalid renderables id", return);
613 const size_t index = getPrepContextIndex(prepId);
614 QSSG_ASSERT_X(index < renderableModelStore.size(), "Missing call to createRenderables()?", return);
615
616 auto &renderables = renderableModelStore[index];
617 auto it = std::find_if(renderables.cbegin(), renderables.cend(), [&model](const QSSGRenderableNodeEntry &e) { return e.node == &model; });
618 if (it != renderables.cend()) {
619 it->globalOpacity = opacity;
621 }
622}
623
625{
626 QSSG_ASSERT_X(verifyPrepContext(prepId, *renderer), "Expired or invalid prep id", return {});
627 const size_t index = getPrepContextIndex(prepId);
628 QSSG_ASSERT_X(index < renderableModelStore.size(), "Missing call to createRenderables()?", return {});
629
630 float ret = model.globalOpacity;
631 auto &renderables = renderableModelStore[index];
632 auto it = std::find_if(renderables.cbegin(), renderables.cend(), [&model](const QSSGRenderableNodeEntry &e) { return e.node == &model; });
633 if (it != renderables.cend() && (it->overridden & QSSGRenderableNodeEntry::Overridden::GlobalOpacity))
634 ret = it->globalOpacity;
635
636 return ret;
637}
638
639void QSSGLayerRenderData::setModelMaterials(QSSGRenderablesId renderablesId, const QSSGRenderModel &model, const QList<QSSGResourceId> &materials)
640{
641 const auto prepId = static_cast<QSSGPrepContextId>(renderablesId);
642 QSSG_ASSERT_X(verifyPrepContext(prepId, *renderer), "Expired or invalid renderable id", return);
643 const size_t index = getPrepContextIndex(prepId);
644 QSSG_ASSERT(index < renderableModelStore.size(), return);
645
646 // Sanity check
647 if (materials.size() > 0 && !QSSG_GUARD(QSSGRenderGraphObject::isMaterial(QSSGRenderGraphObjectUtils::getResource(materials.at(0))->type)))
648 return;
649
650 auto &renderables = renderableModelStore[index];
651 auto it = std::find_if(renderables.cbegin(), renderables.cend(), [&model](const QSSGRenderableNodeEntry &e) { return e.node == &model; });
652 if (it != renderables.cend()) {
653 it->materials.resize(materials.size());
654 std::memcpy(it->materials.data(), materials.data(), it->materials.size() * sizeof(QSSGRenderGraphObject *));
656 }
657}
658
659void QSSGLayerRenderData::setModelMaterials(const QSSGRenderablesId renderablesId, const QList<QSSGResourceId> &materials)
660{
661 const auto prepId = static_cast<QSSGPrepContextId>(renderablesId);
662 QSSG_ASSERT_X(verifyPrepContext(prepId, *renderer), "Expired or invalid renderablesId or renderables id", return);
663
664 const size_t index = getPrepContextIndex(prepId);
665 QSSG_ASSERT(index < renderableModelStore.size(), return);
666
667 auto &renderables = renderableModelStore[index];
668 for (auto &renderable : renderables) {
669 auto &renderableMaterials = renderable.materials;
670 renderableMaterials.resize(materials.size());
671 std::memcpy(renderableMaterials.data(), materials.data(), renderableMaterials.size() * sizeof(QSSGRenderGraphObject *));
672 renderable.overridden |= QSSGRenderableNodeEntry::Overridden::Materials;
673 }
674}
675
677 QSSGPrepContextId prepId,
678 QSSGRenderablesId renderablesId,
679 float lodThreshold)
680{
682 "Expired or invalid prep or renderables id", return QSSGPrepResultId::Invalid);
683 const size_t index = getPrepContextIndex(prepId);
684 QSSG_ASSERT(index < renderableModelStore.size(), return {});
685
686 const auto &extContext = extContexts.at(index);
687
688 QSSG_ASSERT_X(extContext.camera != nullptr, "No camera set!", return {});
689
690 const auto vp = contextInterface.renderer()->viewport();
691 extContext.camera->calculateGlobalVariables(vp);
692
693 auto &renderables = renderableModelStore[index];
694
695 prepareModelMaterials(renderables, true /* Cull renderables without materials */);
696
697 prepareModelMeshes(contextInterface, renderables, false /* globalPickingEnabled */);
698
699 // ### multiview
700 QSSGRenderCameraList camera({ extContext.camera });
701 QSSGRenderCameraDataList cameraData({ getCameraRenderData(extContext.camera) });
702
703 auto &modelContexts = modelContextStore[index];
705
706 auto &renderableObjects = renderableObjectStore[index];
707 QSSG_ASSERT(renderableObjects.isEmpty(), renderableObjects.clear());
708
709 auto &opaqueObjects = opaqueObjectStore[index];
710 QSSG_ASSERT(opaqueObjects.isEmpty(), opaqueObjects.clear());
711
712 auto &transparentObjects = transparentObjectStore[index];
713 QSSG_ASSERT(transparentObjects.isEmpty(), transparentObjects.clear());
714
715 auto &screenTextureObjects = screenTextureObjectStore[index];
716 QSSG_ASSERT(screenTextureObjects.isEmpty(), screenTextureObjects.clear());
717
719 renderables,
721 camera,
722 cameraData,
724 opaqueObjects,
725 transparentObjects,
726 screenTextureObjects,
727 lodThreshold);
728
729 (void)wasDirty;
730
731 return static_cast<QSSGPrepResultId>(prepId);
732}
733
735{
736 switch (filter) {
738 return 0;
740 return 1;
742 return 2;
743 }
744
745 Q_UNREACHABLE_RETURN(0);
746}
747
749 QSSGPrepResultId prepId,
750 QRhiRenderPassDescriptor *renderPassDescriptor,
752 QSSGRenderablesFilters filter)
753{
754 QSSG_ASSERT_X(verifyPrepContext(static_cast<QSSGPrepContextId>(prepId), *renderer), "Expired or invalid result id", return);
755 const size_t index = getPrepContextIndex(static_cast<QSSGPrepContextId>(prepId));
756 QSSG_ASSERT(index < renderableObjectStore.size() && index < extContexts.size(), return);
757
758 auto &extCtx = extContexts[index];
759 QSSG_ASSERT(extCtx.camera, return);
760 extCtx.filter |= filter;
761
763
764 QSSGPassKey passKey { reinterpret_cast<void *>(quintptr(extCtx.owner) ^ extCtx.slot) }; // TODO: Pass this along
765
767 auto psCpy = ps;
768 if (filter == QSSGRenderablesFilter::All) { // If 'All' we set our defaults
770 psCpy.flags.setFlag(QSSGRhiGraphicsPipelineState::Flag::BlendEnabled, false);
771 }
772 const auto &sortedRenderables = getSortedOpaqueRenderableObjects(*extCtx.camera, index);
773 OpaquePass::prep(ctx, *this, passKey, psCpy, featureSet, renderPassDescriptor, sortedRenderables);
775 extCtx.ps[psIndex] = psCpy;
776 }
777
779 auto psCpy = ps;
780 if (filter == QSSGRenderablesFilter::All) { // If 'All' we set our defaults
781 // transparent objects (or, without LayerEnableDepthTest, all objects)
784 }
785 const auto &sortedRenderables = getSortedTransparentRenderableObjects(*extCtx.camera, index);
786 TransparentPass::prep(ctx, *this, passKey, psCpy, featureSet, renderPassDescriptor, sortedRenderables);
788 extCtx.ps[psIndex] = psCpy;
789 }
790}
791
793{
794 QSSG_ASSERT_X(verifyPrepContext(static_cast<QSSGPrepContextId>(prepId), *renderer), "Expired or invalid result id", return);
795 const size_t index = getPrepContextIndex(static_cast<QSSGPrepContextId>(prepId));
796 QSSG_ASSERT(index < renderableObjectStore.size() && index < extContexts.size(), return);
797
798 const auto &extCtx = extContexts.at(index);
799 const auto filter = extCtx.filter;
800
803 const auto &ps = extCtx.ps[psIndex];
804 const auto &sortedRenderables = getSortedOpaqueRenderableObjects(*extCtx.camera, index);
805 OpaquePass::render(ctx, ps, sortedRenderables);
806 }
807
810 const auto &ps = extCtx.ps[psIndex];
811 const auto &sortedRenderables = getSortedTransparentRenderableObjects(*extCtx.camera, index);
812 TransparentPass::render(ctx, ps, sortedRenderables);
813 }
814}
815
817{
818 updateSortedDepthObjectsListImp(camera, index);
819 return sortedDepthWriteCache[index][&camera];
820}
821
823{
824 updateSortedDepthObjectsListImp(camera, index);
825 return sortedOpaqueDepthPrepassCache[index][&camera];;
826}
827
832template <typename T, typename... Args>
833[[nodiscard]] inline T *RENDER_FRAME_NEW(QSSGRenderContextInterface &ctx, Args&&... args)
834{
835 static_assert(std::is_trivially_destructible_v<T>, "Objects allocated using the per-frame allocator needs to be trivially destructible!");
836 return new (QSSGLayerRenderData::perFrameAllocator(ctx)->allocate(sizeof(T)))T(std::forward<Args>(args)...);
837}
838
839template <typename T>
840[[nodiscard]] inline QSSGDataRef<T> RENDER_FRAME_NEW_BUFFER(QSSGRenderContextInterface &ctx, size_t count)
841{
842 static_assert(std::is_trivially_destructible_v<T>, "Objects allocated using the per-frame allocator needs to be trivially destructible!");
843 const size_t asize = sizeof(T) * count;
844 return { reinterpret_cast<T *>(QSSGLayerRenderData::perFrameAllocator(ctx)->allocate(asize)), qsizetype(count) };
845}
846
848 QSSGRenderDefaultMaterial::MaterialLighting inLightingType, const QSSGShaderLightListView &lights, bool receivesShadows)
849{
850 QSSGShaderDefaultMaterialKey theGeneratedKey(qHash(features));
851 const bool lighting = inLightingType != QSSGRenderDefaultMaterial::MaterialLighting::NoLighting;
852 defaultMaterialShaderKeyProperties.m_hasLighting.setValue(theGeneratedKey, lighting);
853 if (lighting) {
854 defaultMaterialShaderKeyProperties.m_hasIbl.setValue(theGeneratedKey, layer.lightProbe != nullptr);
855
856 quint32 numLights = quint32(lights.size());
858 defaultMaterialShaderKeyProperties.m_lightCount.setValue(theGeneratedKey, numLights);
859
860 int shadowMapCount = 0;
861 for (int lightIdx = 0, lightEnd = lights.size(); lightIdx < lightEnd; ++lightIdx) {
862 QSSGRenderLight *theLight(lights[lightIdx].light);
863 const bool isDirectional = theLight->type == QSSGRenderLight::Type::DirectionalLight;
864 const bool isSpot = theLight->type == QSSGRenderLight::Type::SpotLight;
865 const bool castsShadows = theLight->m_castShadow
866 && !theLight->m_fullyBaked
867 && receivesShadows
868 && shadowMapCount < QSSG_MAX_NUM_SHADOW_MAPS;
869 if (castsShadows)
870 ++shadowMapCount;
871
872 defaultMaterialShaderKeyProperties.m_lightFlags[lightIdx].setValue(theGeneratedKey, !isDirectional);
873 defaultMaterialShaderKeyProperties.m_lightSpotFlags[lightIdx].setValue(theGeneratedKey, isSpot);
874 defaultMaterialShaderKeyProperties.m_lightShadowFlags[lightIdx].setValue(theGeneratedKey, castsShadows);
875 }
876 }
877 return theGeneratedKey;
878}
879
882 QSSGRenderableImage *&ioFirstImage,
883 QSSGRenderableImage *&ioNextImage,
885 QSSGShaderDefaultMaterialKey &inShaderKey,
886 quint32 inImageIndex,
887 QSSGRenderDefaultMaterial *inMaterial)
888{
890 const auto &bufferManager = contextInterface.bufferManager();
891
892 if (inImage.clearDirty())
894
895 // This is where the QRhiTexture gets created, if not already done. Note
896 // that the bufferManager is per-QQuickWindow, and so per-render-thread.
897 // Hence using the same Texture (backed by inImage as the backend node) in
898 // multiple windows will work by each scene in each window getting its own
899 // QRhiTexture. And that's why the QSSGRenderImageTexture cannot be a
900 // member of the QSSGRenderImage. Conceptually this matches what we do for
901 // models (QSSGRenderModel -> QSSGRenderMesh retrieved from the
902 // bufferManager in each prepareModelForRender, etc.).
903
904 const QSSGRenderImageTexture texture = bufferManager->loadRenderImage(&inImage);
905
906 if (texture.m_texture) {
907 if (texture.m_flags.hasTransparency()
908 && (inMapType == QSSGRenderableImage::Type::Diffuse // note: Type::BaseColor is skipped here intentionally
909 || inMapType == QSSGRenderableImage::Type::Opacity
910 || inMapType == QSSGRenderableImage::Type::Translucency))
911 {
913 }
914
915 QSSGRenderableImage *theImage = RENDER_FRAME_NEW<QSSGRenderableImage>(contextInterface, inMapType, inImage, texture);
916 QSSGShaderKeyImageMap &theKeyProp = defaultMaterialShaderKeyProperties.m_imageMaps[inImageIndex];
917
918 theKeyProp.setEnabled(inShaderKey, true);
919 switch (inImage.m_mappingMode) {
921 break;
923 theKeyProp.setEnvMap(inShaderKey, true);
924 break;
926 theKeyProp.setLightProbe(inShaderKey, true);
927 break;
928 }
929
930 bool hasA = false;
931 bool hasG = false;
932 bool hasB = false;
933
934
935 //### TODO: More formats
936 switch (texture.m_texture->format()) {
938 hasA = !renderer->contextInterface()->rhiContext()->rhi()->isFeatureSupported(QRhi::RedOrAlpha8IsRed);
939 break;
941 // Leave BGA as false
942 break;
943 default:
944 hasA = true;
945 hasG = true;
946 hasB = true;
947 break;
948 }
949
950 if (inImage.isImageTransformIdentity())
951 theKeyProp.setIdentityTransform(inShaderKey, true);
952
953 if (inImage.m_indexUV == 1)
954 theKeyProp.setUsesUV1(inShaderKey, true);
955
956 if (texture.m_flags.isLinear())
957 theKeyProp.setLinear(inShaderKey, true);
958
959 if (ioFirstImage == nullptr)
960 ioFirstImage = theImage;
961 else
962 ioNextImage->m_nextImage = theImage;
963
964 ioNextImage = theImage;
965
968
970 QSSGShaderKeyTextureChannel &channelKey = defaultMaterialShaderKeyProperties.m_textureChannels[scIndex];
971 switch (inImageIndex) {
973 value = inMaterial->opacityChannel;
974 break;
976 value = inMaterial->roughnessChannel;
977 break;
979 value = inMaterial->metalnessChannel;
980 break;
982 value = inMaterial->occlusionChannel;
983 break;
985 value = inMaterial->translucencyChannel;
986 break;
988 value = inMaterial->heightChannel;
989 break;
991 value = inMaterial->clearcoatChannel;
992 break;
994 value = inMaterial->clearcoatRoughnessChannel;
995 break;
997 value = inMaterial->transmissionChannel;
998 break;
1000 value = inMaterial->thicknessChannel;
1001 break;
1003 value = inMaterial->baseColorChannel;
1004 break;
1006 value = inMaterial->specularAmountChannel;
1007 break;
1009 value = inMaterial->emissiveChannel;
1010 break;
1011 default:
1012 break;
1013 }
1014 bool useDefault = false;
1015 switch (value) {
1017 useDefault = !hasG;
1018 break;
1020 useDefault = !hasB;
1021 break;
1023 useDefault = !hasA;
1024 break;
1025 default:
1026 break;
1027 }
1028 if (useDefault)
1029 value = QSSGRenderDefaultMaterial::R; // Always Fallback to Red
1031 }
1032 }
1033}
1034
1037{
1038 quint32 vertexAttribs = 0;
1039 if (renderableFlags.hasAttributePosition())
1041 if (renderableFlags.hasAttributeNormal())
1042 vertexAttribs |= QSSGShaderKeyVertexAttribute::Normal;
1043 if (renderableFlags.hasAttributeTexCoord0())
1045 if (renderableFlags.hasAttributeTexCoord1())
1047 if (renderableFlags.hasAttributeTexCoordLightmap())
1049 if (renderableFlags.hasAttributeTangent())
1051 if (renderableFlags.hasAttributeBinormal())
1053 if (renderableFlags.hasAttributeColor())
1054 vertexAttribs |= QSSGShaderKeyVertexAttribute::Color;
1055 if (renderableFlags.hasAttributeJointAndWeight())
1057 defaultMaterialShaderKeyProperties.m_vertexAttributes.setValue(key, vertexAttribs);
1058}
1059
1060QSSGDefaultMaterialPreparationResult QSSGLayerRenderData::prepareDefaultMaterialForRender(
1061 QSSGRenderDefaultMaterial &inMaterial,
1062 QSSGRenderableObjectFlags &inExistingFlags,
1063 float inOpacity,
1064 const QSSGShaderLightListView &lights,
1066{
1067 QSSGRenderDefaultMaterial *theMaterial = &inMaterial;
1068 QSSGDefaultMaterialPreparationResult retval(generateLightingKey(theMaterial->lighting, lights, inExistingFlags.receivesShadows()));
1069 retval.renderableFlags = inExistingFlags;
1070 QSSGRenderableObjectFlags &renderableFlags(retval.renderableFlags);
1071 QSSGShaderDefaultMaterialKey &theGeneratedKey(retval.materialKey);
1072 retval.opacity = inOpacity;
1073 float &subsetOpacity(retval.opacity);
1074
1075 if (theMaterial->isDirty())
1076 renderableFlags |= QSSGRenderableObjectFlag::Dirty;
1077
1078 subsetOpacity *= theMaterial->opacity;
1079
1080 QSSGRenderableImage *firstImage = nullptr;
1081
1082 defaultMaterialShaderKeyProperties.m_specularAAEnabled.setValue(theGeneratedKey, layer.specularAAEnabled);
1083
1084 // isDoubleSided
1085 defaultMaterialShaderKeyProperties.m_isDoubleSided.setValue(theGeneratedKey, theMaterial->cullMode == QSSGCullFaceMode::Disabled);
1086
1087 // default materials never define their on position
1088 defaultMaterialShaderKeyProperties.m_overridesPosition.setValue(theGeneratedKey, false);
1089
1090 // default materials dont make use of raw projection or inverse projection matrices
1091 defaultMaterialShaderKeyProperties.m_usesProjectionMatrix.setValue(theGeneratedKey, false);
1092 defaultMaterialShaderKeyProperties.m_usesInverseProjectionMatrix.setValue(theGeneratedKey, false);
1093 // nor they do rely on VAR_COLOR
1094 defaultMaterialShaderKeyProperties.m_usesVarColor.setValue(theGeneratedKey, false);
1095
1096 // alpha Mode
1097 defaultMaterialShaderKeyProperties.m_alphaMode.setValue(theGeneratedKey, theMaterial->alphaMode);
1098
1099 // vertex attribute presence flags
1100 setVertexInputPresence(renderableFlags, theGeneratedKey);
1101
1102 // set the flag indicating the need for gl_PointSize
1103 defaultMaterialShaderKeyProperties.m_usesPointsTopology.setValue(theGeneratedKey, renderableFlags.isPointsTopology());
1104
1105 // propagate the flag indicating the presence of a lightmap
1106 defaultMaterialShaderKeyProperties.m_lightmapEnabled.setValue(theGeneratedKey, renderableFlags.rendersWithLightmap());
1107
1108 defaultMaterialShaderKeyProperties.m_specularGlossyEnabled.setValue(theGeneratedKey, theMaterial->type == QSSGRenderGraphObject::Type::SpecularGlossyMaterial);
1109
1110 // debug modes
1111 defaultMaterialShaderKeyProperties.m_debugMode.setValue(theGeneratedKey, int(layer.debugMode));
1112
1113 // fog
1114 defaultMaterialShaderKeyProperties.m_fogEnabled.setValue(theGeneratedKey, layer.fog.enabled);
1115
1116 // multiview
1117 const auto &rhiCtx = renderer->contextInterface()->rhiContext();
1118 defaultMaterialShaderKeyProperties.m_viewCount.setValue(theGeneratedKey, rhiCtx->mainPassViewCount());
1119 defaultMaterialShaderKeyProperties.m_usesViewIndex.setValue(theGeneratedKey, rhiCtx->mainPassViewCount() >= 2);
1120
1121 if (!defaultMaterialShaderKeyProperties.m_hasIbl.getValue(theGeneratedKey) && theMaterial->iblProbe) {
1123 defaultMaterialShaderKeyProperties.m_hasIbl.setValue(theGeneratedKey, true);
1124 // features.set(ShaderFeatureDefines::enableIblFov(),
1125 // m_Renderer.GetLayerRenderData()->m_Layer.m_ProbeFov < 180.0f );
1126 }
1127
1128 if (subsetOpacity >= QSSG_RENDER_MINIMUM_RENDER_OPACITY) {
1129
1130 // Set the semi-transparency flag as specified in PrincipledMaterial's
1131 // blendMode and alphaMode:
1132 // - the default SourceOver blendMode does not imply alpha blending on
1133 // its own,
1134 // - but other blendMode values do,
1135 // - an alphaMode of Blend guarantees blending to be enabled regardless
1136 // of anything else.
1137 // Additionally:
1138 // - Opacity and texture map alpha are handled elsewhere (that's when a
1139 // blendMode of SourceOver or an alphaMode of Default/Opaque can in the
1140 // end still result in HasTransparency),
1141 // - the presence of an opacityMap guarantees alpha blending regardless
1142 // of its content.
1143
1145 || theMaterial->opacityMap
1146 || theMaterial->alphaMode == QSSGRenderDefaultMaterial::Blend)
1147 {
1149 }
1150
1151 const bool specularEnabled = theMaterial->isSpecularEnabled();
1152 const bool metalnessEnabled = theMaterial->isMetalnessEnabled();
1153 defaultMaterialShaderKeyProperties.m_specularEnabled.setValue(theGeneratedKey, (specularEnabled || metalnessEnabled));
1154 if (specularEnabled || metalnessEnabled)
1155 defaultMaterialShaderKeyProperties.m_specularModel.setSpecularModel(theGeneratedKey, theMaterial->specularModel);
1156
1157 defaultMaterialShaderKeyProperties.m_fresnelScaleBiasEnabled.setValue(theGeneratedKey, theMaterial->isFresnelScaleBiasEnabled());
1158
1159 defaultMaterialShaderKeyProperties.m_clearcoatFresnelScaleBiasEnabled.setValue(theGeneratedKey, theMaterial->isClearcoatFresnelScaleBiasEnabled());
1160
1161 defaultMaterialShaderKeyProperties.m_fresnelEnabled.setValue(theGeneratedKey, theMaterial->isFresnelEnabled());
1162
1163 defaultMaterialShaderKeyProperties.m_fresnelEnabled.setValue(theGeneratedKey, theMaterial->isFresnelEnabled());
1164
1165 defaultMaterialShaderKeyProperties.m_baseColorSingleChannelEnabled.setValue(theGeneratedKey,
1166 theMaterial->isBaseColorSingleChannelEnabled());
1167 defaultMaterialShaderKeyProperties.m_specularSingleChannelEnabled.setValue(theGeneratedKey,
1168 theMaterial->isSpecularAmountSingleChannelEnabled());
1169 defaultMaterialShaderKeyProperties.m_emissiveSingleChannelEnabled.setValue(theGeneratedKey,
1170 theMaterial->isEmissiveSingleChannelEnabled());
1171 defaultMaterialShaderKeyProperties.m_invertOpacityMapValue.setValue(theGeneratedKey,
1172 theMaterial->isInvertOpacityMapValue());
1173 defaultMaterialShaderKeyProperties.m_vertexColorsEnabled.setValue(theGeneratedKey,
1174 theMaterial->isVertexColorsEnabled());
1175 defaultMaterialShaderKeyProperties.m_vertexColorsMaskEnabled.setValue(theGeneratedKey,
1176 theMaterial->isVertexColorsMaskEnabled());
1177 defaultMaterialShaderKeyProperties.m_vertexColorRedMask.setValue(theGeneratedKey,
1178 theMaterial->vertexColorRedMask.toInt());
1179 defaultMaterialShaderKeyProperties.m_vertexColorGreenMask.setValue(theGeneratedKey,
1180 quint16(theMaterial->vertexColorGreenMask.toInt()));
1181 defaultMaterialShaderKeyProperties.m_vertexColorBlueMask.setValue(theGeneratedKey,
1182 quint16(theMaterial->vertexColorBlueMask.toInt()));
1183 defaultMaterialShaderKeyProperties.m_vertexColorAlphaMask.setValue(theGeneratedKey,
1184 quint16(theMaterial->vertexColorAlphaMask.toInt()));
1185
1186 defaultMaterialShaderKeyProperties.m_clearcoatEnabled.setValue(theGeneratedKey,
1187 theMaterial->isClearcoatEnabled());
1188 defaultMaterialShaderKeyProperties.m_transmissionEnabled.setValue(theGeneratedKey,
1189 theMaterial->isTransmissionEnabled());
1190
1191 // Run through the material's images and prepare them for render.
1192 // this may in fact set pickable on the renderable flags if one of the images
1193 // links to a sub presentation or any offscreen rendered object.
1194 QSSGRenderableImage *nextImage = nullptr;
1195#define CHECK_IMAGE_AND_PREPARE(img, imgtype, shadercomponent) \
1196 if ((img)) \
1197 prepareImageForRender(*(img), imgtype, firstImage, nextImage, renderableFlags, \
1198 theGeneratedKey, shadercomponent, &inMaterial)
1199
1200 if (theMaterial->type == QSSGRenderGraphObject::Type::PrincipledMaterial ||
1201 theMaterial->type == QSSGRenderGraphObject::Type::SpecularGlossyMaterial) {
1202 CHECK_IMAGE_AND_PREPARE(theMaterial->colorMap,
1203 QSSGRenderableImage::Type::BaseColor,
1205 CHECK_IMAGE_AND_PREPARE(theMaterial->occlusionMap,
1206 QSSGRenderableImage::Type::Occlusion,
1208 CHECK_IMAGE_AND_PREPARE(theMaterial->heightMap,
1209 QSSGRenderableImage::Type::Height,
1211 CHECK_IMAGE_AND_PREPARE(theMaterial->clearcoatMap,
1212 QSSGRenderableImage::Type::Clearcoat,
1214 CHECK_IMAGE_AND_PREPARE(theMaterial->clearcoatRoughnessMap,
1215 QSSGRenderableImage::Type::ClearcoatRoughness,
1217 CHECK_IMAGE_AND_PREPARE(theMaterial->clearcoatNormalMap,
1218 QSSGRenderableImage::Type::ClearcoatNormal,
1220 CHECK_IMAGE_AND_PREPARE(theMaterial->transmissionMap,
1221 QSSGRenderableImage::Type::Transmission,
1223 CHECK_IMAGE_AND_PREPARE(theMaterial->thicknessMap,
1224 QSSGRenderableImage::Type::Thickness,
1226 if (theMaterial->type == QSSGRenderGraphObject::Type::PrincipledMaterial) {
1227 CHECK_IMAGE_AND_PREPARE(theMaterial->metalnessMap,
1228 QSSGRenderableImage::Type::Metalness,
1230 }
1231 } else {
1232 CHECK_IMAGE_AND_PREPARE(theMaterial->colorMap,
1233 QSSGRenderableImage::Type::Diffuse,
1235 }
1236 CHECK_IMAGE_AND_PREPARE(theMaterial->emissiveMap, QSSGRenderableImage::Type::Emissive, QSSGShaderDefaultMaterialKeyProperties::EmissiveMap);
1237 CHECK_IMAGE_AND_PREPARE(theMaterial->specularReflection,
1238 QSSGRenderableImage::Type::Specular,
1240 CHECK_IMAGE_AND_PREPARE(theMaterial->roughnessMap,
1241 QSSGRenderableImage::Type::Roughness,
1243 CHECK_IMAGE_AND_PREPARE(theMaterial->opacityMap, QSSGRenderableImage::Type::Opacity, QSSGShaderDefaultMaterialKeyProperties::OpacityMap);
1244 CHECK_IMAGE_AND_PREPARE(theMaterial->bumpMap, QSSGRenderableImage::Type::Bump, QSSGShaderDefaultMaterialKeyProperties::BumpMap);
1245 CHECK_IMAGE_AND_PREPARE(theMaterial->specularMap,
1246 QSSGRenderableImage::Type::SpecularAmountMap,
1248 CHECK_IMAGE_AND_PREPARE(theMaterial->normalMap, QSSGRenderableImage::Type::Normal, QSSGShaderDefaultMaterialKeyProperties::NormalMap);
1249 CHECK_IMAGE_AND_PREPARE(theMaterial->translucencyMap,
1250 QSSGRenderableImage::Type::Translucency,
1252 }
1253#undef CHECK_IMAGE_AND_PREPARE
1254
1255 if (subsetOpacity < QSSG_RENDER_MINIMUM_RENDER_OPACITY) {
1256 subsetOpacity = 0.0f;
1257 // You can still pick against completely transparent objects(or rather their bounding
1258 // box)
1259 // you just don't render them.
1262 }
1263
1264 if (subsetOpacity > 1.f - QSSG_RENDER_MINIMUM_RENDER_OPACITY)
1265 subsetOpacity = 1.f;
1266 else
1268
1269 if (inMaterial.isTransmissionEnabled()) {
1270 ioFlags.setRequiresScreenTexture(true);
1271 ioFlags.setRequiresMipmapsForScreenTexture(true);
1273 }
1274
1275 retval.firstImage = firstImage;
1276 if (retval.renderableFlags.isDirty())
1277 retval.dirty = true;
1278 if (retval.dirty)
1279 renderer->addMaterialDirtyClear(&inMaterial);
1280 return retval;
1281}
1282
1283QSSGDefaultMaterialPreparationResult QSSGLayerRenderData::prepareCustomMaterialForRender(
1284 QSSGRenderCustomMaterial &inMaterial, QSSGRenderableObjectFlags &inExistingFlags,
1285 float inOpacity, bool alreadyDirty, const QSSGShaderLightListView &lights,
1287{
1290 lights, inExistingFlags.receivesShadows()));
1291 retval.renderableFlags = inExistingFlags;
1292 QSSGRenderableObjectFlags &renderableFlags(retval.renderableFlags);
1293 QSSGShaderDefaultMaterialKey &theGeneratedKey(retval.materialKey);
1294 retval.opacity = inOpacity;
1295 float &subsetOpacity(retval.opacity);
1296
1297 if (subsetOpacity < QSSG_RENDER_MINIMUM_RENDER_OPACITY) {
1298 subsetOpacity = 0.0f;
1299 // You can still pick against completely transparent objects(or rather their bounding
1300 // box)
1301 // you just don't render them.
1304 }
1305
1306 if (subsetOpacity > 1.f - QSSG_RENDER_MINIMUM_RENDER_OPACITY)
1307 subsetOpacity = 1.f;
1308 else
1310
1311 defaultMaterialShaderKeyProperties.m_specularAAEnabled.setValue(theGeneratedKey, layer.specularAAEnabled);
1312
1313 // isDoubleSided
1314 defaultMaterialShaderKeyProperties.m_isDoubleSided.setValue(theGeneratedKey,
1315 inMaterial.m_cullMode == QSSGCullFaceMode::Disabled);
1316
1317 // Does the material override the position output
1318 const bool overridesPosition = inMaterial.m_renderFlags.testFlag(QSSGRenderCustomMaterial::RenderFlag::OverridesPosition);
1319 defaultMaterialShaderKeyProperties.m_overridesPosition.setValue(theGeneratedKey, overridesPosition);
1320
1321 // Optional usage of PROJECTION_MATRIX and/or INVERSE_PROJECTION_MATRIX
1322 const bool usesProjectionMatrix = inMaterial.m_renderFlags.testFlag(QSSGRenderCustomMaterial::RenderFlag::ProjectionMatrix);
1323 defaultMaterialShaderKeyProperties.m_usesProjectionMatrix.setValue(theGeneratedKey, usesProjectionMatrix);
1324 const bool usesInvProjectionMatrix = inMaterial.m_renderFlags.testFlag(QSSGRenderCustomMaterial::RenderFlag::InverseProjectionMatrix);
1325 defaultMaterialShaderKeyProperties.m_usesInverseProjectionMatrix.setValue(theGeneratedKey, usesInvProjectionMatrix);
1326
1327 // vertex attribute presence flags
1328 setVertexInputPresence(renderableFlags, theGeneratedKey);
1329
1330 // set the flag indicating the need for gl_PointSize
1331 defaultMaterialShaderKeyProperties.m_usesPointsTopology.setValue(theGeneratedKey, renderableFlags.isPointsTopology());
1332
1333 // propagate the flag indicating the presence of a lightmap
1334 defaultMaterialShaderKeyProperties.m_lightmapEnabled.setValue(theGeneratedKey, renderableFlags.rendersWithLightmap());
1335
1336 // debug modes
1337 defaultMaterialShaderKeyProperties.m_debugMode.setValue(theGeneratedKey, int(layer.debugMode));
1338
1339 // fog
1340 defaultMaterialShaderKeyProperties.m_fogEnabled.setValue(theGeneratedKey, layer.fog.enabled);
1341
1342 // multiview
1343 const auto &rhiCtx = renderer->contextInterface()->rhiContext();
1344 defaultMaterialShaderKeyProperties.m_viewCount.setValue(theGeneratedKey, rhiCtx->mainPassViewCount());
1345 defaultMaterialShaderKeyProperties.m_usesViewIndex.setValue(theGeneratedKey,
1346 inMaterial.m_renderFlags.testFlag(QSSGRenderCustomMaterial::RenderFlag::ViewIndex));
1347
1348 // Knowing whether VAR_COLOR is used becomes relevant when there is no
1349 // custom vertex shader, but VAR_COLOR is present in the custom fragment
1350 // snippet, because that case needs special care.
1351 const bool usesVarColor = inMaterial.m_renderFlags.testFlag(QSSGRenderCustomMaterial::RenderFlag::VarColor);
1352 defaultMaterialShaderKeyProperties.m_usesVarColor.setValue(theGeneratedKey, usesVarColor);
1353
1354 if (inMaterial.m_renderFlags.testFlag(QSSGRenderCustomMaterial::RenderFlag::Blending))
1356
1357 if (inMaterial.m_renderFlags.testFlag(QSSGRenderCustomMaterial::RenderFlag::ScreenTexture)) {
1358 ioFlags.setRequiresScreenTexture(true);
1360 }
1361
1362 if (inMaterial.m_renderFlags.testFlag(QSSGRenderCustomMaterial::RenderFlag::ScreenMipTexture)) {
1363 ioFlags.setRequiresScreenTexture(true);
1364 ioFlags.setRequiresMipmapsForScreenTexture(true);
1366 }
1367
1368 if (inMaterial.m_renderFlags.testFlag(QSSGRenderCustomMaterial::RenderFlag::DepthTexture))
1369 ioFlags.setRequiresDepthTexture(true);
1370
1371 if (inMaterial.m_renderFlags.testFlag(QSSGRenderCustomMaterial::RenderFlag::AoTexture)) {
1372 ioFlags.setRequiresDepthTexture(true);
1373 ioFlags.setRequiresSsaoPass(true);
1374 }
1375
1376 retval.firstImage = nullptr;
1377
1378 if (retval.dirty || alreadyDirty)
1379 renderer->addMaterialDirtyClear(&inMaterial);
1380 return retval;
1381}
1382
1383enum class CullUnrenderables { Off, On };
1384template <CullUnrenderables cull = CullUnrenderables::On>
1386{
1387 const auto originalModelCount = renderableModels.size();
1388 auto end = originalModelCount;
1389
1390 for (int idx = 0; idx < end; ++idx) {
1391 const auto &renderable = renderableModels.at(idx);
1392 const QSSGRenderModel &model = *static_cast<QSSGRenderModel *>(renderable.node);
1393 // Ensure we have at least 1 material
1394 if ((renderable.overridden & QSSGRenderableNodeEntry::Overridden::Materials) == 0)
1395 renderable.materials = model.materials;
1396
1397 if constexpr (cull == CullUnrenderables::On) {
1398 const bool isDisabled = ((renderable.overridden & QSSGRenderableNodeEntry::Overridden::Disabled) != 0);
1399 if (isDisabled || renderable.materials.isEmpty()) {
1400 // Swap current (idx) and last item (--end).
1401 // Note, post-decrement idx to ensure we recheck the new current item on next iteration
1402 // and pre-decrement the end move the end of the list to not include the culled renderable.
1403 renderableModels.swapItemsAt(idx--, --end);
1404 }
1405 }
1406 }
1407
1408 if constexpr (cull == CullUnrenderables::On) {
1409 // Any models without materials get dropped right here
1410 if (end != originalModelCount)
1411 renderableModels.resize(end);
1412 }
1413}
1414
1415void QSSGLayerRenderData::prepareModelMaterials(RenderableNodeEntries &renderableModels, bool cullUnrenderables)
1416{
1417 if (cullUnrenderables)
1418 prepareModelMaterialsImpl<CullUnrenderables::On>(renderableModels);
1419 else
1420 prepareModelMaterialsImpl<CullUnrenderables::Off>(renderableModels);
1421}
1422
1423void QSSGLayerRenderData::prepareModelMeshes(const QSSGRenderContextInterface &contextInterface,
1424 RenderableNodeEntries &renderableModels,
1425 bool globalPickingEnabled)
1426{
1427 const auto &bufferManager = contextInterface.bufferManager();
1428
1429 const auto originalModelCount = renderableModels.size();
1430 auto end = originalModelCount;
1431
1432 for (int idx = 0; idx < end; ++idx) {
1433 // It's up to the BufferManager to employ the appropriate caching mechanisms, so
1434 // loadMesh() is expected to be fast if already loaded. Note that preparing
1435 // the same QSSGRenderModel in different QQuickWindows (possible when a
1436 // scene is shared between View3Ds where the View3Ds belong to different
1437 // windows) leads to a different QSSGRenderMesh since the BufferManager is,
1438 // very correctly, per window, and so per scenegraph render thread.
1439
1440 const auto &renderable = renderableModels.at(idx);
1441 const QSSGRenderModel &model = *static_cast<QSSGRenderModel *>(renderable.node);
1442 // Ensure we have a mesh
1443 if (auto *theMesh = bufferManager->loadMesh(&model)) {
1444 renderable.mesh = theMesh;
1445 // Completely transparent models cannot be pickable. But models with completely
1446 // transparent materials still are. This allows the artist to control pickability
1447 // in a somewhat fine-grained style.
1448 const bool canModelBePickable = (model.globalOpacity > QSSG_RENDER_MINIMUM_RENDER_OPACITY)
1449 && (globalPickingEnabled
1451 if (canModelBePickable) {
1452 // Check if there is BVH data, if not generate it
1453 if (!theMesh->bvh) {
1454 if (!model.meshPath.isNull())
1455 theMesh->bvh = bufferManager->loadMeshBVH(model.meshPath);
1456 else if (model.geometry)
1457 theMesh->bvh = bufferManager->loadMeshBVH(model.geometry);
1458
1459 if (theMesh->bvh) {
1460 const auto &roots = theMesh->bvh->roots();
1461 for (qsizetype i = 0, end = qsizetype(roots.size()); i < end; ++i)
1462 theMesh->subsets[i].bvhRoot = roots[i];
1463 }
1464 }
1465 }
1466 } else {
1467 // Swap current (idx) and last item (--end).
1468 // Note, post-decrement idx to ensure we recheck the new current item on next iteration
1469 // and pre-decrement the end move the end of the list to not include the culled renderable.
1471 }
1472 }
1473
1474 // Any models without a mesh get dropped right here
1475 if (end != originalModelCount)
1477
1478 // Now is the time to kick off the vertex/index buffer updates for all the
1479 // new meshes (and their submeshes). This here is the last possible place
1480 // to kick this off because the rest of the rendering pipeline will only
1481 // see the individual sub-objects as "renderable objects".
1482 bufferManager->commitBufferResourceUpdates();
1483}
1484
1486{
1487 lightmapTextures[&modelContext] = lightmapTexture;
1488}
1489
1491{
1492 QRhiTexture *ret = nullptr;
1493 if (modelContext.model.hasLightmap()) {
1494 const auto it = lightmapTextures.constFind(&modelContext);
1495 ret = (it != lightmapTextures.cend()) ? *it : nullptr;
1496 }
1497
1498 return ret;
1499}
1500
1502{
1503 bonemapTextures[&modelContext] = bonemapTexture;
1504}
1505
1507{
1508 QRhiTexture *ret = nullptr;
1509 if (modelContext.model.usesBoneTexture()) {
1510 const auto it = bonemapTextures.constFind(&modelContext);
1511 ret = (it != bonemapTextures.cend()) ? *it : nullptr;
1512 }
1513
1514 return ret;
1515}
1516
1517// inModel is const to emphasize the fact that its members cannot be written
1518// here: in case there is a scene shared between multiple View3Ds in different
1519// QQuickWindows, each window may run this in their own render thread, while
1520// inModel is the same.
1522 const RenderableNodeEntries &renderableModels,
1524 const QSSGRenderCameraList &allCameras,
1525 const QSSGRenderCameraDataList &allCameraData,
1526 TModelContextPtrList &modelContexts,
1527 QSSGRenderableObjectList &opaqueObjects,
1528 QSSGRenderableObjectList &transparentObjects,
1529 QSSGRenderableObjectList &screenTextureObjects,
1530 float lodThreshold)
1531{
1532 const auto &rhiCtx = contextInterface.rhiContext();
1533 const auto &bufferManager = contextInterface.bufferManager();
1534
1535 const auto &debugDrawSystem = contextInterface.debugDrawSystem();
1536 const bool maybeDebugDraw = debugDrawSystem && debugDrawSystem->isEnabled();
1537
1538 bool wasDirty = false;
1539
1540 for (const QSSGRenderableNodeEntry &renderable : renderableModels) {
1541 if ((renderable.overridden & QSSGRenderableNodeEntry::Overridden::Disabled) != 0)
1542 continue;
1543
1544 const QSSGRenderModel &model = *static_cast<QSSGRenderModel *>(renderable.node);
1545 const auto &lights = renderable.lights;
1546 QSSGRenderMesh *theMesh = renderable.mesh;
1547
1548 QSSG_ASSERT_X(theMesh != nullptr, "Only renderables with a mesh will be processed!", continue);
1549
1550 const bool altGlobalTransform = ((renderable.overridden & QSSGRenderableNodeEntry::Overridden::GlobalTransform) != 0);
1551 const auto &globalTransform = altGlobalTransform ? renderable.globalTransform : model.globalTransform;
1552 QSSGModelContext &theModelContext = *RENDER_FRAME_NEW<QSSGModelContext>(contextInterface, model, globalTransform, allCameraData);
1553 modelContexts.push_back(&theModelContext);
1554 // We might over-allocate here, as the material list technically can contain an invalid (nullptr) material.
1555 // We'll fix that by adjusting the size at the end for now...
1556 const auto &meshSubsets = theMesh->subsets;
1557 const auto meshSubsetCount = meshSubsets.size();
1558 theModelContext.subsets = RENDER_FRAME_NEW_BUFFER<QSSGSubsetRenderable>(contextInterface, meshSubsetCount);
1559
1560 // Prepare boneTexture for skinning
1561 if (model.skin) {
1562 auto boneTexture = bufferManager->loadSkinmap(model.skin);
1563 setBonemapTexture(theModelContext, boneTexture.m_texture);
1564 } else if (model.skeleton) {
1565 auto boneTexture = bufferManager->loadSkinmap(&(model.skeleton->boneTexData));
1566 setBonemapTexture(theModelContext, boneTexture.m_texture);
1567 } else {
1568 setBonemapTexture(theModelContext, nullptr);
1569 }
1570
1571 // many renderableFlags are the same for all the subsets
1572 QSSGRenderableObjectFlags renderableFlagsForModel;
1573
1574 if (meshSubsetCount > 0) {
1575 const QSSGRenderSubset &theSubset = meshSubsets.at(0);
1576
1577 renderableFlagsForModel.setCastsShadows(model.castsShadows);
1578 renderableFlagsForModel.setReceivesShadows(model.receivesShadows);
1579 renderableFlagsForModel.setReceivesReflections(model.receivesReflections);
1580 renderableFlagsForModel.setCastsReflections(model.castsReflections);
1581
1582 renderableFlagsForModel.setUsedInBakedLighting(model.usedInBakedLighting);
1583 if (model.hasLightmap()) {
1584 QSSGRenderImageTexture lmImageTexture = bufferManager->loadLightmap(model);
1585 if (lmImageTexture.m_texture) {
1586 renderableFlagsForModel.setRendersWithLightmap(true);
1587 setLightmapTexture(theModelContext, lmImageTexture.m_texture);
1588 }
1589 }
1590
1591 // TODO: This should be a oneshot thing, move the flags over!
1592 // With the RHI we need to be able to tell the material shader
1593 // generator to not generate vertex input attributes that are not
1594 // provided by the mesh. (because unlike OpenGL, other graphics
1595 // APIs may treat unbound vertex inputs as a fatal error)
1596 bool hasJoint = false;
1597 bool hasWeight = false;
1598 bool hasMorphTarget = theSubset.rhi.targetsTexture != nullptr;
1599 for (const QSSGRhiInputAssemblerState::InputSemantic &sem : std::as_const(theSubset.rhi.ia.inputs)) {
1600 if (sem == QSSGRhiInputAssemblerState::PositionSemantic) {
1601 renderableFlagsForModel.setHasAttributePosition(true);
1602 } else if (sem == QSSGRhiInputAssemblerState::NormalSemantic) {
1603 renderableFlagsForModel.setHasAttributeNormal(true);
1604 } else if (sem == QSSGRhiInputAssemblerState::TexCoord0Semantic) {
1605 renderableFlagsForModel.setHasAttributeTexCoord0(true);
1606 } else if (sem == QSSGRhiInputAssemblerState::TexCoord1Semantic) {
1607 renderableFlagsForModel.setHasAttributeTexCoord1(true);
1608 } else if (sem == QSSGRhiInputAssemblerState::TexCoordLightmapSemantic) {
1609 renderableFlagsForModel.setHasAttributeTexCoordLightmap(true);
1610 } else if (sem == QSSGRhiInputAssemblerState::TangentSemantic) {
1611 renderableFlagsForModel.setHasAttributeTangent(true);
1612 } else if (sem == QSSGRhiInputAssemblerState::BinormalSemantic) {
1613 renderableFlagsForModel.setHasAttributeBinormal(true);
1614 } else if (sem == QSSGRhiInputAssemblerState::ColorSemantic) {
1615 renderableFlagsForModel.setHasAttributeColor(true);
1616 // For skinning, we will set the HasAttribute only
1617 // if the mesh has both joint and weight
1618 } else if (sem == QSSGRhiInputAssemblerState::JointSemantic) {
1619 hasJoint = true;
1620 } else if (sem == QSSGRhiInputAssemblerState::WeightSemantic) {
1621 hasWeight = true;
1622 }
1623 }
1624 renderableFlagsForModel.setHasAttributeJointAndWeight(hasJoint && hasWeight);
1625 renderableFlagsForModel.setHasAttributeMorphTarget(hasMorphTarget);
1626 }
1627
1628 QSSGRenderableObjectList bakedLightingObjects;
1629 bool usesBlendParticles = particlesEnabled && theModelContext.model.particleBuffer != nullptr
1630 && model.particleBuffer->particleCount();
1631
1632 // Subset(s)
1633 auto &renderableSubsets = theModelContext.subsets;
1634 const auto &materials = renderable.materials;
1635 const auto materialCount = materials.size();
1636 const bool altModelOpacity = ((renderable.overridden & QSSGRenderableNodeEntry::Overridden::GlobalOpacity) != 0);
1637 const float modelOpacity = altModelOpacity ? renderable.globalOpacity : model.globalOpacity;
1638 QSSGRenderGraphObject *lastMaterial = !materials.isEmpty() ? materials.last() : nullptr;
1639 int idx = 0, subsetIdx = 0;
1640 for (; idx < meshSubsetCount; ++idx) {
1641 // If the materials list < size of subsets, then use the last material for the rest
1642 QSSGRenderGraphObject *theMaterialObject = (idx >= materialCount) ? lastMaterial : materials[idx];
1643 QSSG_ASSERT_X(theMaterialObject != nullptr, "No material found for model!", continue);
1644
1645 const QSSGRenderSubset &theSubset = meshSubsets.at(idx);
1646 QSSGRenderableObjectFlags renderableFlags = renderableFlagsForModel;
1647 float subsetOpacity = modelOpacity;
1648
1649 renderableFlags.setPointsTopology(theSubset.rhi.ia.topology == QRhiGraphicsPipeline::Points);
1650 QSSGRenderableObject *theRenderableObject = &renderableSubsets[subsetIdx++];
1651
1652 bool usesInstancing = theModelContext.model.instancing()
1653 && rhiCtx->rhi()->isFeatureSupported(QRhi::Instancing);
1654 if (usesInstancing && theModelContext.model.instanceTable->hasTransparency())
1656 if (theModelContext.model.hasTransparency)
1658
1659 // Level Of Detail
1660 quint32 subsetLevelOfDetail = 0;
1661 if (!theSubset.lods.isEmpty() && lodThreshold > 0.0f) {
1662 // Accounts for FOV
1663 float lodDistanceMultiplier = cameras[0]->getLevelOfDetailMultiplier();
1664 float distanceThreshold = 0.0f;
1665 const auto scale = QSSGUtils::mat44::getScale(model.globalTransform);
1666 float modelScale = qMax(scale.x(), qMax(scale.y(), scale.z()));
1667 QSSGBounds3 transformedBounds = theSubset.bounds;
1668 if (cameras[0]->type != QSSGRenderGraphObject::Type::OrthographicCamera) {
1669 transformedBounds.transform(model.globalTransform);
1670 if (maybeDebugDraw && debugDrawSystem->isEnabled(QSSGDebugDrawSystem::Mode::MeshLod))
1671 debugDrawSystem->drawBounds(transformedBounds, QColor(Qt::red));
1672 const QVector3D cameraNormal = cameras[0]->getScalingCorrectDirection();
1673 const QVector3D cameraPosition = cameras[0]->getGlobalPos();
1674 const QSSGPlane cameraPlane = QSSGPlane(cameraPosition, cameraNormal);
1675 const QVector3D lodSupportMin = transformedBounds.getSupport(-cameraNormal);
1676 const QVector3D lodSupportMax = transformedBounds.getSupport(cameraNormal);
1677 if (maybeDebugDraw && debugDrawSystem->isEnabled(QSSGDebugDrawSystem::Mode::MeshLod))
1678 debugDrawSystem->drawPoint(lodSupportMin, QColor("orange"));
1679
1680 const float distanceMin = cameraPlane.distance(lodSupportMin);
1681 const float distanceMax = cameraPlane.distance(lodSupportMax);
1682
1683 if (distanceMin * distanceMax < 0.0)
1684 distanceThreshold = 0.0;
1685 else if (distanceMin >= 0.0)
1686 distanceThreshold = distanceMin;
1687 else if (distanceMax <= 0.0)
1688 distanceThreshold = -distanceMax;
1689
1690 } else {
1691 // Orthographic Projection
1692 distanceThreshold = 1.0;
1693 }
1694
1695 int currentLod = -1;
1696 if (model.levelOfDetailBias > 0.0f) {
1697 const float threshold = distanceThreshold * lodDistanceMultiplier;
1698 const float modelBias = 1 / model.levelOfDetailBias;
1699 for (qsizetype i = 0; i < theSubset.lods.count(); ++i) {
1700 float subsetDistance = theSubset.lods[i].distance * modelScale * modelBias;
1701 float screenSize = subsetDistance / threshold;
1702 if (screenSize > lodThreshold)
1703 break;
1704 currentLod = i;
1705 }
1706 }
1707 if (currentLod == -1)
1708 subsetLevelOfDetail = 0;
1709 else
1710 subsetLevelOfDetail = currentLod + 1;
1711 if (maybeDebugDraw && debugDrawSystem->isEnabled(QSSGDebugDrawSystem::Mode::MeshLod))
1712 debugDrawSystem->drawBounds(transformedBounds, QSSGDebugDrawSystem::levelOfDetailColor(subsetLevelOfDetail));
1713 }
1714
1715 QVector3D theModelCenter(theSubset.bounds.center());
1716 theModelCenter = QSSGUtils::mat44::transform(model.globalTransform, theModelCenter);
1717 if (maybeDebugDraw && debugDrawSystem->isEnabled(QSSGDebugDrawSystem::Mode::MeshLodNormal))
1718 debugDrawSystem->debugNormals(*bufferManager, theModelContext, theSubset, subsetLevelOfDetail, (theModelCenter - allCameras[0]->getGlobalPos()).length() * 0.01);
1719
1720 static auto checkF32TypeIndex = [&rhiCtx](QRhiVertexInputAttribute::Format f) {
1725 return true;
1726 }
1727 if (!rhiCtx->rhi()->isFeatureSupported(QRhi::IntAttributes))
1728 qWarning() << "WARN: Model has non-integer type indices for skinning but current RHI backend doesn't support it!";
1729 return false;
1730 };
1731
1732 if (theMaterialObject->type == QSSGRenderGraphObject::Type::DefaultMaterial ||
1733 theMaterialObject->type == QSSGRenderGraphObject::Type::PrincipledMaterial ||
1734 theMaterialObject->type == QSSGRenderGraphObject::Type::SpecularGlossyMaterial) {
1735 QSSGRenderDefaultMaterial &theMaterial(static_cast<QSSGRenderDefaultMaterial &>(*theMaterialObject));
1736 QSSGDefaultMaterialPreparationResult theMaterialPrepResult(prepareDefaultMaterialForRender(theMaterial, renderableFlags, subsetOpacity, lights, ioFlags));
1737 QSSGShaderDefaultMaterialKey &theGeneratedKey(theMaterialPrepResult.materialKey);
1738 subsetOpacity = theMaterialPrepResult.opacity;
1739 QSSGRenderableImage *firstImage(theMaterialPrepResult.firstImage);
1740 wasDirty |= theMaterialPrepResult.dirty;
1741 renderableFlags = theMaterialPrepResult.renderableFlags;
1742
1743 // Blend particles
1744 defaultMaterialShaderKeyProperties.m_blendParticles.setValue(theGeneratedKey, usesBlendParticles);
1745
1746 // Skin
1747 const auto boneCount = model.skin ? model.skin->boneCount :
1748 model.skeleton ? model.skeleton->boneCount : 0;
1749 defaultMaterialShaderKeyProperties.m_boneCount.setValue(theGeneratedKey, boneCount);
1750 if (auto idJoint = theSubset.rhi.ia.inputs.indexOf(QSSGRhiInputAssemblerState::JointSemantic); idJoint != -1) {
1751 const auto attr = theSubset.rhi.ia.inputLayout.attributeAt(idJoint);
1752 defaultMaterialShaderKeyProperties.m_usesFloatJointIndices.setValue(theGeneratedKey, checkF32TypeIndex(attr->format()));
1753 }
1754
1755 // Instancing
1756 defaultMaterialShaderKeyProperties.m_usesInstancing.setValue(theGeneratedKey, usesInstancing);
1757 // Morphing
1758 defaultMaterialShaderKeyProperties.m_targetCount.setValue(theGeneratedKey,
1759 theSubset.rhi.ia.targetCount);
1760 defaultMaterialShaderKeyProperties.m_targetPositionOffset.setValue(theGeneratedKey,
1761 theSubset.rhi.ia.targetOffsets[QSSGRhiInputAssemblerState::PositionSemantic]);
1762 defaultMaterialShaderKeyProperties.m_targetNormalOffset.setValue(theGeneratedKey,
1763 theSubset.rhi.ia.targetOffsets[QSSGRhiInputAssemblerState::NormalSemantic]);
1764 defaultMaterialShaderKeyProperties.m_targetTangentOffset.setValue(theGeneratedKey,
1765 theSubset.rhi.ia.targetOffsets[QSSGRhiInputAssemblerState::TangentSemantic]);
1766 defaultMaterialShaderKeyProperties.m_targetBinormalOffset.setValue(theGeneratedKey,
1767 theSubset.rhi.ia.targetOffsets[QSSGRhiInputAssemblerState::BinormalSemantic]);
1768 defaultMaterialShaderKeyProperties.m_targetTexCoord0Offset.setValue(theGeneratedKey,
1769 theSubset.rhi.ia.targetOffsets[QSSGRhiInputAssemblerState::TexCoord0Semantic]);
1770 defaultMaterialShaderKeyProperties.m_targetTexCoord1Offset.setValue(theGeneratedKey,
1771 theSubset.rhi.ia.targetOffsets[QSSGRhiInputAssemblerState::TexCoord1Semantic]);
1772 defaultMaterialShaderKeyProperties.m_targetColorOffset.setValue(theGeneratedKey,
1773 theSubset.rhi.ia.targetOffsets[QSSGRhiInputAssemblerState::ColorSemantic]);
1774
1775 new (theRenderableObject) QSSGSubsetRenderable(QSSGSubsetRenderable::Type::DefaultMaterialMeshSubset,
1776 renderableFlags,
1777 theModelCenter,
1778 renderer,
1779 theSubset,
1780 theModelContext,
1781 subsetOpacity,
1782 subsetLevelOfDetail,
1783 theMaterial,
1784 firstImage,
1785 theGeneratedKey,
1786 lights);
1787 wasDirty = wasDirty || renderableFlags.isDirty();
1788 } else if (theMaterialObject->type == QSSGRenderGraphObject::Type::CustomMaterial) {
1789 QSSGRenderCustomMaterial &theMaterial(static_cast<QSSGRenderCustomMaterial &>(*theMaterialObject));
1790
1791 const auto &theMaterialSystem(contextInterface.customMaterialSystem());
1792 wasDirty |= theMaterialSystem->prepareForRender(theModelContext.model, theSubset, theMaterial);
1793
1794 QSSGDefaultMaterialPreparationResult theMaterialPrepResult(
1795 prepareCustomMaterialForRender(theMaterial, renderableFlags, subsetOpacity, wasDirty,
1796 lights, ioFlags));
1797 QSSGShaderDefaultMaterialKey &theGeneratedKey(theMaterialPrepResult.materialKey);
1798 subsetOpacity = theMaterialPrepResult.opacity;
1799 QSSGRenderableImage *firstImage(theMaterialPrepResult.firstImage);
1800 renderableFlags = theMaterialPrepResult.renderableFlags;
1801
1802 if (model.particleBuffer && model.particleBuffer->particleCount())
1803 defaultMaterialShaderKeyProperties.m_blendParticles.setValue(theGeneratedKey, true);
1804 else
1805 defaultMaterialShaderKeyProperties.m_blendParticles.setValue(theGeneratedKey, false);
1806
1807 // Skin
1808 const auto boneCount = model.skin ? model.skin->boneCount :
1809 model.skeleton ? model.skeleton->boneCount : 0;
1810 defaultMaterialShaderKeyProperties.m_boneCount.setValue(theGeneratedKey, boneCount);
1811 if (auto idJoint = theSubset.rhi.ia.inputs.indexOf(QSSGRhiInputAssemblerState::JointSemantic); idJoint != -1) {
1812 const auto attr = theSubset.rhi.ia.inputLayout.attributeAt(idJoint);
1813 defaultMaterialShaderKeyProperties.m_usesFloatJointIndices.setValue(theGeneratedKey, checkF32TypeIndex(attr->format()));
1814 }
1815
1816 // Instancing
1817 bool usesInstancing = theModelContext.model.instancing()
1818 && rhiCtx->rhi()->isFeatureSupported(QRhi::Instancing);
1819 defaultMaterialShaderKeyProperties.m_usesInstancing.setValue(theGeneratedKey, usesInstancing);
1820 // Morphing
1821 defaultMaterialShaderKeyProperties.m_targetCount.setValue(theGeneratedKey,
1822 theSubset.rhi.ia.targetCount);
1823 defaultMaterialShaderKeyProperties.m_targetPositionOffset.setValue(theGeneratedKey,
1824 theSubset.rhi.ia.targetOffsets[QSSGRhiInputAssemblerState::PositionSemantic]);
1825 defaultMaterialShaderKeyProperties.m_targetNormalOffset.setValue(theGeneratedKey,
1826 theSubset.rhi.ia.targetOffsets[QSSGRhiInputAssemblerState::NormalSemantic]);
1827 defaultMaterialShaderKeyProperties.m_targetTangentOffset.setValue(theGeneratedKey,
1828 theSubset.rhi.ia.targetOffsets[QSSGRhiInputAssemblerState::TangentSemantic]);
1829 defaultMaterialShaderKeyProperties.m_targetBinormalOffset.setValue(theGeneratedKey,
1830 theSubset.rhi.ia.targetOffsets[QSSGRhiInputAssemblerState::BinormalSemantic]);
1831 defaultMaterialShaderKeyProperties.m_targetTexCoord0Offset.setValue(theGeneratedKey,
1832 theSubset.rhi.ia.targetOffsets[QSSGRhiInputAssemblerState::TexCoord0Semantic]);
1833 defaultMaterialShaderKeyProperties.m_targetTexCoord1Offset.setValue(theGeneratedKey,
1834 theSubset.rhi.ia.targetOffsets[QSSGRhiInputAssemblerState::TexCoord1Semantic]);
1835 defaultMaterialShaderKeyProperties.m_targetColorOffset.setValue(theGeneratedKey,
1836 theSubset.rhi.ia.targetOffsets[QSSGRhiInputAssemblerState::ColorSemantic]);
1837
1838 if (theMaterial.m_iblProbe)
1839 theMaterial.m_iblProbe->clearDirty();
1840
1841 new (theRenderableObject) QSSGSubsetRenderable(QSSGSubsetRenderable::Type::CustomMaterialMeshSubset,
1842 renderableFlags,
1843 theModelCenter,
1844 renderer,
1845 theSubset,
1846 theModelContext,
1847 subsetOpacity,
1848 subsetLevelOfDetail,
1849 theMaterial,
1850 firstImage,
1851 theGeneratedKey,
1852 lights);
1853 }
1854 if (theRenderableObject) // NOTE: Should just go in with the ctor args
1855 theRenderableObject->camdistSq = getCameraDistanceSq(*theRenderableObject, allCameraData[0]);
1856 }
1857
1858 // If the indices don't match then something's off and we need to adjust the subset renderable list size.
1859 if (Q_UNLIKELY(idx != subsetIdx))
1860 renderableSubsets.mSize = subsetIdx + 1;
1861
1862 for (auto &ro : renderableSubsets) {
1863 const auto depthMode = ro.depthWriteMode;
1864 hasDepthWriteObjects |= (depthMode == QSSGDepthDrawMode::Always || depthMode == QSSGDepthDrawMode::OpaqueOnly);
1865 enum ObjectType : quint8 { ScreenTexture, Transparent, Opaque };
1866 static constexpr DepthPrepassObject ppState[][2] = { {DepthPrepassObject::None, DepthPrepassObject::ScreenTexture},
1867 {DepthPrepassObject::None, DepthPrepassObject::Transparent},
1868 {DepthPrepassObject::None, DepthPrepassObject::Opaque} };
1869
1870 if (ro.renderableFlags.requiresScreenTexture()) {
1871 depthPrepassObjectsState |= DepthPrepassObjectStateT(ppState[ObjectType::ScreenTexture][size_t(depthMode == QSSGDepthDrawMode::OpaquePrePass)]);
1872 screenTextureObjects.push_back({&ro, ro.camdistSq});
1873 } else if (ro.renderableFlags.hasTransparency()) {
1874 depthPrepassObjectsState |= DepthPrepassObjectStateT(ppState[ObjectType::Transparent][size_t(depthMode == QSSGDepthDrawMode::OpaquePrePass)]);
1875 transparentObjects.push_back({&ro, ro.camdistSq});
1876 } else {
1877 depthPrepassObjectsState |= DepthPrepassObjectStateT(ppState[ObjectType::Opaque][size_t(depthMode == QSSGDepthDrawMode::OpaquePrePass)]);
1878 opaqueObjects.push_back({&ro, ro.camdistSq});
1879 }
1880
1881 if (ro.renderableFlags.usedInBakedLighting())
1882 bakedLightingObjects.push_back({&ro, ro.camdistSq});
1883 }
1884
1885 if (!bakedLightingObjects.isEmpty())
1886 bakedLightingModels.push_back(QSSGBakedLightingModel(&model, bakedLightingObjects));
1887 }
1888
1889 return wasDirty;
1890}
1891
1893{
1894 QSSG_ASSERT(particlesEnabled, return false);
1895
1897
1898 bool dirty = false;
1899
1900 //
1901 auto &opaqueObjects = opaqueObjectStore[0];
1902 auto &transparentObjects = transparentObjectStore[0];
1903 auto &screenTextureObjects = screenTextureObjectStore[0];
1904
1905 for (const auto &renderable : renderableParticles) {
1906 const QSSGRenderParticles &particles = *static_cast<QSSGRenderParticles *>(renderable.node);
1907 const auto &lights = renderable.lights;
1908
1909 QSSGRenderableObjectFlags renderableFlags;
1910 renderableFlags.setCastsShadows(false);
1911 renderableFlags.setReceivesShadows(false);
1912 renderableFlags.setHasAttributePosition(true);
1913 renderableFlags.setHasAttributeNormal(true);
1914 renderableFlags.setHasAttributeTexCoord0(true);
1915 renderableFlags.setHasAttributeColor(true);
1916 renderableFlags.setHasTransparency(particles.m_hasTransparency);
1917 renderableFlags.setCastsReflections(particles.m_castsReflections);
1918
1919 float opacity = particles.globalOpacity;
1920 QVector3D center(particles.m_particleBuffer.bounds().center());
1921 center = QSSGUtils::mat44::transform(particles.globalTransform, center);
1922
1923 QSSGRenderableImage *firstImage = nullptr;
1924 if (particles.m_sprite) {
1925 const auto &bufferManager = contextInterface.bufferManager();
1926
1927 if (particles.m_sprite->clearDirty())
1928 dirty = true;
1929
1930 const QSSGRenderImageTexture texture = bufferManager->loadRenderImage(particles.m_sprite);
1931 QSSGRenderableImage *theImage = RENDER_FRAME_NEW<QSSGRenderableImage>(contextInterface, QSSGRenderableImage::Type::Diffuse, *particles.m_sprite, texture);
1932 firstImage = theImage;
1933 }
1934
1935 QSSGRenderableImage *colorTable = nullptr;
1936 if (particles.m_colorTable) {
1937 const auto &bufferManager = contextInterface.bufferManager();
1938
1939 if (particles.m_colorTable->clearDirty())
1940 dirty = true;
1941
1942 const QSSGRenderImageTexture texture = bufferManager->loadRenderImage(particles.m_colorTable);
1943
1944 QSSGRenderableImage *theImage = RENDER_FRAME_NEW<QSSGRenderableImage>(contextInterface, QSSGRenderableImage::Type::Diffuse, *particles.m_colorTable, texture);
1945 colorTable = theImage;
1946 }
1947
1948 if (opacity > 0.0f && particles.m_particleBuffer.particleCount()) {
1949 auto *theRenderableObject = RENDER_FRAME_NEW<QSSGParticlesRenderable>(contextInterface,
1950 renderableFlags,
1951 center,
1952 renderer,
1953 particles,
1954 firstImage,
1955 colorTable,
1956 lights,
1957 opacity);
1958 if (theRenderableObject) {
1959 if (theRenderableObject->renderableFlags.requiresScreenTexture())
1960 screenTextureObjects.push_back({theRenderableObject, getCameraDistanceSq(*theRenderableObject, cameraData)});
1961 else if (theRenderableObject->renderableFlags.hasTransparency())
1962 transparentObjects.push_back({theRenderableObject, getCameraDistanceSq(*theRenderableObject, cameraData)});
1963 else
1964 opaqueObjects.push_back({theRenderableObject, getCameraDistanceSq(*theRenderableObject, cameraData)});
1965 }
1966 }
1967 }
1968
1969 return dirty;
1970}
1971
1973 const RenderableItem2DEntries &renderableItem2Ds)
1974{
1975 const bool hasItems = (renderableItem2Ds.size() != 0);
1976 if (hasItems) {
1977 const auto &clipSpaceCorrMatrix = ctxIfc.rhiContext()->rhi()->clipSpaceCorrMatrix();
1978 const QSSGRenderCameraDataList &cameraDatas(getCachedCameraDatas());
1979 for (const auto &theItem2D : renderableItem2Ds) {
1980 theItem2D->mvps.clear();
1981 for (const QSSGRenderCameraData &camData : cameraDatas) {
1982 QMatrix4x4 mvp = camData.viewProjection * theItem2D->globalTransform;
1983 static const QMatrix4x4 flipMatrix(1.0f, 0.0f, 0.0f, 0.0f,
1984 0.0f, -1.0f, 0.0f, 0.0f,
1985 0.0f, 0.0f, 1.0f, 0.0f,
1986 0.0f, 0.0f, 0.0f, 1.0f);
1987 mvp = clipSpaceCorrMatrix * mvp * flipMatrix;
1988 theItem2D->mvps.append(mvp);
1989 }
1990 }
1991 }
1992
1993 return hasItems;
1994}
1995
1997{
1999 const auto &bufferManager = contextInterface.bufferManager();
2000
2001 for (const auto resourceLoader : std::as_const(layer.resourceLoaders))
2002 bufferManager->processResourceLoader(static_cast<QSSGRenderResourceLoader *>(resourceLoader));
2003}
2004
2006{
2007 const auto probeCount = reflectionProbes.size();
2008 requestReflectionMapManager(); // ensure that we have a reflection map manager
2009
2010 for (int i = 0; i < probeCount; i++) {
2012
2013 int reflectionObjectCount = 0;
2014 QVector3D probeExtent = probe->boxSize / 2;
2015 QSSGBounds3 probeBound = QSSGBounds3::centerExtents(probe->getGlobalPos() + 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 vmin = renderableObj->globalTransform * vmin;
2025 vmax = renderableObj->globalTransform * vmax;
2026 nodeBound.minimum = vmin.toVector3D();
2027 nodeBound.maximum = vmax.toVector3D();
2028 if (probeBound.intersects(nodeBound)) {
2029 QVector3D nodeBoundCenter = nodeBound.center();
2030 QVector3D probeBoundCenter = probeBound.center();
2031 float distance = nodeBoundCenter.distanceToPoint(probeBoundCenter);
2032 if (renderableObj->reflectionProbeIndex == -1 || distance < renderableObj->distanceFromReflectionProbe) {
2033 renderableObj->reflectionProbeIndex = i;
2034 renderableObj->distanceFromReflectionProbe = distance;
2035 renderableObj->reflectionProbe.parallaxCorrection = probe->parallaxCorrection;
2036 renderableObj->reflectionProbe.probeCubeMapCenter = probe->getGlobalPos();
2037 renderableObj->reflectionProbe.probeBoxMax = probeBound.maximum;
2038 renderableObj->reflectionProbe.probeBoxMin = probeBound.minimum;
2039 renderableObj->reflectionProbe.enabled = true;
2040 reflectionObjectCount++;
2041 }
2042 }
2043 }
2044 };
2045
2046 const auto &transparentObjects = std::as_const(transparentObjectStore[0]);
2047 const auto &opaqueObjects = std::as_const(opaqueObjectStore[0]);
2048 const auto &screenTextureObjects = std::as_const(screenTextureObjectStore[0]);
2049
2050 for (const auto &handle : std::as_const(transparentObjects))
2051 injectProbe(handle);
2052
2053 for (const auto &handle : std::as_const(opaqueObjects))
2054 injectProbe(handle);
2055
2056 for (const auto &handle : std::as_const(screenTextureObjects))
2057 injectProbe(handle);
2058
2059 if (probe->texture)
2060 reflectionMapManager->addTexturedReflectionMapEntry(i, *probe);
2061 else if (reflectionObjectCount > 0)
2062 reflectionMapManager->addReflectionMapEntry(i, *probe);
2063 }
2064}
2065
2066static bool scopeLight(QSSGRenderNode *node, QSSGRenderNode *lightScope)
2067{
2068 // check if the node is parent of the lightScope
2069 while (node) {
2070 if (node == lightScope)
2071 return true;
2072 node = node->parent;
2073 }
2074 return false;
2075}
2076
2077static const int REDUCED_MAX_LIGHT_COUNT_THRESHOLD_BYTES = 4096; // 256 vec4
2078
2079static inline int effectiveMaxLightCount(const QSSGShaderFeatures &features)
2080{
2083
2084 return QSSG_MAX_NUM_LIGHTS;
2085}
2086
2087void updateDirtySkeletons(const QVector<QSSGRenderableNodeEntry> &renderableNodes)
2088{
2089 // First model using skeleton clears the dirty flag so we need another mechanism
2090 // to tell to the other models the skeleton is dirty.
2091 QSet<QSSGRenderSkeleton *> dirtySkeletons;
2092 for (const auto &node : std::as_const(renderableNodes)) {
2093 if (node.node->type == QSSGRenderGraphObject::Type::Model) {
2094 auto modelNode = static_cast<QSSGRenderModel *>(node.node);
2095 auto skeletonNode = modelNode->skeleton;
2096 bool hcj = false;
2097 if (skeletonNode) {
2098 const bool dirtySkeleton = dirtySkeletons.contains(skeletonNode);
2099 const bool hasDirtyNonJoints = (skeletonNode->containsNonJointNodes
2100 && (hasDirtyNonJointNodes(skeletonNode, hcj) || dirtySkeleton));
2101 const bool dirtyTransform = skeletonNode->isDirty(QSSGRenderNode::DirtyFlag::TransformDirty);
2102 if (skeletonNode->skinningDirty || hasDirtyNonJoints || dirtyTransform) {
2103 skeletonNode->boneTransformsDirty = false;
2104 if (hasDirtyNonJoints && !dirtySkeleton)
2105 dirtySkeletons.insert(skeletonNode);
2106 skeletonNode->skinningDirty = false;
2107 const qsizetype dataSize = BONEDATASIZE4ID(skeletonNode->maxIndex);
2108 if (skeletonNode->boneData.size() < dataSize)
2109 skeletonNode->boneData.resize(dataSize);
2110 skeletonNode->calculateGlobalVariables();
2111 skeletonNode->containsNonJointNodes = false;
2112 for (auto &child : skeletonNode->children)
2113 collectBoneTransforms(&child, skeletonNode, modelNode->inverseBindPoses);
2114 }
2115 skeletonNode->boneCount = skeletonNode->boneData.size() / 2 / 4 / 16;
2116 const int boneTexWidth = qCeil(qSqrt(skeletonNode->boneCount * 4 * 2));
2117 skeletonNode->boneTexData.setSize(QSize(boneTexWidth, boneTexWidth));
2118 skeletonNode->boneData.resize(boneTexWidth * boneTexWidth * 16);
2119 skeletonNode->boneTexData.setTextureData(skeletonNode->boneData);
2120 }
2121 const int numMorphTarget = modelNode->morphTargets.size();
2122 for (int i = 0; i < numMorphTarget; ++i) {
2123 auto morphTarget = static_cast<const QSSGRenderMorphTarget *>(modelNode->morphTargets.at(i));
2124 modelNode->morphWeights[i] = morphTarget->weight;
2125 modelNode->morphAttributes[i] = morphTarget->attributes;
2127 modelNode->morphAttributes[i] &= 0x1; // MorphTarget.Position
2129 modelNode->morphAttributes[i] &= 0x3; // MorphTarget.Position | MorphTarget.Normal
2130 }
2131 }
2132 }
2133
2134 dirtySkeletons.clear();
2135}
2136
2138{
2139 QSSG_ASSERT_X(layerPrepResult.isNull(), "Prep-result was not reset for render!", layerPrepResult = {});
2140
2141 QRect theViewport(renderer->viewport());
2142
2143 // NOTE: The renderer won't change in practice (after being set the first time), but just update
2144 // it anyways.
2145 frameData.m_ctx = renderer->contextInterface();
2146 frameData.clear();
2147
2148 // Create base pipeline state
2149 ps = {}; // Reset
2150 ps.viewport = { float(theViewport.x()), float(theViewport.y()), float(theViewport.width()), float(theViewport.height()), 0.0f, 1.0f };
2151 if (layer.scissorRect.isValid()) {
2153 ps.scissor = { layer.scissorRect.x(),
2154 theViewport.height() - (layer.scissorRect.y() + layer.scissorRect.height()),
2155 layer.scissorRect.width(),
2156 layer.scissorRect.height() };
2157 }
2158
2161
2162 // Enable Wireframe mode
2164
2165 bool wasDirty = false;
2166 bool wasDataDirty = false;
2167 wasDirty = layer.isDirty();
2168
2169 layerPrepResult = { theViewport, layer };
2170
2171 // SSAO
2172 const bool SSAOEnabled = layer.ssaoEnabled();
2174 features.set(QSSGShaderFeatures::Feature::Ssao, SSAOEnabled);
2175
2176 // Effects
2177 bool requiresDepthTexture = SSAOEnabled;
2178 for (QSSGRenderEffect *theEffect = layer.firstEffect; theEffect; theEffect = theEffect->m_nextEffect) {
2179 if (theEffect->isDirty()) {
2180 wasDirty = true;
2181 theEffect->clearDirty();
2182 }
2183 if (theEffect->requiresDepthTexture)
2184 requiresDepthTexture = true;
2185 }
2186 layerPrepResult.flags.setRequiresDepthTexture(requiresDepthTexture);
2187
2188 // Tonemapping. Except when there are effects, then it is up to the
2189 // last pass of the last effect to perform tonemapping.
2190 if (!layer.firstEffect)
2191 QSSGLayerRenderData::setTonemapFeatures(features, layer.tonemapMode);
2192
2193 // We may not be able to have an array of 15 light struct elements in
2194 // the shaders. Switch on the reduced-max-number-of-lights feature
2195 // if necessary. In practice this is relevant with OpenGL ES 3.0 or
2196 // 2.0, because there are still implementations in use that only
2197 // support the spec mandated minimum of 224 vec4s (so 3584 bytes).
2198 const auto &rhiCtx = renderer->contextInterface()->rhiContext();
2199 if (rhiCtx->rhi()->resourceLimit(QRhi::MaxUniformBufferRange) < REDUCED_MAX_LIGHT_COUNT_THRESHOLD_BYTES) {
2201 static bool notified = false;
2202 if (!notified) {
2203 notified = true;
2204 qCDebug(lcQuick3DRender, "Qt Quick 3D maximum number of lights has been reduced from %d to %d due to the graphics driver's limitations",
2206 }
2207 }
2208
2209 // IBL Lightprobe Image
2210 QSSGRenderImageTexture lightProbeTexture;
2211 if (layer.lightProbe) {
2212 const auto &lightProbeSettings = layer.lightProbeSettings;
2213 if (layer.lightProbe->m_format == QSSGRenderTextureFormat::Unknown) {
2214 // Choose on a format that makes sense for a light probe
2215 // At this point it's just a suggestion
2216 if (renderer->contextInterface()->rhiContext()->rhi()->isTextureFormatSupported(QRhiTexture::RGBA16F))
2217 layer.lightProbe->m_format = QSSGRenderTextureFormat::RGBA16F;
2218 else
2219 layer.lightProbe->m_format = QSSGRenderTextureFormat::RGBE8;
2220 }
2221
2222 if (layer.lightProbe->clearDirty())
2223 wasDataDirty = true;
2224
2225 // NOTE: This call can lead to rendering (of envmap) and a texture upload
2226 lightProbeTexture = renderer->contextInterface()->bufferManager()->loadRenderImage(layer.lightProbe, QSSGBufferManager::MipModeBsdf);
2227 if (lightProbeTexture.m_texture) {
2228
2230 features.set(QSSGShaderFeatures::Feature::IblOrientation, !lightProbeSettings.probeOrientation.isIdentity());
2231
2232 // By this point we will know what the actual texture format of the light probe is
2233 // Check if using RGBE format light probe texture (the Rhi format will be RGBA8)
2234 if (lightProbeTexture.m_flags.isRgbe8())
2236 } else {
2237 layer.lightProbe = nullptr;
2238 }
2239
2240 const bool forceIblExposureValues = (features.isSet(QSSGShaderFeatures::Feature::LightProbe) && layer.tonemapMode == QSSGRenderLayer::TonemapMode::Custom);
2241 features.set(QSSGShaderFeatures::Feature::ForceIblExposure, forceIblExposureValues);
2242 }
2243
2244 // Gather Spatial Nodes from Render Tree
2245 // Do not just clear() renderableNodes and friends. Rather, reuse
2246 // the space (even if clear does not actually deallocate, it still
2247 // costs time to run dtors and such). In scenes with a static node
2248 // count in the range of thousands this may matter.
2249 int renderableModelsCount = 0;
2250 int renderableParticlesCount = 0;
2251 int renderableItem2DsCount = 0;
2252 int cameraNodeCount = 0;
2253 int lightNodeCount = 0;
2254 int reflectionProbeCount = 0;
2255 quint32 dfsIndex = 0;
2256 for (auto &theChild : layer.children)
2257 wasDataDirty |= maybeQueueNodeForRender(theChild,
2259 renderableModelsCount,
2261 renderableParticlesCount,
2263 renderableItem2DsCount,
2264 cameras,
2265 cameraNodeCount,
2266 lights,
2267 lightNodeCount,
2269 reflectionProbeCount,
2270 dfsIndex);
2271
2272 if (renderableModels.size() != renderableModelsCount)
2273 renderableModels.resize(renderableModelsCount);
2274 if (renderableParticles.size() != renderableParticlesCount)
2275 renderableParticles.resize(renderableParticlesCount);
2276 if (renderableItem2Ds.size() != renderableItem2DsCount)
2277 renderableItem2Ds.resize(renderableItem2DsCount);
2278
2279 if (cameras.size() != cameraNodeCount)
2280 cameras.resize(cameraNodeCount);
2281 if (lights.size() != lightNodeCount)
2282 lights.resize(lightNodeCount);
2283 if (reflectionProbes.size() != reflectionProbeCount)
2284 reflectionProbes.resize(reflectionProbeCount);
2285
2286 // Cameras
2287 // 1. If there's an explicit camera set and it's active (visible) we'll use that.
2288 // 2. ... if the explicitly set camera is not visible, no further attempts will be done.
2289 // 3. If no explicit camera is set, we'll search and pick the first active camera.
2291 if (!layer.explicitCameras.isEmpty()) {
2292 for (QSSGRenderCamera *cam : std::as_const(layer.explicitCameras)) {
2293 // 1.
2294 wasDataDirty = wasDataDirty || cam->isDirty();
2296 wasDataDirty = wasDataDirty || theResult.m_wasDirty;
2297 if (!theResult.m_computeFrustumSucceeded)
2298 qCCritical(INTERNAL_ERROR, "Failed to calculate camera frustum");
2299
2300 // 2.
2301 if (cam->getGlobalState(QSSGRenderCamera::GlobalState::Active))
2303 }
2304 } else { // this path can never be hit with multiview
2305 // 3.
2306 for (auto iter = cameras.cbegin(); renderedCameras.isEmpty() && iter != cameras.cend(); iter++) {
2307 QSSGRenderCamera *theCamera = *iter;
2308 wasDataDirty = wasDataDirty || theCamera->isDirty();
2310 wasDataDirty = wasDataDirty || theResult.m_wasDirty;
2311 if (!theResult.m_computeFrustumSucceeded)
2312 qCCritical(INTERNAL_ERROR, "Failed to calculate camera frustum");
2313 if (theCamera->getGlobalState(QSSGRenderCamera::GlobalState::Active))
2314 renderedCameras.append(theCamera);
2315 }
2316 }
2317
2319 cam->dpr = renderer->dpr();
2320
2321 float meshLodThreshold = 1.0f;
2322 if (!renderedCameras.isEmpty())
2323 meshLodThreshold = renderedCameras[0]->levelOfDetailPixelThreshold / theViewport.width();
2324
2325 layer.renderedCameras = renderedCameras;
2326
2327 // ResourceLoaders
2329
2330 // Skeletons
2332
2333 // Lights
2334 int shadowMapCount = 0;
2335 bool hasScopedLights = false;
2336 // Determine which lights will actually Render
2337 // Determine how many lights will need shadow maps
2338 // NOTE: This culling is specific to our Forward renderer
2339 const int maxLightCount = effectiveMaxLightCount(features);
2340 const bool showLightCountWarning = !tooManyLightsWarningShown && (lights.size() > maxLightCount);
2341 if (showLightCountWarning) {
2342 qWarning("Too many lights in scene, maximum is %d", maxLightCount);
2344 }
2345
2346 QSSGShaderLightList renderableLights; // All lights (upto 'maxLightCount')
2347
2348 // List should contain only enabled lights (active && birghtness > 0).
2349 {
2350 auto it = lights.crbegin();
2351 const auto end = it + qMin(maxLightCount, lights.size());
2352
2353 for (; it != end; ++it) {
2354 QSSGRenderLight *renderLight = (*it);
2355 hasScopedLights |= (renderLight->m_scope != nullptr);
2356 const bool mightCastShadows = renderLight->m_castShadow && !renderLight->m_fullyBaked;
2357 const bool shadows = mightCastShadows && (shadowMapCount < QSSG_MAX_NUM_SHADOW_MAPS);
2358 shadowMapCount += int(shadows);
2359 const auto &direction = renderLight->getScalingCorrectDirection();
2360 renderableLights.push_back(QSSGShaderLight{ renderLight, shadows, direction });
2361 }
2362
2363 if ((shadowMapCount >= QSSG_MAX_NUM_SHADOW_MAPS) && !tooManyShadowLightsWarningShown) {
2364 qWarning("Too many shadow casting lights in scene, maximum is %d", QSSG_MAX_NUM_SHADOW_MAPS);
2366 }
2367 }
2368
2369 if (shadowMapCount > 0) { // Setup Shadow Maps Entries for Lights casting shadows
2370 requestShadowMapManager(); // Ensure we have a shadow map manager
2371
2372 for (int i = 0, end = renderableLights.size(); i != end; ++i) {
2373 const auto &shaderLight = renderableLights.at(i);
2374 if (shaderLight.shadows) {
2375 quint32 mapSize = shaderLight.light->m_shadowMapRes;
2376 ShadowMapModes mapMode = (shaderLight.light->type != QSSGRenderLight::Type::DirectionalLight)
2379 shadowMapManager->addShadowMapEntry(i,
2380 mapSize,
2381 mapSize,
2382 mapMode,
2383 shaderLight.light->debugObjectName);
2385 // Any light with castShadow=true triggers shadow mapping
2386 // in the generated shaders. The fact that some (or even
2387 // all) objects may opt out from receiving shadows plays no
2388 // role here whatsoever.
2389 features.set(QSSGShaderFeatures::Feature::Ssm, true);
2390 }
2391 }
2392 }
2393
2394 // Give each renderable a copy of the lights available
2395 // Also setup scoping for scoped lights
2396
2398 if (hasScopedLights) { // Filter out scoped lights from the global lights list
2399 for (const auto &shaderLight : std::as_const(renderableLights)) {
2400 if (!shaderLight.light->m_scope)
2401 globalLights.push_back(shaderLight);
2402 }
2403
2404 const auto prepareLightsWithScopedLights = [&renderableLights, this](QVector<QSSGRenderableNodeEntry> &renderableNodes) {
2405 for (qint32 idx = 0, end = renderableNodes.size(); idx < end; ++idx) {
2406 QSSGRenderableNodeEntry &theNodeEntry(renderableNodes[idx]);
2407 QSSGShaderLightList filteredLights;
2408 for (const auto &light : std::as_const(renderableLights)) {
2409 if (light.light->m_scope && !scopeLight(theNodeEntry.node, light.light->m_scope))
2410 continue;
2411 filteredLights.push_back(light);
2412 }
2413
2414 if (filteredLights.isEmpty()) { // Node without scoped lights, just reference the global light list.
2415 theNodeEntry.lights = QSSGDataView(globalLights);
2416 } else {
2417 // This node has scoped lights, i.e., it's lights differ from the global list
2418 // we therefore create a bespoke light list for it. Technically this might be the same for
2419 // more then this one node, but the overhead for tracking that is not worth it.
2420 auto customLightList = RENDER_FRAME_NEW_BUFFER<QSSGShaderLight>(*renderer->contextInterface(), filteredLights.size());
2421 std::copy(filteredLights.cbegin(), filteredLights.cend(), customLightList.begin());
2422 theNodeEntry.lights = customLightList;
2423 }
2424 }
2425 };
2426
2427 prepareLightsWithScopedLights(renderableModels);
2428 prepareLightsWithScopedLights(renderableParticles);
2429 } else { // Just a simple copy
2430 globalLights = renderableLights;
2431 // No scoped lights, all nodes can just reference the global light list.
2432 const auto prepareLights = [this](QVector<QSSGRenderableNodeEntry> &renderableNodes) {
2433 for (qint32 idx = 0, end = renderableNodes.size(); idx < end; ++idx) {
2434 QSSGRenderableNodeEntry &theNodeEntry(renderableNodes[idx]);
2435 theNodeEntry.lights = QSSGDataView(globalLights);
2436 }
2437 };
2438
2439 prepareLights(renderableModels);
2440 prepareLights(renderableParticles);
2441 }
2442
2443 bool hasUserExtensions = false;
2444
2445 {
2446 // Give user provided passes a chance to modify the renderable data before starting
2447 // Note: All non-active extensions should be filtered out by now
2449 for (size_t i = 0; i != size_t(QSSGRenderLayer::RenderExtensionStage::Count); ++i) {
2450 const auto &renderExtensions = layer.renderExtensions[i];
2451 auto &userPass = userPasses[i];
2452 for (auto rit = renderExtensions.crbegin(), rend = renderExtensions.crend(); rit != rend; ++rit) {
2453 hasUserExtensions = true;
2454 if ((*rit)->prepareData(frameData)) {
2455 wasDirty |= true;
2456 userPass.extensions.push_back(*rit);
2457 }
2458 }
2459 }
2460 }
2461
2462 // Ensure materials (If there are user extensions we don't cull renderables without materials!)
2463 prepareModelMaterials(renderableModels, !hasUserExtensions);
2464 // Ensure meshes for models
2466
2467 auto &opaqueObjects = opaqueObjectStore[0];
2468 auto &transparentObjects = transparentObjectStore[0];
2469 auto &screenTextureObjects = screenTextureObjectStore[0];
2470
2471 if (!renderedCameras.isEmpty()) { // NOTE: We shouldn't really get this far without a camera...
2472 wasDirty |= prepareModelsForRender(*renderer->contextInterface(), renderableModels, layerPrepResult.flags, renderedCameras, getCachedCameraDatas(), modelContexts, opaqueObjects, transparentObjects, screenTextureObjects, meshLodThreshold);
2473 if (particlesEnabled) {
2474 const auto &cameraDatas = getCachedCameraDatas();
2475 wasDirty |= prepareParticlesForRender(renderableParticles, cameraDatas[0]);
2476 }
2478 }
2479
2481
2482 wasDirty = wasDirty || wasDataDirty;
2485
2486 //
2487 const bool animating = wasDirty;
2488 if (animating)
2489 layer.progAAPassIndex = 0;
2490
2491 const bool progressiveAA = layer.antialiasingMode == QSSGRenderLayer::AAMode::ProgressiveAA && !animating;
2492 layer.progressiveAAIsActive = progressiveAA;
2493 const bool temporalAA = layer.temporalAAEnabled && !progressiveAA && layer.antialiasingMode != QSSGRenderLayer::AAMode::MSAA;
2494
2495 layer.temporalAAIsActive = temporalAA;
2496
2497 QVector2D vertexOffsetsAA;
2498
2499 if (progressiveAA && layer.progAAPassIndex > 0 && layer.progAAPassIndex < quint32(layer.antialiasingQuality)) {
2500 int idx = layer.progAAPassIndex - 1;
2501 vertexOffsetsAA = s_ProgressiveAAVertexOffsets[idx] / QVector2D{ float(theViewport.width()/2.0), float(theViewport.height()/2.0) };
2502 }
2503
2504 if (temporalAA) {
2505 const int t = 1 - 2 * (layer.tempAAPassIndex % 2);
2506 const float f = t * layer.temporalAAStrength;
2507 vertexOffsetsAA = { f / float(theViewport.width()/2.0), f / float(theViewport.height()/2.0) };
2508 }
2509
2510 if (!renderedCameras.isEmpty()) {
2511 if (temporalAA || progressiveAA /*&& !vertexOffsetsAA.isNull()*/) {
2512 QMatrix4x4 offsetProjection = renderedCameras[0]->projection;
2513 QMatrix4x4 invProjection = renderedCameras[0]->projection.inverted();
2514 if (renderedCameras[0]->type == QSSGRenderCamera::Type::OrthographicCamera) {
2515 offsetProjection(0, 3) -= vertexOffsetsAA.x();
2516 offsetProjection(1, 3) -= vertexOffsetsAA.y();
2517 } else if (renderedCameras[0]->type == QSSGRenderCamera::Type::PerspectiveCamera) {
2518 offsetProjection(0, 2) += vertexOffsetsAA.x();
2519 offsetProjection(1, 2) += vertexOffsetsAA.y();
2520 }
2521 for (auto &modelContext : std::as_const(modelContexts)) {
2522 for (int mvpIdx = 0; mvpIdx < renderedCameras.count(); ++mvpIdx)
2523 modelContext->modelViewProjections[mvpIdx] = offsetProjection * invProjection * modelContext->modelViewProjections[mvpIdx];
2524 }
2525 }
2526 }
2527
2528 const bool hasItem2Ds = (renderableItem2DsCount > 0);
2529 const bool layerEnableDepthTest = layer.layerFlags.testFlag(QSSGRenderLayer::LayerFlag::EnableDepthTest);
2530 const bool layerEnabledDepthPrePass = layer.layerFlags.testFlag(QSSGRenderLayer::LayerFlag::EnableDepthPrePass);
2531 const bool depthTestEnableDefault = layerEnableDepthTest && (!opaqueObjects.isEmpty() || depthPrepassObjectsState || hasDepthWriteObjects);
2532 const bool zPrePassForced = (depthPrepassObjectsState != 0);
2533 zPrePassActive = zPrePassForced || (layerEnabledDepthPrePass && layerEnableDepthTest && (hasDepthWriteObjects || hasItem2Ds));
2534 const bool depthWriteEnableDefault = depthTestEnableDefault && (!layerEnabledDepthPrePass || !zPrePassActive);
2535
2536 ps.flags.setFlag(QSSGRhiGraphicsPipelineState::Flag::DepthTestEnabled, depthTestEnableDefault);
2537 ps.flags.setFlag(QSSGRhiGraphicsPipelineState::Flag::DepthWriteEnabled, depthWriteEnableDefault);
2538
2539 // Prepare passes
2541 // If needed, generate a depth texture with the opaque objects. This
2542 // and the SSAO texture must come first since other passes may want to
2543 // expose these textures to their shaders.
2546
2547 // Screen space ambient occlusion. Relies on the depth texture and generates an AO map.
2550
2551 // Shadows. Generates a 2D or cube shadow map. (opaque + pre-pass transparent objects)
2554
2555 if (zPrePassActive)
2557
2558 // Screen texture with opaque objects.
2561
2562 // Reflection pass
2564
2565 auto &underlayPass = userPasses[size_t(QSSGRenderLayer::RenderExtensionStage::Underlay)];
2566 if (underlayPass.hasData())
2567 activePasses.push_back(&underlayPass);
2568
2569 const bool hasOpaqueObjects = (opaqueObjects.size() > 0);
2570
2571 if (hasOpaqueObjects)
2573
2574 // NOTE: When the a screen texture is used, the skybox pass will be called twice. First from
2575 // the screen texture pass and later as part of the normal run through the list.
2576 if (renderer->contextInterface()->rhiContext()->rhi()->isFeatureSupported(QRhi::TexelFetch)) {
2577 if (layer.background == QSSGRenderLayer::Background::SkyBoxCubeMap && layer.skyBoxCubeMap)
2579 else if (layer.background == QSSGRenderLayer::Background::SkyBox && layer.lightProbe)
2581 }
2582
2583 if (hasItem2Ds)
2585
2588
2589 // Note: Transparent pass includeds opaque objects when layerEnableDepthTest is false.
2590 if (transparentObjects.size() > 0 || (!layerEnableDepthTest && hasOpaqueObjects))
2592
2594 if (overlayPass.hasData())
2595 activePasses.push_back(&overlayPass);
2596
2597 if (layer.gridEnabled)
2599
2600 if (const auto &dbgDrawSystem = renderer->contextInterface()->debugDrawSystem(); dbgDrawSystem && dbgDrawSystem->isEnabled() && dbgDrawSystem->hasContent())
2602}
2603
2604template<typename T>
2605static void clearTable(std::vector<T> &entry)
2606{
2607 for (auto &e : entry)
2608 e.clear();
2609}
2610
2612{
2613 for (const auto &pass : activePasses)
2614 pass->resetForFrame();
2616 bakedLightingModels.clear();
2617 layerPrepResult = {};
2619 renderedCameraData.reset();
2622 renderableItem2Ds.clear();
2623 lightmapTextures.clear();
2624 bonemapTextures.clear();
2627 features = QSSGShaderFeatures();
2628 hasDepthWriteObjects = false;
2629 depthPrepassObjectsState = { DepthPrepassObjectStateT(DepthPrepassObject::None) };
2630 zPrePassActive = false;
2631
2632 clearTable(renderableModelStore);
2633 clearTable(modelContextStore);
2634 clearTable(renderableObjectStore);
2635 clearTable(opaqueObjectStore);
2636 clearTable(transparentObjectStore);
2637 clearTable(screenTextureObjectStore);
2638 clearTable(sortedOpaqueObjectCache);
2639 clearTable(sortedTransparentObjectCache);
2640 clearTable(sortedScreenTextureObjectCache);
2641 clearTable(sortedOpaqueDepthPrepassCache);
2642 clearTable(sortedDepthWriteCache);
2643}
2644
2646 : layer(&inLayer)
2647{
2648 viewport = inViewport;
2649}
2650
2652{
2653 return viewport.height() >= 2.0f && viewport.width() >= 2.0f;
2654}
2655
2661
2663{
2664 // When using ssaa we need to zoom with the ssaa multiplier since otherwise the
2665 // orthographic camera will be zoomed out due to the bigger viewport. We therefore
2666 // scale the magnification before calulating the camera variables and then revert.
2667 // Since the same camera can be used in several View3Ds with or without ssaa we
2668 // cannot store the magnification permanently.
2669 const float horizontalMagnification = inCamera.horizontalMagnification;
2670 const float verticalMagnification = inCamera.verticalMagnification;
2671 inCamera.dpr = dpr;
2672 inCamera.horizontalMagnification *= layer->ssaaEnabled ? layer->ssaaMultiplier : 1.0f;
2673 inCamera.verticalMagnification *= layer->ssaaEnabled ? layer->ssaaMultiplier : 1.0f;
2674 const auto result = inCamera.calculateGlobalVariables(viewport);
2675 inCamera.horizontalMagnification = horizontalMagnification;
2676 inCamera.verticalMagnification = verticalMagnification;
2677 return result;
2678}
2679
2681 : layer(inLayer)
2682 , renderer(&inRenderer)
2683 , particlesEnabled(checkParticleSupport(inRenderer.contextInterface()->rhi()))
2684{
2685}
2686
2688{
2689 delete m_lightmapper;
2690 for (auto &pass : activePasses)
2691 pass->resetForFrame();
2692
2693 for (auto &renderResult : renderResults)
2694 renderResult.reset();
2695}
2696
2697static void sortInstances(QByteArray &sortedData, QList<QSSGRhiSortData> &sortData, const void *instances,
2698 int stride, int count, const QVector3D &cameraDirection)
2699{
2700 sortData.resize(count);
2702 // create sort data
2703 {
2704 const QSSGRenderInstanceTableEntry *instance = reinterpret_cast<const QSSGRenderInstanceTableEntry *>(instances);
2705 for (int i = 0; i < count; i++) {
2706 const QVector3D pos = QVector3D(instance->row0.w(), instance->row1.w(), instance->row2.w());
2707 sortData[i] = {QVector3D::dotProduct(pos, cameraDirection), i};
2708 instance++;
2709 }
2710 }
2711
2712 // sort
2713 std::sort(sortData.begin(), sortData.end(), [](const QSSGRhiSortData &a, const QSSGRhiSortData &b){
2714 return a.d > b.d;
2715 });
2716
2717 // copy instances
2718 {
2719 const QSSGRenderInstanceTableEntry *instance = reinterpret_cast<const QSSGRenderInstanceTableEntry *>(instances);
2720 QSSGRenderInstanceTableEntry *dest = reinterpret_cast<QSSGRenderInstanceTableEntry *>(sortedData.data());
2721 for (auto &s : sortData)
2722 *dest++ = instance[s.indexOrOffset];
2723 }
2724}
2725
2726static void cullLodInstances(QByteArray &lodData, const void *instances, int count,
2727 const QVector3D &cameraPosition, float minThreshold, float maxThreshold)
2728{
2729 const QSSGRenderInstanceTableEntry *instance = reinterpret_cast<const QSSGRenderInstanceTableEntry *>(instances);
2730 QSSGRenderInstanceTableEntry *dest = reinterpret_cast<QSSGRenderInstanceTableEntry *>(lodData.data());
2731 for (int i = 0; i < count; ++i) {
2732 const float x = cameraPosition.x() - instance->row0.w();
2733 const float y = cameraPosition.y() - instance->row1.w();
2734 const float z = cameraPosition.z() - instance->row2.w();
2735 const float distanceSq = x * x + y * y + z * z;
2736 if (distanceSq >= minThreshold * minThreshold && (maxThreshold < 0 || distanceSq < maxThreshold * maxThreshold))
2737 *dest = *instance;
2738 else
2739 *dest= {};
2740 dest++;
2741 instance++;
2742 }
2743}
2744
2746 QSSGSubsetRenderable *renderable,
2747 const QVector3D &cameraDirection,
2748 const QVector3D &cameraPosition,
2749 float minThreshold,
2750 float maxThreshold)
2751{
2753 auto &modelContext = renderable->modelContext;
2754 auto &instanceBuffer = renderable->instanceBuffer; // intentional ref2ptr
2755 if (!modelContext.model.instancing() || instanceBuffer)
2756 return instanceBuffer;
2757 auto *table = modelContext.model.instanceTable;
2758 bool usesLod = minThreshold >= 0 || maxThreshold >= 0;
2759 QSSGRhiInstanceBufferData &instanceData(usesLod ? rhiCtxD->instanceBufferData(&modelContext.model) : rhiCtxD->instanceBufferData(table));
2760 quint32 instanceBufferSize = table->dataSize();
2761 // Create or resize the instance buffer ### if (instanceData.owned)
2762 bool sortingChanged = table->isDepthSortingEnabled() != instanceData.sorting;
2763 bool cameraDirectionChanged = !qFuzzyCompare(instanceData.sortedCameraDirection, cameraDirection);
2764 bool cameraPositionChanged = !qFuzzyCompare(instanceData.cameraPosition, cameraPosition);
2765 bool updateInstanceBuffer = table->serial() != instanceData.serial || sortingChanged || (cameraDirectionChanged && table->isDepthSortingEnabled());
2766 bool updateForLod = cameraPositionChanged && usesLod;
2767 if (sortingChanged && !table->isDepthSortingEnabled()) {
2768 instanceData.sortedData.clear();
2769 instanceData.sortData.clear();
2770 instanceData.sortedCameraDirection = {};
2771 }
2772 instanceData.sorting = table->isDepthSortingEnabled();
2773 if (instanceData.buffer && instanceData.buffer->size() < instanceBufferSize) {
2774 updateInstanceBuffer = true;
2775 // qDebug() << "Resizing instance buffer";
2776 instanceData.buffer->setSize(instanceBufferSize);
2777 instanceData.buffer->create();
2778 }
2779 if (!instanceData.buffer) {
2780 // qDebug() << "Creating instance buffer";
2781 updateInstanceBuffer = true;
2782 instanceData.buffer = rhiCtx->rhi()->newBuffer(QRhiBuffer::Dynamic, QRhiBuffer::VertexBuffer, instanceBufferSize);
2783 instanceData.buffer->create();
2784 }
2785 if (updateInstanceBuffer || updateForLod) {
2786 const void *data = nullptr;
2787 if (table->isDepthSortingEnabled()) {
2788 if (updateInstanceBuffer) {
2789 QMatrix4x4 invGlobalTransform = modelContext.model.globalTransform.inverted();
2790 instanceData.sortedData.resize(table->dataSize());
2791 sortInstances(instanceData.sortedData,
2792 instanceData.sortData,
2793 table->constData(),
2794 table->stride(),
2795 table->count(),
2796 invGlobalTransform.map(cameraDirection).normalized());
2797 }
2798 data = instanceData.sortedData.constData();
2799 instanceData.sortedCameraDirection = cameraDirection;
2800 } else {
2801 data = table->constData();
2802 }
2803 if (data) {
2804 if (updateForLod) {
2805 if (table->isDepthSortingEnabled()) {
2806 instanceData.lodData.resize(table->dataSize());
2807 cullLodInstances(instanceData.lodData, instanceData.sortedData.constData(), instanceData.sortedData.size(), cameraPosition, minThreshold, maxThreshold);
2808 data = instanceData.lodData.constData();
2809 } else {
2810 instanceData.lodData.resize(table->dataSize());
2811 cullLodInstances(instanceData.lodData, table->constData(), table->count(), cameraPosition, minThreshold, maxThreshold);
2812 data = instanceData.lodData.constData();
2813 }
2814 }
2816 rub->updateDynamicBuffer(instanceData.buffer, 0, instanceBufferSize, data);
2817 rhiCtx->commandBuffer()->resourceUpdate(rub);
2818 //qDebug() << "****** UPDATING INST BUFFER. Size" << instanceBufferSize;
2819 } else {
2820 qWarning() << "NO DATA IN INSTANCE TABLE";
2821 }
2822 instanceData.serial = table->serial();
2823 instanceData.cameraPosition = cameraPosition;
2824 }
2825 instanceBuffer = instanceData.buffer;
2826 return instanceBuffer;
2827}
2828
2830{
2832 static bool bakeRequested = false;
2833 static bool bakeFlagChecked = false;
2834 if (!bakeFlagChecked) {
2835 bakeFlagChecked = true;
2836 const bool cmdLineReq = QCoreApplication::arguments().contains(QStringLiteral("--bake-lightmaps"));
2837 const bool envReq = qEnvironmentVariableIntValue("QT_QUICK3D_BAKE_LIGHTMAPS");
2838 bakeRequested = cmdLineReq || envReq;
2839 }
2840 if (!bakeRequested)
2841 return;
2842 }
2843
2844 const auto &sortedBakedLightingModels = getSortedBakedLightingModels(); // front to back
2845
2846 QSSGRhiContext *rhiCtx = renderer->contextInterface()->rhiContext().get();
2847
2848 if (!m_lightmapper)
2849 m_lightmapper = new QSSGLightmapper(rhiCtx, renderer);
2850
2851 // sortedBakedLightingModels contains all models with
2852 // usedInBakedLighting: true. These, together with lights that
2853 // have a bakeMode set to either Indirect or All, form the
2854 // lightmapped scene. A lightmap is stored persistently only
2855 // for models that have their lightmapKey set.
2856
2858 m_lightmapper->setOptions(layer.lmOptions);
2860
2861 for (int i = 0, ie = sortedBakedLightingModels.size(); i != ie; ++i)
2862 m_lightmapper->add(sortedBakedLightingModels[i]);
2863
2864 QRhiCommandBuffer *cb = rhiCtx->commandBuffer();
2865 cb->debugMarkBegin("Quick3D lightmap baking");
2867 cb->debugMarkEnd();
2868
2870 qDebug("Lightmap baking done, exiting application");
2872 }
2873
2875}
2876
2878{
2879 return frameData;
2880}
2881
2883{
2884 QSSGRenderGraphObject *ret = nullptr;
2885 if (auto res = reinterpret_cast<QSSGRenderGraphObject *>(id))
2886 ret = res;
2887
2888 return ret;
2889}
2890
2892{
2893 if ((!camera_ || camera_ == renderedCameras[0]) && renderedCameraData.has_value())
2894 return renderedCameraData.value()[0];
2895 if (camera_)
2896 return getCameraDataImpl(camera_);
2897 return {};
2898}
2899
2901{
2902 if ((!camera_ || camera_ == renderedCameras[0]) && renderedCameraData.has_value())
2903 return renderedCameraData.value()[0];
2904 if (camera_)
2905 return getCameraDataImpl(camera_);
2906 return {};
2907}
2908
2913
2915{
2917 if (const auto &rhiCtx = ctx.rhiContext(); rhiCtx->isValid()) {
2918 QRhi *rhi = rhiCtx->rhi();
2920 props.isYUpInNDC = rhi->isYUpInNDC();
2921 props.isClipDepthZeroToOne = rhi->isClipDepthZeroToOne();
2922 }
2923
2924 return props;
2925}
2926
2928{
2929 if (!shadowMapManager && QSSG_GUARD(renderer && renderer->contextInterface()))
2930 shadowMapManager.reset(new QSSGRenderShadowMap(*renderer->contextInterface()));
2931 return shadowMapManager;
2932}
2933
2935{
2936 if (!reflectionMapManager && QSSG_GUARD(renderer && renderer->contextInterface()))
2937 reflectionMapManager.reset(new QSSGRenderReflectionMap(*renderer->contextInterface()));
2938 return reflectionMapManager;
2939}
2940
static void prep(const QSSGRenderContextInterface &ctx, QSSGLayerRenderData &data, QSSGPassKey passKey, QSSGRhiGraphicsPipelineState &ps, QSSGShaderFeatures shaderFeatures, QRhiRenderPassDescriptor *rpDesc, const QSSGRenderableObjectList &sortedOpaqueObjects)
static void render(const QSSGRenderContextInterface &ctx, const QSSGRhiGraphicsPipelineState &ps, const QSSGRenderableObjectList &sortedOpaqueObjects)
\inmodule QtCore
Definition qbytearray.h:57
char * data()
\macro QT_NO_CAST_FROM_BYTEARRAY
Definition qbytearray.h:611
The QColor class provides colors based on RGB, HSV or CMYK values.
Definition qcolor.h:31
static QStringList arguments()
const_iterator constFind(const Key &key) const noexcept
Definition qhash.h:1299
const_iterator cend() const noexcept
Definition qhash.h:1218
void clear() noexcept(std::is_nothrow_destructible< Node >::value)
Removes all items from the hash and frees up all memory used by it.
Definition qhash.h:951
Definition qlist.h:75
qsizetype size() const noexcept
Definition qlist.h:397
bool isEmpty() const noexcept
Definition qlist.h:401
reference emplace_back(Args &&... args)
Definition qlist.h:683
void swapItemsAt(qsizetype i, qsizetype j)
Definition qlist.h:667
void push_back(parameter_type t)
Definition qlist.h:675
iterator end()
Definition qlist.h:626
const_reference at(qsizetype i) const noexcept
Definition qlist.h:446
iterator begin()
Definition qlist.h:625
void resize(qsizetype size)
Definition qlist.h:403
const_iterator cend() const noexcept
Definition qlist.h:631
const_iterator cbegin() const noexcept
Definition qlist.h:630
void clear()
Definition qlist.h:434
The QMatrix4x4 class represents a 4x4 transformation matrix in 3D space.
Definition qmatrix4x4.h:25
QMatrix4x4 inverted(bool *invertible=nullptr) const
Returns the inverse of this matrix.
const float * constData() const
Returns a constant pointer to the raw data of this matrix.
Definition qmatrix4x4.h:147
\inmodule QtCore\reentrant
Definition qrect.h:484
constexpr qreal height() const noexcept
Returns the height of the rectangle.
Definition qrect.h:732
constexpr qreal width() const noexcept
Returns the width of the rectangle.
Definition qrect.h:729
constexpr QSizeF size() const noexcept
Returns the size of the rectangle.
Definition qrect.h:735
\inmodule QtCore\reentrant
Definition qrect.h:30
@ Dynamic
Definition qrhi.h:851
@ VertexBuffer
Definition qrhi.h:855
virtual bool create()=0
Creates the corresponding native graphics resources.
\inmodule QtGui
Definition qrhi.h:1651
void resourceUpdate(QRhiResourceUpdateBatch *resourceUpdates)
Sometimes committing resource updates is necessary or just more convenient without starting a render ...
Definition qrhi.cpp:9384
\inmodule QtGui
Definition qrhi.h:1142
\inmodule QtGui
Definition qrhi.h:1731
void updateDynamicBuffer(QRhiBuffer *buf, quint32 offset, quint32 size, const void *data)
Enqueues updating a region of a QRhiBuffer buf created with the type QRhiBuffer::Dynamic.
Definition qrhi.cpp:8991
\inmodule QtGui
Definition qrhi.h:895
@ RGBA32F
Definition qrhi.h:926
@ RED_OR_ALPHA8
Definition qrhi.h:923
@ RGBA16F
Definition qrhi.h:925
Format
Specifies the type of the element data.
Definition qrhi.h:234
\inmodule QtGuiPrivate \inheaderfile rhi/qrhi.h
Definition qrhi.h:1804
QRhiBuffer * newBuffer(QRhiBuffer::Type type, QRhiBuffer::UsageFlags usage, quint32 size)
Definition qrhi.cpp:10508
bool isClipDepthZeroToOne() const
Definition qrhi.cpp:10069
bool isTextureFormatSupported(QRhiTexture::Format format, QRhiTexture::Flags flags={}) const
Definition qrhi.cpp:10102
bool isYUpInFramebuffer() const
Definition qrhi.cpp:10030
bool isYUpInNDC() const
Definition qrhi.cpp:10044
@ MaxUniformBufferRange
Definition qrhi.h:1898
@ IntAttributes
Definition qrhi.h:1854
@ TexelFetch
Definition qrhi.h:1852
@ RedOrAlpha8IsRed
Definition qrhi.h:1842
@ Instancing
Definition qrhi.h:1836
QRhiResourceUpdateBatch * nextResourceUpdateBatch()
Definition qrhi.cpp:9252
Class representing 3D range or axis aligned bounding box.
static Q_ALWAYS_INLINE QSSGBounds3 centerExtents(const QVector3D &center, const QVector3D &extent)
returns the AABB from center and extents vectors.
Q_ALWAYS_INLINE QVector3D center() const
returns the center of this axis aligned box.
QVector3D getSupport(const QVector3D &direction) const
static QSSGBounds3 transform(const QMatrix3x3 &matrix, const QSSGBounds3 &bounds)
gets the transformed bounds of the passed AABB (resulting in a bigger AABB).
\inmodule QtQuick3D
QVector< QSSGBakedLightingModel > renderedBakedLightingModels
const QSSGRenderableObjectList & getSortedTransparentRenderableObjects(const QSSGRenderCamera &camera, size_t index=0)
QRhiTexture * getLightmapTexture(const QSSGModelContext &modelContext) const
QSSGLayerRenderPreparationResult layerPrepResult
static qsizetype frustumCullingInline(const QSSGClippingFrustum &clipFrustum, QSSGRenderableObjectList &renderables)
void setVertexInputPresence(const QSSGRenderableObjectFlags &renderableFlags, QSSGShaderDefaultMaterialKey &key)
bool prepareItem2DsForRender(const QSSGRenderContextInterface &ctxIfc, const RenderableItem2DEntries &renderableItem2Ds)
const QSSGRenderableObjectList & getSortedScreenTextureRenderableObjects(const QSSGRenderCamera &camera, size_t index=0)
QSSGLightmapper::Callback lightmapBakingOutputCallback
QVarLengthArray< QSSGRenderPass *, 16 > activePasses
QMatrix4x4 getGlobalTransform(QSSGPrepContextId prepId, const QSSGRenderModel &model)
QSSGShaderFeatures getShaderFeatures() const
QVector< QSSGBakedLightingModel > bakedLightingModels
QSSGPrepContextId getOrCreateExtensionContext(const QSSGRenderExtension &ext, QSSGRenderCamera *camera=nullptr, quint32 slot=0)
QSSGFrameData & getFrameData()
void setLightmapTexture(const QSSGModelContext &modelContext, QRhiTexture *lightmapTexture)
InfiniteGridPass infiniteGridPass
QSSGLayerRenderData(QSSGRenderLayer &inLayer, QSSGRenderer &inRenderer)
void prepareImageForRender(QSSGRenderImage &inImage, QSSGRenderableImage::Type inMapType, QSSGRenderableImage *&ioFirstImage, QSSGRenderableImage *&ioNextImage, QSSGRenderableObjectFlags &ioFlags, QSSGShaderDefaultMaterialKey &ioGeneratedShaderKey, quint32 inImageIndex, QSSGRenderDefaultMaterial *inMaterial=nullptr)
SkyboxCubeMapPass skyboxCubeMapPass
void setGlobalTransform(QSSGRenderablesId renderablesId, const QSSGRenderModel &model, const QMatrix4x4 &mvp)
const QSSGRenderReflectionMapPtr & requestReflectionMapManager()
static void setTonemapFeatures(QSSGShaderFeatures &features, QSSGRenderLayer::TonemapMode tonemapMode)
float getGlobalOpacity(QSSGPrepContextId prepId, const QSSGRenderModel &model)
QSSGLightmapper * m_lightmapper
const QSSGRenderableObjectList & getSortedrenderedOpaqueDepthPrepassObjects(const QSSGRenderCamera &camera, size_t index=0)
QSSGShaderDefaultMaterialKey generateLightingKey(QSSGRenderDefaultMaterial::MaterialLighting inLightingType, const QSSGShaderLightListView &lights, bool receivesShadows=true)
ReflectionMapPass reflectionMapPass
void renderRenderables(QSSGRenderContextInterface &ctx, QSSGPrepResultId prepId)
QSSGRenderablesId createRenderables(QSSGPrepContextId prepId, const QList< QSSGNodeId > &nodes, QSSGRenderHelpers::CreateFlags createFlags)
static GlobalRenderProperties globalRenderProperties(const QSSGRenderContextInterface &ctx)
RenderableNodeEntries renderableModels
const QVector< QSSGBakedLightingModel > & getSortedBakedLightingModels()
ScreenReflectionPass reflectionPass
void setModelMaterials(QSSGRenderablesId renderablesId, const QSSGRenderModel &model, const QList< QSSGResourceId > &materials)
bool prepareParticlesForRender(const RenderableNodeEntries &renderableParticles, const QSSGRenderCameraData &cameraData)
const RenderableItem2DEntries & getRenderableItem2Ds()
QRhiTexture * getBonemapTexture(const QSSGModelContext &modelContext) const
static qsizetype frustumCulling(const QSSGClippingFrustum &clipFrustum, const QSSGRenderableObjectList &renderables, QSSGRenderableObjectList &visibleRenderables)
static bool prepareInstancing(QSSGRhiContext *rhiCtx, QSSGSubsetRenderable *renderable, const QVector3D &cameraDirection, const QVector3D &cameraPosition, float minThreshold, float maxThreshold)
RenderableItem2DEntries renderedItem2Ds
QSSGRenderContextInterface * contextInterface() const
void setGlobalOpacity(QSSGRenderablesId renderablesId, const QSSGRenderModel &model, float opacity)
const QSSGRenderableObjectList & getSortedRenderedDepthWriteObjects(const QSSGRenderCamera &camera, size_t index=0)
QSSGRenderCameraData getCameraRenderData(const QSSGRenderCamera *camera)
void setBonemapTexture(const QSSGModelContext &modelContext, QRhiTexture *bonemapTexture)
const QSSGRenderShadowMapPtr & requestShadowMapManager()
QVector< QSSGRenderLight * > lights
UserPass userPasses[USERPASSES]
QVector< QSSGRenderItem2D * > renderableItem2Ds
bool prepareModelsForRender(QSSGRenderContextInterface &ctx, const RenderableNodeEntries &renderableModels, QSSGLayerRenderPreparationResultFlags &ioFlags, const QSSGRenderCameraList &allCameras, const QSSGRenderCameraDataList &allCameraData, TModelContextPtrList &modelContexts, QSSGRenderableObjectList &opaqueObjects, QSSGRenderableObjectList &transparentObjects, QSSGRenderableObjectList &screenTextureObjects, float lodThreshold=0.0f)
QVector< QSSGRenderReflectionProbe * > reflectionProbes
QVector< QSSGRenderCamera * > cameras
void prepareRenderables(QSSGRenderContextInterface &ctx, QSSGPrepResultId prepId, QRhiRenderPassDescriptor *renderPassDescriptor, const QSSGRhiGraphicsPipelineState &ps, QSSGRenderablesFilters filter)
QSSGShaderLightList globalLights
QSSGRenderGraphObject * getCamera(QSSGCameraId id) const
const QSSGRenderableObjectList & getSortedOpaqueRenderableObjects(const QSSGRenderCamera &camera, size_t index=0)
TModelContextPtrList modelContexts
static constexpr size_t USERPASSES
RenderableNodeEntries renderableParticles
QSSGRenderCameraList renderedCameras
std::optional< QSSGRenderCameraDataList > renderedCameraData
static const std::unique_ptr< QSSGPerFrameAllocator > & perFrameAllocator(QSSGRenderContextInterface &ctx)
TransparentPass transparentPass
qsizetype add(const QSSGBakedLightingModel &model)
void setOptions(const QSSGLightmapperOptions &options)
void setOutputCallback(Callback callback)
Representation of a plane.
Definition qssgplane_p.h:31
const std::unique_ptr< QSSGRhiContext > & rhiContext() const
const std::unique_ptr< QSSGDebugDrawSystem > & debugDrawSystem() const
const std::unique_ptr< QSSGCustomMaterialSystem > & customMaterialSystem() const
const std::unique_ptr< QSSGBufferManager > & bufferManager() const
const std::unique_ptr< QSSGRenderer > & renderer() const
\inmodule QtQuick3D
static constexpr bool isCamera(Type type) noexcept
static constexpr bool isRenderable(Type type) noexcept
static constexpr bool isMaterial(Type type) noexcept
static constexpr bool isLight(Type type) noexcept
static bool isGlobalPickingEnabled(const QSSGRenderer &renderer)
float dpr() const
QRect viewport() const
constexpr quint32 frameCount() const
QSSGRenderContextInterface * contextInterface() const
static QSSGRhiContextPrivate * get(QSSGRhiContext *q)
\inmodule QtQuick3D
QRhiCommandBuffer * commandBuffer() const
QRhi * rhi() const
QRhiGraphicsPipeline::PolygonMode polygonMode
QRhiGraphicsPipeline::CompareOp depthFunc
qsizetype size() const
Definition qset.h:50
const_iterator cend() const noexcept
Definition qset.h:142
\inmodule QtCore
Definition qsize.h:25
bool isEmpty() const
qsizetype count() const
void append(const T &t)
void push_back(const T &t)
The QVector2D class represents a vector or vertex in 2D space.
Definition qvectornd.h:31
The QVector3D class represents a vector or vertex in 3D space.
Definition qvectornd.h:171
QT_WARNING_POP float distanceToPoint(QVector3D point) const noexcept
Definition qvectornd.h:792
constexpr float y() const noexcept
Returns the y coordinate of this point.
Definition qvectornd.h:671
constexpr float x() const noexcept
Returns the x coordinate of this point.
Definition qvectornd.h:670
static constexpr float dotProduct(QVector3D v1, QVector3D v2) noexcept
Returns the dot product of v1 and v2.
Definition qvectornd.h:770
constexpr float z() const noexcept
Returns the z coordinate of this point.
Definition qvectornd.h:672
The QVector4D class represents a vector or vertex in 4D space.
Definition qvectornd.h:330
constexpr float w() const noexcept
Returns the w coordinate of this point.
Definition qvectornd.h:881
static void render(const QSSGRenderContextInterface &ctx, const QSSGRhiGraphicsPipelineState &ps, const QSSGRenderableObjectList &sortedTransparentObjects)
static void prep(const QSSGRenderContextInterface &ctx, QSSGLayerRenderData &data, QSSGPassKey passKey, QSSGRhiGraphicsPipelineState &ps, QSSGShaderFeatures shaderFeatures, QRhiRenderPassDescriptor *rpDesc, const QSSGRenderableObjectList &sortedTransparentObjects)
QList< QSSGRenderExtension * > extensions
EGLContext ctx
QCamera * camera
Definition camera.cpp:19
list append(new Employee("Blackpool", "Stephen"))
QSet< QString >::iterator it
direction
T * getResource(QSSGResourceId resId)
constexpr quint32 nextMultipleOf4(quint32 value)
QVector3D Q_QUICK3DUTILS_EXPORT transform(const QMatrix3x3 &m, const QVector3D &v)
Definition qssgutils.cpp:43
QVector3D Q_QUICK3DUTILS_EXPORT transform(const QMatrix4x4 &m, const QVector3D &v)
Definition qssgutils.cpp:86
QVector3D Q_QUICK3DUTILS_EXPORT getScale(const QMatrix4x4 &m)
Combined button and popup list for selecting options.
@ red
Definition qnamespace.h:35
constexpr Initialization Uninitialized
#define Q_STATIC_ASSERT(Condition)
Definition qassert.h:108
#define Q_UNLIKELY(x)
#define qApp
DBusConnection const char DBusError DBusBusType DBusError return DBusConnection DBusHandleMessageFunction void DBusFreeFunction return DBusConnection return DBusConnection return const char DBusError return DBusConnection DBusMessage dbus_uint32_t return DBusConnection dbus_bool_t DBusConnection DBusAddWatchFunction DBusRemoveWatchFunction DBusWatchToggledFunction void DBusFreeFunction return DBusConnection DBusDispatchStatusFunction void DBusFreeFunction DBusTimeout return DBusTimeout return DBusWatch return DBusWatch unsigned int return DBusError const DBusError return const DBusMessage return DBusMessage return DBusMessage return DBusMessage return DBusMessage return DBusMessage return DBusMessageIter int const void return DBusMessageIter DBusMessageIter return DBusMessageIter void DBusMessageIter void int return DBusMessage DBusMessageIter return DBusMessageIter return DBusMessageIter DBusMessageIter const char const char const char const char return DBusMessage return DBusMessage const char return DBusMessage dbus_bool_t return DBusMessage dbus_uint32_t return DBusMessage void
DBusConnection const char DBusError DBusBusType DBusError return DBusConnection DBusHandleMessageFunction void DBusFreeFunction return DBusConnection return DBusConnection return const char DBusError return DBusConnection DBusMessage dbus_uint32_t return DBusConnection dbus_bool_t DBusConnection DBusAddWatchFunction DBusRemoveWatchFunction DBusWatchToggledFunction void DBusFreeFunction return DBusConnection DBusDispatchStatusFunction void DBusFreeFunction DBusTimeout return DBusTimeout return DBusWatch return DBusWatch unsigned int return DBusError const DBusError return const DBusMessage return DBusMessage return DBusMessage return DBusMessage return DBusMessage return DBusMessage return DBusMessageIter * iter
EGLOutputLayerEXT EGLint EGLAttrib value
[5]
EGLOutputLayerEXT layer
size_t qHash(const QFileSystemWatcherPathKey &key, size_t seed=0)
bool qFuzzyCompare(qfloat16 p1, qfloat16 p2) noexcept
Definition qfloat16.h:333
qfloat16 qSqrt(qfloat16 f)
Definition qfloat16.h:289
#define qDebug
[1]
Definition qlogging.h:164
#define qWarning
Definition qlogging.h:166
#define Q_LOGGING_CATEGORY(name,...)
#define qCCritical(category,...)
#define qCDebug(category,...)
return ret
int qCeil(T v)
Definition qmath.h:36
constexpr const T & qMin(const T &a, const T &b)
Definition qminmax.h:40
constexpr const T & qMax(const T &a, const T &b)
Definition qminmax.h:42
GLboolean GLboolean GLboolean b
GLuint GLfloat GLfloat GLfloat GLfloat GLfloat z
GLuint64 GLenum void * handle
GLint GLint GLint GLint GLint x
[0]
GLuint64 key
GLboolean GLboolean GLboolean GLboolean a
[7]
GLenum GLuint GLintptr GLsizeiptr size
[1]
GLuint index
[2]
GLuint GLuint end
GLenum GLsizei dataSize
GLenum GLuint GLenum GLsizei length
GLenum GLuint id
[7]
GLenum GLenum GLsizei count
GLint GLsizei GLsizei GLenum GLenum GLsizei void * data
GLfloat GLfloat f
const void GLsizei GLsizei stride
GLsizei GLsizei GLfloat distance
GLenum type
GLenum GLenum dst
GLenum GLuint texture
GLint GLint GLint GLint GLint GLint GLint GLbitfield GLenum filter
GLenum GLuint GLsizei const GLenum * props
GLint y
GLhandleARB obj
[2]
GLdouble s
[6]
Definition qopenglext.h:235
GLuint res
GLuint entry
GLdouble GLdouble t
Definition qopenglext.h:243
GLuint64EXT * result
[6]
GLenum GLenum GLenum GLenum GLenum scale
GLenum GLenum GLsizei void * table
static QT_BEGIN_NAMESPACE qreal dpr(const QWindow *w)
#define Q_ASSERT(cond)
Definition qrandom.cpp:47
#define QSSG_ASSERT_X(cond, msg, action)
#define QSSG_ASSERT(cond, action)
#define QSSG_GUARD(cond)
void updateDirtySkeletons(const QVector< QSSGRenderableNodeEntry > &renderableNodes)
static constexpr size_t getPrepContextIndex(QSSGPrepContextId id)
static bool scopeLight(QSSGRenderNode *node, QSSGRenderNode *lightScope)
static void collectNodeFront(V node, QVector< T > &dst, int &dstPos)
#define POS4BONETRANS(x)
static constexpr bool furthestToNearestCompare(const QSSGRenderableObjectHandle &lhs, const QSSGRenderableObjectHandle &rhs) noexcept
static constexpr bool verifyPrepContext(QSSGPrepContextId id, const QSSGRenderer &renderer)
#define MAX_MORPH_TARGET_INDEX_SUPPORTS_NORMALS
static void clearTable(std::vector< T > &entry)
#define MAX_MORPH_TARGET_INDEX_SUPPORTS_TANGENTS
static const QVector2D s_ProgressiveAAVertexOffsets[QSSGLayerRenderData::MAX_AA_LEVELS]
static constexpr quint16 PREP_CTX_INDEX_MASK
#define POS4BONENORM(x)
static const int REDUCED_MAX_LIGHT_COUNT_THRESHOLD_BYTES
static bool checkParticleSupport(QRhi *rhi)
#define BONEDATASIZE4ID(x)
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 void prepareModelMaterialsImpl(QSSGLayerRenderData::RenderableNodeEntries &renderableModels)
static void collectBoneTransforms(QSSGRenderNode *node, QSSGRenderSkeleton *skeletonNode, const QVector< QMatrix4x4 > &poses)
static bool hasDirtyNonJointNodes(QSSGRenderNode *node, bool &hasChildJoints)
static int effectiveMaxLightCount(const QSSGShaderFeatures &features)
static QSSGRenderCameraData getCameraDataImpl(const QSSGRenderCamera *camera)
static bool maybeQueueNodeForRender(QSSGRenderNode &inNode, QVector< QSSGRenderableNodeEntry > &outRenderableModels, int &ioRenderableModelsCount, QVector< QSSGRenderableNodeEntry > &outRenderableParticles, int &ioRenderableParticlesCount, QVector< QSSGRenderItem2D * > &outRenderableItem2Ds, int &ioRenderableItem2DsCount, QVector< QSSGRenderCamera * > &outCameras, int &ioCameraCount, QVector< QSSGRenderLight * > &outLights, int &ioLightCount, QVector< QSSGRenderReflectionProbe * > &outReflectionProbes, int &ioReflectionProbeCount, quint32 &ioDFSIndex)
static constexpr QSSGPrepContextId createPrepId(size_t index, quint32 frame)
T * RENDER_FRAME_NEW(QSSGRenderContextInterface &ctx, Args &&... args)
void collectNode(V node, QVector< T > &dst, int &dstPos)
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)
#define QSSG_RENDER_MINIMUM_RENDER_OPACITY
QSSGRenderablesFilter
QSSGPrepResultId
QSSGRenderablesId
QSSGPrepContextId
std::shared_ptr< QSSGRenderReflectionMap > QSSGRenderReflectionMapPtr
std::shared_ptr< QSSGRenderShadowMap > QSSGRenderShadowMapPtr
@ VSM
variance shadow mapping
@ CUBE
cubemap omnidirectional shadows
#define QSSG_REDUCED_MAX_NUM_LIGHTS
#define QSSG_MAX_NUM_LIGHTS
#define QSSG_MAX_NUM_SHADOW_MAPS
SSL_CTX int(* cb)(SSL *ssl, unsigned char **out, unsigned char *outlen, const unsigned char *in, unsigned int inlen, void *arg)
#define QStringLiteral(str)
Q_CORE_EXPORT int qEnvironmentVariableIntValue(const char *varName, bool *ok=nullptr) noexcept
unsigned int quint32
Definition qtypes.h:50
unsigned short quint16
Definition qtypes.h:48
size_t quintptr
Definition qtypes.h:167
int qint32
Definition qtypes.h:49
unsigned long long quint64
Definition qtypes.h:61
ptrdiff_t qsizetype
Definition qtypes.h:165
unsigned char quint8
Definition qtypes.h:46
QSqlQueryModel * model
[16]
QObject::connect nullptr
QSemaphore sem(5)
[0]
QString dir
[11]
QLayoutItem * child
[0]
QFrame frame
[0]
QJSValueList args
QSvgRenderer * renderer
[0]
static bool invokeMethod(QObject *obj, const char *member, Qt::ConnectionType, QGenericReturnArgument ret, QGenericArgument val0=QGenericArgument(nullptr), QGenericArgument val1=QGenericArgument(), QGenericArgument val2=QGenericArgument(), QGenericArgument val3=QGenericArgument(), QGenericArgument val4=QGenericArgument(), QGenericArgument val5=QGenericArgument(), QGenericArgument val6=QGenericArgument(), QGenericArgument val7=QGenericArgument(), QGenericArgument val8=QGenericArgument(), QGenericArgument val9=QGenericArgument())
\threadsafe This is an overloaded member function, provided for convenience. It differs from the abov...
QSSGDefaultMaterialPreparationResult(QSSGShaderDefaultMaterialKey inMaterialKey)
QSSGLayerRenderPreparationResultFlags flags
QSSGCameraGlobalCalculationResult setupCameraForRender(QSSGRenderCamera &inCamera, float dpr=1.0f)
const QSSGRenderModel & model
QSSGBounds3 bounds() const
std::optional< QSSGClippingFrustum > clippingFrustum
QSSGRenderImageTextureFlags m_flags
@ EnableDepthPrePass
True when we render a depth pass before.
bool isEnabled() const
QSSGRenderSkeleton * skeleton
bool usesBoneTexture() const
bool hasLightmap() const
QVector< QSSGRenderGraphObject * > materials
bool calculateGlobalVariables()
QMatrix4x4 globalTransform
QSSGRenderNode * parent
QVector3D getGlobalPos() const
constexpr bool isDirty(DirtyFlag dirtyFlag=DirtyFlag::DirtyMask) const
QSSGRenderImage * m_colorTable
QSSGParticleBuffer m_particleBuffer
void setCastsReflections(bool inCastsReflections)
void setCastsShadows(bool inCastsShadows)
void setHasTransparency(bool inHasTransparency)
void setReceivesShadows(bool inReceivesShadows)
QSSGShaderKeyUnsigned< 8 > m_targetTexCoord1Offset
QSSGShaderKeyBoolean m_lightSpotFlags[LightCount]
QSSGShaderKeyTextureChannel m_textureChannels[SingleChannelImageCount]
QSSGShaderKeyUnsigned< 16 > m_vertexColorAlphaMask
QSSGShaderKeyImageMap m_imageMaps[ImageMapCount]
QSSGShaderKeyUnsigned< 8 > m_targetPositionOffset
QSSGShaderKeyUnsigned< 8 > m_targetTexCoord0Offset
QSSGShaderKeyUnsigned< 16 > m_vertexColorBlueMask
QSSGShaderKeyVertexAttribute m_vertexAttributes
QSSGShaderKeyBoolean m_lightFlags[LightCount]
QSSGShaderKeyUnsigned< 8 > m_targetBinormalOffset
QSSGShaderKeyUnsigned< 16 > m_vertexColorGreenMask
QSSGShaderKeyBoolean m_lightShadowFlags[LightCount]
QSSGShaderKeyUnsigned< 16 > m_vertexColorRedMask
constexpr bool isSet(Feature feature) const
void set(Feature feature, bool val)
void setValue(QSSGDataRef< quint32 > inDataStore, bool inValue) const
bool getValue(QSSGDataView< quint32 > inDataStore) const
void setEnabled(QSSGDataRef< quint32 > inKeySet, bool val)
void setSpecularModel(QSSGDataRef< quint32 > inKeySet, QSSGRenderDefaultMaterial::MaterialSpecularModel inModel)
void setTextureChannel(TexturChannelBits channel, QSSGDataRef< quint32 > inKeySet)
void setValue(QSSGDataRef< quint32 > inDataStore, quint32 inValue) const