Qt 6.x
The Qt SDK
Loading...
Searching...
No Matches
qssgrenderer.cpp
Go to the documentation of this file.
1// Copyright (C) 2008-2012 NVIDIA Corporation.
2// Copyright (C) 2019 The Qt Company Ltd.
3// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
4
5#include <QtQuick3DRuntimeRender/private/qssgrenderitem2d_p.h>
6#include <QtQuick3DRuntimeRender/private/qssgrenderer_p.h>
7#include <QtQuick3DRuntimeRender/private/qssgrendercontextcore_p.h>
8#include <QtQuick3DRuntimeRender/private/qssgrendercamera_p.h>
9#include <QtQuick3DRuntimeRender/private/qssgrenderlight_p.h>
10#include <QtQuick3DRuntimeRender/private/qssgrenderimage_p.h>
11#include <QtQuick3DRuntimeRender/private/qssgrenderbuffermanager_p.h>
12#include <QtQuick3DRuntimeRender/private/qssgrendercontextcore_p.h>
13#include <QtQuick3DRuntimeRender/private/qssgrendereffect_p.h>
14#include <QtQuick3DRuntimeRender/private/qssgrhicustommaterialsystem_p.h>
15#include <QtQuick3DRuntimeRender/private/qssgrendershadercodegenerator_p.h>
16#include <QtQuick3DRuntimeRender/private/qssgrenderdefaultmaterialshadergenerator_p.h>
17#include <QtQuick3DRuntimeRender/private/qssgperframeallocator_p.h>
18#include <QtQuick3DRuntimeRender/private/qssgrhiquadrenderer_p.h>
19#include <QtQuick3DRuntimeRender/private/qssgrendertexturedata_p.h>
20#include <QtQuick3DRuntimeRender/private/qssglayerrenderdata_p.h>
21#include <QtQuick3DRuntimeRender/private/qssgrhiparticles_p.h>
22
23#include <QtQuick3DUtils/private/qquick3dprofiler_p.h>
24#include <QtQuick3DUtils/private/qssgdataref_p.h>
25#include <QtQuick3DUtils/private/qssgutils_p.h>
26#include <QtQuick3DUtils/private/qssgassert_p.h>
27#include <qtquick3d_tracepoints_p.h>
28
29#include <QtCore/QMutexLocker>
30#include <QtCore/QBitArray>
31
32#include <cstdlib>
33#include <algorithm>
34#include <limits>
35
37
40
41static constexpr float QSSG_PI = float(M_PI);
42static constexpr float QSSG_HALFPI = float(M_PI_2);
43
44static const QRhiShaderResourceBinding::StageFlags RENDERER_VISIBILITY_ALL =
46
48 QSSGSubsetRenderable &subsetRenderable,
49 const QSSGShaderFeatures &featureSet)
50{
51 const auto &renderer(subsetRenderable.renderer);
52 const auto &shaderPipeline = renderer->getShaderPipelineForDefaultMaterial(subsetRenderable, featureSet);
53 if (shaderPipeline)
54 ps->shaderPipeline = shaderPipeline.get();
55 return shaderPipeline;
56}
57
59 QSSGParticlesRenderable &particleRenderable)
60{
61 const auto &renderer(particleRenderable.renderer);
62 auto featureLevel = particleRenderable.particles.m_featureLevel;
63 const auto &shaderPipeline = renderer->getRhiParticleShader(featureLevel);
64 if (shaderPipeline)
65 ps->shaderPipeline = shaderPipeline.get();
66 return shaderPipeline;
67}
68
70 QSSGRhiContext *rhiCtx,
71 const QSSGLayerRenderData &inData,
72 char *ubufData,
74 QSSGSubsetRenderable &subsetRenderable,
76 const QVector2D *depthAdjust,
77 const QMatrix4x4 *alteredModelViewProjection)
78{
79 const auto &renderer(subsetRenderable.renderer);
80 const QMatrix4x4 clipSpaceCorrMatrix = rhiCtx->rhi()->clipSpaceCorrMatrix();
81 const QMatrix4x4 &mvp(alteredModelViewProjection ? *alteredModelViewProjection
82 : subsetRenderable.modelContext.modelViewProjection);
83
84 const auto &modelNode = subsetRenderable.modelContext.model;
85 QRhiTexture *lightmapTexture = inData.getLightmapTexture(subsetRenderable.modelContext);
86
87 const QMatrix4x4 &localInstanceTransform(modelNode.localInstanceTransform);
88 const QMatrix4x4 &globalInstanceTransform(modelNode.globalInstanceTransform);
89 const QMatrix4x4 &modelMatrix(modelNode.usesBoneTexture() ? QMatrix4x4() : subsetRenderable.globalTransform);
90
92 shaderPipeline,
93 ubufData,
94 ps,
95 subsetRenderable.material,
96 subsetRenderable.shaderDescription,
97 renderer->defaultMaterialShaderKeyProperties(),
98 camera,
99 mvp,
100 subsetRenderable.modelContext.normalMatrix,
101 modelMatrix,
102 clipSpaceCorrMatrix,
103 localInstanceTransform,
104 globalInstanceTransform,
105 toDataView(modelNode.morphWeights),
106 subsetRenderable.firstImage,
107 subsetRenderable.opacity,
108 renderer->getLayerGlobalRenderProperties(),
109 subsetRenderable.lights,
110 subsetRenderable.reflectionProbe,
111 subsetRenderable.renderableFlags.receivesShadows(),
112 subsetRenderable.renderableFlags.receivesReflections(),
113 depthAdjust,
114 lightmapTexture);
115}
116
117void QSSGRenderer::releaseCachedResources()
118{
119 delete m_rhiQuadRenderer;
120 m_rhiQuadRenderer = nullptr;
121 delete m_rhiCubeRenderer;
122 m_rhiCubeRenderer = nullptr;
123}
124
126
128{
129 m_contextInterface = nullptr;
130 releaseCachedResources();
131}
132
134{
135 m_contextInterface = ctx;
136}
137
139{
140 QSSGLayerRenderData *theRenderData = getOrCreateLayerRenderData(inLayer);
141 Q_ASSERT(theRenderData);
142 beginLayerRender(*theRenderData);
143 theRenderData->resetForFrame();
144 theRenderData->prepareForRender();
146 return theRenderData->layerPrepResult->flags.wasDirty();
147}
148
149// Phase 1: prepare. Called when the renderpass is not yet started on the command buffer.
151{
152 QSSGLayerRenderData *theRenderData = getOrCreateLayerRenderData(inLayer);
153 QSSG_ASSERT(theRenderData && theRenderData->camera, return);
154
155 const auto layerPrepResult = theRenderData->layerPrepResult;
156 if (layerPrepResult->isLayerVisible()) {
158 QSSGRhiContext *rhiCtx = contextInterface()->rhiContext().get();
159 QSSG_ASSERT(rhiCtx->isValid() && rhiCtx->rhi()->isRecordingFrame(), return);
160 theRenderData->maybeBakeLightmap();
161 beginLayerRender(*theRenderData);
162 // Process active passes. "PreMain" passes are individual passes
163 // that does can and should be done in the rhi prepare phase.
164 // It is assumed that passes are sorted in the list with regards to
165 // execution order.
166 const auto &activePasses = theRenderData->activePasses;
167 for (const auto &pass : activePasses) {
168 pass->renderPrep(*this, *theRenderData);
169 if (pass->passType() == QSSGRenderPass::Type::Standalone)
170 pass->renderPass(*this);
171 }
172
174 }
175}
176
177// Phase 2: render. Called within an active renderpass on the command buffer.
179{
180 QSSGLayerRenderData *theRenderData = getOrCreateLayerRenderData(inLayer);
181 QSSG_ASSERT(theRenderData && theRenderData->camera, return);
182 if (theRenderData->layerPrepResult->isLayerVisible()) {
183 QSSG_ASSERT(theRenderData->camera, return);
184 beginLayerRender(*theRenderData);
185 const auto &activePasses = theRenderData->activePasses;
186 for (const auto &pass : activePasses) {
187 if (pass->passType() == QSSGRenderPass::Type::Main || pass->passType() == QSSGRenderPass::Type::Extension)
188 pass->renderPass(*this);
189 }
191 }
192}
193
194template<typename Container>
195static void cleanupResourcesImpl(const QSSGRenderContextInterface &rci, const Container &resources)
196{
197 const auto &rhi = rci.rhiContext();
198 if (!rhi->isValid())
199 return;
200
201 const auto &bufferManager = rci.bufferManager();
202
203 for (const auto &resource : resources) {
204 if (resource->type == QSSGRenderGraphObject::Type::Geometry) {
205 auto geometry = static_cast<QSSGRenderGeometry*>(resource);
206 bufferManager->releaseGeometry(geometry);
207 } else if (resource->type == QSSGRenderGraphObject::Type::Model) {
208 auto model = static_cast<QSSGRenderModel*>(resource);
209 rhi->cleanupDrawCallData(model);
210 } else if (resource->type == QSSGRenderGraphObject::Type::TextureData) {
211 auto textureData = static_cast<QSSGRenderTextureData *>(resource);
212 bufferManager->releaseTextureData(textureData);
213 }
214
215 // ### There might be more types that need to be supported
216
217 delete resource;
218 }
219}
220
222{
223 cleanupResourcesImpl(*m_contextInterface, resources);
224 resources.clear();
225}
226
228{
229 cleanupResourcesImpl(*m_contextInterface, resources);
230 resources.clear();
231}
232
234{
235 if (layer.renderData == nullptr)
236 layer.renderData = new QSSGLayerRenderData(layer, *this);
237
238 return layer.renderData;
239}
240
242{
243 m_materialClearDirty.insert(material);
244}
245
246static QByteArray logPrefix() { return QByteArrayLiteral("mesh default material pipeline-- "); }
247
248
250 QSSGShaderLibraryManager &shaderLibraryManager,
251 QSSGShaderCache &shaderCache,
252 QSSGProgramGenerator &shaderProgramGenerator,
253 QSSGShaderDefaultMaterialKeyProperties &shaderKeyProperties,
254 const QSSGShaderFeatures &featureSet,
255 QByteArray &shaderString)
256{
257 shaderString = logPrefix();
259
260 // This is not a cheap operation. This function assumes that it will not be
261 // hit for every material for every model in every frame (except of course
262 // for materials that got changed). In practice this is ensured by the
263 // cheaper-to-lookup cache in getShaderPipelineForDefaultMaterial().
264 theKey.toString(shaderString, shaderKeyProperties);
265
266 // Check the in-memory, per-QSSGShaderCache (and so per-QQuickWindow)
267 // runtime cache. That may get cleared upon an explicit call to
268 // QQuickWindow::releaseResources(), but will otherwise store all
269 // encountered shader pipelines in any View3D in the window.
270 if (const auto &maybePipeline = shaderCache.tryGetRhiShaderPipeline(shaderString, featureSet))
271 return maybePipeline;
272
273 // Check if there's a pre-built (offline generated) shader for available.
274 const QByteArray qsbcKey = QQsbCollection::EntryDesc::generateSha(shaderString, QQsbCollection::toFeatureSet(featureSet));
275 const QQsbCollection::EntryMap &pregenEntries = shaderLibraryManager.m_preGeneratedShaderEntries;
276 if (!pregenEntries.isEmpty()) {
277 const auto foundIt = pregenEntries.constFind(QQsbCollection::Entry(qsbcKey));
278 if (foundIt != pregenEntries.cend())
279 return shaderCache.newPipelineFromPregenerated(shaderString, featureSet, *foundIt, renderable.material);
280 }
281
282 // Try the persistent (disk-based) cache then.
283 if (const auto &maybePipeline = shaderCache.tryNewPipelineFromPersistentCache(qsbcKey, shaderString, featureSet))
284 return maybePipeline;
285
286 // Otherwise, build new shader code and run the resulting shaders through
287 // the shader conditioning pipeline.
288 QSSGMaterialVertexPipeline vertexPipeline(shaderProgramGenerator,
289 shaderKeyProperties,
290 renderable.defaultMaterial().adapter);
291
293 vertexPipeline,
294 renderable.shaderDescription,
295 shaderKeyProperties,
296 featureSet,
297 renderable.material,
298 renderable.lights,
299 renderable.firstImage,
300 shaderLibraryManager,
301 shaderCache);
302}
303
304QSSGRhiShaderPipelinePtr QSSGRenderer::generateRhiShaderPipeline(QSSGSubsetRenderable &inRenderable,
305 const QSSGShaderFeatures &inFeatureSet)
306{
307 const auto &theCache = m_contextInterface->shaderCache();
308 const auto &shaderProgramGenerator = contextInterface()->shaderProgramGenerator();
309 const auto &shaderLibraryManager = contextInterface()->shaderLibraryManager();
310 return generateRhiShaderPipelineImpl(inRenderable, *shaderLibraryManager, *theCache, *shaderProgramGenerator, m_defaultMaterialShaderKeyProperties, inFeatureSet, m_generatedShaderString);
311}
312
314{
315 QSSGRHICTX_STAT(m_contextInterface->rhiContext().get(), start(layer));
316}
317
319{
320 // We need to do this endFrame(), as the material nodes might not exist after this!
321 for (auto *matObj : std::as_const(m_materialClearDirty)) {
322 if (matObj->type == QSSGRenderGraphObject::Type::CustomMaterial) {
323 static_cast<QSSGRenderCustomMaterial *>(matObj)->clearDirty();
324 } else if (matObj->type == QSSGRenderGraphObject::Type::DefaultMaterial ||
325 matObj->type == QSSGRenderGraphObject::Type::PrincipledMaterial ||
326 matObj->type == QSSGRenderGraphObject::Type::SpecularGlossyMaterial) {
327 static_cast<QSSGRenderDefaultMaterial *>(matObj)->clearDirty();
328 }
329 }
330 m_materialClearDirty.clear();
331
332 QSSGRHICTX_STAT(m_contextInterface->rhiContext().get(), stop(layer));
333}
334
336 QSSGBufferManager &bufferManager,
337 const QSSGRenderRay &ray)
338{
339 PickResultList pickResults;
341 getLayerHitObjectList(layer, bufferManager, ray, m_globalPickingEnabled, pickResults);
342 // Things are rendered in a particular order and we need to respect that ordering.
343 std::stable_sort(pickResults.begin(), pickResults.end(), [](const QSSGRenderPickResult &lhs, const QSSGRenderPickResult &rhs) {
344 return lhs.m_distanceSq < rhs.m_distanceSq;
345 });
346 return pickResults;
347}
348
350 QSSGBufferManager &bufferManager,
351 const QSSGRenderRay &ray,
353{
354 static const auto processResults = [](PickResultList &pickResults) {
355 if (pickResults.empty())
357 // Things are rendered in a particular order and we need to respect that ordering.
358 std::stable_sort(pickResults.begin(), pickResults.end(), [](const QSSGRenderPickResult &lhs, const QSSGRenderPickResult &rhs) {
359 return lhs.m_distanceSq < rhs.m_distanceSq;
360 });
361 return QSSGPickResultProcessResult{ pickResults.at(0), true };
362 };
363
365 PickResultList pickResults;
366 if (target) {
367 // Pick against only one target
368 intersectRayWithSubsetRenderable(bufferManager, ray, *target, pickResults);
369 return processResults(pickResults);
370 } else {
371 getLayerHitObjectList(layer, bufferManager, ray, m_globalPickingEnabled, pickResults);
372 QSSGPickResultProcessResult retval = processResults(pickResults);
373 if (retval.m_wasPickConsumed)
374 return retval;
375 }
376
378}
379
381{
382 return m_globalPickingEnabled;
383}
384
386{
387 m_globalPickingEnabled = isEnabled;
388}
389
391{
392 if (!m_contextInterface->rhiContext()->isValid())
393 return nullptr;
394
395 if (!m_rhiQuadRenderer)
396 m_rhiQuadRenderer = new QSSGRhiQuadRenderer;
397
398 return m_rhiQuadRenderer;
399}
400
402{
403 if (!m_contextInterface->rhiContext()->isValid())
404 return nullptr;
405
406 if (!m_rhiCubeRenderer)
407 m_rhiCubeRenderer = new QSSGRhiCubeRenderer;
408
409 return m_rhiCubeRenderer;
410
411}
412
414{
415 m_currentLayer = &inLayer;
416}
418{
419 m_currentLayer = nullptr;
420}
421
423{
424 return m_progressiveAARenderRequest;
425}
426
428static void dfs(const QSSGRenderNode &node, RenderableList &renderables)
429{
431 renderables.push_back(&node);
432
433 for (const auto &child : node.children)
434 dfs(child, renderables);
435}
436
438 QSSGBufferManager &bufferManager,
439 const QSSGRenderRay &ray,
440 bool inPickEverything,
441 PickResultList &outIntersectionResult)
442{
443 RenderableList renderables;
444 for (const auto &childNode : layer.children)
445 dfs(childNode, renderables);
446
447 for (int idx = renderables.size() - 1; idx >= 0; --idx) {
448 const auto &pickableObject = renderables.at(idx);
449 if (inPickEverything || pickableObject->getLocalState(QSSGRenderNode::LocalState::Pickable))
450 intersectRayWithSubsetRenderable(bufferManager, ray, *pickableObject, outIntersectionResult);
451 }
452}
453
455 const QSSGRenderRay &inRay,
456 const QSSGRenderNode &node,
457 QSSGRenderer::PickResultList &outIntersectionResultList)
458{
459 // Item2D's requires special handling
460 if (node.type == QSSGRenderGraphObject::Type::Item2D) {
461 const QSSGRenderItem2D &item2D = static_cast<const QSSGRenderItem2D &>(node);
462 intersectRayWithItem2D(inRay, item2D, outIntersectionResultList);
463 return;
464 }
465
466 if (node.type != QSSGRenderGraphObject::Type::Model)
467 return;
468
469 const QSSGRenderModel &model = static_cast<const QSSGRenderModel &>(node);
470
471 // We have to have a guard here, as the meshes are usually loaded on the render thread,
472 // and we assume all meshes are loaded before picking and none are removed, which
473 // is usually true, except for custom geometry which can be updated at any time. So this
474 // guard should really only be locked whenever a custom geometry buffer is being updated
475 // on the render thread. Still naughty though because this can block the render thread.
476 QMutexLocker mutexLocker(bufferManager.meshUpdateMutex());
477 auto mesh = bufferManager.getMeshForPicking(model);
478 if (!mesh)
479 return;
480
481 const auto &subMeshes = mesh->subsets;
482 QSSGBounds3 modelBounds;
483 for (const auto &subMesh : subMeshes)
484 modelBounds.include(subMesh.bounds);
485
486 if (modelBounds.isEmpty())
487 return;
488
489 const bool instancing = model.instancing(); // && instancePickingEnabled
490 int instanceCount = instancing ? model.instanceTable->count() : 1;
491
492 for (int instanceIndex = 0; instanceIndex < instanceCount; ++instanceIndex) {
493
494 QMatrix4x4 modelTransform;
495 if (instancing) {
496 modelTransform = model.globalInstanceTransform * model.instanceTable->getTransform(instanceIndex) * model.localInstanceTransform;
497 } else {
498 modelTransform = model.globalTransform;
499 }
500 auto rayData = QSSGRenderRay::createRayData(modelTransform, inRay);
501
502 auto hit = QSSGRenderRay::intersectWithAABBv2(rayData, modelBounds);
503
504 // If we don't intersect with the model at all, then there's no need to go furher down!
505 if (!hit.intersects())
506 continue;
507
508 // Check each submesh to find the closest intersection point
509 float minRayLength = std::numeric_limits<float>::max();
510 QSSGRenderRay::IntersectionResult intersectionResult;
512
513 int subset = 0;
514 int resultSubset = 0;
515 for (const auto &subMesh : subMeshes) {
517 if (subMesh.bvhRoot) {
518 hit = QSSGRenderRay::intersectWithAABBv2(rayData, subMesh.bvhRoot->boundingData);
519 if (hit.intersects()) {
520 results.clear();
521 inRay.intersectWithBVH(rayData, subMesh.bvhRoot, mesh, results);
522 float subMeshMinRayLength = std::numeric_limits<float>::max();
523 for (const auto &subMeshResult : std::as_const(results)) {
524 if (subMeshResult.rayLengthSquared < subMeshMinRayLength) {
525 result = subMeshResult;
526 subMeshMinRayLength = result.rayLengthSquared;
527 }
528 }
529 }
530 } else {
531 hit = QSSGRenderRay::intersectWithAABBv2(rayData, subMesh.bounds);
532 if (hit.intersects())
534 }
535 if (result.intersects && result.rayLengthSquared < minRayLength) {
536 intersectionResult = result;
537 minRayLength = intersectionResult.rayLengthSquared;
538 resultSubset = subset;
539 }
540 subset++;
541 }
542
543 if (intersectionResult.intersects)
544 outIntersectionResultList.push_back(QSSGRenderPickResult { &model,
545 intersectionResult.rayLengthSquared,
546 intersectionResult.relXY,
547 intersectionResult.scenePosition,
548 intersectionResult.localPosition,
549 intersectionResult.faceNormal,
550 resultSubset,
551 instanceIndex
552 });
553 }
554}
555
556void QSSGRenderer::intersectRayWithItem2D(const QSSGRenderRay &inRay, const QSSGRenderItem2D &item2D, QSSGRenderer::PickResultList &outIntersectionResultList)
557{
558 // Get the plane (and normal) that the item 2D is on
559 const QVector3D p0 = item2D.getGlobalPos();
560 const QVector3D normal = -item2D.getDirection();
561
562 const float d = QVector3D::dotProduct(inRay.direction, normal);
563 float intersectionTime = 0;
564 if (d > 1e-6f) {
565 const QVector3D p0l0 = p0 - inRay.origin;
566 intersectionTime = QVector3D::dotProduct(p0l0, normal) / d;
567 if (intersectionTime >= 0) {
568 // Intersection
569 const QVector3D intersectionPoint = inRay.origin + inRay.direction * intersectionTime;
570 const QMatrix4x4 inverseGlobalTransform = item2D.globalTransform.inverted();
571 const QVector3D localIntersectionPoint = mat44::transform(inverseGlobalTransform, intersectionPoint);
572 const QVector2D qmlCoordinate(localIntersectionPoint.x(), -localIntersectionPoint.y());
573 outIntersectionResultList.push_back(QSSGRenderPickResult { &item2D,
574 intersectionTime * intersectionTime,
575 qmlCoordinate,
576 intersectionPoint,
577 localIntersectionPoint,
578 normal });
579 }
580 }
581}
582
584 const QSSGShaderFeatures &inFeatureSet)
585{
586 if (Q_UNLIKELY(m_currentLayer == nullptr)) {
587 Q_ASSERT(false);
588 return nullptr;
589 }
590
591 // This function is the main entry point for retrieving the shaders for a
592 // default material, and is called for every material for every model in
593 // every frame. Therefore, like with custom materials, employ a first level
594 // cache (a simple hash table), with a key that's quick to
595 // generate/hash/compare. Even though there are other levels of caching in
596 // the components that get invoked from here, those may not be suitable
597 // performance wise. So bail out right here as soon as possible.
598
600 timer.start();
601
602 QSSGRhiShaderPipelinePtr shaderPipeline;
603
604 // This just references inFeatureSet and inRenderable.shaderDescription -
605 // cheap to construct and is good enough for the find()
607 inFeatureSet,
608 inRenderable.shaderDescription);
609 auto it = m_shaderMap.find(skey);
610 if (it == m_shaderMap.end()) {
611 Q_TRACE_SCOPE(QSSG_generateShader);
612 Q_QUICK3D_PROFILE_START(QQuick3DProfiler::Quick3DGenerateShader);
613 shaderPipeline = generateRhiShaderPipeline(inRenderable, inFeatureSet);
614 Q_QUICK3D_PROFILE_END_WITH_ID(QQuick3DProfiler::Quick3DGenerateShader, 0, inRenderable.material.profilingId);
615 // make skey useable as a key for the QHash (makes copies of materialKey and featureSet, instead of just referencing)
616 skey.detach();
617 // insert it no matter what, no point in trying over and over again
618 m_shaderMap.insert(skey, shaderPipeline);
619 } else {
620 shaderPipeline = it.value();
621 }
622
623 if (shaderPipeline != nullptr) {
624 if (m_currentLayer && m_currentLayer->camera) {
625 if (!m_currentLayer->cameraData.has_value())
626 [[maybe_unused]] const auto cd = m_currentLayer->getCachedCameraData();
627 }
628 }
629
630 m_contextInterface->rhiContext()->stats().registerMaterialShaderGenerationTime(timer.elapsed());
631
632 return shaderPipeline;
633}
634
636{
637 QSSGLayerRenderData &theData = *m_currentLayer;
638 const QSSGRenderLayer &theLayer = theData.layer;
639 if (!theData.cameraData.has_value() && theData.camera) // NOTE: Ensure we have a valid value!
640 [[maybe_unused]] const auto cd = theData.getCachedCameraData();
641
642 bool isYUpInFramebuffer = true;
643 bool isYUpInNDC = true;
644 bool isClipDepthZeroToOne = true;
645 if (m_contextInterface->rhiContext()->isValid()) {
646 QRhi *rhi = m_contextInterface->rhiContext()->rhi();
647 isYUpInFramebuffer = rhi->isYUpInFramebuffer();
648 isYUpInNDC = rhi->isYUpInNDC();
649 isClipDepthZeroToOne = rhi->isClipDepthZeroToOne();
650 }
651
652 return QSSGLayerGlobalRenderProperties{ theLayer,
653 *theData.camera,
654 theData.cameraData.value(), // ensured/checked further up in this function
655 theData.getShadowMapManager().get(),
659 theLayer.lightProbe,
660 theLayer.probeHorizon,
661 theLayer.probeExposure,
662 theLayer.probeOrientation,
663 isYUpInFramebuffer,
664 isYUpInNDC,
665 isClipDepthZeroToOne};
666}
667
669{
678}
679
681
682std::pair<QSSGBoxPoints, QSSGBoxPoints> RenderHelpers::calculateSortedObjectBounds(const QVector<QSSGRenderableObjectHandle> &sortedOpaqueObjects,
683 const QVector<QSSGRenderableObjectHandle> &sortedTransparentObjects)
684{
685 QSSGBounds3 boundsCasting;
686 QSSGBounds3 boundsReceiving;
687 for (const auto handles : { &sortedOpaqueObjects, &sortedTransparentObjects }) {
688 // Since we may have nodes that are not a child of the camera parent we go through all
689 // the opaque objects and include them in the bounds. Failing to do this can result in
690 // too small bounds.
691 for (const QSSGRenderableObjectHandle &handle : *handles) {
692 const QSSGRenderableObject &obj = *handle.obj;
693 // We skip objects not casting or receiving shadows since they don't influence or need to be covered by the shadow map
694 if (obj.renderableFlags.castsShadows())
695 boundsCasting.include(obj.globalBounds);
696 if (obj.renderableFlags.receivesShadows())
697 boundsReceiving.include(obj.globalBounds);
698 }
699 }
700 return { boundsCasting.toQSSGBoxPointsNoEmptyCheck(), boundsReceiving.toQSSGBoxPointsNoEmptyCheck() };
701}
702
703static QVector3D calcCenter(const QSSGBoxPoints &vertices)
704{
705 QVector3D center = vertices[0];
706 for (int i = 1; i < 8; ++i) {
707 center += vertices[i];
708 }
709 return center * 0.125f;
710}
711
713{
715 for (int i = 0; i < 8; ++i) {
716 const float distanceZ = QVector3D::dotProduct(points[i], forward);
717 const float distanceY = QVector3D::dotProduct(points[i], up);
718 const float distanceX = QVector3D::dotProduct(points[i], right);
719 bounds.include(QVector3D(distanceX, distanceY, distanceZ));
720 }
721 return bounds;
722}
723
725{
726 QMatrix4x4 viewProjection;
727 inCamera.calculateViewProjectionMatrix(viewProjection);
728
729 bool invertible = false;
730 QMatrix4x4 inv = viewProjection.inverted(&invertible);
731 Q_ASSERT(invertible);
732
733 return { inv.map(QVector3D(-1, -1, -1)), inv.map(QVector3D(+1, -1, -1)), inv.map(QVector3D(+1, +1, -1)),
734 inv.map(QVector3D(-1, +1, -1)), inv.map(QVector3D(-1, -1, +1)), inv.map(QVector3D(+1, -1, +1)),
735 inv.map(QVector3D(+1, +1, +1)), inv.map(QVector3D(-1, +1, +1)) };
736}
737
739{
740 Q_ASSERT(inProbe != nullptr);
741
742 // setup light matrix
743 quint32 mapRes = 1 << inProbe->reflectionMapRes;
744 QRectF theViewport(0.0f, 0.0f, (float)mapRes, (float)mapRes);
745 static const QQuaternion rotOfs[6] {
752 };
753
754 const QVector3D inProbePos = inProbe->getGlobalPos();
755 const QVector3D inProbePivot = inProbe->pivot;
756
757 for (int i = 0; i < 6; ++i) {
758 inCameras[i].parent = nullptr;
759 inCameras[i].clipNear = 1.0f;
760 inCameras[i].clipFar = qMax<float>(2.0f, 10000.0f);
761 inCameras[i].fov = qDegreesToRadians(90.f);
762
763 inCameras[i].localTransform = QSSGRenderNode::calculateTransformMatrix(inProbePos, QSSGRenderNode::initScale, inProbePivot, rotOfs[i]);
764 inCameras[i].calculateGlobalVariables(theViewport);
765 }
766}
767
768static void setupCameraForShadowMap(const QSSGRenderCamera &inCamera,
769 const QSSGRenderLight *inLight,
770 QSSGRenderCamera &theCamera,
771 const QSSGBoxPoints &castingBox,
772 const QSSGBoxPoints &receivingBox)
773{
774 using namespace RenderHelpers;
775
776 // setup light matrix
777 quint32 mapRes = 1 << inLight->m_shadowMapRes;
778 QRectF theViewport(0.0f, 0.0f, (float)mapRes, (float)mapRes);
779 theCamera.clipNear = 1.0f;
780 theCamera.clipFar = inLight->m_shadowMapFar;
781 // Setup camera projection
782 QVector3D inLightPos = inLight->getGlobalPos();
783 QVector3D inLightDir = inLight->getDirection();
784 QVector3D inLightPivot = inLight->pivot;
785
786 inLightPos -= inLightDir * inCamera.clipNear;
787 theCamera.fov = qDegreesToRadians(90.f);
788 theCamera.parent = nullptr;
789
790 if (inLight->type == QSSGRenderLight::Type::DirectionalLight) {
791 Q_ASSERT(theCamera.type == QSSGRenderCamera::Type::OrthographicCamera);
792 const QVector3D forward = inLightDir.normalized();
793 const QVector3D right = qFuzzyCompare(qAbs(forward.y()), 1.0f)
794 ? QVector3D::crossProduct(forward, QVector3D(1, 0, 0)).normalized()
795 : QVector3D::crossProduct(forward, QVector3D(0, 1, 0)).normalized();
796 const QVector3D up = QVector3D::crossProduct(right, forward).normalized();
797
798 // Calculate bounding box of the scene camera frustum
799 const QSSGBoxPoints frustumPoints = computeFrustumBounds(inCamera);
800 const QSSGBounds3 frustumBounds = calculateShadowCameraBoundingBox(frustumPoints, forward, up, right);
801 const QSSGBounds3 sceneCastingBounds = calculateShadowCameraBoundingBox(castingBox, forward, up, right);
802 const QSSGBounds3 boundsReceiving = calculateShadowCameraBoundingBox(receivingBox, forward, up, right);
803
804 QVector3D finalDims;
805 QVector3D center;
806 // Select smallest bounds from either scene or camera frustum
807 if (sceneCastingBounds.isFinite() && boundsReceiving.isFinite() // handle empty scene
808 && sceneCastingBounds.extents().lengthSquared() < frustumBounds.extents().lengthSquared()) {
809 center = calcCenter(castingBox);
810 const QVector3D centerReceiving = calcCenter(receivingBox);
811
812 // Since we need to make sure every rendered geometry can get a valid depth value from the shadow map
813 // we need to expand the scene bounding box along its z-axis so that it covers also receiving objects in the scene.
814 //
815 // We take the z dimensions of the casting bounds and expand it to include the z dimensions of the receiving objects.
816 // We call the casting bounding box 'a' and the receiving bounding box 'b'.
817
818 // length of boxes
819 const float aLength = sceneCastingBounds.dimensions().z();
820 const float bLength = boundsReceiving.dimensions().z();
821
822 // center position of boxes
823 const float aCenter = QVector3D::dotProduct(center, forward);
824 const float bCenter = QVector3D::dotProduct(centerReceiving, forward);
825
826 // distance between boxes
827 const float d = bCenter - aCenter;
828
829 // start/end positions
830 const float a0 = 0.f;
831 const float a1 = aLength;
832 const float b0 = (aLength * 0.5f) + d - (bLength * 0.5f);
833 const float b1 = (aLength * 0.5f) + d + (bLength * 0.5f);
834
835 // goal start/end position
836 const float ap0 = qMin(a0, b0);
837 const float ap1 = qMax(a1, b1);
838 // goal length
839 const float length = ap1 - ap0;
840 // goal center postion
841 const float c = (ap1 + ap0) * 0.5f;
842
843 // how much to move in forward direction
844 const float move = c - aLength * 0.5f;
845
846 center = center + forward * move;
847 finalDims = sceneCastingBounds.dimensions();
848 finalDims.setZ(length);
849 } else {
850 center = calcCenter(frustumPoints);
851 finalDims = frustumBounds.dimensions();
852 }
853
854 // Expand dimensions a little bit to avoid precision problems
855 finalDims *= 1.05f;
856
857 // Apply bounding box parameters to shadow map camera projection matrix
858 // so that the whole scene is fit inside the shadow map
859 theViewport.setHeight(finalDims.y());
860 theViewport.setWidth(finalDims.x());
861 theCamera.clipNear = -0.5f * finalDims.z();
862 theCamera.clipFar = 0.5f * finalDims.z();
864 } else if (inLight->type == QSSGRenderLight::Type::PointLight) {
865 theCamera.lookAt(inLightPos, QVector3D(0, 1.0, 0), QVector3D(0, 0, 0), inLightPivot);
866 }
867
868 theCamera.calculateGlobalVariables(theViewport);
869}
870
872 QSSGRhiShaderPipeline *shaderPipeline,
873 QSSGRenderableImage *renderableImage,
875 bool isCustomMaterialMeshSubset = false)
876{
877 static const auto imageAffectsAlpha = [](QSSGRenderableImage::Type mapType) {
878 return mapType == QSSGRenderableImage::Type::BaseColor ||
879 mapType == QSSGRenderableImage::Type::Diffuse ||
880 mapType == QSSGRenderableImage::Type::Translucency ||
881 mapType == QSSGRenderableImage::Type::Opacity;
882 };
883
884 while (renderableImage) {
885 const auto mapType = renderableImage->m_mapType;
886 if (imageAffectsAlpha(mapType)) {
887 const char *samplerName = QSSGMaterialShaderGenerator::getSamplerName(mapType);
888 const int samplerHint = int(mapType);
889 int samplerBinding = shaderPipeline->bindingForTexture(samplerName, samplerHint);
890 if (samplerBinding >= 0) {
891 QRhiTexture *texture = renderableImage->m_texture.m_texture;
892 if (samplerBinding >= 0 && texture) {
893 const bool mipmapped = texture->flags().testFlag(QRhiTexture::MipMapped);
894 QRhiSampler *sampler = rhiCtx->sampler({ toRhi(renderableImage->m_imageNode.m_minFilterType),
895 toRhi(renderableImage->m_imageNode.m_magFilterType),
896 mipmapped ? toRhi(renderableImage->m_imageNode.m_mipFilterType) : QRhiSampler::None,
897 toRhi(renderableImage->m_imageNode.m_horizontalTilingMode),
898 toRhi(renderableImage->m_imageNode.m_verticalTilingMode),
900 });
901 bindings.addTexture(samplerBinding, RENDERER_VISIBILITY_ALL, texture, sampler);
902 }
903 } // else this is not necessarily an error, e.g. having metalness/roughness maps with metalness disabled
904 }
905 renderableImage = renderableImage->m_nextImage;
906 }
907 // For custom Materials we can't know which maps affect alpha, so map all
908 if (isCustomMaterialMeshSubset) {
910 shaderPipeline->fragmentStage()->shader().description().combinedImageSamplers();
912 auto it = std::find_if(samplerVars.cbegin(), samplerVars.cend(),
913 [&var](const QShaderDescription::InOutVariable &v) { return var.binding == v.binding; });
914 if (it == samplerVars.cend())
915 samplerVars.append(var);
916 }
917
918 int maxSamplerBinding = -1;
919 for (const QShaderDescription::InOutVariable &var : samplerVars)
920 maxSamplerBinding = qMax(maxSamplerBinding, var.binding);
921
922 // Will need to set unused image-samplers to something dummy
923 // because the shader code contains all custom property textures,
924 // and not providing a binding for all of them is invalid with some
925 // graphics APIs (and will need a real texture because setting a
926 // null handle or similar is not permitted with some of them so the
927 // srb does not accept null QRhiTextures either; but first let's
928 // figure out what bindings are unused in this frame)
929 QBitArray samplerBindingsSpecified(maxSamplerBinding + 1);
930
931 if (maxSamplerBinding >= 0) {
932 // custom property textures
933 int customTexCount = shaderPipeline->extraTextureCount();
934 for (int i = 0; i < customTexCount; ++i) {
935 const QSSGRhiTexture &t(shaderPipeline->extraTextureAt(i));
936 const int samplerBinding = shaderPipeline->bindingForTexture(t.name);
937 if (samplerBinding >= 0) {
938 samplerBindingsSpecified.setBit(samplerBinding);
939 QRhiSampler *sampler = rhiCtx->sampler(t.samplerDesc);
940 bindings.addTexture(samplerBinding,
942 t.texture,
943 sampler);
944 }
945 }
946 }
947
948 // use a dummy texture for the unused samplers in the shader
951 QRhiResourceUpdateBatch *resourceUpdates = rhiCtx->rhi()->nextResourceUpdateBatch();
952 QRhiTexture *dummyTexture = rhiCtx->dummyTexture({}, resourceUpdates);
953 QRhiTexture *dummyCubeTexture = rhiCtx->dummyTexture(QRhiTexture::CubeMap, resourceUpdates);
954 rhiCtx->commandBuffer()->resourceUpdate(resourceUpdates);
955
956 for (const QShaderDescription::InOutVariable &var : samplerVars) {
957 if (!samplerBindingsSpecified.testBit(var.binding)) {
958 QRhiTexture *t = var.type == QShaderDescription::SamplerCube ? dummyCubeTexture : dummyTexture;
959 bindings.addTexture(var.binding, RENDERER_VISIBILITY_ALL, t, dummySampler);
960 }
961 }
962 }
963}
964
965static void setupCubeShadowCameras(const QSSGRenderLight *inLight, QSSGRenderCamera inCameras[6])
966{
967 Q_ASSERT(inLight != nullptr);
968 Q_ASSERT(inLight->type != QSSGRenderLight::Type::DirectionalLight);
969
970 // setup light matrix
971 quint32 mapRes = 1 << inLight->m_shadowMapRes;
972 QRectF theViewport(0.0f, 0.0f, (float)mapRes, (float)mapRes);
973 static const QQuaternion rotOfs[6] {
980 };
981
982 const QVector3D inLightPos = inLight->getGlobalPos();
983 const QVector3D inLightPivot = inLight->pivot;
984
985 for (int i = 0; i < 6; ++i) {
986 inCameras[i].parent = nullptr;
987 inCameras[i].clipNear = 1.0f;
988 inCameras[i].clipFar = qMax<float>(2.0f, inLight->m_shadowMapFar);
989 inCameras[i].fov = qDegreesToRadians(90.f);
990 inCameras[i].localTransform = QSSGRenderNode::calculateTransformMatrix(inLightPos, QSSGRenderNode::initScale, inLightPivot, rotOfs[i]);
991 inCameras[i].calculateGlobalVariables(theViewport);
992 }
993
994 /*
995 if ( inLight->type == RenderLightTypes::Point ) return;
996
997 QVector3D viewDirs[6];
998 QVector3D viewUp[6];
999 QMatrix3x3 theDirMatrix( inLight->m_GlobalTransform.getUpper3x3() );
1000
1001 viewDirs[0] = theDirMatrix.transform( QVector3D( 1.f, 0.f, 0.f ) );
1002 viewDirs[2] = theDirMatrix.transform( QVector3D( 0.f, -1.f, 0.f ) );
1003 viewDirs[4] = theDirMatrix.transform( QVector3D( 0.f, 0.f, 1.f ) );
1004 viewDirs[0].normalize(); viewDirs[2].normalize(); viewDirs[4].normalize();
1005 viewDirs[1] = -viewDirs[0];
1006 viewDirs[3] = -viewDirs[2];
1007 viewDirs[5] = -viewDirs[4];
1008
1009 viewUp[0] = viewDirs[2];
1010 viewUp[1] = viewDirs[2];
1011 viewUp[2] = viewDirs[5];
1012 viewUp[3] = viewDirs[4];
1013 viewUp[4] = viewDirs[2];
1014 viewUp[5] = viewDirs[2];
1015
1016 for (int i = 0; i < 6; ++i)
1017 {
1018 inCameras[i].LookAt( inLightPos, viewUp[i], inLightPos + viewDirs[i] );
1019 inCameras[i].CalculateGlobalVariables( theViewport, QVector2D( theViewport.m_Width,
1020 theViewport.m_Height ) );
1021 }
1022 */
1023}
1024
1025static int setupInstancing(QSSGSubsetRenderable *renderable, QSSGRhiGraphicsPipelineState *ps, QSSGRhiContext *rhiCtx, const QVector3D &cameraDirection, const QVector3D &cameraPosition)
1026{
1027 // TODO: non-static so it can be used from QSSGCustomMaterialSystem::rhiPrepareRenderable()?
1028 const bool instancing = renderable->prepareInstancing(rhiCtx, cameraDirection, cameraPosition, renderable->instancingLodMin, renderable->instancingLodMax);
1029 int instanceBufferBinding = 0;
1030 if (instancing) {
1031 // set up new bindings for instanced buffers
1032 const quint32 stride = renderable->modelContext.model.instanceTable->stride();
1034 std::copy(ps->ia.inputLayout.cbeginBindings(), ps->ia.inputLayout.cendBindings(), std::back_inserter(bindings));
1036 instanceBufferBinding = bindings.size() - 1;
1037 ps->ia.inputLayout.setBindings(bindings.cbegin(), bindings.cend());
1038 }
1039 return instanceBufferBinding;
1040}
1041
1043 QSSGPassKey passKey,
1044 const QSSGLayerRenderData &inData,
1045 QSSGReflectionMapEntry *pEntry,
1047 const QVector<QSSGRenderableObjectHandle> &sortedOpaqueObjects,
1048 QSSGRenderCamera &inCamera,
1050 int cubeFace)
1051{
1052 using namespace RenderHelpers;
1053
1056 rhiPrepareSkyBoxForReflectionMap(rhiCtx, passKey, inData.layer, inCamera, renderer, pEntry, cubeFace);
1057
1058 QSSGShaderFeatures features = inData.getShaderFeatures();
1059
1060 for (const auto &handle : sortedOpaqueObjects) {
1061 QSSGRenderableObject &inObject = *handle.obj;
1062
1063 QMatrix4x4 modelViewProjection;
1064 if (inObject.type == QSSGRenderableObject::Type::DefaultMaterialMeshSubset || inObject.type == QSSGRenderableObject::Type::CustomMaterialMeshSubset) {
1065 QSSGSubsetRenderable &renderable(static_cast<QSSGSubsetRenderable &>(inObject));
1066 const bool hasSkinning = renderer.defaultMaterialShaderKeyProperties().m_boneCount.getValue(renderable.shaderDescription) > 0;
1067 modelViewProjection = hasSkinning ? pEntry->m_viewProjection
1068 : pEntry->m_viewProjection * renderable.globalTransform;
1069 }
1070
1071 rhiPrepareRenderable(rhiCtx, passKey, inData, inObject, pEntry->m_rhiRenderPassDesc, ps, features, 1,
1072 &inCamera, &modelViewProjection, cubeFace, pEntry);
1073 }
1074}
1075
1076static inline void addDepthTextureBindings(QSSGRhiContext *rhiCtx,
1077 QSSGRhiShaderPipeline *shaderPipeline,
1079{
1080 if (shaderPipeline->depthTexture()) {
1081 int binding = shaderPipeline->bindingForTexture("qt_depthTexture", int(QSSGRhiSamplerBindingHints::DepthTexture));
1082 if (binding >= 0) {
1083 // nearest min/mag, no mipmap
1086 bindings.addTexture(binding, QRhiShaderResourceBinding::FragmentStage, shaderPipeline->depthTexture(), sampler);
1087 } // else ignore, not an error
1088 }
1089
1090 // SSAO texture
1091 if (shaderPipeline->ssaoTexture()) {
1092 int binding = shaderPipeline->bindingForTexture("qt_aoTexture", int(QSSGRhiSamplerBindingHints::AoTexture));
1093 if (binding >= 0) {
1094 // linear min/mag, no mipmap
1097 bindings.addTexture(binding, QRhiShaderResourceBinding::FragmentStage, shaderPipeline->ssaoTexture(), sampler);
1098 } // else ignore, not an error
1099 }
1100}
1101
1103 const QSSGLayerRenderData &inData,
1104 QSSGPassKey passKey,
1105 const QSSGLayerGlobalRenderProperties &globalRenderProperties,
1106 QSSGShadowMapEntry *pEntry,
1108 const QVector2D *depthAdjust,
1109 const QVector<QSSGRenderableObjectHandle> &sortedOpaqueObjects,
1110 QSSGRenderCamera &inCamera,
1111 bool orthographic,
1112 int cubeFace)
1113{
1114 QSSGShaderFeatures featureSet;
1115 if (orthographic)
1117 else
1119
1120 for (const auto &handle : sortedOpaqueObjects) {
1121 QSSGRenderableObject *theObject = handle.obj;
1122 QSSG_ASSERT(theObject->renderableFlags.castsShadows(), continue);
1123
1124 QSSGShaderFeatures objectFeatureSet = featureSet;
1125 const bool isOpaqueDepthPrePass = theObject->depthWriteMode == QSSGDepthDrawMode::OpaquePrePass;
1126 if (isOpaqueDepthPrePass)
1128
1129 QSSGRhiDrawCallData *dcd = nullptr;
1130 QMatrix4x4 modelViewProjection;
1131 QSSGSubsetRenderable &renderable(static_cast<QSSGSubsetRenderable &>(*theObject));
1132 const auto &renderer(renderable.renderer);
1133 if (theObject->type == QSSGRenderableObject::Type::DefaultMaterialMeshSubset || theObject->type == QSSGRenderableObject::Type::CustomMaterialMeshSubset) {
1134 const bool hasSkinning = renderer->defaultMaterialShaderKeyProperties().m_boneCount.getValue(renderable.shaderDescription) > 0;
1135 modelViewProjection = hasSkinning ? pEntry->m_lightVP
1136 : pEntry->m_lightVP * renderable.globalTransform;
1137 dcd = &rhiCtx->drawCallData({ passKey, &renderable.modelContext.model,
1138 pEntry, cubeFace + int(renderable.subset.offset << 3), QSSGRhiDrawCallDataKey::Shadow });
1139 }
1140
1142 QSSGRhiShaderPipelinePtr shaderPipeline;
1143 QSSGSubsetRenderable &subsetRenderable(static_cast<QSSGSubsetRenderable &>(*theObject));
1144 if (theObject->type == QSSGSubsetRenderable::Type::DefaultMaterialMeshSubset) {
1146 const bool blendParticles = subsetRenderable.renderer->defaultMaterialShaderKeyProperties().m_blendParticles.getValue(subsetRenderable.shaderDescription);
1147
1148 shaderPipeline = shadersForDefaultMaterial(ps, subsetRenderable, objectFeatureSet);
1149 if (!shaderPipeline)
1150 continue;
1151 shaderPipeline->ensureCombinedMainLightsUniformBuffer(&dcd->ubuf);
1152 char *ubufData = dcd->ubuf->beginFullDynamicBufferUpdateForCurrentFrame();
1153 updateUniformsForDefaultMaterial(*shaderPipeline, rhiCtx, inData, ubufData, ps, subsetRenderable, inCamera, depthAdjust, &modelViewProjection);
1154 if (blendParticles)
1155 QSSGParticleRenderer::updateUniformsForParticleModel(*shaderPipeline, ubufData, &subsetRenderable.modelContext.model, subsetRenderable.subset.offset);
1157 if (blendParticles)
1158 QSSGParticleRenderer::prepareParticlesForModel(*shaderPipeline, rhiCtx, bindings, &subsetRenderable.modelContext.model);
1159 } else if (theObject->type == QSSGSubsetRenderable::Type::CustomMaterialMeshSubset) {
1161
1162 QSSGCustomMaterialSystem &customMaterialSystem(*subsetRenderable.renderer->contextInterface()->customMaterialSystem().get());
1163 shaderPipeline = customMaterialSystem.shadersForCustomMaterial(ps, subsetRenderable.customMaterial(), subsetRenderable, objectFeatureSet);
1164 if (!shaderPipeline)
1165 continue;
1166 shaderPipeline->ensureCombinedMainLightsUniformBuffer(&dcd->ubuf);
1167 char *ubufData = dcd->ubuf->beginFullDynamicBufferUpdateForCurrentFrame();
1168 // inCamera is the shadow camera, not the same as inData.camera
1169 customMaterialSystem.updateUniformsForCustomMaterial(*shaderPipeline, rhiCtx, inData, ubufData, ps, subsetRenderable.customMaterial(), subsetRenderable,
1170 inCamera, depthAdjust, &modelViewProjection);
1172 }
1173
1174 if (theObject->type == QSSGRenderableObject::Type::DefaultMaterialMeshSubset || theObject->type == QSSGRenderableObject::Type::CustomMaterialMeshSubset) {
1175
1176 ps->shaderPipeline = shaderPipeline.get();
1177 ps->ia = subsetRenderable.subset.rhi.ia;
1178 int instanceBufferBinding = setupInstancing(&subsetRenderable, ps, rhiCtx, globalRenderProperties.cameraData.direction, globalRenderProperties.cameraData.position);
1179 ps->ia.bakeVertexInputLocations(*shaderPipeline, instanceBufferBinding);
1180
1181
1183
1184 // Depth and SSAO textures, in case a custom material's shader code does something with them.
1185 addDepthTextureBindings(rhiCtx, shaderPipeline.get(), bindings);
1186
1187 if (isOpaqueDepthPrePass) {
1189 shaderPipeline.get(),
1190 subsetRenderable.firstImage,
1191 bindings,
1192 (theObject->type == QSSGRenderableObject::Type::CustomMaterialMeshSubset));
1193 }
1194
1195 // There is no screen texture at this stage. But the shader from a
1196 // custom material may rely on it, and an object with that material
1197 // can end up in the shadow map's object list. So bind a dummy
1198 // texture then due to the lack of other options.
1199 int binding = shaderPipeline->bindingForTexture("qt_screenTexture", int(QSSGRhiSamplerBindingHints::ScreenTexture));
1200 if (binding >= 0) {
1203 QRhiResourceUpdateBatch *resourceUpdates = rhiCtx->rhi()->nextResourceUpdateBatch();
1204 QRhiTexture *dummyTexture = rhiCtx->dummyTexture({}, resourceUpdates);
1205 rhiCtx->commandBuffer()->resourceUpdate(resourceUpdates);
1206 bindings.addTexture(binding,
1208 dummyTexture, sampler);
1209 }
1210
1211 QRhiShaderResourceBindings *srb = rhiCtx->srb(bindings);
1213 pEntry->m_rhiRenderPassDesc,
1214 srb);
1215 subsetRenderable.rhiRenderData.shadowPass.srb[cubeFace] = srb;
1216 }
1217 }
1218}
1219
1221{
1222 // Assuming default values in the other TargetBlend fields
1223 switch (materialBlend) {
1226 targetBlend->dstColor = QRhiGraphicsPipeline::One;
1227 targetBlend->srcAlpha = QRhiGraphicsPipeline::One;
1228 targetBlend->dstAlpha = QRhiGraphicsPipeline::One;
1229 break;
1232 targetBlend->dstColor = QRhiGraphicsPipeline::Zero;
1233 targetBlend->srcAlpha = QRhiGraphicsPipeline::One;
1234 targetBlend->dstAlpha = QRhiGraphicsPipeline::One;
1235 break;
1236 default:
1237 // Use SourceOver for everything else
1240 targetBlend->srcAlpha = QRhiGraphicsPipeline::One;
1242 break;
1243 }
1244}
1245
1247 QSSGPassKey passKey,
1248 const QSSGLayerRenderData &inData,
1249 QSSGRenderableObject &inObject,
1250 QRhiRenderPassDescriptor *renderPassDescriptor,
1252 QSSGShaderFeatures featureSet,
1253 int samples,
1254 QSSGRenderCamera *inCamera,
1255 QMatrix4x4 *alteredModelViewProjection,
1256 int cubeFace,
1258{
1259 QSSGRenderCamera *camera = inData.camera;
1260 if (inCamera)
1261 camera = inCamera;
1262
1263 switch (inObject.type) {
1264 case QSSGRenderableObject::Type::DefaultMaterialMeshSubset:
1265 {
1266 QSSGSubsetRenderable &subsetRenderable(static_cast<QSSGSubsetRenderable &>(inObject));
1267
1268 if (cubeFace < 0 && subsetRenderable.reflectionProbeIndex >= 0 && subsetRenderable.renderableFlags.testFlag(QSSGRenderableObjectFlag::ReceivesReflections))
1270
1271 if (cubeFace >= 0) {
1272 // Disable tonemapping for the reflection pass
1273 featureSet.disableTonemapping();
1274 }
1275
1276 if (subsetRenderable.renderableFlags.rendersWithLightmap())
1278
1279 const auto &shaderPipeline = shadersForDefaultMaterial(ps, subsetRenderable, featureSet);
1280 if (shaderPipeline) {
1281 // Unlike the subsetRenderable (which is allocated per frame so is
1282 // not persistent in any way), the model reference is persistent in
1283 // the sense that it references the model node in the scene graph.
1284 // Combined with the layer node (multiple View3Ds may share the
1285 // same scene!), this is suitable as a key to get the uniform
1286 // buffers that were used with the rendering of the same model in
1287 // the previous frame.
1289 const auto &modelNode = subsetRenderable.modelContext.model;
1290 const bool blendParticles = subsetRenderable.renderer->defaultMaterialShaderKeyProperties().m_blendParticles.getValue(subsetRenderable.shaderDescription);
1291
1292 QSSGRhiDrawCallData &dcd(cubeFace >= 0 ? rhiCtx->drawCallData({ passKey, &modelNode,
1293 entry, cubeFace + int(subsetRenderable.subset.offset << 3),
1294 QSSGRhiDrawCallDataKey::Reflection })
1295 : rhiCtx->drawCallData({ passKey, &modelNode,
1296 &subsetRenderable.material, 0, QSSGRhiDrawCallDataKey::Main }));
1297
1298 shaderPipeline->ensureCombinedMainLightsUniformBuffer(&dcd.ubuf);
1299 char *ubufData = dcd.ubuf->beginFullDynamicBufferUpdateForCurrentFrame();
1300 updateUniformsForDefaultMaterial(*shaderPipeline, rhiCtx, inData, ubufData, ps, subsetRenderable, *camera, nullptr, alteredModelViewProjection);
1301 if (blendParticles)
1302 QSSGParticleRenderer::updateUniformsForParticleModel(*shaderPipeline, ubufData, &subsetRenderable.modelContext.model, subsetRenderable.subset.offset);
1304
1305 if (blendParticles)
1306 QSSGParticleRenderer::prepareParticlesForModel(*shaderPipeline, rhiCtx, bindings, &subsetRenderable.modelContext.model);
1307
1308 // Skinning
1309 if (QRhiTexture *boneTexture = inData.getBonemapTexture(subsetRenderable.modelContext)) {
1310 int binding = shaderPipeline->bindingForTexture("qt_boneTexture");
1311 if (binding >= 0) {
1312 QRhiSampler *boneSampler = rhiCtx->sampler({ QRhiSampler::Nearest,
1318 });
1319 bindings.addTexture(binding,
1321 boneTexture,
1322 boneSampler);
1323 }
1324 }
1325 // Morphing
1326 auto *targetsTexture = subsetRenderable.subset.rhi.targetsTexture;
1327 if (targetsTexture) {
1328 int binding = shaderPipeline->bindingForTexture("qt_morphTargetTexture");
1329 if (binding >= 0) {
1330 QRhiSampler *targetsSampler = rhiCtx->sampler({ QRhiSampler::Nearest,
1336 });
1337 bindings.addTexture(binding, QRhiShaderResourceBinding::VertexStage, subsetRenderable.subset.rhi.targetsTexture, targetsSampler);
1338 }
1339 }
1340
1341 ps->samples = samples;
1342
1344 fillTargetBlend(&ps->targetBlend, subsetRenderable.defaultMaterial().blendMode);
1345
1346 ps->ia = subsetRenderable.subset.rhi.ia;
1347 QVector3D cameraDirection = inData.cameraData->direction;
1348 if (inCamera)
1349 cameraDirection = inCamera->getScalingCorrectDirection();
1350 QVector3D cameraPosition = inData.cameraData->position;
1351 if (inCamera)
1352 cameraPosition = inCamera->getGlobalPos();
1353 int instanceBufferBinding = setupInstancing(&subsetRenderable, ps, rhiCtx, cameraDirection, cameraPosition);
1354 ps->ia.bakeVertexInputLocations(*shaderPipeline, instanceBufferBinding);
1355
1356 bindings.addUniformBuffer(0, RENDERER_VISIBILITY_ALL, dcd.ubuf, 0, shaderPipeline->ub0Size());
1357
1358 if (shaderPipeline->isLightingEnabled()) {
1360 shaderPipeline->ub0LightDataOffset(),
1361 shaderPipeline->ub0LightDataSize());
1362 }
1363
1364 // Texture maps
1365 QSSGRenderableImage *renderableImage = subsetRenderable.firstImage;
1366 while (renderableImage) {
1367 const char *samplerName = QSSGMaterialShaderGenerator::getSamplerName(renderableImage->m_mapType);
1368 const int samplerHint = int(renderableImage->m_mapType);
1369 int samplerBinding = shaderPipeline->bindingForTexture(samplerName, samplerHint);
1370 if (samplerBinding >= 0) {
1371 QRhiTexture *texture = renderableImage->m_texture.m_texture;
1372 if (samplerBinding >= 0 && texture) {
1373 const bool mipmapped = texture->flags().testFlag(QRhiTexture::MipMapped);
1374 QSSGRhiSamplerDescription samplerDesc = {
1375 toRhi(renderableImage->m_imageNode.m_minFilterType),
1376 toRhi(renderableImage->m_imageNode.m_magFilterType),
1377 mipmapped ? toRhi(renderableImage->m_imageNode.m_mipFilterType) : QRhiSampler::None,
1378 toRhi(renderableImage->m_imageNode.m_horizontalTilingMode),
1379 toRhi(renderableImage->m_imageNode.m_verticalTilingMode),
1381 };
1382 rhiCtx->checkAndAdjustForNPoT(texture, &samplerDesc);
1383 QRhiSampler *sampler = rhiCtx->sampler(samplerDesc);
1384 bindings.addTexture(samplerBinding, RENDERER_VISIBILITY_ALL, texture, sampler);
1385 }
1386 } // else this is not necessarily an error, e.g. having metalness/roughness maps with metalness disabled
1387 renderableImage = renderableImage->m_nextImage;
1388 }
1389
1390 if (shaderPipeline->isLightingEnabled()) {
1391 // Shadow map textures
1392 const int shadowMapCount = shaderPipeline->shadowMapCount();
1393 for (int i = 0; i < shadowMapCount; ++i) {
1394 QSSGRhiShadowMapProperties &shadowMapProperties(shaderPipeline->shadowMapAt(i));
1395 QRhiTexture *texture = shadowMapProperties.shadowMapTexture;
1399 const QByteArray &name(shadowMapProperties.shadowMapTextureUniformName);
1400 if (shadowMapProperties.cachedBinding < 0)
1401 shadowMapProperties.cachedBinding = shaderPipeline->bindingForTexture(name);
1402 if (shadowMapProperties.cachedBinding < 0) {
1403 qWarning("No combined image sampler for shadow map texture '%s'", name.data());
1404 continue;
1405 }
1407 texture, sampler);
1408 }
1409
1410 // Prioritize reflection texture over Light Probe texture because
1411 // reflection texture also contains the irradiance and pre filtered
1412 // values for the light probe.
1414 int reflectionSampler = shaderPipeline->bindingForTexture("qt_reflectionMap");
1417 QRhiTexture* reflectionTexture = inData.getReflectionMapManager()->reflectionMapEntry(subsetRenderable.reflectionProbeIndex)->m_rhiPrefilteredCube;
1418 if (reflectionSampler >= 0 && reflectionTexture)
1419 bindings.addTexture(reflectionSampler, QRhiShaderResourceBinding::FragmentStage, reflectionTexture, sampler);
1420 } else if (shaderPipeline->lightProbeTexture()) {
1421 int binding = shaderPipeline->bindingForTexture("qt_lightProbe", int(QSSGRhiSamplerBindingHints::LightProbe));
1422 if (binding >= 0) {
1423 auto tiling = shaderPipeline->lightProbeTiling();
1425 toRhi(tiling.first), toRhi(tiling.second), QRhiSampler::Repeat });
1427 shaderPipeline->lightProbeTexture(), sampler);
1428 } else {
1429 qWarning("Could not find sampler for lightprobe");
1430 }
1431 }
1432
1433 // Screen Texture
1434 if (shaderPipeline->screenTexture()) {
1435 int binding = shaderPipeline->bindingForTexture("qt_screenTexture", int(QSSGRhiSamplerBindingHints::ScreenTexture));
1436 if (binding >= 0) {
1437 // linear min/mag, mipmap filtering depends on the
1438 // texture, with SCREEN_TEXTURE there are no mipmaps, but
1439 // once SCREEN_MIP_TEXTURE is seen the texture (the same
1440 // one) has mipmaps generated.
1441 QRhiSampler::Filter mipFilter = shaderPipeline->screenTexture()->flags().testFlag(QRhiTexture::MipMapped)
1445 bindings.addTexture(binding,
1447 shaderPipeline->screenTexture(), sampler);
1448 } // else ignore, not an error
1449 }
1450
1451 if (shaderPipeline->lightmapTexture()) {
1452 int binding = shaderPipeline->bindingForTexture("qt_lightmap", int(QSSGRhiSamplerBindingHints::LightmapTexture));
1453 if (binding >= 0) {
1456 bindings.addTexture(binding,
1458 shaderPipeline->lightmapTexture(), sampler);
1459 } // else ignore, not an error
1460 }
1461 }
1462
1463 // Depth and SSAO textures
1464 addDepthTextureBindings(rhiCtx, shaderPipeline.get(), bindings);
1465
1466 // Instead of always doing a QHash find in srb(), store the binding
1467 // list and the srb object in the per-model+material
1468 // QSSGRhiUniformBufferSet. While this still needs comparing the
1469 // binding list, to see if something has changed, it results in
1470 // significant gains with lots of models in the scene (because the
1471 // srb hash table becomes large then, so avoiding the lookup as
1472 // much as possible is helpful)
1474 bool srbChanged = false;
1475 if (!srb || bindings != dcd.bindings) {
1476 srb = rhiCtx->srb(bindings);
1477 dcd.bindings = bindings;
1478 srbChanged = true;
1479 }
1480
1481 if (cubeFace >= 0)
1482 subsetRenderable.rhiRenderData.reflectionPass.srb[cubeFace] = srb;
1483 else
1484 subsetRenderable.rhiRenderData.mainPass.srb = srb;
1485
1486 const QSSGGraphicsPipelineStateKey pipelineKey = QSSGGraphicsPipelineStateKey::create(*ps, renderPassDescriptor, srb);
1487 if (dcd.pipeline
1488 && !srbChanged
1489 && dcd.renderTargetDescriptionHash == pipelineKey.extra.renderTargetDescriptionHash // we have the hash code anyway, use it to early out upon mismatch
1491 && dcd.ps == *ps)
1492 {
1493 if (cubeFace >= 0)
1494 subsetRenderable.rhiRenderData.reflectionPass.pipeline = dcd.pipeline;
1495 else
1496 subsetRenderable.rhiRenderData.mainPass.pipeline = dcd.pipeline;
1497 } else {
1498 if (cubeFace >= 0) {
1499 subsetRenderable.rhiRenderData.reflectionPass.pipeline = rhiCtx->pipeline(pipelineKey,
1500 renderPassDescriptor,
1501 srb);
1502 dcd.pipeline = subsetRenderable.rhiRenderData.reflectionPass.pipeline;
1503 } else {
1504 subsetRenderable.rhiRenderData.mainPass.pipeline = rhiCtx->pipeline(pipelineKey,
1505 renderPassDescriptor,
1506 srb);
1507 dcd.pipeline = subsetRenderable.rhiRenderData.mainPass.pipeline;
1508 }
1511 dcd.ps = *ps;
1512 }
1513 }
1514 break;
1515 }
1516 case QSSGRenderableObject::Type::CustomMaterialMeshSubset:
1517 {
1518 QSSGSubsetRenderable &subsetRenderable(static_cast<QSSGSubsetRenderable &>(inObject));
1519 const QSSGRenderCustomMaterial &material(subsetRenderable.customMaterial());
1520 QSSGCustomMaterialSystem &customMaterialSystem(*subsetRenderable.renderer->contextInterface()->customMaterialSystem().get());
1521
1522 featureSet.set(QSSGShaderFeatures::Feature::LightProbe, inData.layer.lightProbe || material.m_iblProbe);
1523
1524 if (cubeFace < 0 && subsetRenderable.reflectionProbeIndex >= 0 && subsetRenderable.renderableFlags.testFlag(QSSGRenderableObjectFlag::ReceivesReflections))
1526
1527 if (cubeFace >= 0) {
1528 // Disable tonemapping for the reflection pass
1529 featureSet.disableTonemapping();
1530 }
1531
1532 if (subsetRenderable.renderableFlags.rendersWithLightmap())
1534
1535 customMaterialSystem.rhiPrepareRenderable(ps, passKey, subsetRenderable, featureSet,
1536 material, inData, renderPassDescriptor, samples,
1537 inCamera, cubeFace, alteredModelViewProjection, entry);
1538 break;
1539 }
1540 case QSSGRenderableObject::Type::Particles:
1541 {
1542 QSSGParticlesRenderable &particleRenderable(static_cast<QSSGParticlesRenderable &>(inObject));
1543 const auto &shaderPipeline = shadersForParticleMaterial(ps, particleRenderable);
1544 if (shaderPipeline) {
1545 QSSGParticleRenderer::rhiPrepareRenderable(*shaderPipeline, passKey, rhiCtx, ps, particleRenderable, inData, renderPassDescriptor, samples,
1546 inCamera, cubeFace, entry);
1547 }
1548 break;
1549 }
1550 }
1551}
1552
1555 QSSGRenderableObject &object,
1556 bool *needsSetViewport,
1557 int cubeFace)
1558{
1559 switch (object.type) {
1560 case QSSGRenderableObject::Type::DefaultMaterialMeshSubset:
1561 {
1562 QSSGSubsetRenderable &subsetRenderable(static_cast<QSSGSubsetRenderable &>(object));
1563
1564 QRhiGraphicsPipeline *ps = subsetRenderable.rhiRenderData.mainPass.pipeline;
1566
1567 if (cubeFace >= 0) {
1568 ps = subsetRenderable.rhiRenderData.reflectionPass.pipeline;
1569 srb = subsetRenderable.rhiRenderData.reflectionPass.srb[cubeFace];
1570 }
1571
1572 if (!ps || !srb)
1573 return;
1574
1575 QRhiBuffer *vertexBuffer = subsetRenderable.subset.rhi.vertexBuffer->buffer();
1576 QRhiBuffer *indexBuffer = subsetRenderable.subset.rhi.indexBuffer ? subsetRenderable.subset.rhi.indexBuffer->buffer() : nullptr;
1577
1578 QRhiCommandBuffer *cb = rhiCtx->commandBuffer();
1579 // QRhi optimizes out unnecessary binding of the same pipline
1580 cb->setGraphicsPipeline(ps);
1581 cb->setShaderResources(srb);
1582
1583 if (*needsSetViewport) {
1584 cb->setViewport(state.viewport);
1585 if (state.scissorEnable)
1586 cb->setScissor(state.scissor);
1587 *needsSetViewport = false;
1588 }
1589
1590 QRhiCommandBuffer::VertexInput vertexBuffers[2];
1591 int vertexBufferCount = 1;
1592 vertexBuffers[0] = QRhiCommandBuffer::VertexInput(vertexBuffer, 0);
1593 quint32 instances = 1;
1594 if ( subsetRenderable.modelContext.model.instancing()) {
1595 instances = subsetRenderable.modelContext.model.instanceCount();
1596 // If the instance count is 0, the bail out before trying to do any
1597 // draw calls. Making an instanced draw call with a count of 0 is invalid
1598 // for Metal and likely other API's as well.
1599 // It is possible that the particale system may produce 0 instances here
1600 if (instances == 0)
1601 return;
1602 vertexBuffers[1] = QRhiCommandBuffer::VertexInput(subsetRenderable.instanceBuffer, 0);
1604 }
1605 Q_QUICK3D_PROFILE_START(QQuick3DProfiler::Quick3DRenderCall);
1606 if (state.usesStencilRef)
1607 cb->setStencilRef(state.stencilRef);
1608 if (indexBuffer) {
1609 cb->setVertexInput(0, vertexBufferCount, vertexBuffers, indexBuffer, 0, subsetRenderable.subset.rhi.indexBuffer->indexFormat());
1610 cb->drawIndexed(subsetRenderable.subset.lodCount(subsetRenderable.subsetLevelOfDetail), instances, subsetRenderable.subset.lodOffset(subsetRenderable.subsetLevelOfDetail));
1611 QSSGRHICTX_STAT(rhiCtx, drawIndexed(subsetRenderable.subset.lodCount(subsetRenderable.subsetLevelOfDetail), instances));
1612 } else {
1613 cb->setVertexInput(0, vertexBufferCount, vertexBuffers);
1614 cb->draw(subsetRenderable.subset.count, instances, subsetRenderable.subset.offset);
1615 QSSGRHICTX_STAT(rhiCtx, draw(subsetRenderable.subset.count, instances));
1616 }
1617 Q_QUICK3D_PROFILE_END_WITH_IDS(QQuick3DProfiler::Quick3DRenderCall, (subsetRenderable.subset.count | quint64(instances) << 32),
1618 QVector<int>({subsetRenderable.modelContext.model.profilingId,
1619 subsetRenderable.material.profilingId}));
1620 break;
1621 }
1622 case QSSGRenderableObject::Type::CustomMaterialMeshSubset:
1623 {
1624 QSSGSubsetRenderable &subsetRenderable(static_cast<QSSGSubsetRenderable &>(object));
1625 QSSGCustomMaterialSystem &customMaterialSystem(*subsetRenderable.renderer->contextInterface()->customMaterialSystem().get());
1626 customMaterialSystem.rhiRenderRenderable(rhiCtx, subsetRenderable, needsSetViewport, cubeFace, state);
1627 break;
1628 }
1629 case QSSGRenderableObject::Type::Particles:
1630 {
1631 QSSGParticlesRenderable &renderable(static_cast<QSSGParticlesRenderable &>(object));
1632 QSSGParticleRenderer::rhiRenderRenderable(rhiCtx, renderable, needsSetViewport, cubeFace, state);
1633 break;
1634 }
1635 }
1636}
1637
1639 QSSGPassKey passKey,
1641 QSSGRenderShadowMap &shadowMapManager,
1642 const QSSGRenderCamera &camera,
1643 const QSSGShaderLightList &globalLights,
1644 const QVector<QSSGRenderableObjectHandle> &sortedOpaqueObjects,
1646 const QSSGBoxPoints &castingObjectsBox,
1647 const QSSGBoxPoints &receivingObjectsBox)
1648{
1649 const QSSGLayerGlobalRenderProperties &globalRenderProperties = renderer.getLayerGlobalRenderProperties();
1650 QSSG_ASSERT(globalRenderProperties.layer.renderData, return);
1651 const QSSGLayerRenderData &layerData = *globalRenderProperties.layer.renderData;
1652
1653 static const auto rhiRenderOneShadowMap = [](QSSGRhiContext *rhiCtx,
1655 const QVector<QSSGRenderableObjectHandle> &sortedOpaqueObjects,
1656 int cubeFace) {
1657 QRhiCommandBuffer *cb = rhiCtx->commandBuffer();
1658 bool needsSetViewport = true;
1659
1660 for (const auto &handle : sortedOpaqueObjects) {
1661 QSSGRenderableObject *theObject = handle.obj;
1662 QSSG_ASSERT(theObject->renderableFlags.castsShadows(), continue);
1663 if (theObject->type == QSSGRenderableObject::Type::DefaultMaterialMeshSubset || theObject->type == QSSGRenderableObject::Type::CustomMaterialMeshSubset) {
1664 QSSGSubsetRenderable *renderable(static_cast<QSSGSubsetRenderable *>(theObject));
1665
1666 QRhiBuffer *vertexBuffer = renderable->subset.rhi.vertexBuffer->buffer();
1667 QRhiBuffer *indexBuffer = renderable->subset.rhi.indexBuffer
1668 ? renderable->subset.rhi.indexBuffer->buffer()
1669 : nullptr;
1670
1671 // Ideally we shouldn't need to deal with this, as only "valid" objects should be processed at this point.
1672 if (!renderable->rhiRenderData.shadowPass.pipeline)
1673 continue;
1674
1675 Q_QUICK3D_PROFILE_START(QQuick3DProfiler::Quick3DRenderCall);
1676
1677 cb->setGraphicsPipeline(renderable->rhiRenderData.shadowPass.pipeline);
1678
1680 cb->setShaderResources(srb);
1681
1682 if (needsSetViewport) {
1683 cb->setViewport(ps->viewport);
1684 needsSetViewport = false;
1685 }
1686
1687 QRhiCommandBuffer::VertexInput vertexBuffers[2];
1688 int vertexBufferCount = 1;
1689 vertexBuffers[0] = QRhiCommandBuffer::VertexInput(vertexBuffer, 0);
1690 quint32 instances = 1;
1691 if (renderable->modelContext.model.instancing()) {
1692 instances = renderable->modelContext.model.instanceCount();
1693 vertexBuffers[1] = QRhiCommandBuffer::VertexInput(renderable->instanceBuffer, 0);
1695 }
1696 if (indexBuffer) {
1697 cb->setVertexInput(0, vertexBufferCount, vertexBuffers, indexBuffer, 0, renderable->subset.rhi.indexBuffer->indexFormat());
1698 cb->drawIndexed(renderable->subset.count, instances, renderable->subset.offset);
1699 QSSGRHICTX_STAT(rhiCtx, drawIndexed(renderable->subset.count, instances));
1700 } else {
1701 cb->setVertexInput(0, vertexBufferCount, vertexBuffers);
1702 cb->draw(renderable->subset.count, instances, renderable->subset.offset);
1703 QSSGRHICTX_STAT(rhiCtx, draw(renderable->subset.count, instances));
1704 }
1705 Q_QUICK3D_PROFILE_END_WITH_IDS(QQuick3DProfiler::Quick3DRenderCall, (renderable->subset.count | quint64(instances) << 32),
1706 QVector<int>({renderable->modelContext.model.profilingId,
1707 renderable->material.profilingId}));
1708 }
1709 }
1710 };
1711
1712 static const auto rhiBlurShadowMap = [](QSSGRhiContext *rhiCtx,
1713 QSSGShadowMapEntry *pEntry,
1715 float shadowFilter,
1716 float shadowMapFar,
1717 bool orthographic) {
1718 // may not be able to do the blur pass if the number of max color
1719 // attachments is the gl/vk spec mandated minimum of 4, and we need 6.
1720 // (applicable only to !orthographic, whereas orthographic always works)
1721 if (!pEntry->m_rhiBlurRenderTarget0 || !pEntry->m_rhiBlurRenderTarget1)
1722 return;
1723
1724 QRhi *rhi = rhiCtx->rhi();
1726 QRhiTexture *map = orthographic ? pEntry->m_rhiDepthMap : pEntry->m_rhiDepthCube;
1727 QRhiTexture *workMap = orthographic ? pEntry->m_rhiDepthCopy : pEntry->m_rhiCubeCopy;
1728 const QSize size = map->pixelSize();
1729 ps.viewport = QRhiViewport(0, 0, float(size.width()), float(size.height()));
1730
1731 const auto &blurXPipeline = orthographic ? renderer.getRhiOrthographicShadowBlurXShader()
1732 : renderer.getRhiCubemapShadowBlurXShader();
1733 if (!blurXPipeline)
1734 return;
1735 ps.shaderPipeline = blurXPipeline.get();
1736
1737 ps.colorAttachmentCount = orthographic ? 1 : 6;
1738
1739 // construct a key that is unique for this frame (we use a dynamic buffer
1740 // so even if the same key gets used in the next frame, just updating the
1741 // contents on the same QRhiBuffer is ok due to QRhi's internal double buffering)
1742 QSSGRhiDrawCallData &dcd = rhiCtx->drawCallData({ map, nullptr, nullptr, 0, QSSGRhiDrawCallDataKey::ShadowBlur });
1743 if (!dcd.ubuf) {
1745 dcd.ubuf->create();
1746 }
1747
1748 // the blur also needs Y reversed in order to get correct results (while
1749 // the second blur step would end up with the correct orientation without
1750 // this too, but we need to blur the correct fragments in the second step
1751 // hence the flip is important)
1752 QMatrix4x4 flipY;
1753 // correct for D3D and Metal but not for Vulkan because there the Y is down
1754 // in NDC so that kind of self-corrects...
1755 if (rhi->isYUpInFramebuffer() != rhi->isYUpInNDC())
1756 flipY.data()[5] = -1.0f;
1757 float cameraProperties[2] = { shadowFilter, shadowMapFar };
1758 char *ubufData = dcd.ubuf->beginFullDynamicBufferUpdateForCurrentFrame();
1759 memcpy(ubufData, flipY.constData(), 64);
1760 memcpy(ubufData + 64, cameraProperties, 8);
1762
1766
1770 QRhiShaderResourceBindings *srb = rhiCtx->srb(bindings);
1771
1772 QSSGRhiQuadRenderer::Flags quadFlags;
1773 if (orthographic) // orthoshadowshadowblurx and y have attr_uv as well
1774 quadFlags |= QSSGRhiQuadRenderer::UvCoords;
1775 renderer.rhiQuadRenderer()->prepareQuad(rhiCtx, nullptr);
1776 renderer.rhiQuadRenderer()->recordRenderQuadPass(rhiCtx, &ps, srb, pEntry->m_rhiBlurRenderTarget0, quadFlags);
1777
1778 // repeat for blur Y, now depthCopy -> depthMap or cubeCopy -> depthCube
1779
1780 const auto &blurYPipeline = orthographic ? renderer.getRhiOrthographicShadowBlurYShader()
1781 : renderer.getRhiCubemapShadowBlurYShader();
1782 if (!blurYPipeline)
1783 return;
1784 ps.shaderPipeline = blurYPipeline.get();
1785
1786 bindings.clear();
1789 srb = rhiCtx->srb(bindings);
1790
1791 renderer.rhiQuadRenderer()->prepareQuad(rhiCtx, nullptr);
1792 renderer.rhiQuadRenderer()->recordRenderQuadPass(rhiCtx, &ps, srb, pEntry->m_rhiBlurRenderTarget1, quadFlags);
1793 };
1794
1795 QRhi *rhi = rhiCtx->rhi();
1796 QRhiCommandBuffer *cb = rhiCtx->commandBuffer();
1797
1798 // We need to deal with a clip depth range of [0, 1] or
1799 // [-1, 1], depending on the graphics API underneath.
1800 QVector2D depthAdjust; // (d + depthAdjust[0]) * depthAdjust[1] = d mapped to [0, 1]
1801 if (rhi->isClipDepthZeroToOne()) {
1802 // d is [0, 1] so no need for any mapping
1803 depthAdjust[0] = 0.0f;
1804 depthAdjust[1] = 1.0f;
1805 } else {
1806 // d is [-1, 1]
1807 depthAdjust[0] = 1.0f;
1808 depthAdjust[1] = 0.5f;
1809 }
1810
1811 // Create shadow map for each light in the scene
1812 for (int i = 0, ie = globalLights.size(); i != ie; ++i) {
1813 if (!globalLights[i].shadows || globalLights[i].light->m_fullyBaked)
1814 continue;
1815
1816 QSSGShadowMapEntry *pEntry = shadowMapManager.shadowMapEntry(i);
1817 if (!pEntry)
1818 continue;
1819
1820 Q_ASSERT(pEntry->m_rhiDepthStencil);
1821 const bool orthographic = pEntry->m_rhiDepthMap && pEntry->m_rhiDepthCopy;
1822 if (orthographic) {
1823 const QSize size = pEntry->m_rhiDepthMap->pixelSize();
1824 ps.viewport = QRhiViewport(0, 0, float(size.width()), float(size.height()));
1825
1826 const auto &light = globalLights[i].light;
1827 const auto cameraType = (light->type == QSSGRenderLight::Type::DirectionalLight) ? QSSGRenderCamera::Type::OrthographicCamera : QSSGRenderCamera::Type::CustomCamera;
1828 QSSGRenderCamera theCamera(cameraType);
1829 setupCameraForShadowMap(camera, light, theCamera, castingObjectsBox, receivingObjectsBox);
1830 theCamera.calculateViewProjectionMatrix(pEntry->m_lightVP);
1831 pEntry->m_lightView = theCamera.globalTransform.inverted(); // pre-calculate this for the material
1832
1833 rhiPrepareResourcesForShadowMap(rhiCtx, layerData, passKey, globalRenderProperties, pEntry, &ps, &depthAdjust,
1834 sortedOpaqueObjects, theCamera, true, 0);
1835
1836 // Render into the 2D texture pEntry->m_rhiDepthMap, using
1837 // pEntry->m_rhiDepthStencil as the (throwaway) depth/stencil buffer.
1839 cb->beginPass(rt, Qt::white, { 1.0f, 0 }, nullptr, QSSGRhiContext::commonPassFlags());
1840 Q_QUICK3D_PROFILE_START(QQuick3DProfiler::Quick3DRenderPass);
1841 QSSGRHICTX_STAT(rhiCtx, beginRenderPass(rt));
1842 rhiRenderOneShadowMap(rhiCtx, &ps, sortedOpaqueObjects, 0);
1843 cb->endPass();
1844 QSSGRHICTX_STAT(rhiCtx, endRenderPass());
1845 Q_QUICK3D_PROFILE_END_WITH_STRING(QQuick3DProfiler::Quick3DRenderPass, 0, QByteArrayLiteral("shadow_map"));
1846
1847 Q_QUICK3D_PROFILE_START(QQuick3DProfiler::Quick3DRenderPass);
1848 rhiBlurShadowMap(rhiCtx, pEntry, renderer, globalLights[i].light->m_shadowFilter, globalLights[i].light->m_shadowMapFar, true);
1849 Q_QUICK3D_PROFILE_END_WITH_STRING(QQuick3DProfiler::Quick3DRenderPass, 0, QByteArrayLiteral("shadow_map_blur"));
1850 } else {
1851 Q_ASSERT(pEntry->m_rhiDepthCube && pEntry->m_rhiCubeCopy);
1852 const QSize size = pEntry->m_rhiDepthCube->pixelSize();
1853 ps.viewport = QRhiViewport(0, 0, float(size.width()), float(size.height()));
1854
1855 QSSGRenderCamera theCameras[6] { QSSGRenderCamera{QSSGRenderCamera::Type::PerspectiveCamera},
1856 QSSGRenderCamera{QSSGRenderCamera::Type::PerspectiveCamera},
1857 QSSGRenderCamera{QSSGRenderCamera::Type::PerspectiveCamera},
1858 QSSGRenderCamera{QSSGRenderCamera::Type::PerspectiveCamera},
1859 QSSGRenderCamera{QSSGRenderCamera::Type::PerspectiveCamera},
1860 QSSGRenderCamera{QSSGRenderCamera::Type::PerspectiveCamera} };
1861 setupCubeShadowCameras(globalLights[i].light, theCameras);
1862 pEntry->m_lightView = QMatrix4x4();
1863
1864 const bool swapYFaces = !rhi->isYUpInFramebuffer();
1865 for (const auto face : QSSGRenderTextureCubeFaces) {
1866 theCameras[quint8(face)].calculateViewProjectionMatrix(pEntry->m_lightVP);
1867 pEntry->m_lightCubeView[quint8(face)] = theCameras[quint8(face)].globalTransform.inverted(); // pre-calculate this for the material
1868
1869 rhiPrepareResourcesForShadowMap(rhiCtx, layerData, passKey, globalRenderProperties, pEntry, &ps, &depthAdjust,
1870 sortedOpaqueObjects, theCameras[quint8(face)], false, quint8(face));
1871 }
1872
1873 for (const auto face : QSSGRenderTextureCubeFaces) {
1874 // Render into one face of the cubemap texture pEntry->m_rhiDephCube, using
1875 // pEntry->m_rhiDepthStencil as the (throwaway) depth/stencil buffer.
1876
1878 // FACE S T GL
1879 // +x -z, -y right
1880 // -x +z, -y left
1881 // +y +x, +z top
1882 // -y +x, -z bottom
1883 // +z +x, -y front
1884 // -z -x, -y back
1885 // FACE S T D3D
1886 // +x -z, +y right
1887 // -x +z, +y left
1888 // +y +x, -z bottom
1889 // -y +x, +z top
1890 // +z +x, +y front
1891 // -z -x, +y back
1892 if (swapYFaces) {
1893 // +Y and -Y faces get swapped (D3D, Vulkan, Metal).
1894 // See shadowMapping.glsllib. This is complemented there by reversing T as well.
1895 if (outFace == QSSGRenderTextureCubeFace::PosY)
1897 else if (outFace == QSSGRenderTextureCubeFace::NegY)
1899 }
1900 QRhiTextureRenderTarget *rt = pEntry->m_rhiRenderTargets[quint8(outFace)];
1901 cb->beginPass(rt, Qt::white, { 1.0f, 0 }, nullptr, QSSGRhiContext::commonPassFlags());
1902 QSSGRHICTX_STAT(rhiCtx, beginRenderPass(rt));
1903 Q_QUICK3D_PROFILE_START(QQuick3DProfiler::Quick3DRenderPass);
1904 rhiRenderOneShadowMap(rhiCtx, &ps, sortedOpaqueObjects, quint8(face));
1905 cb->endPass();
1906 QSSGRHICTX_STAT(rhiCtx, endRenderPass());
1907 Q_QUICK3D_PROFILE_END_WITH_STRING(QQuick3DProfiler::Quick3DRenderPass, 0, QSSG_RENDERPASS_NAME("shadow_cube", 0, outFace));
1908 }
1909
1910 Q_QUICK3D_PROFILE_START(QQuick3DProfiler::Quick3DRenderPass);
1911 rhiBlurShadowMap(rhiCtx, pEntry, renderer, globalLights[i].light->m_shadowFilter, globalLights[i].light->m_shadowMapFar, false);
1912 Q_QUICK3D_PROFILE_END_WITH_STRING(QQuick3DProfiler::Quick3DRenderPass, 0, QByteArrayLiteral("shadow_cube_blur"));
1913 }
1914 }
1915}
1916
1918 QSSGPassKey passKey,
1919 const QSSGLayerRenderData &inData,
1921 QSSGRenderReflectionMap &reflectionMapManager,
1922 const QVector<QSSGRenderReflectionProbe *> &reflectionProbes,
1923 const QVector<QSSGRenderableObjectHandle> &reflectionPassObjects,
1925{
1926 QRhi *rhi = rhiCtx->rhi();
1927 QRhiCommandBuffer *cb = rhiCtx->commandBuffer();
1928
1929 const bool renderSkybox = (inData.layer.background == QSSGRenderLayer::Background::SkyBox ||
1932
1933 for (int i = 0, ie = reflectionProbes.size(); i != ie; ++i) {
1934 QSSGReflectionMapEntry *pEntry = reflectionMapManager.reflectionMapEntry(i);
1935 if (!pEntry)
1936 continue;
1937
1938 if (!pEntry->m_needsRender)
1939 continue;
1940
1941 if (reflectionProbes[i]->refreshMode == QSSGRenderReflectionProbe::ReflectionRefreshMode::FirstFrame && pEntry->m_rendered)
1942 continue;
1943
1944 if (reflectionProbes[i]->texture)
1945 continue;
1946
1947 Q_ASSERT(pEntry->m_rhiDepthStencil);
1948 Q_ASSERT(pEntry->m_rhiCube);
1949
1950 const QSize size = pEntry->m_rhiCube->pixelSize();
1951 ps->viewport = QRhiViewport(0, 0, float(size.width()), float(size.height()));
1952
1953 QSSGRenderCamera theCameras[6] { QSSGRenderCamera{QSSGRenderCamera::Type::PerspectiveCamera},
1954 QSSGRenderCamera{QSSGRenderCamera::Type::PerspectiveCamera},
1955 QSSGRenderCamera{QSSGRenderCamera::Type::PerspectiveCamera},
1956 QSSGRenderCamera{QSSGRenderCamera::Type::PerspectiveCamera},
1957 QSSGRenderCamera{QSSGRenderCamera::Type::PerspectiveCamera},
1958 QSSGRenderCamera{QSSGRenderCamera::Type::PerspectiveCamera} };
1959 setupCubeReflectionCameras(reflectionProbes[i], theCameras);
1960 const bool swapYFaces = !rhi->isYUpInFramebuffer();
1961 for (const auto face : QSSGRenderTextureCubeFaces) {
1962 theCameras[quint8(face)].calculateViewProjectionMatrix(pEntry->m_viewProjection);
1963
1964 rhiPrepareResourcesForReflectionMap(rhiCtx, passKey, inData, pEntry, ps,
1965 reflectionPassObjects, theCameras[quint8(face)], renderer, quint8(face));
1966 }
1967 QRhiRenderPassDescriptor *renderPassDesc = nullptr;
1968 for (auto face : QSSGRenderTextureCubeFaces) {
1970 face = pEntry->m_timeSliceFace;
1971
1973 // Faces are swapped similarly to shadow maps due to differences in backends
1974 // Prefilter step handles correcting orientation differences in the final render
1975 if (swapYFaces) {
1976 if (outFace == QSSGRenderTextureCubeFace::PosY)
1978 else if (outFace == QSSGRenderTextureCubeFace::NegY)
1980 }
1981 QRhiTextureRenderTarget *rt = pEntry->m_rhiRenderTargets[quint8(outFace)];
1982 cb->beginPass(rt, reflectionProbes[i]->clearColor, { 1.0f, 0 }, nullptr, QSSGRhiContext::commonPassFlags());
1983 QSSGRHICTX_STAT(rhiCtx, beginRenderPass(rt));
1984 Q_QUICK3D_PROFILE_START(QQuick3DProfiler::Quick3DRenderPass);
1985
1986 if (renderSkybox && pEntry->m_skyBoxSrbs[quint8(face)]) {
1987 const bool isSkyBox = inData.layer.background == QSSGRenderLayer::Background::SkyBox;
1988 const auto &shaderPipeline = isSkyBox ? renderer.getRhiSkyBoxShader(QSSGRenderLayer::TonemapMode::None, inData.layer.skyBoxIsRgbe8)
1989 : renderer.getRhiSkyBoxCubeShader();
1990 Q_ASSERT(shaderPipeline);
1991 ps->shaderPipeline = shaderPipeline.get();
1993 if (!renderPassDesc)
1994 renderPassDesc = rt->newCompatibleRenderPassDescriptor();
1995 rt->setRenderPassDescriptor(renderPassDesc);
1996 isSkyBox ? renderer.rhiQuadRenderer()->recordRenderQuad(rhiCtx, ps, srb, renderPassDesc, {})
1997 : renderer.rhiCubeRenderer()->recordRenderCube(rhiCtx, ps, srb, renderPassDesc, {});
1998 }
1999
2000 bool needsSetViewport = true;
2001 for (const auto &handle : reflectionPassObjects)
2002 rhiRenderRenderable(rhiCtx, *ps, *handle.obj, &needsSetViewport, quint8(face));
2003
2004 cb->endPass();
2005 QSSGRHICTX_STAT(rhiCtx, endRenderPass());
2006 Q_QUICK3D_PROFILE_END_WITH_STRING(QQuick3DProfiler::Quick3DRenderPass, 0, QSSG_RENDERPASS_NAME("reflection_cube", 0, outFace));
2007
2009 break;
2010 }
2011 if (renderPassDesc)
2012 renderPassDesc->deleteLater();
2013
2014 pEntry->renderMips(rhiCtx);
2015
2017 pEntry->m_timeSliceFace = QSSGBaseTypeHelpers::next(pEntry->m_timeSliceFace); // Wraps
2018
2019 if (reflectionProbes[i]->refreshMode == QSSGRenderReflectionProbe::ReflectionRefreshMode::FirstFrame)
2020 pEntry->m_rendered = true;
2021
2022 reflectionProbes[i]->hasScheduledUpdate = false;
2023 pEntry->m_needsRender = false;
2024 }
2025}
2026
2028{
2029 QRhi *rhi = rhiCtx->rhi();
2030 bool needsBuild = false;
2031
2032 if (!renderableTex->texture) {
2033 // the ambient occlusion texture is always non-msaa, even if multisampling is used in the main pass
2034 renderableTex->texture = rhiCtx->rhi()->newTexture(QRhiTexture::RGBA8, size, 1, QRhiTexture::RenderTarget);
2035 needsBuild = true;
2036 } else if (renderableTex->texture->pixelSize() != size) {
2037 renderableTex->texture->setPixelSize(size);
2038 needsBuild = true;
2039 }
2040
2041 if (needsBuild) {
2042 if (!renderableTex->texture->create()) {
2043 qWarning("Failed to build ambient occlusion texture (size %dx%d)", size.width(), size.height());
2044 renderableTex->reset();
2045 return false;
2046 }
2047 renderableTex->resetRenderTarget();
2048 renderableTex->rt = rhi->newTextureRenderTarget({ renderableTex->texture });
2049 renderableTex->rt->setName(QByteArrayLiteral("Ambient occlusion"));
2050 renderableTex->rpDesc = renderableTex->rt->newCompatibleRenderPassDescriptor();
2051 renderableTex->rt->setRenderPassDescriptor(renderableTex->rpDesc);
2052 if (!renderableTex->rt->create()) {
2053 qWarning("Failed to build render target for ambient occlusion texture");
2054 renderableTex->reset();
2055 return false;
2056 }
2057 }
2058
2059 return true;
2060}
2061
2063 QSSGPassKey passKey,
2065 QSSGRhiShaderPipeline &shaderPipeline,
2068 const QSSGRhiRenderableTexture &rhiAoTexture,
2069 const QSSGRhiRenderableTexture &rhiDepthTexture,
2070 const QSSGRenderCamera &camera)
2071{
2072 // no texelFetch in GLSL <= 120 and GLSL ES 100
2073 if (!rhiCtx->rhi()->isFeatureSupported(QRhi::TexelFetch)) {
2074 QRhiCommandBuffer *cb = rhiCtx->commandBuffer();
2075 // just clear and stop there
2076 cb->beginPass(rhiAoTexture.rt, Qt::white, { 1.0f, 0 });
2077 QSSGRHICTX_STAT(rhiCtx, beginRenderPass(rhiAoTexture.rt));
2078 cb->endPass();
2079 QSSGRHICTX_STAT(rhiCtx, endRenderPass());
2080 return;
2081 }
2082
2083 ps.shaderPipeline = &shaderPipeline;
2084
2085 const float R2 = ao.aoDistance * ao.aoDistance * 0.16f;
2086 const QSize textureSize = rhiAoTexture.texture->pixelSize();
2087 const float rw = float(textureSize.width());
2088 const float rh = float(textureSize.height());
2089 const float fov = camera.verticalFov(rw / rh);
2090 const float tanHalfFovY = tanf(0.5f * fov * (rh / rw));
2091 const float invFocalLenX = tanHalfFovY * (rw / rh);
2092
2093 const QVector4D aoProps(ao.aoStrength * 0.01f, ao.aoDistance * 0.4f, ao.aoSoftness * 0.02f, ao.aoBias);
2094 const QVector4D aoProps2(float(ao.aoSamplerate), (ao.aoDither) ? 1.0f : 0.0f, 0.0f, 0.0f);
2095 const QVector4D aoScreenConst(1.0f / R2, rh / (2.0f * tanHalfFovY), 1.0f / rw, 1.0f / rh);
2096 const QVector4D uvToEyeConst(2.0f * invFocalLenX, -2.0f * tanHalfFovY, -invFocalLenX, tanHalfFovY);
2097 const QVector2D cameraProps(camera.clipNear, camera.clipFar);
2098
2099 // layout(std140, binding = 0) uniform buf {
2100 // vec4 aoProperties;
2101 // vec4 aoProperties2;
2102 // vec4 aoScreenConst;
2103 // vec4 uvToEyeConst;
2104 // vec2 cameraProperties;
2105
2106 const int UBUF_SIZE = 72;
2107 QSSGRhiDrawCallData &dcd(rhiCtx->drawCallData({ passKey, nullptr, nullptr, 0, QSSGRhiDrawCallDataKey::AoTexture }));
2108 if (!dcd.ubuf) {
2110 dcd.ubuf->create();
2111 }
2112
2113 char *ubufData = dcd.ubuf->beginFullDynamicBufferUpdateForCurrentFrame();
2114 memcpy(ubufData, &aoProps, 16);
2115 memcpy(ubufData + 16, &aoProps2, 16);
2116 memcpy(ubufData + 32, &aoScreenConst, 16);
2117 memcpy(ubufData + 48, &uvToEyeConst, 16);
2118 memcpy(ubufData + 64, &cameraProps, 8);
2120
2126 QRhiShaderResourceBindings *srb = rhiCtx->srb(bindings);
2127
2128 renderer.rhiQuadRenderer()->prepareQuad(rhiCtx, nullptr);
2129 renderer.rhiQuadRenderer()->recordRenderQuadPass(rhiCtx, &ps, srb, rhiAoTexture.rt, {});
2130}
2131
2133{
2134 QRhi *rhi = rhiCtx->rhi();
2135 bool needsBuild = false;
2136 QRhiTexture::Flags flags = QRhiTexture::RenderTarget;
2137 if (wantsMips)
2139
2140 if (!renderableTex->texture) {
2141 // always non-msaa, even if multisampling is used in the main pass
2142 renderableTex->texture = rhi->newTexture(QRhiTexture::RGBA8, size, 1, flags);
2143 needsBuild = true;
2144 } else if (renderableTex->texture->pixelSize() != size) {
2145 renderableTex->texture->setPixelSize(size);
2146 needsBuild = true;
2147 }
2148
2149 if (!renderableTex->depthStencil) {
2151 needsBuild = true;
2152 } else if (renderableTex->depthStencil->pixelSize() != size) {
2153 renderableTex->depthStencil->setPixelSize(size);
2154 needsBuild = true;
2155 }
2156
2157 if (needsBuild) {
2158 if (!renderableTex->texture->create()) {
2159 qWarning("Failed to build screen texture (size %dx%d)", size.width(), size.height());
2160 renderableTex->reset();
2161 return false;
2162 }
2163 if (!renderableTex->depthStencil->create()) {
2164 qWarning("Failed to build depth-stencil buffer for screen texture (size %dx%d)",
2165 size.width(), size.height());
2166 renderableTex->reset();
2167 return false;
2168 }
2169 renderableTex->resetRenderTarget();
2171 desc.setColorAttachments({ QRhiColorAttachment(renderableTex->texture) });
2172 desc.setDepthStencilBuffer(renderableTex->depthStencil);
2173 renderableTex->rt = rhi->newTextureRenderTarget(desc);
2174 renderableTex->rt->setName(QByteArrayLiteral("Screen texture"));
2175 renderableTex->rpDesc = renderableTex->rt->newCompatibleRenderPassDescriptor();
2176 renderableTex->rt->setRenderPassDescriptor(renderableTex->rpDesc);
2177 if (!renderableTex->rt->create()) {
2178 qWarning("Failed to build render target for screen texture");
2179 renderableTex->reset();
2180 return false;
2181 }
2182 }
2183
2184 return true;
2185}
2186
2188{
2189 QRhiCommandBuffer *cb = rhiCtx->commandBuffer();
2190 cb->debugMarkBegin(QByteArrayLiteral("Quick3D prepare grid"));
2191
2193
2194 int uniformBinding = 0;
2195 const int ubufSize = 64 * 2 * sizeof(float) + 4 * sizeof(float) + 4 * sizeof(quint32); // 2x mat4 + 4x float + 1x bool
2196
2197 QSSGRhiDrawCallData &dcd(rhiCtx->drawCallData({ &layer, nullptr, nullptr, 0, QSSGRhiDrawCallDataKey::Main })); // Change to Grid?
2198
2199 QRhi *rhi = rhiCtx->rhi();
2200 if (!dcd.ubuf) {
2202 dcd.ubuf->create();
2203 }
2204
2205 // Param
2206 const float nearF = inCamera.clipNear;
2207 const float farF = inCamera.clipFar;
2208 const float scale = layer.gridScale;
2209 const quint32 gridFlags = layer.gridFlags;
2210
2211 const float yFactor = rhi->isYUpInNDC() ? 1.0f : -1.0f;
2212
2213 QMatrix4x4 viewProj(Qt::Uninitialized);
2214 inCamera.calculateViewProjectionMatrix(viewProj);
2215 QMatrix4x4 invViewProj = viewProj.inverted();
2216
2217 char *ubufData = dcd.ubuf->beginFullDynamicBufferUpdateForCurrentFrame();
2218 memcpy(ubufData + 64 * 0, viewProj.constData(), 64);
2219 memcpy(ubufData + 64 * 1, invViewProj.constData(), 64);
2220 memcpy(ubufData + 64 * 2 + 0, &nearF, 4);
2221 memcpy(ubufData + 64 * 2 + 4 * 1, &farF, 4);
2222 memcpy(ubufData + 64 * 2 + 4 * 2, &scale, 4);
2223 memcpy(ubufData + 64 * 2 + 4 * 3, &yFactor, 4);
2224 memcpy(ubufData + 64 * 2 + 4 * 4, &gridFlags, 4);
2226
2227 bindings.addUniformBuffer(uniformBinding, RENDERER_VISIBILITY_ALL, dcd.ubuf);
2228
2229 layer.gridSrb = rhiCtx->srb(bindings);
2230 renderer.rhiQuadRenderer()->prepareQuad(rhiCtx, nullptr);
2231
2232 cb->debugMarkEnd();
2233}
2234
2235namespace {
2236void rhiPrepareSkyBox_helper(QSSGRhiContext *rhiCtx,
2237 QSSGPassKey passKey,
2239 QSSGRenderCamera &inCamera,
2241 QSSGReflectionMapEntry *entry = nullptr,
2242 int cubeFace = -1)
2243{
2244 const bool cubeMapMode = layer.background == QSSGRenderLayer::Background::SkyBoxCubeMap;
2245 const QSSGRenderImageTexture lightProbeTexture =
2246 cubeMapMode ? renderer.contextInterface()->bufferManager()->loadRenderImage(layer.skyBoxCubeMap, QSSGBufferManager::MipModeDisable)
2247 : renderer.contextInterface()->bufferManager()->loadRenderImage(layer.lightProbe, QSSGBufferManager::MipModeBsdf);
2248 const bool hasValidTexture = lightProbeTexture.m_texture != nullptr;
2249 if (hasValidTexture) {
2250 if (cubeFace < 0)
2251 layer.skyBoxIsRgbe8 = lightProbeTexture.m_flags.isRgbe8();
2252
2254
2257 cubeMapMode ? QRhiSampler::None : QRhiSampler::Linear, // cube map doesn't have mipmaps
2261 int samplerBinding = 1; //the shader code is hand-written, so we don't need to look that up
2262 const int ubufSize = 2 * 4 * 3 * sizeof(float) + 2 * 4 * 4 * sizeof(float) + 2 * sizeof(float); // 2x mat3 + 2x mat4 + 2 floats
2263 bindings.addTexture(samplerBinding,
2265 lightProbeTexture.m_texture, sampler);
2266
2267 QSSGRhiDrawCallData &dcd(cubeFace >= 0 ? rhiCtx->drawCallData({ passKey, nullptr, entry, cubeFace, QSSGRhiDrawCallDataKey::Reflection })
2268 : rhiCtx->drawCallData({ passKey, nullptr, nullptr, 0, QSSGRhiDrawCallDataKey::SkyBox }));
2269
2270 QRhi *rhi = rhiCtx->rhi();
2271 if (!dcd.ubuf) {
2272 dcd.ubuf = rhi->newBuffer(QRhiBuffer::Dynamic, QRhiBuffer::UniformBuffer, ubufSize);
2273 dcd.ubuf->create();
2274 }
2275
2276 const QMatrix4x4 &inverseProjection = inCamera.projection.inverted();
2277 const QMatrix4x4 &viewMatrix = inCamera.globalTransform;
2278 QMatrix4x4 viewProjection(Qt::Uninitialized); // For cube mode
2279 inCamera.calculateViewProjectionWithoutTranslation(0.1f, 5.0f, viewProjection);
2280
2281 float adjustY = rhi->isYUpInNDC() ? 1.0f : -1.0f;
2282 const float exposure = layer.probeExposure;
2283 // orientation
2284 const QMatrix3x3 &rotationMatrix(layer.probeOrientation);
2285 const float blurAmount = layer.skyboxBlurAmount;
2286 const float maxMipLevel = float(lightProbeTexture.m_mipmapCount - 2);
2287
2288 const QVector4D skyboxProperties = {
2289 adjustY,
2290 exposure,
2291 blurAmount,
2292 maxMipLevel
2293 };
2294
2295 char *ubufData = dcd.ubuf->beginFullDynamicBufferUpdateForCurrentFrame();
2296 memcpy(ubufData, viewMatrix.constData(), 44);
2297 memcpy(ubufData + 48, inverseProjection.constData(), 64);
2298 memcpy(ubufData + 112, rotationMatrix.constData(), 12);
2299 memcpy(ubufData + 128, (char *)rotationMatrix.constData() + 12, 12);
2300 memcpy(ubufData + 144, (char *)rotationMatrix.constData() + 24, 12);
2301 memcpy(ubufData + 160, &skyboxProperties, 16);
2302 memcpy(ubufData + 176, viewProjection.constData(), 64); //###
2303 dcd.ubuf->endFullDynamicBufferUpdateForCurrentFrame();
2304
2305 bindings.addUniformBuffer(0, RENDERER_VISIBILITY_ALL, dcd.ubuf);
2306
2307 if (cubeFace >= 0)
2308 entry->m_skyBoxSrbs[cubeFace] = rhiCtx->srb(bindings);
2309 else
2310 layer.skyBoxSrb = rhiCtx->srb(bindings);
2311
2312 if (cubeMapMode)
2313 renderer.rhiCubeRenderer()->prepareCube(rhiCtx, nullptr);
2314 else
2315 renderer.rhiQuadRenderer()->prepareQuad(rhiCtx, nullptr);
2316 }
2317}
2318} // namespace
2319
2321 QSSGPassKey passKey,
2323 QSSGRenderCamera &inCamera,
2325{
2326 QRhiCommandBuffer *cb = rhiCtx->commandBuffer();
2327 cb->debugMarkBegin(QByteArrayLiteral("Quick3D prepare skybox"));
2328
2329 rhiPrepareSkyBox_helper(rhiCtx, passKey, layer, inCamera, renderer);
2330
2331 cb->debugMarkEnd();
2332}
2333
2335 QSSGPassKey passKey,
2337 QSSGRenderCamera &inCamera,
2340 int cubeFace)
2341{
2342 QRhiCommandBuffer *cb = rhiCtx->commandBuffer();
2343 cb->debugMarkBegin(QByteArrayLiteral("Quick3D prepare skybox for reflection cube map"));
2344
2345 rhiPrepareSkyBox_helper(rhiCtx, passKey, layer, inCamera, renderer, entry, cubeFace);
2346
2347 cb->debugMarkEnd();
2348}
2349
2351 QSSGPassKey passKey,
2352 const QSSGRhiGraphicsPipelineState &basePipelineState,
2354 QSSGLayerRenderData &inData,
2355 const QVector<QSSGRenderableObjectHandle> &sortedOpaqueObjects,
2356 const QVector<QSSGRenderableObjectHandle> &sortedTransparentObjects,
2358 int samples)
2359{
2360 static const auto rhiPrepareDepthPassForObject = [](QSSGRhiContext *rhiCtx,
2361 QSSGPassKey passKey,
2362 QSSGLayerRenderData &inData,
2367 QSSGRhiShaderPipelinePtr shaderPipeline;
2368
2369 const bool isOpaqueDepthPrePass = obj->depthWriteMode == QSSGDepthDrawMode::OpaquePrePass;
2370 QSSGShaderFeatures featureSet;
2372 if (isOpaqueDepthPrePass)
2374
2375 QSSGRhiDrawCallData *dcd = nullptr;
2376 if (obj->type == QSSGRenderableObject::Type::DefaultMaterialMeshSubset || obj->type == QSSGRenderableObject::Type::CustomMaterialMeshSubset) {
2377 QSSGSubsetRenderable &subsetRenderable(static_cast<QSSGSubsetRenderable &>(*obj));
2378 const void *modelNode = &subsetRenderable.modelContext.model;
2379 dcd = &rhiCtx->drawCallData({ passKey, modelNode, &subsetRenderable.material, 0, ubufSel });
2380 }
2381
2382 if (obj->type == QSSGRenderableObject::Type::DefaultMaterialMeshSubset) {
2383 QSSGSubsetRenderable &subsetRenderable(static_cast<QSSGSubsetRenderable &>(*obj));
2384 ps->cullMode = QSSGRhiGraphicsPipelineState::toCullMode(subsetRenderable.defaultMaterial().cullMode);
2385
2386 shaderPipeline = shadersForDefaultMaterial(ps, subsetRenderable, featureSet);
2387 if (shaderPipeline) {
2388 shaderPipeline->ensureCombinedMainLightsUniformBuffer(&dcd->ubuf);
2389 char *ubufData = dcd->ubuf->beginFullDynamicBufferUpdateForCurrentFrame();
2390 updateUniformsForDefaultMaterial(*shaderPipeline, rhiCtx, inData, ubufData, ps, subsetRenderable, *inData.camera, nullptr, nullptr);
2392 } else {
2393 return false;
2394 }
2395 } else if (obj->type == QSSGRenderableObject::Type::CustomMaterialMeshSubset) {
2396 QSSGSubsetRenderable &subsetRenderable(static_cast<QSSGSubsetRenderable &>(*obj));
2397 ps->cullMode = QSSGRhiGraphicsPipelineState::toCullMode(subsetRenderable.customMaterial().m_cullMode);
2398
2399 QSSGCustomMaterialSystem &customMaterialSystem(*subsetRenderable.renderer->contextInterface()->customMaterialSystem().get());
2400 shaderPipeline = customMaterialSystem.shadersForCustomMaterial(ps, subsetRenderable.customMaterial(), subsetRenderable, featureSet);
2401
2402 if (shaderPipeline) {
2403 shaderPipeline->ensureCombinedMainLightsUniformBuffer(&dcd->ubuf);
2404 char *ubufData = dcd->ubuf->beginFullDynamicBufferUpdateForCurrentFrame();
2405 customMaterialSystem.updateUniformsForCustomMaterial(*shaderPipeline, rhiCtx, inData, ubufData, ps, subsetRenderable.customMaterial(), subsetRenderable,
2406 *inData.camera, nullptr, nullptr);
2408 } else {
2409 return false;
2410 }
2411 }
2412
2413 // the rest is common, only relying on QSSGSubsetRenderableBase, not the subclasses
2414 if (obj->type == QSSGRenderableObject::Type::DefaultMaterialMeshSubset || obj->type == QSSGRenderableObject::Type::CustomMaterialMeshSubset) {
2415 QSSGSubsetRenderable &subsetRenderable(static_cast<QSSGSubsetRenderable &>(*obj));
2416 ps->ia = subsetRenderable.subset.rhi.ia;
2417
2418 int instanceBufferBinding = setupInstancing(&subsetRenderable, ps, rhiCtx, inData.cameraData->direction, inData.cameraData->position);
2419 ps->ia.bakeVertexInputLocations(*shaderPipeline, instanceBufferBinding);
2420
2423
2424 // Depth and SSAO textures, in case a custom material's shader code does something with them.
2425 addDepthTextureBindings(rhiCtx, shaderPipeline.get(), bindings);
2426
2427 if (isOpaqueDepthPrePass) {
2429 shaderPipeline.get(),
2430 subsetRenderable.firstImage,
2431 bindings,
2432 (obj->type == QSSGRenderableObject::Type::CustomMaterialMeshSubset));
2433 }
2434
2435 QRhiShaderResourceBindings *srb = rhiCtx->srb(bindings);
2436
2437 subsetRenderable.rhiRenderData.depthPrePass.pipeline = rhiCtx->pipeline(QSSGGraphicsPipelineStateKey::create(*ps, rpDesc, srb),
2438 rpDesc,
2439 srb);
2440 subsetRenderable.rhiRenderData.depthPrePass.srb = srb;
2441 }
2442
2443 return true;
2444 };
2445
2446 // Phase 1 (prepare) for the Z prepass or the depth texture generation.
2447 // These renders opaque (Z prepass), or opaque and transparent (depth
2448 // texture), objects with depth test/write enabled, and color write
2449 // disabled, using a very simple set of shaders.
2450
2451 QSSGRhiGraphicsPipelineState ps = basePipelineState; // viewport and others are filled out already
2452 // We took a copy of the pipeline state since we do not want to conflict
2453 // with what rhiPrepare() collects for its own use. So here just change
2454 // whatever we need.
2455
2456 ps.samples = samples;
2457 ps.depthTestEnable = true;
2458 ps.depthWriteEnable = true;
2459 ps.targetBlend.colorWrite = {};
2460
2461 for (const QSSGRenderableObjectHandle &handle : sortedOpaqueObjects) {
2462 if (!rhiPrepareDepthPassForObject(rhiCtx, passKey, inData, handle.obj, rpDesc, &ps, ubufSel))
2463 return false;
2464 }
2465
2466 for (const QSSGRenderableObjectHandle &handle : sortedTransparentObjects) {
2467 if (!rhiPrepareDepthPassForObject(rhiCtx, passKey, inData, handle.obj, rpDesc, &ps, ubufSel))
2468 return false;
2469 }
2470
2471 return true;
2472}
2473
2475 const QSSGRhiGraphicsPipelineState &pipelineState,
2476 const QVector<QSSGRenderableObjectHandle> &sortedOpaqueObjects,
2477 const QVector<QSSGRenderableObjectHandle> &sortedTransparentObjects,
2478 bool *needsSetViewport)
2479{
2480 static const auto rhiRenderDepthPassForImp = [](QSSGRhiContext *rhiCtx,
2481 const QSSGRhiGraphicsPipelineState &pipelineState,
2483 bool *needsSetViewport) {
2484 for (const auto &oh : objects) {
2485 QSSGRenderableObject *obj = oh.obj;
2486
2487 // casts to SubsetRenderableBase so it works for both default and custom materials
2488 if (obj->type == QSSGRenderableObject::Type::DefaultMaterialMeshSubset || obj->type == QSSGRenderableObject::Type::CustomMaterialMeshSubset) {
2489 QRhiCommandBuffer *cb = rhiCtx->commandBuffer();
2490 QSSGSubsetRenderable *subsetRenderable(static_cast<QSSGSubsetRenderable *>(obj));
2491
2492 QRhiBuffer *vertexBuffer = subsetRenderable->subset.rhi.vertexBuffer->buffer();
2493 QRhiBuffer *indexBuffer = subsetRenderable->subset.rhi.indexBuffer
2494 ? subsetRenderable->subset.rhi.indexBuffer->buffer()
2495 : nullptr;
2496
2498 if (!ps)
2499 return;
2500
2502 if (!srb)
2503 return;
2504
2505 Q_QUICK3D_PROFILE_START(QQuick3DProfiler::Quick3DRenderCall);
2506 cb->setGraphicsPipeline(ps);
2507 cb->setShaderResources(srb);
2508
2509 if (*needsSetViewport) {
2510 cb->setViewport(pipelineState.viewport);
2511 *needsSetViewport = false;
2512 }
2513
2514 QRhiCommandBuffer::VertexInput vertexBuffers[2];
2515 int vertexBufferCount = 1;
2516 vertexBuffers[0] = QRhiCommandBuffer::VertexInput(vertexBuffer, 0);
2517 quint32 instances = 1;
2518 if (subsetRenderable->modelContext.model.instancing()) {
2519 instances = subsetRenderable->modelContext.model.instanceCount();
2520 vertexBuffers[1] = QRhiCommandBuffer::VertexInput(subsetRenderable->instanceBuffer, 0);
2522 }
2523
2524 if (indexBuffer) {
2525 cb->setVertexInput(0, vertexBufferCount, vertexBuffers, indexBuffer, 0, subsetRenderable->subset.rhi.indexBuffer->indexFormat());
2526 cb->drawIndexed(subsetRenderable->subset.count, instances, subsetRenderable->subset.offset);
2527 QSSGRHICTX_STAT(rhiCtx, drawIndexed(subsetRenderable->subset.count, instances));
2528 } else {
2529 cb->setVertexInput(0, vertexBufferCount, vertexBuffers);
2530 cb->draw(subsetRenderable->subset.count, instances, subsetRenderable->subset.offset);
2531 QSSGRHICTX_STAT(rhiCtx, draw(subsetRenderable->subset.count, instances));
2532 }
2533 Q_QUICK3D_PROFILE_END_WITH_IDS(QQuick3DProfiler::Quick3DRenderCall, (subsetRenderable->subset.count | quint64(instances) << 32),
2534 QVector<int>({subsetRenderable->modelContext.model.profilingId,
2535 subsetRenderable->material.profilingId}));
2536 }
2537 }
2538 };
2539
2540 rhiRenderDepthPassForImp(rhiCtx, pipelineState, sortedOpaqueObjects, needsSetViewport);
2541 rhiRenderDepthPassForImp(rhiCtx, pipelineState, sortedTransparentObjects, needsSetViewport);
2542}
2543
2545{
2546 QRhi *rhi = rhiCtx->rhi();
2547 bool needsBuild = false;
2548
2549 if (!renderableTex->texture) {
2554 qWarning("Depth texture not supported");
2555 // the depth texture is always non-msaa, even if multisampling is used in the main pass
2556 renderableTex->texture = rhiCtx->rhi()->newTexture(format, size, 1, QRhiTexture::RenderTarget);
2557 needsBuild = true;
2558 } else if (renderableTex->texture->pixelSize() != size) {
2559 renderableTex->texture->setPixelSize(size);
2560 needsBuild = true;
2561 }
2562
2563 if (needsBuild) {
2564 if (!renderableTex->texture->create()) {
2565 qWarning("Failed to build depth texture (size %dx%d, format %d)",
2566 size.width(), size.height(), int(renderableTex->texture->format()));
2567 renderableTex->reset();
2568 return false;
2569 }
2570 renderableTex->resetRenderTarget();
2572 rtDesc.setDepthTexture(renderableTex->texture);
2573 renderableTex->rt = rhi->newTextureRenderTarget(rtDesc);
2574 renderableTex->rt->setName(QByteArrayLiteral("Depth texture"));
2575 renderableTex->rpDesc = renderableTex->rt->newCompatibleRenderPassDescriptor();
2576 renderableTex->rt->setRenderPassDescriptor(renderableTex->rpDesc);
2577 if (!renderableTex->rt->create()) {
2578 qWarning("Failed to build render target for depth texture");
2579 renderableTex->reset();
2580 return false;
2581 }
2582 }
2583
2584 return true;
2585}
2586
QSSGRhiRenderableTexture rhiDepthTexture
\inmodule QtCore
Definition qbitarray.h:13
bool testBit(qsizetype i) const
Returns true if the bit at index position i is 1; otherwise returns false.
Definition qbitarray.h:84
void setBit(qsizetype i)
Sets the bit at index position i to 1.
Definition qbitarray.h:88
\inmodule QtCore
Definition qbytearray.h:57
\inmodule QtCore
constexpr bool testFlag(Enum flag) const noexcept
Definition qflags.h:126
iterator find(const Key &key)
Returns an iterator pointing to the item with the key in the hash.
Definition qhash.h:1258
iterator end() noexcept
Returns an \l{STL-style iterators}{STL-style iterator} pointing to the imaginary item after the last ...
Definition qhash.h:1206
iterator insert(const Key &key, const T &value)
Inserts a new item with the key and a value of value.
Definition qhash.h:1283
Definition qlist.h:74
qsizetype size() const noexcept
Definition qlist.h:386
const_iterator cend() const noexcept
Definition qlist.h:614
void append(parameter_type t)
Definition qlist.h:441
const_iterator cbegin() const noexcept
Definition qlist.h:613
void clear()
Definition qlist.h:417
The QMatrix4x4 class represents a 4x4 transformation matrix in 3D space.
Definition qmatrix4x4.h:25
float * data()
Returns a pointer to the raw data of this matrix.
QMatrix4x4 inverted(bool *invertible=nullptr) const
Returns the inverse of this matrix.
QPoint map(const QPoint &point) const
Maps point by multiplying this matrix by point.
Definition qmatrix4x4.h:908
const float * constData() const
Returns a constant pointer to the raw data of this matrix.
Definition qmatrix4x4.h:147
\inmodule QtCore
Definition qmutex.h:317
static FeatureSet toFeatureSet(const T &ssgFeatureSet)
The QQuaternion class represents a quaternion consisting of a vector and scalar.
Definition qquaternion.h:21
static QQuaternion fromEulerAngles(const QVector3D &eulerAngles)
static QQuaternion fromDirection(const QVector3D &direction, const QVector3D &up)
\inmodule QtCore\reentrant
Definition qrect.h:483
constexpr void setWidth(qreal w) noexcept
Sets the width of the rectangle to the given finite width.
Definition qrect.h:804
constexpr void setHeight(qreal h) noexcept
Sets the height of the rectangle to the given finite height.
Definition qrect.h:807
\inmodule QtGui
Definition qrhi.h:834
virtual char * beginFullDynamicBufferUpdateForCurrentFrame()
Definition qrhi.cpp:3844
@ Dynamic
Definition qrhi.h:839
virtual void endFullDynamicBufferUpdateForCurrentFrame()
To be called when the entire contents of the buffer data has been updated in the memory block returne...
Definition qrhi.cpp:3854
@ UniformBuffer
Definition qrhi.h:845
virtual bool create()=0
Creates the corresponding native graphics resources.
\inmodule QtGui
Definition qrhi.h:568
\inmodule QtGui
Definition qrhi.h:1614
QPair< QRhiBuffer *, quint32 > VertexInput
Synonym for QPair<QRhiBuffer *, quint32>.
Definition qrhi.h:1643
void resourceUpdate(QRhiResourceUpdateBatch *resourceUpdates)
Sometimes committing resource updates is necessary or just more convenient without starting a render ...
Definition qrhi.cpp:8986
\inmodule QtGui
Definition qrhi.h:1241
void setPixelSize(const QSize &sz)
Sets the size (in pixels) to sz.
Definition qrhi.h:1093
QSize pixelSize() const
Definition qrhi.h:1092
virtual bool create()=0
Creates the corresponding native graphics resources.
\inmodule QtGui
Definition qrhi.h:1119
void setRenderPassDescriptor(QRhiRenderPassDescriptor *desc)
Sets the QRhiRenderPassDescriptor desc for use with this render target.
Definition qrhi.h:1142
\inmodule QtGui
Definition qrhi.h:1694
void setName(const QByteArray &name)
Sets a name for the object.
Definition qrhi.cpp:3455
void deleteLater()
When called without a frame being recorded, this function is equivalent to deleting the object.
Definition qrhi.cpp:3419
\inmodule QtGui
Definition qrhi.h:1007
Filter
Specifies the minification, magnification, or mipmap filtering.
Definition qrhi.h:1009
@ ClampToEdge
Definition qrhi.h:1017
\inmodule QtGui
Definition qrhi.h:1190
QShader shader() const
Definition qrhi.h:389
void setDepthTexture(QRhiTexture *texture)
Sets the texture for depth-stencil.
Definition qrhi.h:635
\inmodule QtGui
Definition qrhi.h:1161
virtual QRhiRenderPassDescriptor * newCompatibleRenderPassDescriptor()=0
virtual bool create()=0
Creates the corresponding native graphics resources.
\inmodule QtGui
Definition qrhi.h:883
Format format() const
Definition qrhi.h:960
@ UsedWithGenerateMips
Definition qrhi.h:891
@ MipMapped
Definition qrhi.h:888
@ RenderTarget
Definition qrhi.h:886
@ CubeMap
Definition qrhi.h:887
virtual bool create()=0
Creates the corresponding native graphics resources.
Format
Specifies the texture format.
Definition qrhi.h:902
Flags flags() const
Definition qrhi.h:980
QSize pixelSize() const
Definition qrhi.h:963
void setPixelSize(const QSize &sz)
Sets the texture size, specified in pixels, to sz.
Definition qrhi.h:964
void setBindings(std::initializer_list< QRhiVertexInputBinding > list)
Sets the bindings from the specified list.
Definition qrhi.h:317
const QRhiVertexInputBinding * cendBindings() const
Definition qrhi.h:325
const QRhiVertexInputBinding * cbeginBindings() const
Definition qrhi.h:324
\inmodule QtGui
Definition qrhi.h:85
\inmodule QtGui
Definition qrhi.h:1767
QRhiBuffer * newBuffer(QRhiBuffer::Type type, QRhiBuffer::UsageFlags usage, quint32 size)
Definition qrhi.cpp:10079
bool isClipDepthZeroToOne() const
Definition qrhi.cpp:9640
bool isTextureFormatSupported(QRhiTexture::Format format, QRhiTexture::Flags flags={}) const
Definition qrhi.cpp:9673
QMatrix4x4 clipSpaceCorrMatrix() const
Definition qrhi.cpp:9662
bool isYUpInFramebuffer() const
Definition qrhi.cpp:9601
bool isYUpInNDC() const
Definition qrhi.cpp:9615
bool isFeatureSupported(QRhi::Feature feature) const
Definition qrhi.cpp:9681
QRhiRenderBuffer * newRenderBuffer(QRhiRenderBuffer::Type type, const QSize &pixelSize, int sampleCount=1, QRhiRenderBuffer::Flags flags={}, QRhiTexture::Format backingFormatHint=QRhiTexture::UnknownFormat)
Definition qrhi.cpp:10106
QRhiTextureRenderTarget * newTextureRenderTarget(const QRhiTextureRenderTargetDescription &desc, QRhiTextureRenderTarget::Flags flags={})
Definition qrhi.cpp:10245
bool isRecordingFrame() const
Definition qrhi.cpp:10365
QRhiTexture * newTexture(QRhiTexture::Format format, const QSize &pixelSize, int sampleCount=1, QRhiTexture::Flags flags={})
Definition qrhi.cpp:10133
@ TexelFetch
Definition qrhi.h:1814
QRhiResourceUpdateBatch * nextResourceUpdateBatch()
Definition qrhi.cpp:8854
static constexpr QSSGRenderTextureCubeFace next(QSSGRenderTextureCubeFace face)
Class representing 3D range or axis aligned bounding box.
bool isFinite() const
Q_ALWAYS_INLINE QVector3D dimensions() const
returns the dimensions (width/height/depth) of this axis aligned box.
Q_ALWAYS_INLINE float extents(quint32 axis) const
get component of the box's extents along a given axis
Q_ALWAYS_INLINE QSSGBoxPoints toQSSGBoxPointsNoEmptyCheck() const
void include(const QVector3D &v)
expands the volume to include v
Q_ALWAYS_INLINE bool isEmpty() const
QSSGRenderMesh * getMeshForPicking(const QSSGRenderModel &model) const
void rhiRenderRenderable(QSSGRhiContext *rhiCtx, QSSGSubsetRenderable &renderable, bool *needsSetViewport, int cubeFace, const QSSGRhiGraphicsPipelineState &state)
void updateUniformsForCustomMaterial(QSSGRhiShaderPipeline &shaderPipeline, QSSGRhiContext *rhiCtx, const QSSGLayerRenderData &inData, char *ubufData, QSSGRhiGraphicsPipelineState *ps, const QSSGRenderCustomMaterial &material, QSSGSubsetRenderable &renderable, const QSSGRenderCamera &camera, const QVector2D *depthAdjust, const QMatrix4x4 *alteredModelViewProjection)
void rhiPrepareRenderable(QSSGRhiGraphicsPipelineState *ps, QSSGPassKey passKey, QSSGSubsetRenderable &renderable, const QSSGShaderFeatures &featureSet, const QSSGRenderCustomMaterial &material, const QSSGLayerRenderData &layerData, QRhiRenderPassDescriptor *renderPassDescriptor, int samples, QSSGRenderCamera *camera=nullptr, int cubeFace=-1, QMatrix4x4 *modelViewProjection=nullptr, QSSGReflectionMapEntry *entry=nullptr)
QSSGRhiShaderPipelinePtr shadersForCustomMaterial(QSSGRhiGraphicsPipelineState *ps, const QSSGRenderCustomMaterial &material, QSSGSubsetRenderable &renderable, const QSSGShaderFeatures &featureSet)
QRhiTexture * getLightmapTexture(const QSSGModelContext &modelContext) const
QSSGShaderFeatures getShaderFeatures() const
const QSSGRenderReflectionMapPtr & getReflectionMapManager() const
QSSGRenderCamera * camera
QVarLengthArray< QSSGRenderPass *, 12 > activePasses
QRhiTexture * getBonemapTexture(const QSSGModelContext &modelContext) const
std::optional< QSSGLayerRenderPreparationResult > layerPrepResult
const QSSGRenderShadowMapPtr & getShadowMapManager() const
std::optional< QSSGCameraRenderData > cameraData
static void rhiRenderRenderable(QSSGRhiContext *rhiCtx, QSSGParticlesRenderable &renderable, bool *needsSetViewport, int cubeFace, const QSSGRhiGraphicsPipelineState &state)
static void updateUniformsForParticleModel(QSSGRhiShaderPipeline &shaderPipeline, char *ubufData, const QSSGRenderModel *model, quint32 offset)
static void prepareParticlesForModel(QSSGRhiShaderPipeline &shaderPipeline, QSSGRhiContext *rhiCtx, QSSGRhiShaderResourceBindingList &bindings, const QSSGRenderModel *model)
static void rhiPrepareRenderable(QSSGRhiShaderPipeline &shaderPipeline, QSSGPassKey passKey, QSSGRhiContext *rhiCtx, QSSGRhiGraphicsPipelineState *ps, QSSGParticlesRenderable &renderable, const QSSGLayerRenderData &inData, QRhiRenderPassDescriptor *renderPassDescriptor, int samples, QSSGRenderCamera *camera=nullptr, int cubeFace=-1, QSSGReflectionMapEntry *entry=nullptr)
const std::unique_ptr< QSSGRhiContext > & rhiContext() const
const std::unique_ptr< QSSGProgramGenerator > & shaderProgramGenerator() const
const std::unique_ptr< QSSGCustomMaterialSystem > & customMaterialSystem() const
const std::shared_ptr< QSSGShaderLibraryManager > & shaderLibraryManager() const
const std::unique_ptr< QSSGBufferManager > & bufferManager() const
const std::unique_ptr< QSSGShaderCache > & shaderCache() const
QSSGReflectionMapEntry * reflectionMapEntry(int probeIdx)
QSSGShadowMapEntry * shadowMapEntry(int lightIdx)
void beginFrame(QSSGRenderLayer *layer)
bool prepareLayerForRender(QSSGRenderLayer &inLayer)
void endFrame(QSSGRenderLayer *layer)
void rhiRender(QSSGRenderLayer &inLayer)
static void setTonemapFeatures(QSSGShaderFeatures &features, QSSGRenderLayer::TonemapMode tonemapMode)
friend class QSSGLayerRenderData
bool isGlobalPickingEnabled() const
QSSGRhiShaderPipelinePtr getShaderPipelineForDefaultMaterial(QSSGSubsetRenderable &inRenderable, const QSSGShaderFeatures &inFeatureSet)
void beginLayerRender(QSSGLayerRenderData &inLayer)
static QSSGRhiShaderPipelinePtr generateRhiShaderPipelineImpl(QSSGSubsetRenderable &renderable, QSSGShaderLibraryManager &shaderLibraryManager, QSSGShaderCache &shaderCache, QSSGProgramGenerator &shaderProgramGenerator, QSSGShaderDefaultMaterialKeyProperties &shaderKeyProperties, const QSSGShaderFeatures &featureSet, QByteArray &shaderString)
void setRenderContextInterface(QSSGRenderContextInterface *ctx)
void endLayerRender()
static void intersectRayWithSubsetRenderable(QSSGBufferManager &bufferManager, const QSSGRenderRay &inRay, const QSSGRenderNode &node, PickResultList &outIntersectionResultList)
bool rendererRequestsFrames() const
void setGlobalPickingEnabled(bool isEnabled)
QSSGShaderDefaultMaterialKeyProperties & defaultMaterialShaderKeyProperties()
PickResultList syncPickAll(const QSSGRenderLayer &layer, QSSGBufferManager &bufferManager, const QSSGRenderRay &ray)
void addMaterialDirtyClear(QSSGRenderGraphObject *material)
QSSGRhiCubeRenderer * rhiCubeRenderer()
static void getLayerHitObjectList(const QSSGRenderLayer &layer, QSSGBufferManager &bufferManager, const QSSGRenderRay &ray, bool inPickEverything, PickResultList &outIntersectionResult)
void rhiPrepare(QSSGRenderLayer &inLayer)
void cleanupResources(QList< QSSGRenderGraphObject * > &resources)
QSSGLayerGlobalRenderProperties getLayerGlobalRenderProperties()
QSSGLayerRenderData * getOrCreateLayerRenderData(QSSGRenderLayer &layer)
QSSGRenderPickResult syncPick(const QSSGRenderLayer &layer, QSSGBufferManager &bufferManager, const QSSGRenderRay &ray, QSSGRenderNode *target=nullptr)
QSSGRhiQuadRenderer * rhiQuadRenderer()
QSSGRenderContextInterface * contextInterface() const
static void intersectRayWithItem2D(const QSSGRenderRay &inRay, const QSSGRenderItem2D &item2D, PickResultList &outIntersectionResultList)
QRhiTexture * dummyTexture(QRhiTexture::Flags flags, QRhiResourceUpdateBatch *rub, const QSize &size=QSize(64, 64), const QColor &fillColor=Qt::black)
bool isValid() const
QRhiCommandBuffer * commandBuffer() const
static QRhiCommandBuffer::BeginPassFlags commonPassFlags()
QSSGRhiDrawCallData & drawCallData(const QSSGRhiDrawCallDataKey &key)
void checkAndAdjustForNPoT(QRhiTexture *texture, QSSGRhiSamplerDescription *samplerDescription)
QRhiShaderResourceBindings * srb(const QSSGRhiShaderResourceBindingList &bindings)
QRhiGraphicsPipeline * pipeline(const QSSGGraphicsPipelineStateKey &key, QRhiRenderPassDescriptor *rpDesc, QRhiShaderResourceBindings *srb)
QRhi * rhi() const
QRhiSampler * sampler(const QSSGRhiSamplerDescription &samplerDescription)
void prepareCube(QSSGRhiContext *rhiCtx, QRhiResourceUpdateBatch *maybeRub)
void prepareQuad(QSSGRhiContext *rhiCtx, QRhiResourceUpdateBatch *maybeRub)
QRhiTexture * ssaoTexture() const
const QRhiShaderStage * fragmentStage() const
QRhiTexture * depthTexture() const
int bindingForTexture(const char *name, int hint=-1)
const QRhiShaderStage * vertexStage() const
const QSSGRhiTexture & extraTextureAt(int index) const
QSSGRhiShaderPipelinePtr tryGetRhiShaderPipeline(const QByteArray &inKey, const QSSGShaderFeatures &inFeatures)
QSSGRhiShaderPipelinePtr newPipelineFromPregenerated(const QByteArray &inKey, const QSSGShaderFeatures &inFeatures, QQsbCollection::Entry entry, const QSSGRenderGraphObject &obj, QSSGRhiShaderPipeline::StageFlags stageFlags={})
QSSGRhiShaderPipelinePtr tryNewPipelineFromPersistentCache(const QByteArray &qsbcKey, const QByteArray &inKey, const QSSGShaderFeatures &inFeatures, QSSGRhiShaderPipeline::StageFlags stageFlags={})
QQsbCollection::EntryMap m_preGeneratedShaderEntries
Definition qset.h:18
bool isEmpty() const
Definition qset.h:52
const_iterator cend() const noexcept
Definition qset.h:142
void clear()
Definition qset.h:61
const_iterator constFind(const T &value) const
Definition qset.h:161
iterator insert(const T &value)
Definition qset.h:155
QList< InOutVariable > combinedImageSamplers() const
QShaderDescription description() const
Definition qshader.cpp:340
\inmodule QtCore
Definition qsize.h:25
constexpr int height() const noexcept
Returns the height.
Definition qsize.h:132
constexpr int width() const noexcept
Returns the width.
Definition qsize.h:129
void start(int msec)
Starts or restarts the timer with a timeout interval of msec milliseconds.
Definition qtimer.cpp:208
constexpr size_type size() const noexcept
const T & at(qsizetype idx) const
const_iterator cbegin() const noexcept
const_iterator cend() const noexcept
iterator end() noexcept
void append(const T &t)
void push_back(const T &t)
iterator begin() noexcept
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
QVector3D normalized() const noexcept
Returns the normalized unit vector form of this vector.
Definition qvectornd.h:695
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
constexpr void setZ(float z) noexcept
Sets the z coordinate of this point to the given finite z coordinate.
Definition qvectornd.h:676
static constexpr float dotProduct(QVector3D v1, QVector3D v2) noexcept
Returns the dot product of v1 and v2.
Definition qvectornd.h:770
static constexpr QVector3D crossProduct(QVector3D v1, QVector3D v2) noexcept
Returns the cross-product of vectors v1 and v2, which is normal to the plane spanned by v1 and v2.
Definition qvectornd.h:775
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
QSSGRhiRenderableTexture rhiAoTexture
QSSGRhiRenderableTexture rhiScreenTexture
EGLContext ctx
QCamera * camera
Definition camera.cpp:19
QMap< QString, QString > map
[6]
QSet< QString >::iterator it
else opt state
[0]
Combined button and popup list for selecting options.
@ white
Definition qnamespace.h:30
constexpr Initialization Uninitialized
void rhiRenderDepthPass(QSSGRhiContext *rhiCtx, const QSSGRhiGraphicsPipelineState &ps, const QVector< QSSGRenderableObjectHandle > &sortedOpaqueObjects, const QVector< QSSGRenderableObjectHandle > &sortedTransparentObjects, bool *needsSetViewport)
Q_QUICK3DRUNTIMERENDER_EXPORT void rhiRenderRenderable(QSSGRhiContext *rhiCtx, const QSSGRhiGraphicsPipelineState &state, QSSGRenderableObject &object, bool *needsSetViewport, int cubeFace=-1)
bool rhiPrepareScreenTexture(QSSGRhiContext *rhiCtx, const QSize &size, bool wantsMips, QSSGRhiRenderableTexture *renderableTex)
void rhiRenderShadowMap(QSSGRhiContext *rhiCtx, QSSGPassKey passKey, QSSGRhiGraphicsPipelineState &ps, QSSGRenderShadowMap &shadowMapManager, const QSSGRenderCamera &camera, const QSSGShaderLightList &globalLights, const QVector< QSSGRenderableObjectHandle > &sortedOpaqueObjects, QSSGRenderer &renderer, const QSSGBoxPoints &castingObjectsBox, const QSSGBoxPoints &receivingObjectsBox)
void rhiRenderReflectionMap(QSSGRhiContext *rhiCtx, QSSGPassKey passKey, const QSSGLayerRenderData &inData, QSSGRhiGraphicsPipelineState *ps, QSSGRenderReflectionMap &reflectionMapManager, const QVector< QSSGRenderReflectionProbe * > &reflectionProbes, const QVector< QSSGRenderableObjectHandle > &reflectionPassObjects, QSSGRenderer &renderer)
bool rhiPrepareAoTexture(QSSGRhiContext *rhiCtx, const QSize &size, QSSGRhiRenderableTexture *renderableTex)
void rhiPrepareSkyBoxForReflectionMap(QSSGRhiContext *rhiCtx, QSSGPassKey passKey, QSSGRenderLayer &layer, QSSGRenderCamera &inCamera, QSSGRenderer &renderer, QSSGReflectionMapEntry *entry, int cubeFace)
Q_QUICK3DRUNTIMERENDER_EXPORT void rhiPrepareRenderable(QSSGRhiContext *rhiCtx, QSSGPassKey passKey, const QSSGLayerRenderData &inData, QSSGRenderableObject &inObject, QRhiRenderPassDescriptor *renderPassDescriptor, QSSGRhiGraphicsPipelineState *ps, QSSGShaderFeatures featureSet, int samples, QSSGRenderCamera *inCamera=nullptr, QMatrix4x4 *alteredModelViewProjection=nullptr, int cubeFace=-1, QSSGReflectionMapEntry *entry=nullptr)
bool rhiPrepareDepthTexture(QSSGRhiContext *rhiCtx, const QSize &size, QSSGRhiRenderableTexture *renderableTex)
void rhiRenderAoTexture(QSSGRhiContext *rhiCtx, QSSGPassKey passKey, QSSGRenderer &renderer, QSSGRhiShaderPipeline &shaderPipeline, QSSGRhiGraphicsPipelineState &ps, const SSAOMapPass::AmbientOcclusion &ao, const QSSGRhiRenderableTexture &rhiAoTexture, const QSSGRhiRenderableTexture &rhiDepthTexture, const QSSGRenderCamera &camera)
std::pair< QSSGBoxPoints, QSSGBoxPoints > calculateSortedObjectBounds(const QVector< QSSGRenderableObjectHandle > &sortedOpaqueObjects, const QVector< QSSGRenderableObjectHandle > &sortedTransparentObjects)
void rhiPrepareSkyBox(QSSGRhiContext *rhiCtx, QSSGPassKey passKey, QSSGRenderLayer &layer, QSSGRenderCamera &inCamera, QSSGRenderer &renderer)
bool rhiPrepareDepthPass(QSSGRhiContext *rhiCtx, QSSGPassKey passKey, const QSSGRhiGraphicsPipelineState &basePipelineState, QRhiRenderPassDescriptor *rpDesc, QSSGLayerRenderData &inData, const QVector< QSSGRenderableObjectHandle > &sortedOpaqueObjects, const QVector< QSSGRenderableObjectHandle > &sortedTransparentObjects, QSSGRhiDrawCallDataKey::Selector ubufSel, int samples)
void rhiPrepareGrid(QSSGRhiContext *rhiCtx, QSSGRenderLayer &layer, QSSGRenderCamera &inCamera, QSSGRenderer &renderer)
QVector3D Q_QUICK3DUTILS_EXPORT transform(const QMatrix4x4 &m, const QVector3D &v)
Definition qssgutils.cpp:86
static const int UBUF_SIZE
#define QByteArrayLiteral(str)
Definition qbytearray.h:52
#define Q_UNLIKELY(x)
static int instanceCount
EGLOutputLayerEXT layer
bool qFuzzyCompare(qfloat16 p1, qfloat16 p2) noexcept
Definition qfloat16.h:287
#define qWarning
Definition qlogging.h:162
constexpr float qRadiansToDegrees(float radians)
Definition qmath.h:281
#define M_PI_2
Definition qmath.h:213
#define M_PI
Definition qmath.h:209
constexpr float qDegreesToRadians(float degrees)
Definition qmath.h:260
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
constexpr T qAbs(const T &t)
Definition qnumeric.h:328
GLsizei const GLfloat * v
[13]
GLuint64 GLenum void * handle
GLsizei samples
GLenum GLuint GLintptr GLsizeiptr size
[1]
GLuint sampler
GLenum GLuint GLenum GLsizei length
GLdouble GLdouble right
GLenum face
const void GLsizei GLsizei stride
GLenum type
GLenum target
GLbitfield flags
GLenum GLuint texture
GLuint start
GLuint name
GLint GLsizei GLsizei GLenum format
GLfixed GLfixed GLint GLint GLfixed points
GLhandleARB obj
[2]
const GLubyte * c
GLuint entry
GLdouble GLdouble t
Definition qopenglext.h:243
GLuint64EXT * result
[6]
GLenum GLenum GLenum GLenum GLenum scale
const void GLsizei GLsizei GLint vertexBufferCount
#define Q_QUICK3D_PROFILE_START(Type)
#define Q_QUICK3D_PROFILE_END_WITH_ID(Type, Payload, POID)
#define QSSG_RENDERPASS_NAME(passName, level, face)
#define Q_QUICK3D_PROFILE_END_WITH_STRING(Type, Payload, Str)
#define Q_QUICK3D_PROFILE_END_WITH_IDS(Type, Payload, POIDs)
#define Q_ASSERT(cond)
Definition qrandom.cpp:47
#define QSSG_ASSERT(cond, action)
std::array< QVector3D, 8 > QSSGBoxPoints
QSSGDataView< T > toDataView(const T &type)
static constexpr QSSGRenderTextureCubeFace QSSGRenderTextureCubeFaces[]
QSSGRenderTextureCubeFace
static void cleanupResourcesImpl(const QSSGRenderContextInterface &rci, const Container &resources)
static QSSGBounds3 calculateShadowCameraBoundingBox(const QSSGBoxPoints &points, const QVector3D &forward, const QVector3D &up, const QVector3D &right)
static const QRhiShaderResourceBinding::StageFlags RENDERER_VISIBILITY_ALL
static void rhiPrepareResourcesForShadowMap(QSSGRhiContext *rhiCtx, const QSSGLayerRenderData &inData, QSSGPassKey passKey, const QSSGLayerGlobalRenderProperties &globalRenderProperties, QSSGShadowMapEntry *pEntry, QSSGRhiGraphicsPipelineState *ps, const QVector2D *depthAdjust, const QVector< QSSGRenderableObjectHandle > &sortedOpaqueObjects, QSSGRenderCamera &inCamera, bool orthographic, int cubeFace)
static void setupCameraForShadowMap(const QSSGRenderCamera &inCamera, const QSSGRenderLight *inLight, QSSGRenderCamera &theCamera, const QSSGBoxPoints &castingBox, const QSSGBoxPoints &receivingBox)
static void dfs(const QSSGRenderNode &node, RenderableList &renderables)
static void setupCubeReflectionCameras(const QSSGRenderReflectionProbe *inProbe, QSSGRenderCamera inCameras[6])
static QVector3D calcCenter(const QSSGBoxPoints &vertices)
static void addDepthTextureBindings(QSSGRhiContext *rhiCtx, QSSGRhiShaderPipeline *shaderPipeline, QSSGRhiShaderResourceBindingList &bindings)
static void addOpaqueDepthPrePassBindings(QSSGRhiContext *rhiCtx, QSSGRhiShaderPipeline *shaderPipeline, QSSGRenderableImage *renderableImage, QSSGRhiShaderResourceBindingList &bindings, bool isCustomMaterialMeshSubset=false)
static constexpr float QSSG_HALFPI
static QSSGRhiShaderPipelinePtr shadersForDefaultMaterial(QSSGRhiGraphicsPipelineState *ps, QSSGSubsetRenderable &subsetRenderable, const QSSGShaderFeatures &featureSet)
static QSSGRhiShaderPipelinePtr shadersForParticleMaterial(QSSGRhiGraphicsPipelineState *ps, QSSGParticlesRenderable &particleRenderable)
static void setupCubeShadowCameras(const QSSGRenderLight *inLight, QSSGRenderCamera inCameras[6])
static QSSGBoxPoints computeFrustumBounds(const QSSGRenderCamera &inCamera)
static constexpr float QSSG_PI
static QByteArray logPrefix()
static int setupInstancing(QSSGSubsetRenderable *renderable, QSSGRhiGraphicsPipelineState *ps, QSSGRhiContext *rhiCtx, const QVector3D &cameraDirection, const QVector3D &cameraPosition)
static void rhiPrepareResourcesForReflectionMap(QSSGRhiContext *rhiCtx, QSSGPassKey passKey, const QSSGLayerRenderData &inData, QSSGReflectionMapEntry *pEntry, QSSGRhiGraphicsPipelineState *ps, const QVector< QSSGRenderableObjectHandle > &sortedOpaqueObjects, QSSGRenderCamera &inCamera, QSSGRenderer &renderer, int cubeFace)
static void updateUniformsForDefaultMaterial(QSSGRhiShaderPipeline &shaderPipeline, QSSGRhiContext *rhiCtx, const QSSGLayerRenderData &inData, char *ubufData, QSSGRhiGraphicsPipelineState *ps, QSSGSubsetRenderable &subsetRenderable, const QSSGRenderCamera &camera, const QVector2D *depthAdjust, const QMatrix4x4 *alteredModelViewProjection)
static void fillTargetBlend(QRhiGraphicsPipeline::TargetBlend *targetBlend, QSSGRenderDefaultMaterial::MaterialBlendMode materialBlend)
#define QSSGRHICTX_STAT(ctx, f)
std::shared_ptr< QSSGRhiShaderPipeline > QSSGRhiShaderPipelinePtr
QRhiSampler::Filter toRhi(QSSGRenderTextureFilterOp op)
SSL_CTX int(* cb)(SSL *ssl, unsigned char **out, unsigned char *outlen, const unsigned char *in, unsigned int inlen, void *arg)
#define a0
#define a1
const void * QSSGPassKey
#define Q_TRACE_SCOPE(x,...)
Definition qtrace_p.h:146
@ desc
unsigned int quint32
Definition qtypes.h:45
unsigned long long quint64
Definition qtypes.h:56
unsigned char quint8
Definition qtypes.h:41
QSqlQueryModel * model
[16]
QTimer * timer
[3]
myFilter draw(painter, QPoint(0, 0), originalPixmap)
QLayoutItem * child
[0]
QSvgRenderer * renderer
[0]
QByteArray generateSha() const
static QSSGGraphicsPipelineStateKey create(const QSSGRhiGraphicsPipelineState &state, const QRhiRenderPassDescriptor *rpDesc, const QRhiShaderResourceBindings *srb)
struct QSSGGraphicsPipelineStateKey::@759 extra
QVector< quint32 > renderTargetDescription
static QSSGRhiShaderPipelinePtr generateMaterialRhiShader(const QByteArray &inShaderKeyPrefix, QSSGMaterialVertexPipeline &vertexGenerator, const QSSGShaderDefaultMaterialKey &key, QSSGShaderDefaultMaterialKeyProperties &inProperties, const QSSGShaderFeatures &inFeatureSet, const QSSGRenderGraphObject &inMaterial, const QSSGShaderLightListView &inLights, QSSGRenderableImage *inFirstImage, QSSGShaderLibraryManager &shaderLibraryManager, QSSGShaderCache &theCache)
static const char * getSamplerName(QSSGRenderableImage::Type type)
static void setRhiMaterialProperties(const QSSGRenderContextInterface &, QSSGRhiShaderPipeline &shaders, char *ubufData, QSSGRhiGraphicsPipelineState *inPipelineState, const QSSGRenderGraphObject &inMaterial, const QSSGShaderDefaultMaterialKey &inKey, QSSGShaderDefaultMaterialKeyProperties &inProperties, const QSSGRenderCamera &inCamera, const QMatrix4x4 &inModelViewProjection, const QMatrix3x3 &inNormalMatrix, const QMatrix4x4 &inGlobalTransform, const QMatrix4x4 &clipSpaceCorrMatrix, const QMatrix4x4 &localInstanceTransform, const QMatrix4x4 &globalInstanceTransform, const QSSGDataView< float > &inMorphWeights, QSSGRenderableImage *inFirstImage, float inOpacity, const QSSGLayerGlobalRenderProperties &inRenderProperties, const QSSGShaderLightListView &inLights, const QSSGShaderReflectionProbe &reflectionProbe, bool receivesShadows, bool receivesReflections, const QVector2D *shadowDepthAdjust, QRhiTexture *lightmapTexture)
const QSSGRenderModel & model
const QSSGRenderParticles & particles
QVarLengthArray< QRhiTextureRenderTarget *, 6 > m_rhiRenderTargets
QSSGRenderReflectionProbe::ReflectionTimeSlicing m_timeSlicing
QVarLengthArray< QRhiShaderResourceBindings *, 6 > m_skyBoxSrbs
QRhiRenderPassDescriptor * m_rhiRenderPassDesc
void renderMips(QSSGRhiContext *context)
QSSGRenderTextureCubeFace m_timeSliceFace
void calculateViewProjectionMatrix(QMatrix4x4 &outMatrix) const
void calculateViewProjectionWithoutTranslation(float near, float far, QMatrix4x4 &outMatrix) const
QSSGCameraGlobalCalculationResult calculateGlobalVariables(const QRectF &inViewport)
void lookAt(const QVector3D &inCameraPos, const QVector3D &inUpDir, const QVector3D &inTargetPos, const QVector3D &pivot)
QSSGShaderMaterialAdapter * adapter
QSSGRenderDefaultMaterial::MaterialBlendMode blendMode
static Q_REQUIRED_RESULT constexpr bool isRenderable(Type type) Q_DECL_NOTHROW
QSSGRenderImageTextureFlags m_flags
QSSGRenderTextureFilterOp m_mipFilterType
QSSGRenderTextureCoordOp m_horizontalTilingMode
QSSGRenderTextureFilterOp m_minFilterType
QSSGRenderTextureCoordOp m_verticalTilingMode
QSSGRenderTextureFilterOp m_magFilterType
QMatrix3x3 probeOrientation
QSSGRenderImage * lightProbe
QSSGLayerRenderData * renderData
QSSGRenderLayer::Background background
QVector< QSSGRenderSubset > subsets
int instanceCount() const
bool instancing() const
QSSGRenderInstanceTable * instanceTable
static constexpr QVector3D initScale
QVector3D getDirection() const
QMatrix4x4 globalTransform
QVector3D getScalingCorrectDirection() const
static QMatrix4x4 calculateTransformMatrix(QVector3D position, QVector3D scale, QVector3D pivot, QQuaternion rotation)
QSSGRenderNode * parent
QMatrix4x4 localTransform
QVector3D getGlobalPos() const
QSSGRenderParticles::FeatureLevel m_featureLevel
static void intersectWithBVH(const RayData &data, const QSSGMeshBVHNode *bvh, const QSSGRenderMesh *mesh, QVector< IntersectionResult > &intersections, int depth=0)
static IntersectionResult createIntersectionResult(const RayData &data, const HitResult &hit)
QVector3D direction
static RayData createRayData(const QMatrix4x4 &globalTransform, const QSSGRenderRay &ray)
static HitResult intersectWithAABBv2(const RayData &data, const QSSGBounds3 &bounds)
QSSGRhiBufferPtr indexBuffer
QSSGRhiInputAssemblerState ia
quint32 lodOffset(int lodLevel) const
quint32 lodCount(int lodLevel) const
QRhiTexture * targetsTexture
QSSGRhiBufferPtr vertexBuffer
struct QSSGRenderSubset::@751 rhi
QSSGRenderImageTexture m_texture
QSSGRenderableImage * m_nextImage
const QSSGRenderImage & m_imageNode
const QMatrix4x4 & globalTransform
QSSGRenderableObjectFlags renderableFlags
QSSGRhiShaderResourceBindingList bindings
QSSGRhiGraphicsPipelineState ps
QRhiShaderResourceBindings * srb
QVector< quint32 > renderTargetDescription
QRhiGraphicsPipeline * pipeline
QSSGRhiInputAssemblerState ia
QRhiGraphicsPipeline::CullMode cullMode
static QRhiGraphicsPipeline::CullMode toCullMode(QSSGCullFaceMode cullFaceMode)
QRhiGraphicsPipeline::TargetBlend targetBlend
const QSSGRhiShaderPipeline * shaderPipeline
void bakeVertexInputLocations(const QSSGRhiShaderPipeline &shaders, int instanceBufferBinding=0)
QRhiVertexInputLayout inputLayout
QRhiTextureRenderTarget * rt
QRhiRenderPassDescriptor * rpDesc
QRhiRenderBuffer * depthStencil
void addUniformBuffer(int binding, QRhiShaderResourceBinding::StageFlags stage, QRhiBuffer *buf, int offset, int size)
void addTexture(int binding, QRhiShaderResourceBinding::StageFlags stage, QRhiTexture *tex, QRhiSampler *sampler)
void toString(QByteArray &ioString, const QSSGShaderDefaultMaterialKeyProperties &inProperties) const
constexpr bool isSet(Feature feature) const
void set(Feature feature, bool val)
bool getValue(QSSGDataView< quint32 > inDataStore) const
QMatrix4x4 m_lightVP
light view projection matrix
QRhiRenderPassDescriptor * m_rhiRenderPassDesc
QVarLengthArray< QRhiTextureRenderTarget *, 6 > m_rhiRenderTargets
QRhiRenderBuffer * m_rhiDepthStencil
QMatrix4x4 m_lightCubeView[6]
light cubemap view matrices
QMatrix4x4 m_lightView
light view transform
QRhiGraphicsPipeline * pipeline
bool prepareInstancing(QSSGRhiContext *rhiCtx, const QVector3D &cameraDirection, const QVector3D &cameraPosition, float minThreshold, float maxThreshold)
const QSSGShaderLightListView & lights
struct QSSGSubsetRenderable::@762 rhiRenderData
QSSGShaderReflectionProbe reflectionProbe
struct QSSGSubsetRenderable::@762::@763 mainPass
const QSSGRenderSubset & subset
struct QSSGSubsetRenderable::@762::@764 depthPrePass
struct QSSGSubsetRenderable::@762::@766 reflectionPass
struct QSSGSubsetRenderable::@762::@765 shadowPass
QRhiShaderResourceBindings * srb
const QSSGRenderCustomMaterial & customMaterial() const
QSSGShaderDefaultMaterialKey shaderDescription
QSSGRenderableImage * firstImage
const QSSGRenderDefaultMaterial & defaultMaterial() const
const QSSGModelContext & modelContext
const QSSGRenderGraphObject & material