10#include <QtQuick3DRuntimeRender/private/qssgrenderitem2d_p.h>
11#include "../qssgrendercontextcore.h"
12#include <QtQuick3DRuntimeRender/private/qssgrendercamera_p.h>
13#include <QtQuick3DRuntimeRender/private/qssgrenderlight_p.h>
14#include <QtQuick3DRuntimeRender/private/qssgrenderimage_p.h>
15#include <QtQuick3DRuntimeRender/private/qssgrenderbuffermanager_p.h>
16#include "../qssgrendercontextcore.h"
17#include <QtQuick3DRuntimeRender/private/qssgrendereffect_p.h>
18#include <QtQuick3DRuntimeRender/private/qssgrhicustommaterialsystem_p.h>
19#include <QtQuick3DRuntimeRender/private/qssgrendershadercodegenerator_p.h>
20#include <QtQuick3DRuntimeRender/private/qssgrenderdefaultmaterialshadergenerator_p.h>
21#include <QtQuick3DRuntimeRender/private/qssgperframeallocator_p.h>
22#include <QtQuick3DRuntimeRender/private/qssgrhiquadrenderer_p.h>
23#include <QtQuick3DRuntimeRender/private/qssgrendertexturedata_p.h>
24#include <QtQuick3DRuntimeRender/private/qssgrenderskymaterial_p.h>
25#include <QtQuick3DRuntimeRender/private/qssglayerrenderdata_p.h>
26#include <QtQuick3DRuntimeRender/private/qssgrhiparticles_p.h>
27#include <QtQuick3DRuntimeRender/private/qssgvertexpipelineimpl_p.h>
28#include "../qssgshadermapkey_p.h"
29#include "../qssgrenderpickresult_p.h"
30#include "../graphobjects/qssgrenderroot_p.h"
31#include "../graphobjects/qssgrendermodel_p.h"
32#include "../graphobjects/qssgrenderdefaultmaterial_p.h"
33#include "../graphobjects/qssgrendercustommaterial_p.h"
35#include <QtQuick3DUtils/private/qquick3dprofiler_p.h>
36#include <QtQuick3DUtils/private/qssgdataref_p.h>
37#include <QtQuick3DUtils/private/qssgutils_p.h>
38#include <QtQuick3DUtils/private/qssgassert_p.h>
39#include <QtQuick3DUtils/private/qssgfrustum_p.h>
40#include <qtquick3d_tracepoints_p.h>
42#include <QtQuick/private/qsgcontext_p.h>
43#include <QtQuick/private/qsgrenderer_p.h>
45#include <QtCore/QMutexLocker>
46#include <QtCore/QBitArray>
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
81struct QSSGRenderableImage;
82class QSSGSubsetRenderable;
84void QSSGRenderer::releaseCachedResources()
86 m_rhiQuadRenderer.reset();
87 m_rhiCubeRenderer.reset();
90void QSSGRenderer::registerItem2DData(QSSGRenderItem2DData &data)
93 for (
const auto *item2DData : m_item2DDatas) {
94 if (item2DData == &data)
98 m_item2DDatas.push_back(&data);
101void QSSGRenderer::unregisterItem2DData(QSSGRenderItem2DData &data)
103 const auto foundIt = std::find(m_item2DDatas.begin(), m_item2DDatas.end(), &data);
104 if (foundIt != m_item2DDatas.end())
105 m_item2DDatas.erase(foundIt);
108void QSSGRenderer::releaseItem2DData(
const QSSGRenderItem2D &item2D)
110 for (
auto *item2DData : m_item2DDatas)
111 item2DData->releaseRenderData(item2D);
114QSSGRenderer::QSSGRenderer() =
default;
116QSSGRenderer::~QSSGRenderer()
118 m_contextInterface =
nullptr;
119 releaseCachedResources();
122void QSSGRenderer::cleanupUnreferencedBuffers(QSSGRenderLayer *inLayer)
125 m_contextInterface->bufferManager()->cleanupUnreferencedBuffers(m_frameCount, inLayer);
128void QSSGRenderer::resetResourceCounters(QSSGRenderLayer *inLayer)
130 m_contextInterface->bufferManager()->resetUsageCounters(m_frameCount, inLayer);
133bool QSSGRenderer::prepareLayerForRender(QSSGRenderLayer &inLayer)
135 QSSGLayerRenderData *theRenderData = getOrCreateLayerRenderData(inLayer);
136 Q_ASSERT(theRenderData);
140 Q_ASSERT(inLayer.rootNode);
141 if (inLayer.rootNode->isDirty(QSSGRenderRoot::DirtyFlag::TreeDirty))
142 inLayer.rootNode->reindex();
144 beginLayerRender(*theRenderData);
145 theRenderData->resetForFrame();
146 theRenderData->prepareForRender();
148 return theRenderData->layerPrepResult.getFlags().wasDirty();
152void QSSGRenderer::rhiPrepare(QSSGRenderLayer &inLayer)
154 QSSGLayerRenderData *theRenderData = getOrCreateLayerRenderData(inLayer);
155 QSSG_ASSERT(theRenderData && !theRenderData->renderedCameras.isEmpty(),
return);
157 const auto layerPrepResult = theRenderData->layerPrepResult;
158 if (layerPrepResult.isLayerVisible()) {
160 QSSGRhiContext *rhiCtx = contextInterface()->rhiContext().get();
161 QSSG_ASSERT(rhiCtx->isValid() && rhiCtx->rhi()->isRecordingFrame(),
return);
162 beginLayerRender(*theRenderData);
163 theRenderData->maybeProcessLightmapBaking();
168 const auto &activePasses = theRenderData->activePasses;
169 for (
const auto &pass : activePasses) {
170 pass->renderPrep(*
this, *theRenderData);
171 if (pass->passType() == QSSGRenderPass::Type::Standalone)
172 pass->renderPass(*
this);
180void QSSGRenderer::rhiRender(QSSGRenderLayer &inLayer)
182 QSSGLayerRenderData *theRenderData = getOrCreateLayerRenderData(inLayer);
183 QSSG_ASSERT(theRenderData && !theRenderData->renderedCameras.isEmpty(),
return);
184 if (theRenderData->layerPrepResult.isLayerVisible()) {
185 beginLayerRender(*theRenderData);
186 const auto &activePasses = theRenderData->activePasses;
187 for (
const auto &pass : activePasses) {
188 if (pass->passType() == QSSGRenderPass::Type::Main || pass->passType() == QSSGRenderPass::Type::Extension)
189 pass->renderPass(*
this);
195template<
typename Container>
198 const auto &rhiCtx = rci.rhiContext();
199 if (!rhiCtx->isValid())
202 const auto &bufferManager = rci.bufferManager();
204 for (
const auto &resource : resources) {
205 if (resource->type == QSSGRenderGraphObject::Type::Geometry) {
206 auto geometry =
static_cast<QSSGRenderGeometry*>(resource);
207 bufferManager->releaseGeometry(geometry);
208 }
else if (resource->type == QSSGRenderGraphObject::Type::Model) {
209 auto model =
static_cast<QSSGRenderModel*>(resource);
210 QSSGRhiContextPrivate::get(rhiCtx.get())->cleanupDrawCallData(model);
211 delete model->particleBuffer;
212 }
else if (QSSGRenderGraphObject::isMaterial(resource->type)) {
213 QSSGRhiContextPrivate::get(rhiCtx.get())->cleanupDrawCallDataForResource(resource);
214 }
else if (resource->type == QSSGRenderGraphObject::Type::SkyMaterial) {
215 auto *skyMaterial =
static_cast<QSSGRenderSkyMaterial *>(resource);
216 auto *rhiCtxD = QSSGRhiContextPrivate::get(rhiCtx.get());
217 rhiCtxD->cleanupDrawCallDataForSkyMaterial(skyMaterial);
218 }
else if (resource->type == QSSGRenderGraphObject::Type::TextureData || resource->type == QSSGRenderGraphObject::Type::Skin) {
219 static_assert(std::is_base_of_v<QSSGRenderTextureData, QSSGRenderSkin>,
"QSSGRenderSkin is expected to be a QSSGRenderTextureData type!");
220 auto textureData =
static_cast<QSSGRenderTextureData *>(resource);
221 bufferManager->releaseTextureData(textureData);
222 }
else if (QSSGRenderGraphObject::isExtension(resource->type)) {
223 auto *rext =
static_cast<QSSGRenderExtension *>(resource);
224 bufferManager->releaseExtensionResult(*rext);
226 QSSGRhiContextPrivate::get(rhiCtx.get())->cleanupDrawCallDataForCid(resource);
227 }
else if (resource->type == QSSGRenderGraphObject::Type::ModelInstance) {
228 auto *rhiCtxD = QSSGRhiContextPrivate::get(rhiCtx.get());
229 auto *table =
static_cast<QSSGRenderInstanceTable *>(resource);
230 rhiCtxD->releaseInstanceBuffer(table);
231 }
else if (resource->type == QSSGRenderGraphObject::Type::Item2D) {
232 auto *item2D =
static_cast<QSSGRenderItem2D *>(resource);
233 rci.renderer()->releaseItem2DData(*item2D);
234 }
else if (resource->type == QSSGRenderGraphObject::Type::RenderPass) {
235 auto *userPass =
static_cast<QSSGRenderUserPass *>(resource);
236 bufferManager->releaseUserRenderPass(*userPass);
245void QSSGRenderer::cleanupResources(QList<QSSGRenderGraphObject *> &resources)
247 cleanupResourcesImpl(*m_contextInterface, resources);
251void QSSGRenderer::cleanupResources(QSet<QSSGRenderGraphObject *> &resources)
253 cleanupResourcesImpl(*m_contextInterface, resources);
257QSSGLayerRenderData *QSSGRenderer::getOrCreateLayerRenderData(QSSGRenderLayer &layer)
259 if (layer.renderData ==
nullptr)
260 layer.renderData =
new QSSGLayerRenderData(layer, *
this);
262 return layer.renderData;
265void QSSGRenderer::addMaterialDirtyClear(QSSGRenderGraphObject *material)
267 m_materialClearDirty.insert(material);
273QSSGRhiShaderPipelinePtr QSSGRendererPrivate::generateRhiShaderPipelineImpl(QSSGSubsetRenderable &renderable,
274 QSSGShaderLibraryManager &shaderLibraryManager,
275 QSSGShaderCache &shaderCache,
276 QSSGProgramGenerator &shaderProgramGenerator,
277 const QSSGShaderDefaultMaterialKeyProperties &shaderKeyProperties,
278 const QSSGShaderFeatures &featureSet,
279 const QSSGUserShaderAugmentation &shaderAugmentation,
280 QByteArray &shaderString)
282 shaderString = rendererLogPrefix();
283 QSSGShaderDefaultMaterialKey theKey(renderable.shaderDescription);
289 theKey.toString(shaderString, shaderKeyProperties);
292 for (
const auto &def : shaderAugmentation.defines)
293 shaderString.append(def.name).append(
';').append(def.value).append(
';');
294 shaderString.append(shaderAugmentation.preamble).append(
';');
295 shaderString.append(shaderAugmentation.body).append(
';');
301 if (
const auto &maybePipeline = shaderCache.tryGetRhiShaderPipeline(shaderString, featureSet))
302 return maybePipeline;
305 const QByteArray qsbcKey = QQsbCollection::EntryDesc::generateSha(shaderString, QQsbCollection::toFeatureSet(featureSet));
306 const QQsbCollection::EntryMap &pregenEntries = shaderLibraryManager.m_preGeneratedShaderEntries;
307 if (!pregenEntries.isEmpty()) {
308 const auto foundIt = pregenEntries.constFind(QQsbCollection::Entry(qsbcKey));
309 if (foundIt != pregenEntries.cend())
310 return shaderCache.newPipelineFromPregenerated(shaderString, featureSet, *foundIt, renderable.material);
314 if (
const auto &maybePipeline = shaderCache.tryNewPipelineFromPersistentCache(qsbcKey, shaderString, featureSet))
315 return maybePipeline;
319 const auto &material =
static_cast<
const QSSGRenderDefaultMaterial &>(renderable.getMaterial());
320 QSSGMaterialVertexPipeline vertexPipeline(shaderProgramGenerator,
324 return QSSGMaterialShaderGenerator::generateMaterialRhiShader(rendererLogPrefix(),
327 renderable.shaderDescription,
331 shaderLibraryManager,
336QSSGRhiShaderPipelinePtr QSSGRendererPrivate::generateRhiShaderPipeline(QSSGRenderer &renderer,
337 QSSGSubsetRenderable &inRenderable,
338 const QSSGShaderFeatures &inFeatureSet,
339 const QSSGUserShaderAugmentation &shaderAugmentation = {})
341 auto *currentLayer = renderer.m_currentLayer;
342 auto &generatedShaderString = currentLayer->generatedShaderString;
343 const auto &m_contextInterface = renderer.m_contextInterface;
344 const auto &theCache = m_contextInterface->shaderCache();
345 const auto &shaderProgramGenerator = m_contextInterface->shaderProgramGenerator();
346 const auto &shaderLibraryManager = m_contextInterface->shaderLibraryManager();
347 return QSSGRendererPrivate::generateRhiShaderPipelineImpl(inRenderable, *shaderLibraryManager, *theCache, *shaderProgramGenerator, currentLayer->defaultMaterialShaderKeyProperties, inFeatureSet, shaderAugmentation, generatedShaderString);
350void QSSGRenderer::beginFrame(QSSGRenderLayer &layer,
bool allowRecursion)
352 const bool executeBeginFrame = !(allowRecursion && (m_activeFrameRef++ != 0));
353 if (executeBeginFrame) {
354 m_contextInterface->perFrameAllocator()->reset();
355 QSSGRHICTX_STAT(m_contextInterface->rhiContext().get(), start(&layer));
356 resetResourceCounters(&layer);
360bool QSSGRenderer::endFrame(QSSGRenderLayer &layer,
bool allowRecursion)
362 const bool executeEndFrame = !(allowRecursion && (--m_activeFrameRef != 0));
363 if (executeEndFrame) {
364 cleanupUnreferencedBuffers(&layer);
367 for (
auto *matObj : std::as_const(m_materialClearDirty)) {
368 if (matObj->type == QSSGRenderGraphObject::Type::CustomMaterial) {
369 static_cast<QSSGRenderCustomMaterial *>(matObj)->clearDirty();
370 }
else if (matObj->type == QSSGRenderGraphObject::Type::DefaultMaterial ||
371 matObj->type == QSSGRenderGraphObject::Type::PrincipledMaterial ||
372 matObj->type == QSSGRenderGraphObject::Type::SpecularGlossyMaterial) {
373 static_cast<QSSGRenderDefaultMaterial *>(matObj)->clearDirty();
376 m_materialClearDirty.clear();
378 QSSGRHICTX_STAT(m_contextInterface->rhiContext().get(), stop(&layer));
383 return executeEndFrame;
386QSSGRendererPrivate::PickResultList QSSGRendererPrivate::syncPickAll(
const QSSGRenderContextInterface &ctx,
387 const QSSGRenderLayer &layer,
388 const QSSGRenderRay &ray)
390 const auto &bufferManager = ctx.bufferManager();
391 const bool isGlobalPickingEnabled = QSSGRendererPrivate::isGlobalPickingEnabled(*ctx.renderer());
392 PickResultList pickResults;
393 Q_ASSERT(layer.getGlobalState(QSSGRenderNode::GlobalState::Active));
394 getLayerHitObjectList(layer, *bufferManager, ray, isGlobalPickingEnabled, pickResults);
396 std::stable_sort(pickResults.begin(), pickResults.end(), [](
const QSSGRenderPickResult &lhs,
const QSSGRenderPickResult &rhs) {
397 return lhs.m_distanceSq < rhs.m_distanceSq;
402QSSGRendererPrivate::PickResultList QSSGRendererPrivate::syncPick(
const QSSGRenderContextInterface &ctx,
403 const QSSGRenderLayer &layer,
404 const QSSGRenderRay &ray,
405 QSSGRenderNode *target)
407 const auto &bufferManager = ctx.bufferManager();
408 const bool isGlobalPickingEnabled = QSSGRendererPrivate::isGlobalPickingEnabled(*ctx.renderer());
410 Q_ASSERT(layer.getGlobalState(QSSGRenderNode::GlobalState::Active));
411 PickResultList pickResults;
413 intersectRayWithSubsetRenderable(layer, *bufferManager, ray, *target, pickResults);
415 getLayerHitObjectList(layer, *bufferManager, ray, isGlobalPickingEnabled, pickResults);
417 std::stable_sort(pickResults.begin(), pickResults.end(), [](
const QSSGRenderPickResult &lhs,
const QSSGRenderPickResult &rhs) {
418 return lhs.m_distanceSq < rhs.m_distanceSq;
423using RenderableList = QVarLengthArray<
const QSSGRenderNode *>;
424static void getPickableRecursive(
const QSSGRenderNode &node, RenderableList &renderables,
bool pickEverything =
false)
426 if (QSSGRenderGraphObject::isRenderable(node.type) && (pickEverything || node.getLocalState(QSSGRenderNode::LocalState::Pickable))) {
427 renderables.push_back(&node);
430 for (
const auto &child : node.children)
431 getPickableRecursive(child, renderables, pickEverything);
434std::optional<QSSGRenderPickResult> QSSGRendererPrivate::syncPickClosestPoint(
const QSSGRenderContextInterface &ctx,
435 const QSSGRenderLayer &layer,
436 const QVector3D ¢er,
const float radiusSquared,
437 QSSGRenderNode *target)
439 const auto &bufferManager = ctx.bufferManager();
441 Q_ASSERT(layer.getGlobalState(QSSGRenderNode::GlobalState::Active));
442 std::optional<QSSGRenderPickResult> result = std::nullopt;
444 result = closestPointOnSubsetRenderable(layer, *bufferManager, center, radiusSquared, *target);
446 const bool pickEverything = QSSGRendererPrivate::isGlobalPickingEnabled(*ctx.renderer());
447 RenderableList renderables;
448 for (
const auto &childNode : layer.children)
449 getPickableRecursive(childNode, renderables, pickEverything);
450 float bestDistSquared = radiusSquared;
451 for (
const auto &childNode : renderables) {
452 const auto res = closestPointOnSubsetRenderable(layer, *bufferManager, center, bestDistSquared, *childNode);
453 if (res.has_value()) {
454 bestDistSquared = res.value().m_distanceSq;
463QSSGRendererPrivate::PickResultList QSSGRendererPrivate::syncPickSubset(
const QSSGRenderLayer &layer,
464 QSSGBufferManager &bufferManager,
465 const QSSGRenderRay &ray,
466 QVarLengthArray<QSSGRenderNode*> subset)
468 QSSGRendererPrivate::PickResultList pickResults;
469 Q_ASSERT(layer.getGlobalState(QSSGRenderNode::GlobalState::Active));
471 for (
auto target : subset)
472 intersectRayWithSubsetRenderable(layer, bufferManager, ray, *target, pickResults);
474 std::stable_sort(pickResults.begin(), pickResults.end(), [](
const QSSGRenderPickResult &lhs,
const QSSGRenderPickResult &rhs) {
475 return lhs.m_distanceSq < rhs.m_distanceSq;
480void QSSGRendererPrivate::setGlobalPickingEnabled(QSSGRenderer &renderer,
bool isEnabled)
482 renderer.m_globalPickingEnabled = isEnabled;
485void QSSGRendererPrivate::setRenderContextInterface(QSSGRenderer &renderer, QSSGRenderContextInterface *ctx)
487 renderer.m_contextInterface = ctx;
490void QSSGRendererPrivate::setSgRenderContext(QSSGRenderer &renderer, QSGRenderContext *sgRenderCtx)
492 renderer.m_qsgRenderContext = sgRenderCtx;
495QSGRenderContext *QSSGRendererPrivate::getSgRenderContext(
const QSSGRenderer &renderer)
497 return renderer.m_qsgRenderContext.data();
500const std::unique_ptr<QSSGRhiQuadRenderer> &QSSGRenderer::rhiQuadRenderer()
const
502 if (!m_rhiQuadRenderer)
503 m_rhiQuadRenderer = std::make_unique<QSSGRhiQuadRenderer>();
505 return m_rhiQuadRenderer;
508const std::unique_ptr<QSSGRhiCubeRenderer> &QSSGRenderer::rhiCubeRenderer()
const
510 if (!m_rhiCubeRenderer)
511 m_rhiCubeRenderer = std::make_unique<QSSGRhiCubeRenderer>();
513 return m_rhiCubeRenderer;
517void QSSGRenderer::beginSubLayerRender(QSSGLayerRenderData &inLayer)
519 inLayer.saveRenderState(*
this);
520 m_currentLayer =
nullptr;
523void QSSGRenderer::endSubLayerRender(QSSGLayerRenderData &inLayer)
525 inLayer.restoreRenderState(*
this);
526 m_currentLayer = &inLayer;
529void QSSGRenderer::beginLayerRender(QSSGLayerRenderData &inLayer)
531 m_currentLayer = &inLayer;
533void QSSGRenderer::endLayerRender()
535 m_currentLayer =
nullptr;
538static void dfs(
const QSSGRenderNode &node, RenderableList &renderables)
540 if (QSSGRenderGraphObject::isRenderable(node.type))
541 renderables.push_back(&node);
543 for (
const auto &child : node.children)
544 dfs(child, renderables);
547void QSSGRendererPrivate::getLayerHitObjectList(
const QSSGRenderLayer &layer,
548 QSSGBufferManager &bufferManager,
549 const QSSGRenderRay &ray,
550 bool inPickEverything,
551 PickResultList &outIntersectionResult)
553 RenderableList renderables;
554 for (
const auto &childNode : layer.children)
555 dfs(childNode, renderables);
557 for (
int idx = renderables.size() - 1; idx >= 0; --idx) {
558 const auto &pickableObject = renderables.at(idx);
559 if (inPickEverything || pickableObject->getLocalState(QSSGRenderNode::LocalState::Pickable))
560 intersectRayWithSubsetRenderable(layer, bufferManager, ray, *pickableObject, outIntersectionResult);
566static inline QVector3D multiply(
const QMatrix3x3& M,
const QVector3D& v)
569 M(0,0) * v.x() + M(0,1) * v.y() + M(0,2) * v.z(),
570 M(1,0) * v.x() + M(1,1) * v.y() + M(1,2) * v.z(),
571 M(2,0) * v.x() + M(2,1) * v.y() + M(2,2) * v.z()
576static inline bool isUniformScaleMetric(
const QMatrix3x3& G,
float& s2,
float tolerance = 1e-5f) {
577 const float gxx = G(0,0), gyy = G(1,1), gzz = G(2,2);
578 const float gxy = G(0,1), gxz = G(0,2), gyz = G(1,2);
581 s2 = (gxx + gyy + gzz) / 3.0f;
584 const float scale =
std::max({ std::fabs(gxx), std::fabs(gyy), std::fabs(gzz), 1.0f });
587 const bool offDiagOK = (std::fabs(gxy) <= tolerance * scale) &&
588 (std::fabs(gxz) <= tolerance * scale) &&
589 (std::fabs(gyz) <= tolerance * scale) &&
590 (std::fabs(G(1,0)) <= tolerance * scale) &&
591 (std::fabs(G(2,0)) <= tolerance * scale) &&
592 (std::fabs(G(2,1)) <= tolerance * scale);
594 const bool diagOK = (std::fabs(gxx - s2) <= tolerance * scale) &&
595 (std::fabs(gyy - s2) <= tolerance * scale) &&
596 (std::fabs(gzz - s2) <= tolerance * scale);
598 return offDiagOK && diagOK && (s2 >= 0.0f);
603 inline float operator()(
const QVector3D& u,
const QVector3D& v)
const {
604 return QVector3D::dotProduct(u, v);
611 inline float operator()(
const QVector3D& u,
const QVector3D& v)
const {
613 return QVector3D::dotProduct(u, multiply(G, v));
620 QMatrix4x4 globalTransform;
621 QMatrix3x3 pullbackMetric;
622 QVector3D centerLocal;
628static inline SphereData createSphereData(
const QMatrix4x4 &globalTransform,
629 const QVector3D ¢erWorld)
631 QMatrix4x4 inv = globalTransform.inverted();
634 const QVector3D centerLocal = QSSGUtils::mat44::transform(inv, centerWorld);
636 const QMatrix3x3 A = QSSGUtils::mat44::getUpper3x3(globalTransform);
637 const QMatrix3x3 G = A.transposed() * A;
639 return SphereData{ globalTransform, G, centerLocal };
643static inline float distanceSqPointTransformedAABB(
const QVector3D &localPoint,
644 const QSSGBounds3 &localAABB,
648 QVector3D closestLocal(
649 qBound(localAABB.minimum.x(), localPoint.x(), localAABB.maximum.x()),
650 qBound(localAABB.minimum.y(), localPoint.y(), localAABB.maximum.y()),
651 qBound(localAABB.minimum.z(), localPoint.z(), localAABB.maximum.z())
655 QVector3D localDiff = localPoint - closestLocal;
660 return dot(localDiff, localDiff);
663struct ClosestPointResult
666 float distSq =
std::numeric_limits<
float>::max();
667 QVector3D localPoint;
668 QVector3D scenePoint;
669 QVector3D faceNormal;
670 QVector3D sceneNormal;
673 int instanceIndex = -1;
676static void closestPointBVHLeafNode(
const SphereData &data,
677 const QSSGMeshBVHNode *node,
678 const QSSGRenderMesh *mesh,
681 ClosestPointResult &best)
683 const int begin = node->offset;
684 const int end = begin + node->count;
685 const auto &triangles = mesh->bvh->triangles();
688 float uniformScaleFactor = 1.0f;
689 const bool isUniformScale = isUniformScaleMetric(data.pullbackMetric, uniformScaleFactor);
690 const MetricDot metricDot { data.pullbackMetric };
692 for (
int i = begin; i < end; ++i) {
693 const auto &triangle = triangles[i];
696 const float triangleDistSq = distanceSqPointTransformedAABB(data.centerLocal, triangle.bounds, data.pullbackMetric);
697 if (triangleDistSq >= best.distSq)
702 QVector3D closestPoint;
704 if (isUniformScale) {
705 closestPoint = closestPointOnTriangle(data.centerLocal,
706 triangle.vertex1, triangle.vertex2, triangle.vertex3,
710 closestPoint = closestPointOnTriangle(data.centerLocal,
711 triangle.vertex1, triangle.vertex2, triangle.vertex3,
717 const QVector3D delta = data.centerLocal - closestPoint;
718 const float distSq = metricDot(delta, delta);
721 if (distSq < best.distSq) {
722 best.distSq = distSq;
723 best.localPoint = closestPoint;
724 best.scenePoint = QSSGUtils::mat44::transform(data.globalTransform, closestPoint);
725 best.subset = subset;
726 best.instanceIndex = instanceIndex;
731 best.uv = u * triangle.uvCoord1 + v * triangle.uvCoord2 + w * triangle.uvCoord3;
734 const QVector3D edge1 = triangle.vertex2 - triangle.vertex1;
735 const QVector3D edge2 = triangle.vertex3 - triangle.vertex1;
736 best.faceNormal = QVector3D::normal(edge1, edge2).normalized();
737 const QMatrix3x3 normalMatrix = data.globalTransform.normalMatrix();
738 best.sceneNormal = QSSGUtils::mat33::transform(normalMatrix, best.faceNormal);
743static void closestPointBVH(
const SphereData &data,
744 const QSSGMeshBVHNode *node,
745 const QSSGRenderMesh *mesh,
748 ClosestPointResult &best)
750 if (!node || !mesh || !mesh->bvh)
754 const float aabbDistSq = distanceSqPointTransformedAABB(data.centerLocal, node->boundingData, data.pullbackMetric);
755 if (aabbDistSq >= best.distSq)
759 if (node->count != 0) {
760 closestPointBVHLeafNode(data, node, mesh, subset, instanceIndex, best);
765 const auto *leftChild =
static_cast<
const QSSGMeshBVHNode *>(node->left);
766 const auto *rightChild =
static_cast<
const QSSGMeshBVHNode *>(node->right);
769 const float leftDistSq = leftChild
770 ? distanceSqPointTransformedAABB(data.centerLocal, leftChild->boundingData, data.pullbackMetric)
771 : std::numeric_limits<
float>::max();
772 const float rightDistSq = rightChild
773 ? distanceSqPointTransformedAABB(data.centerLocal, rightChild->boundingData, data.pullbackMetric)
774 : std::numeric_limits<
float>::max();
777 if (leftDistSq < rightDistSq) {
778 if (leftDistSq < best.distSq)
779 closestPointBVH(data, leftChild, mesh, subset, instanceIndex, best);
780 if (rightDistSq < best.distSq)
781 closestPointBVH(data, rightChild, mesh, subset, instanceIndex, best);
783 if (rightDistSq < best.distSq)
784 closestPointBVH(data, rightChild, mesh, subset, instanceIndex, best);
785 if (leftDistSq < best.distSq)
786 closestPointBVH(data, leftChild, mesh, subset, instanceIndex, best);
791std::optional<QSSGRenderPickResult>
792QSSGRendererPrivate::closestPointOnSubsetRenderable(
const QSSGRenderLayer& layer,
793 QSSGBufferManager& bufferManager,
794 const QVector3D& center,
795 const float radiusSquared,
796 const QSSGRenderNode& node)
798 if (!layer.renderData)
801 const auto *renderData = layer.renderData;
808 if (node.type != QSSGRenderGraphObject::Type::Model)
811 const auto &model =
static_cast<
const QSSGRenderModel &>(node);
819 QMutexLocker mutexLocker(bufferManager.meshUpdateMutex());
821 auto mesh = bufferManager.getMeshForPicking(model, model.hasLightmap() ? layer.lightmapSource : QString());
826 QSSGBounds3 modelBounds;
827 for (
const auto &subset : std::as_const(mesh->subsets))
828 modelBounds.include(subset.bounds);
830 if (modelBounds.isEmpty())
833 const bool instancing = model.instancing();
834 int instanceCount = instancing ? model.instanceTable->count() : 1;
835 const auto instanceTransforms = instancing ? renderData->getInstanceTransforms(model) : QSSGLayerRenderData::InstanceTransforms{};
837 ClosestPointResult best;
838 best.distSq = radiusSquared;
840 for (
int i = 0; i < instanceCount; ++i) {
841 int instanceIndex = 0;
842 QMatrix4x4 modelTransform;
845 modelTransform = instanceTransforms.global * model.instanceTable->getTransform(instanceIndex) * instanceTransforms.local;
847 modelTransform = renderData->getGlobalTransform(model);
849 const SphereData data = createSphereData(modelTransform, center);
851 if (distanceSqPointTransformedAABB(data.centerLocal, modelBounds, data.pullbackMetric) > best.distSq)
854 for (
int subsetIndex = 0; subsetIndex < mesh->subsets.size(); ++subsetIndex) {
855 const auto &subset = mesh->subsets[subsetIndex];
858 if (distanceSqPointTransformedAABB(data.centerLocal, subset.bounds, data.pullbackMetric) >= best.distSq)
861 if (!subset.bvhRoot.isNull()) {
862 const auto *bvhRoot =
static_cast<
const QSSGMeshBVHNode *>(subset.bvhRoot);
863 closestPointBVH(data, bvhRoot, mesh, subsetIndex, instanceIndex, best);
868 return QSSGRenderPickResult{
888 if (model.materials.isEmpty())
889 return QSSGCullFaceMode::Back;
890 const int idx = qMin(subset,
int(model.materials.size()) - 1);
891 const QSSGRenderGraphObject *material = model.materials.at(idx);
893 return QSSGCullFaceMode::Back;
894 switch (material->type) {
895 case QSSGRenderGraphObject::Type::DefaultMaterial:
896 case QSSGRenderGraphObject::Type::PrincipledMaterial:
897 case QSSGRenderGraphObject::Type::SpecularGlossyMaterial:
898 return static_cast<
const QSSGRenderDefaultMaterial *>(material)->cullMode;
899 case QSSGRenderGraphObject::Type::CustomMaterial:
900 return static_cast<
const QSSGRenderCustomMaterial *>(material)->m_cullMode;
902 return QSSGCullFaceMode::Back;
911 case QSSGCullFaceMode::Unknown:
912 case QSSGCullFaceMode::Back:
913 return QSSGCullFaceMode::Front;
914 case QSSGCullFaceMode::Front:
915 return QSSGCullFaceMode::Back;
916 case QSSGCullFaceMode::Disabled:
917 case QSSGCullFaceMode::FrontAndBack:
923void QSSGRendererPrivate::intersectRayWithSubsetRenderable(
const QSSGRenderLayer &layer,
924 QSSGBufferManager &bufferManager,
925 const QSSGRenderRay &inRay,
926 const QSSGRenderNode &node,
927 PickResultList &outIntersectionResultList)
929 if (!layer.renderData)
932 const auto *renderData = layer.renderData;
935 if (node.type == QSSGRenderGraphObject::Type::Item2D) {
936 const QSSGRenderItem2D &item2D =
static_cast<
const QSSGRenderItem2D &>(node);
937 intersectRayWithItem2D(layer, inRay, item2D, outIntersectionResultList);
941 if (node.type != QSSGRenderGraphObject::Type::Model)
944 const QSSGRenderModel &model =
static_cast<
const QSSGRenderModel &>(node);
951 QMutexLocker mutexLocker(bufferManager.meshUpdateMutex());
952 auto mesh = bufferManager.getMeshForPicking(model, model.hasLightmap() ? layer.lightmapSource : QString());
956 const auto &subMeshes = mesh->subsets;
957 QSSGBounds3 modelBounds;
958 for (
const auto &subMesh : subMeshes)
959 modelBounds.include(subMesh.bounds);
961 if (modelBounds.isEmpty())
964 const bool instancing = model.instancing();
965 int instanceCount = instancing ? model.instanceTable->count() : 1;
967 const auto instanceTransforms = instancing ? renderData->getInstanceTransforms(model) : QSSGLayerRenderData::InstanceTransforms{};
969 for (
int instanceIndex = 0; instanceIndex < instanceCount; ++instanceIndex) {
971 QMatrix4x4 modelTransform;
973 modelTransform = instanceTransforms.global * model.instanceTable->getTransform(instanceIndex) * instanceTransforms.local;
975 modelTransform = renderData->getGlobalTransform(model);
977 auto rayData = QSSGRenderRay::createRayData(modelTransform, inRay);
979 auto hit = QSSGRenderRay::intersectWithAABBv2(rayData, modelBounds);
982 if (!hit.intersects())
985 const bool mirrored = modelTransform.determinant() < 0.0f;
988 float minRayLength = std::numeric_limits<
float>::max();
989 QSSGRenderRay::IntersectionResult intersectionResult;
990 QVector<QSSGRenderRay::IntersectionResult> results;
993 int resultSubset = 0;
994 for (
const auto &subMesh : subMeshes) {
995 QSSGRenderRay::IntersectionResult result;
996 if (!subMesh.bvhRoot.isNull()) {
997 hit = QSSGRenderRay::intersectWithAABBv2(rayData, subMesh.bvhRoot->boundingData);
998 if (hit.intersects()) {
1000 QSSGCullFaceMode cullMode = cullModeForSubset(model, subset);
1002 cullMode = mirroredCullMode(cullMode);
1003 inRay.intersectWithBVH(rayData,
static_cast<
const QSSGMeshBVHNode *>(subMesh.bvhRoot), mesh, results, cullMode);
1004 float subMeshMinRayLength = std::numeric_limits<
float>::max();
1005 for (
const auto &subMeshResult : std::as_const(results)) {
1006 if (subMeshResult.rayLengthSquared < subMeshMinRayLength) {
1007 result = subMeshResult;
1008 subMeshMinRayLength = result.rayLengthSquared;
1013 hit = QSSGRenderRay::intersectWithAABBv2(rayData, subMesh.bounds);
1014 if (hit.intersects())
1015 result = QSSGRenderRay::createIntersectionResult(rayData, hit);
1017 if (result.intersects && result.rayLengthSquared < minRayLength) {
1018 intersectionResult = result;
1019 minRayLength = intersectionResult.rayLengthSquared;
1020 resultSubset = subset;
1025 if (intersectionResult.intersects)
1026 outIntersectionResultList.push_back(QSSGRenderPickResult { &model,
1027 intersectionResult.rayLengthSquared,
1028 intersectionResult.relXY,
1029 intersectionResult.scenePosition,
1030 intersectionResult.localPosition,
1031 intersectionResult.faceNormal,
1032 intersectionResult.sceneFaceNormal,
1039void QSSGRendererPrivate::intersectRayWithItem2D(
const QSSGRenderLayer &layer,
1040 const QSSGRenderRay &inRay,
1041 const QSSGRenderItem2D &item2D,
1042 PickResultList &outIntersectionResultList)
1044 const auto &globalTransform = layer.renderData->getGlobalTransform(item2D);
1047 const QVector3D p0 = QSSGRenderNode::getGlobalPos(globalTransform);
1048 const QVector3D normal = -QSSGRenderNode::getDirection(globalTransform);
1050 const float d = QVector3D::dotProduct(inRay.direction, normal);
1051 float intersectionTime = 0;
1053 const QVector3D p0l0 = p0 - inRay.origin;
1054 intersectionTime = QVector3D::dotProduct(p0l0, normal) / d;
1055 if (intersectionTime >= 0) {
1057 const QVector3D intersectionPoint = inRay.origin + inRay.direction * intersectionTime;
1058 const QMatrix4x4 inverseGlobalTransform = globalTransform.inverted();
1059 const QVector3D localIntersectionPoint = QSSGUtils::mat44::transform(inverseGlobalTransform, intersectionPoint);
1060 const QVector2D qmlCoordinate(localIntersectionPoint.x(), -localIntersectionPoint.y());
1061 outIntersectionResultList.push_back(QSSGRenderPickResult { &item2D,
1062 intersectionTime * intersectionTime,
1065 localIntersectionPoint,
1066 -normal, -normal });
1071QSSGRhiShaderPipelinePtr QSSGRendererPrivate::getShaderPipelineForDefaultMaterial(QSSGRenderer &renderer,
1072 QSSGSubsetRenderable &inRenderable,
1073 const QSSGShaderFeatures &inFeatureSet,
1074 const QSSGUserShaderAugmentation &shaderAugmentation)
1076 auto *m_currentLayer = renderer.m_currentLayer;
1077 QSSG_ASSERT(m_currentLayer !=
nullptr,
return {});
1086 auto &shaderMap = m_currentLayer->shaderMap;
1088 QElapsedTimer timer;
1091 QSSGRhiShaderPipelinePtr shaderPipeline;
1095 QByteArray name = shaderAugmentation.hash;
1096 for (
const auto &def : shaderAugmentation.defines)
1097 name.append(def.name).append(def.value);
1098 QSSGShaderMapKey skey = QSSGShaderMapKey(name,
1100 inRenderable.shaderDescription);
1101 auto it = shaderMap.find(skey);
1102 if (it == shaderMap.end()) {
1103 Q_TRACE_SCOPE(QSSG_generateShader);
1104 Q_QUICK3D_PROFILE_START(QQuick3DProfiler::Quick3DGenerateShader);
1105 shaderPipeline = QSSGRendererPrivate::generateRhiShaderPipeline(renderer, inRenderable, inFeatureSet, shaderAugmentation);
1106 Q_QUICK3D_PROFILE_END_WITH_ID(QQuick3DProfiler::Quick3DGenerateShader, 0, inRenderable.material.profilingId);
1110 shaderMap.insert(skey, shaderPipeline);
1112 shaderPipeline = it.value();
1115 if (shaderPipeline !=
nullptr) {
1116 if (m_currentLayer && !m_currentLayer->renderedCameras.isEmpty())
1117 m_currentLayer->ensureCachedCameraDatas();
1120 const auto &rhiContext = renderer.m_contextInterface->rhiContext();
1121 QSSGRhiContextStats::get(*rhiContext).registerMaterialShaderGenerationTime(timer.elapsed());
1123 return shaderPipeline;
1126QList<
const QSSGRenderNode *> QSSGRendererPrivate::syncPickInFrustum(
const QSSGRenderContextInterface &ctx,
1127 const QSSGRenderLayer &layer,
1128 const QSSGFrustum &frustum)
1130 if (!layer.renderData)
1133 auto &bufferManager = ctx.bufferManager();
1134 const bool pickEverything = QSSGRendererPrivate::isGlobalPickingEnabled(*ctx.renderer());
1136 RenderableList nodes;
1137 for (
const auto &child : layer.children)
1138 getPickableRecursive(child, nodes, pickEverything);
1140 QList<
const QSSGRenderNode *> ret;
1141 for (
const auto node : nodes) {
1142 auto aabb = node->getBounds(*bufferManager);
1143 if (!aabb.isEmpty()) {
1144 const auto &transform = layer.renderData->getGlobalTransform(*node);
1145 aabb.transform(transform);
1146 if (frustum.contains(aabb))
friend class QSSGRenderContextInterface
static void cleanupResourcesImpl(const QSSGRenderContextInterface &rci, const Container &resources)
static void getPickableRecursive(const QSSGRenderNode &node, RenderableList &renderables, bool pickEverything=false)
static void dfs(const QSSGRenderNode &node, RenderableList &renderables)
static QByteArray rendererLogPrefix()
static QSSGCullFaceMode mirroredCullMode(QSSGCullFaceMode cullMode)
static QSSGCullFaceMode cullModeForSubset(const QSSGRenderModel &model, int subset)