9#include <QtQuick3DRuntimeRender/private/qssgrenderitem2d_p.h>
10#include "../qssgrendercontextcore.h"
11#include <QtQuick3DRuntimeRender/private/qssgrendercamera_p.h>
12#include <QtQuick3DRuntimeRender/private/qssgrenderlight_p.h>
13#include <QtQuick3DRuntimeRender/private/qssgrenderimage_p.h>
14#include <QtQuick3DRuntimeRender/private/qssgrenderbuffermanager_p.h>
15#include "../qssgrendercontextcore.h"
16#include <QtQuick3DRuntimeRender/private/qssgrendereffect_p.h>
17#include <QtQuick3DRuntimeRender/private/qssgrhicustommaterialsystem_p.h>
18#include <QtQuick3DRuntimeRender/private/qssgrendershadercodegenerator_p.h>
19#include <QtQuick3DRuntimeRender/private/qssgrenderdefaultmaterialshadergenerator_p.h>
20#include <QtQuick3DRuntimeRender/private/qssgperframeallocator_p.h>
21#include <QtQuick3DRuntimeRender/private/qssgrhiquadrenderer_p.h>
22#include <QtQuick3DRuntimeRender/private/qssgrendertexturedata_p.h>
23#include <QtQuick3DRuntimeRender/private/qssglayerrenderdata_p.h>
24#include <QtQuick3DRuntimeRender/private/qssgrhiparticles_p.h>
25#include <QtQuick3DRuntimeRender/private/qssgvertexpipelineimpl_p.h>
26#include "../qssgshadermapkey_p.h"
27#include "../qssgrenderpickresult_p.h"
28#include "../graphobjects/qssgrenderroot_p.h"
30#include <QtQuick3DUtils/private/qquick3dprofiler_p.h>
31#include <QtQuick3DUtils/private/qssgdataref_p.h>
32#include <QtQuick3DUtils/private/qssgutils_p.h>
33#include <QtQuick3DUtils/private/qssgassert_p.h>
34#include <QtQuick3DUtils/private/qssgfrustum_p.h>
35#include <qtquick3d_tracepoints_p.h>
37#include <QtQuick/private/qsgcontext_p.h>
38#include <QtQuick/private/qsgrenderer_p.h>
40#include <QtCore/QMutexLocker>
41#include <QtCore/QBitArray>
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
76struct QSSGRenderableImage;
77class QSSGSubsetRenderable;
79void QSSGRenderer::releaseCachedResources()
81 m_rhiQuadRenderer.reset();
82 m_rhiCubeRenderer.reset();
85void QSSGRenderer::registerItem2DData(QSSGRenderItem2DData &data)
88 for (
const auto *item2DData : m_item2DDatas) {
89 if (item2DData == &data)
93 m_item2DDatas.push_back(&data);
96void QSSGRenderer::unregisterItem2DData(QSSGRenderItem2DData &data)
98 const auto foundIt = std::find(m_item2DDatas.begin(), m_item2DDatas.end(), &data);
99 if (foundIt != m_item2DDatas.end())
100 m_item2DDatas.erase(foundIt);
103void QSSGRenderer::releaseItem2DData(
const QSSGRenderItem2D &item2D)
105 for (
auto *item2DData : m_item2DDatas)
106 item2DData->releaseRenderData(item2D);
109QSSGRenderer::QSSGRenderer() =
default;
111QSSGRenderer::~QSSGRenderer()
113 m_contextInterface =
nullptr;
114 releaseCachedResources();
117void QSSGRenderer::cleanupUnreferencedBuffers(QSSGRenderLayer *inLayer)
120 m_contextInterface->bufferManager()->cleanupUnreferencedBuffers(m_frameCount, inLayer);
123void QSSGRenderer::resetResourceCounters(QSSGRenderLayer *inLayer)
125 m_contextInterface->bufferManager()->resetUsageCounters(m_frameCount, inLayer);
128bool QSSGRenderer::prepareLayerForRender(QSSGRenderLayer &inLayer)
130 QSSGLayerRenderData *theRenderData = getOrCreateLayerRenderData(inLayer);
131 Q_ASSERT(theRenderData);
135 Q_ASSERT(inLayer.rootNode);
136 if (inLayer.rootNode->isDirty(QSSGRenderRoot::DirtyFlag::TreeDirty))
137 inLayer.rootNode->reindex();
139 beginLayerRender(*theRenderData);
140 theRenderData->resetForFrame();
141 theRenderData->prepareForRender();
143 return theRenderData->layerPrepResult.getFlags().wasDirty();
147void QSSGRenderer::rhiPrepare(QSSGRenderLayer &inLayer)
149 QSSGLayerRenderData *theRenderData = getOrCreateLayerRenderData(inLayer);
150 QSSG_ASSERT(theRenderData && !theRenderData->renderedCameras.isEmpty(),
return);
152 const auto layerPrepResult = theRenderData->layerPrepResult;
153 if (layerPrepResult.isLayerVisible()) {
155 QSSGRhiContext *rhiCtx = contextInterface()->rhiContext().get();
156 QSSG_ASSERT(rhiCtx->isValid() && rhiCtx->rhi()->isRecordingFrame(),
return);
157 beginLayerRender(*theRenderData);
158 theRenderData->maybeProcessLightmapBaking();
163 const auto &activePasses = theRenderData->activePasses;
164 for (
const auto &pass : activePasses) {
165 pass->renderPrep(*
this, *theRenderData);
166 if (pass->passType() == QSSGRenderPass::Type::Standalone)
167 pass->renderPass(*
this);
175void QSSGRenderer::rhiRender(QSSGRenderLayer &inLayer)
177 QSSGLayerRenderData *theRenderData = getOrCreateLayerRenderData(inLayer);
178 QSSG_ASSERT(theRenderData && !theRenderData->renderedCameras.isEmpty(),
return);
179 if (theRenderData->layerPrepResult.isLayerVisible()) {
180 beginLayerRender(*theRenderData);
181 const auto &activePasses = theRenderData->activePasses;
182 for (
const auto &pass : activePasses) {
183 if (pass->passType() == QSSGRenderPass::Type::Main || pass->passType() == QSSGRenderPass::Type::Extension)
184 pass->renderPass(*
this);
190template<
typename Container>
193 const auto &rhiCtx = rci.rhiContext();
194 if (!rhiCtx->isValid())
197 const auto &bufferManager = rci.bufferManager();
199 for (
const auto &resource : resources) {
200 if (resource->type == QSSGRenderGraphObject::Type::Geometry) {
201 auto geometry =
static_cast<QSSGRenderGeometry*>(resource);
202 bufferManager->releaseGeometry(geometry);
203 }
else if (resource->type == QSSGRenderGraphObject::Type::Model) {
204 auto model =
static_cast<QSSGRenderModel*>(resource);
205 QSSGRhiContextPrivate::get(rhiCtx.get())->cleanupDrawCallData(model);
206 delete model->particleBuffer;
207 }
else if (resource->type == QSSGRenderGraphObject::Type::TextureData || resource->type == QSSGRenderGraphObject::Type::Skin) {
208 static_assert(std::is_base_of_v<QSSGRenderTextureData, QSSGRenderSkin>,
"QSSGRenderSkin is expected to be a QSSGRenderTextureData type!");
209 auto textureData =
static_cast<QSSGRenderTextureData *>(resource);
210 bufferManager->releaseTextureData(textureData);
211 }
else if (resource->type == QSSGRenderGraphObject::Type::RenderExtension) {
212 auto *rext =
static_cast<QSSGRenderExtension *>(resource);
213 bufferManager->releaseExtensionResult(*rext);
214 }
else if (resource->type == QSSGRenderGraphObject::Type::ModelInstance) {
215 auto *rhiCtxD = QSSGRhiContextPrivate::get(rhiCtx.get());
216 auto *table =
static_cast<QSSGRenderInstanceTable *>(resource);
217 rhiCtxD->releaseInstanceBuffer(table);
218 }
else if (resource->type == QSSGRenderGraphObject::Type::Item2D) {
219 auto *item2D =
static_cast<QSSGRenderItem2D *>(resource);
220 rci.renderer()->releaseItem2DData(*item2D);
221 }
else if (resource->type == QSSGRenderGraphObject::Type::RenderPass) {
222 auto *userPass =
static_cast<QSSGRenderUserPass *>(resource);
223 bufferManager->releaseUserRenderPass(*userPass);
232void QSSGRenderer::cleanupResources(QList<QSSGRenderGraphObject *> &resources)
234 cleanupResourcesImpl(*m_contextInterface, resources);
238void QSSGRenderer::cleanupResources(QSet<QSSGRenderGraphObject *> &resources)
240 cleanupResourcesImpl(*m_contextInterface, resources);
244QSSGLayerRenderData *QSSGRenderer::getOrCreateLayerRenderData(QSSGRenderLayer &layer)
246 if (layer.renderData ==
nullptr)
247 layer.renderData =
new QSSGLayerRenderData(layer, *
this);
249 return layer.renderData;
252void QSSGRenderer::addMaterialDirtyClear(QSSGRenderGraphObject *material)
254 m_materialClearDirty.insert(material);
260QSSGRhiShaderPipelinePtr QSSGRendererPrivate::generateRhiShaderPipelineImpl(QSSGSubsetRenderable &renderable,
261 QSSGShaderLibraryManager &shaderLibraryManager,
262 QSSGShaderCache &shaderCache,
263 QSSGProgramGenerator &shaderProgramGenerator,
264 const QSSGShaderDefaultMaterialKeyProperties &shaderKeyProperties,
265 const QSSGShaderFeatures &featureSet,
266 const QSSGUserShaderAugmentation &shaderAugmentation,
267 QByteArray &shaderString)
269 shaderString = rendererLogPrefix();
270 QSSGShaderDefaultMaterialKey theKey(renderable.shaderDescription);
276 theKey.toString(shaderString, shaderKeyProperties);
282 if (
const auto &maybePipeline = shaderCache.tryGetRhiShaderPipeline(shaderString, featureSet))
283 return maybePipeline;
286 const QByteArray qsbcKey = QQsbCollection::EntryDesc::generateSha(shaderString, QQsbCollection::toFeatureSet(featureSet));
287 const QQsbCollection::EntryMap &pregenEntries = shaderLibraryManager.m_preGeneratedShaderEntries;
288 if (!pregenEntries.isEmpty()) {
289 const auto foundIt = pregenEntries.constFind(QQsbCollection::Entry(qsbcKey));
290 if (foundIt != pregenEntries.cend())
291 return shaderCache.newPipelineFromPregenerated(shaderString, featureSet, *foundIt, renderable.material);
295 if (
const auto &maybePipeline = shaderCache.tryNewPipelineFromPersistentCache(qsbcKey, shaderString, featureSet))
296 return maybePipeline;
300 const auto &material =
static_cast<
const QSSGRenderDefaultMaterial &>(renderable.getMaterial());
301 QSSGMaterialVertexPipeline vertexPipeline(shaderProgramGenerator,
305 return QSSGMaterialShaderGenerator::generateMaterialRhiShader(rendererLogPrefix(),
307 renderable.shaderDescription,
311 shaderLibraryManager,
316QSSGRhiShaderPipelinePtr QSSGRendererPrivate::generateRhiShaderPipeline(QSSGRenderer &renderer,
317 QSSGSubsetRenderable &inRenderable,
318 const QSSGShaderFeatures &inFeatureSet,
319 const QSSGUserShaderAugmentation &shaderAugmentation = {})
321 auto *currentLayer = renderer.m_currentLayer;
322 auto &generatedShaderString = currentLayer->generatedShaderString;
323 const auto &m_contextInterface = renderer.m_contextInterface;
324 const auto &theCache = m_contextInterface->shaderCache();
325 const auto &shaderProgramGenerator = m_contextInterface->shaderProgramGenerator();
326 const auto &shaderLibraryManager = m_contextInterface->shaderLibraryManager();
327 return QSSGRendererPrivate::generateRhiShaderPipelineImpl(inRenderable, *shaderLibraryManager, *theCache, *shaderProgramGenerator, currentLayer->defaultMaterialShaderKeyProperties, inFeatureSet, shaderAugmentation, generatedShaderString);
330void QSSGRenderer::beginFrame(QSSGRenderLayer &layer,
bool allowRecursion)
332 const bool executeBeginFrame = !(allowRecursion && (m_activeFrameRef++ != 0));
333 if (executeBeginFrame) {
334 m_contextInterface->perFrameAllocator()->reset();
335 QSSGRHICTX_STAT(m_contextInterface->rhiContext().get(), start(&layer));
336 resetResourceCounters(&layer);
340bool QSSGRenderer::endFrame(QSSGRenderLayer &layer,
bool allowRecursion)
342 const bool executeEndFrame = !(allowRecursion && (--m_activeFrameRef != 0));
343 if (executeEndFrame) {
344 cleanupUnreferencedBuffers(&layer);
347 for (
auto *matObj : std::as_const(m_materialClearDirty)) {
348 if (matObj->type == QSSGRenderGraphObject::Type::CustomMaterial) {
349 static_cast<QSSGRenderCustomMaterial *>(matObj)->clearDirty();
350 }
else if (matObj->type == QSSGRenderGraphObject::Type::DefaultMaterial ||
351 matObj->type == QSSGRenderGraphObject::Type::PrincipledMaterial ||
352 matObj->type == QSSGRenderGraphObject::Type::SpecularGlossyMaterial) {
353 static_cast<QSSGRenderDefaultMaterial *>(matObj)->clearDirty();
356 m_materialClearDirty.clear();
358 QSSGRHICTX_STAT(m_contextInterface->rhiContext().get(), stop(&layer));
363 return executeEndFrame;
366QSSGRendererPrivate::PickResultList QSSGRendererPrivate::syncPickAll(
const QSSGRenderContextInterface &ctx,
367 const QSSGRenderLayer &layer,
368 const QSSGRenderRay &ray)
370 const auto &bufferManager = ctx.bufferManager();
371 const bool isGlobalPickingEnabled = QSSGRendererPrivate::isGlobalPickingEnabled(*ctx.renderer());
372 PickResultList pickResults;
373 Q_ASSERT(layer.getGlobalState(QSSGRenderNode::GlobalState::Active));
374 getLayerHitObjectList(layer, *bufferManager, ray, isGlobalPickingEnabled, pickResults);
376 std::stable_sort(pickResults.begin(), pickResults.end(), [](
const QSSGRenderPickResult &lhs,
const QSSGRenderPickResult &rhs) {
377 return lhs.m_distanceSq < rhs.m_distanceSq;
382QSSGRendererPrivate::PickResultList QSSGRendererPrivate::syncPick(
const QSSGRenderContextInterface &ctx,
383 const QSSGRenderLayer &layer,
384 const QSSGRenderRay &ray,
385 QSSGRenderNode *target)
387 const auto &bufferManager = ctx.bufferManager();
388 const bool isGlobalPickingEnabled = QSSGRendererPrivate::isGlobalPickingEnabled(*ctx.renderer());
390 Q_ASSERT(layer.getGlobalState(QSSGRenderNode::GlobalState::Active));
391 PickResultList pickResults;
393 intersectRayWithSubsetRenderable(layer, *bufferManager, ray, *target, pickResults);
395 getLayerHitObjectList(layer, *bufferManager, ray, isGlobalPickingEnabled, pickResults);
397 std::stable_sort(pickResults.begin(), pickResults.end(), [](
const QSSGRenderPickResult &lhs,
const QSSGRenderPickResult &rhs) {
398 return lhs.m_distanceSq < rhs.m_distanceSq;
403using RenderableList = QVarLengthArray<
const QSSGRenderNode *>;
404static void getPickableRecursive(
const QSSGRenderNode &node, RenderableList &renderables,
bool pickEverything =
false)
406 if (QSSGRenderGraphObject::isRenderable(node.type) && (pickEverything || node.getLocalState(QSSGRenderNode::LocalState::Pickable))) {
407 renderables.push_back(&node);
410 for (
const auto &child : node.children)
411 getPickableRecursive(child, renderables, pickEverything);
414std::optional<QSSGRenderPickResult> QSSGRendererPrivate::syncPickClosestPoint(
const QSSGRenderContextInterface &ctx,
415 const QSSGRenderLayer &layer,
416 const QVector3D ¢er,
const float radiusSquared,
417 QSSGRenderNode *target)
419 const auto &bufferManager = ctx.bufferManager();
421 Q_ASSERT(layer.getGlobalState(QSSGRenderNode::GlobalState::Active));
422 std::optional<QSSGRenderPickResult> result = std::nullopt;
424 result = closestPointOnSubsetRenderable(layer, *bufferManager, center, radiusSquared, *target);
426 const bool pickEverything = QSSGRendererPrivate::isGlobalPickingEnabled(*ctx.renderer());
427 RenderableList renderables;
428 for (
const auto &childNode : layer.children)
429 getPickableRecursive(childNode, renderables, pickEverything);
430 float bestDistSquared = radiusSquared;
431 for (
const auto &childNode : renderables) {
432 const auto res = closestPointOnSubsetRenderable(layer, *bufferManager, center, bestDistSquared, *childNode);
433 if (res.has_value()) {
434 bestDistSquared = res.value().m_distanceSq;
443QSSGRendererPrivate::PickResultList QSSGRendererPrivate::syncPickSubset(
const QSSGRenderLayer &layer,
444 QSSGBufferManager &bufferManager,
445 const QSSGRenderRay &ray,
446 QVarLengthArray<QSSGRenderNode*> subset)
448 QSSGRendererPrivate::PickResultList pickResults;
449 Q_ASSERT(layer.getGlobalState(QSSGRenderNode::GlobalState::Active));
451 for (
auto target : subset)
452 intersectRayWithSubsetRenderable(layer, bufferManager, ray, *target, pickResults);
454 std::stable_sort(pickResults.begin(), pickResults.end(), [](
const QSSGRenderPickResult &lhs,
const QSSGRenderPickResult &rhs) {
455 return lhs.m_distanceSq < rhs.m_distanceSq;
460void QSSGRendererPrivate::setGlobalPickingEnabled(QSSGRenderer &renderer,
bool isEnabled)
462 renderer.m_globalPickingEnabled = isEnabled;
465void QSSGRendererPrivate::setRenderContextInterface(QSSGRenderer &renderer, QSSGRenderContextInterface *ctx)
467 renderer.m_contextInterface = ctx;
470void QSSGRendererPrivate::setSgRenderContext(QSSGRenderer &renderer, QSGRenderContext *sgRenderCtx)
472 renderer.m_qsgRenderContext = sgRenderCtx;
475QSGRenderContext *QSSGRendererPrivate::getSgRenderContext(
const QSSGRenderer &renderer)
477 return renderer.m_qsgRenderContext.data();
480const std::unique_ptr<QSSGRhiQuadRenderer> &QSSGRenderer::rhiQuadRenderer()
const
482 if (!m_rhiQuadRenderer)
483 m_rhiQuadRenderer = std::make_unique<QSSGRhiQuadRenderer>();
485 return m_rhiQuadRenderer;
488const std::unique_ptr<QSSGRhiCubeRenderer> &QSSGRenderer::rhiCubeRenderer()
const
490 if (!m_rhiCubeRenderer)
491 m_rhiCubeRenderer = std::make_unique<QSSGRhiCubeRenderer>();
493 return m_rhiCubeRenderer;
497void QSSGRenderer::beginSubLayerRender(QSSGLayerRenderData &inLayer)
499 inLayer.saveRenderState(*
this);
500 m_currentLayer =
nullptr;
503void QSSGRenderer::endSubLayerRender(QSSGLayerRenderData &inLayer)
505 inLayer.restoreRenderState(*
this);
506 m_currentLayer = &inLayer;
509void QSSGRenderer::beginLayerRender(QSSGLayerRenderData &inLayer)
511 m_currentLayer = &inLayer;
513void QSSGRenderer::endLayerRender()
515 m_currentLayer =
nullptr;
518static void dfs(
const QSSGRenderNode &node, RenderableList &renderables)
520 if (QSSGRenderGraphObject::isRenderable(node.type))
521 renderables.push_back(&node);
523 for (
const auto &child : node.children)
524 dfs(child, renderables);
527void QSSGRendererPrivate::getLayerHitObjectList(
const QSSGRenderLayer &layer,
528 QSSGBufferManager &bufferManager,
529 const QSSGRenderRay &ray,
530 bool inPickEverything,
531 PickResultList &outIntersectionResult)
533 RenderableList renderables;
534 for (
const auto &childNode : layer.children)
535 dfs(childNode, renderables);
537 for (
int idx = renderables.size() - 1; idx >= 0; --idx) {
538 const auto &pickableObject = renderables.at(idx);
539 if (inPickEverything || pickableObject->getLocalState(QSSGRenderNode::LocalState::Pickable))
540 intersectRayWithSubsetRenderable(layer, bufferManager, ray, *pickableObject, outIntersectionResult);
546static inline QVector3D multiply(
const QMatrix3x3& M,
const QVector3D& v)
549 M(0,0) * v.x() + M(0,1) * v.y() + M(0,2) * v.z(),
550 M(1,0) * v.x() + M(1,1) * v.y() + M(1,2) * v.z(),
551 M(2,0) * v.x() + M(2,1) * v.y() + M(2,2) * v.z()
556static inline bool isUniformScaleMetric(
const QMatrix3x3& G,
float& s2,
float tolerance = 1e-5f) {
557 const float gxx = G(0,0), gyy = G(1,1), gzz = G(2,2);
558 const float gxy = G(0,1), gxz = G(0,2), gyz = G(1,2);
561 s2 = (gxx + gyy + gzz) / 3.0f;
564 const float scale =
std::max({ std::fabs(gxx), std::fabs(gyy), std::fabs(gzz), 1.0f });
567 const bool offDiagOK = (std::fabs(gxy) <= tolerance * scale) &&
568 (std::fabs(gxz) <= tolerance * scale) &&
569 (std::fabs(gyz) <= tolerance * scale) &&
570 (std::fabs(G(1,0)) <= tolerance * scale) &&
571 (std::fabs(G(2,0)) <= tolerance * scale) &&
572 (std::fabs(G(2,1)) <= tolerance * scale);
574 const bool diagOK = (std::fabs(gxx - s2) <= tolerance * scale) &&
575 (std::fabs(gyy - s2) <= tolerance * scale) &&
576 (std::fabs(gzz - s2) <= tolerance * scale);
578 return offDiagOK && diagOK && (s2 >= 0.0f);
583 inline float operator()(
const QVector3D& u,
const QVector3D& v)
const {
584 return QVector3D::dotProduct(u, v);
591 inline float operator()(
const QVector3D& u,
const QVector3D& v)
const {
593 return QVector3D::dotProduct(u, multiply(G, v));
603static QVector3D closestPointOnTriangle(
const QVector3D &p,
608 float &u,
float &v,
float &w)
610 const QVector3D ab = b - a;
611 const QVector3D ac = c - a;
612 const QVector3D ap = p - a;
615 const float d1 = dot(ab, ap);
616 const float d2 = dot(ac, ap);
617 if (d1 <= 0.f && d2 <= 0.f) {
618 u = 1.0f; v = 0.0f; w = 0.0f;
623 const QVector3D bp = p - b;
624 const float d3 = dot(ab, bp);
625 const float d4 = dot(ac, bp);
626 if (d3 >= 0.f && d4 <= d3) {
627 u = 0.0f; v = 1.0f; w = 0.0f;
632 const float vc = d1 * d4 - d3 * d2;
633 if (vc <= 0.f && d1 >= 0.f && d3 <= 0.f) {
634 const float v_edge = d1 / (d1 - d3);
635 u = 1.0f - v_edge; v = v_edge; w = 0.0f;
636 return a + v_edge * ab;
640 const QVector3D cp = p - c;
641 const float d5 = dot(ab, cp);
642 const float d6 = dot(ac, cp);
643 if (d6 >= 0.f && d5 <= d6) {
644 u = 0.0f; v = 0.0f; w = 1.0f;
649 const float vb = d5 * d2 - d1 * d6;
650 if (vb <= 0.f && d2 >= 0.f && d6 <= 0.f) {
651 const float w_edge = d2 / (d2 - d6);
652 u = 1.0f - w_edge; v = 0.0f; w = w_edge;
653 return a + w_edge * ac;
657 const float va = d3 * d6 - d5 * d4;
658 if (va <= 0.f && (d4 - d3) >= 0.f && (d5 - d6) >= 0.f) {
659 const QVector3D bc = c - b;
660 const float w_edge = (d4 - d3) / ((d4 - d3) + (d5 - d6));
661 u = 0.0f; v = 1.0f - w_edge; w = w_edge;
662 return b + w_edge * bc;
666 const float denom = va + vb + vc;
669 if (
std::abs(denom) < 1e-20f) {
671 const float da = dot(ap, ap);
672 const float db = dot(bp, bp);
673 const float dc = dot(cp, cp);
674 if (da <= db && da <= dc) {
675 u = 1.0f; v = 0.0f; w = 0.0f;
679 u = 0.0f; v = 1.0f; w = 0.0f;
682 u = 0.0f; v = 0.0f; w = 1.0f;
686 const float invDenom = 1.0f / denom;
690 return a + v * ab + w * ac;
695 QMatrix4x4 globalTransform;
696 QMatrix3x3 pullbackMetric;
697 QVector3D centerLocal;
703static inline SphereData createSphereData(
const QMatrix4x4 &globalTransform,
704 const QVector3D ¢erWorld)
706 QMatrix4x4 inv = globalTransform.inverted();
709 const QVector3D centerLocal = QSSGUtils::mat44::transform(inv, centerWorld);
711 const QMatrix3x3 A = QSSGUtils::mat44::getUpper3x3(globalTransform);
712 const QMatrix3x3 G = A.transposed() * A;
714 return SphereData{ globalTransform, G, centerLocal };
718static inline float distanceSqPointTransformedAABB(
const QVector3D &localPoint,
719 const QSSGBounds3 &localAABB,
723 QVector3D closestLocal(
724 qBound(localAABB.minimum.x(), localPoint.x(), localAABB.maximum.x()),
725 qBound(localAABB.minimum.y(), localPoint.y(), localAABB.maximum.y()),
726 qBound(localAABB.minimum.z(), localPoint.z(), localAABB.maximum.z())
730 QVector3D localDiff = localPoint - closestLocal;
735 return dot(localDiff, localDiff);
738struct ClosestPointResult
741 float distSq =
std::numeric_limits<
float>::max();
742 QVector3D localPoint;
743 QVector3D scenePoint;
744 QVector3D faceNormal;
745 QVector3D sceneNormal;
748 int instanceIndex = -1;
751static void closestPointBVHLeafNode(
const SphereData &data,
752 const QSSGMeshBVHNode *node,
753 const QSSGRenderMesh *mesh,
756 ClosestPointResult &best)
758 const int begin = node->offset;
759 const int end = begin + node->count;
760 const auto &triangles = mesh->bvh->triangles();
763 float uniformScaleFactor = 1.0f;
764 const bool isUniformScale = isUniformScaleMetric(data.pullbackMetric, uniformScaleFactor);
765 const MetricDot metricDot { data.pullbackMetric };
767 for (
int i = begin; i < end; ++i) {
768 const auto &triangle = triangles[i];
771 const float triangleDistSq = distanceSqPointTransformedAABB(data.centerLocal, triangle.bounds, data.pullbackMetric);
772 if (triangleDistSq >= best.distSq)
777 QVector3D closestPoint;
779 if (isUniformScale) {
780 closestPoint = closestPointOnTriangle(data.centerLocal,
781 triangle.vertex1, triangle.vertex2, triangle.vertex3,
785 closestPoint = closestPointOnTriangle(data.centerLocal,
786 triangle.vertex1, triangle.vertex2, triangle.vertex3,
792 const QVector3D delta = data.centerLocal - closestPoint;
793 const float distSq = metricDot(delta, delta);
796 if (distSq < best.distSq) {
797 best.distSq = distSq;
798 best.localPoint = closestPoint;
799 best.scenePoint = QSSGUtils::mat44::transform(data.globalTransform, closestPoint);
800 best.subset = subset;
801 best.instanceIndex = instanceIndex;
806 best.uv = u * triangle.uvCoord1 + v * triangle.uvCoord2 + w * triangle.uvCoord3;
809 const QVector3D edge1 = triangle.vertex2 - triangle.vertex1;
810 const QVector3D edge2 = triangle.vertex3 - triangle.vertex1;
811 best.faceNormal = QVector3D::normal(edge1, edge2).normalized();
812 const QMatrix3x3 normalMatrix = data.globalTransform.normalMatrix();
813 best.sceneNormal = QSSGUtils::mat33::transform(normalMatrix, best.faceNormal);
818static void closestPointBVH(
const SphereData &data,
819 const QSSGMeshBVHNode *node,
820 const QSSGRenderMesh *mesh,
823 ClosestPointResult &best)
825 if (!node || !mesh || !mesh->bvh)
829 const float aabbDistSq = distanceSqPointTransformedAABB(data.centerLocal, node->boundingData, data.pullbackMetric);
830 if (aabbDistSq >= best.distSq)
834 if (node->count != 0) {
835 closestPointBVHLeafNode(data, node, mesh, subset, instanceIndex, best);
840 const auto *leftChild =
static_cast<
const QSSGMeshBVHNode *>(node->left);
841 const auto *rightChild =
static_cast<
const QSSGMeshBVHNode *>(node->right);
844 const float leftDistSq = leftChild
845 ? distanceSqPointTransformedAABB(data.centerLocal, leftChild->boundingData, data.pullbackMetric)
846 : std::numeric_limits<
float>::max();
847 const float rightDistSq = rightChild
848 ? distanceSqPointTransformedAABB(data.centerLocal, rightChild->boundingData, data.pullbackMetric)
849 : std::numeric_limits<
float>::max();
852 if (leftDistSq < rightDistSq) {
853 if (leftDistSq < best.distSq)
854 closestPointBVH(data, leftChild, mesh, subset, instanceIndex, best);
855 if (rightDistSq < best.distSq)
856 closestPointBVH(data, rightChild, mesh, subset, instanceIndex, best);
858 if (rightDistSq < best.distSq)
859 closestPointBVH(data, rightChild, mesh, subset, instanceIndex, best);
860 if (leftDistSq < best.distSq)
861 closestPointBVH(data, leftChild, mesh, subset, instanceIndex, best);
866std::optional<QSSGRenderPickResult>
867QSSGRendererPrivate::closestPointOnSubsetRenderable(
const QSSGRenderLayer& layer,
868 QSSGBufferManager& bufferManager,
869 const QVector3D& center,
870 const float radiusSquared,
871 const QSSGRenderNode& node)
873 if (!layer.renderData)
876 const auto *renderData = layer.renderData;
883 if (node.type != QSSGRenderGraphObject::Type::Model)
886 const auto &model =
static_cast<
const QSSGRenderModel &>(node);
894 QMutexLocker mutexLocker(bufferManager.meshUpdateMutex());
896 auto mesh = bufferManager.getMeshForPicking(model);
901 QSSGBounds3 modelBounds;
902 for (
const auto &subset : mesh->subsets)
903 modelBounds.include(subset.bounds);
905 if (modelBounds.isEmpty())
908 const bool instancing = model.instancing();
909 int instanceCount = instancing ? model.instanceTable->count() : 1;
910 const auto instanceTransforms = instancing ? renderData->getInstanceTransforms(model) : QSSGLayerRenderData::InstanceTransforms{};
912 ClosestPointResult best;
913 best.distSq = radiusSquared;
915 for (
int i = 0; i < instanceCount; ++i) {
916 int instanceIndex = 0;
917 QMatrix4x4 modelTransform;
920 modelTransform = instanceTransforms.global * model.instanceTable->getTransform(instanceIndex) * instanceTransforms.local;
922 modelTransform = renderData->getGlobalTransform(model);
924 const SphereData data = createSphereData(modelTransform, center);
926 if (distanceSqPointTransformedAABB(data.centerLocal, modelBounds, data.pullbackMetric) > best.distSq)
929 for (
int subsetIndex = 0; subsetIndex < mesh->subsets.size(); ++subsetIndex) {
930 const auto &subset = mesh->subsets[subsetIndex];
933 if (distanceSqPointTransformedAABB(data.centerLocal, subset.bounds, data.pullbackMetric) >= best.distSq)
936 if (!subset.bvhRoot.isNull()) {
937 const auto *bvhRoot =
static_cast<
const QSSGMeshBVHNode *>(subset.bvhRoot);
938 closestPointBVH(data, bvhRoot, mesh, subsetIndex, instanceIndex, best);
943 return QSSGRenderPickResult{
959void QSSGRendererPrivate::intersectRayWithSubsetRenderable(
const QSSGRenderLayer &layer,
960 QSSGBufferManager &bufferManager,
961 const QSSGRenderRay &inRay,
962 const QSSGRenderNode &node,
963 PickResultList &outIntersectionResultList)
965 if (!layer.renderData)
968 const auto *renderData = layer.renderData;
971 if (node.type == QSSGRenderGraphObject::Type::Item2D) {
972 const QSSGRenderItem2D &item2D =
static_cast<
const QSSGRenderItem2D &>(node);
973 intersectRayWithItem2D(layer, inRay, item2D, outIntersectionResultList);
977 if (node.type != QSSGRenderGraphObject::Type::Model)
980 const QSSGRenderModel &model =
static_cast<
const QSSGRenderModel &>(node);
987 QMutexLocker mutexLocker(bufferManager.meshUpdateMutex());
988 auto mesh = bufferManager.getMeshForPicking(model);
992 const auto &subMeshes = mesh->subsets;
993 QSSGBounds3 modelBounds;
994 for (
const auto &subMesh : subMeshes)
995 modelBounds.include(subMesh.bounds);
997 if (modelBounds.isEmpty())
1000 const bool instancing = model.instancing();
1001 int instanceCount = instancing ? model.instanceTable->count() : 1;
1003 const auto instanceTransforms = instancing ? renderData->getInstanceTransforms(model) : QSSGLayerRenderData::InstanceTransforms{};
1005 for (
int instanceIndex = 0; instanceIndex < instanceCount; ++instanceIndex) {
1007 QMatrix4x4 modelTransform;
1009 modelTransform = instanceTransforms.global * model.instanceTable->getTransform(instanceIndex) * instanceTransforms.local;
1011 modelTransform = renderData->getGlobalTransform(model);
1013 auto rayData = QSSGRenderRay::createRayData(modelTransform, inRay);
1015 auto hit = QSSGRenderRay::intersectWithAABBv2(rayData, modelBounds);
1018 if (!hit.intersects())
1022 float minRayLength = std::numeric_limits<
float>::max();
1023 QSSGRenderRay::IntersectionResult intersectionResult;
1024 QVector<QSSGRenderRay::IntersectionResult> results;
1027 int resultSubset = 0;
1028 for (
const auto &subMesh : subMeshes) {
1029 QSSGRenderRay::IntersectionResult result;
1030 if (!subMesh.bvhRoot.isNull()) {
1031 hit = QSSGRenderRay::intersectWithAABBv2(rayData, subMesh.bvhRoot->boundingData);
1032 if (hit.intersects()) {
1034 inRay.intersectWithBVH(rayData,
static_cast<
const QSSGMeshBVHNode *>(subMesh.bvhRoot), mesh, results);
1035 float subMeshMinRayLength = std::numeric_limits<
float>::max();
1036 for (
const auto &subMeshResult : std::as_const(results)) {
1037 if (subMeshResult.rayLengthSquared < subMeshMinRayLength) {
1038 result = subMeshResult;
1039 subMeshMinRayLength = result.rayLengthSquared;
1044 hit = QSSGRenderRay::intersectWithAABBv2(rayData, subMesh.bounds);
1045 if (hit.intersects())
1046 result = QSSGRenderRay::createIntersectionResult(rayData, hit);
1048 if (result.intersects && result.rayLengthSquared < minRayLength) {
1049 intersectionResult = result;
1050 minRayLength = intersectionResult.rayLengthSquared;
1051 resultSubset = subset;
1056 if (intersectionResult.intersects)
1057 outIntersectionResultList.push_back(QSSGRenderPickResult { &model,
1058 intersectionResult.rayLengthSquared,
1059 intersectionResult.relXY,
1060 intersectionResult.scenePosition,
1061 intersectionResult.localPosition,
1062 intersectionResult.faceNormal,
1063 intersectionResult.sceneFaceNormal,
1070void QSSGRendererPrivate::intersectRayWithItem2D(
const QSSGRenderLayer &layer,
1071 const QSSGRenderRay &inRay,
1072 const QSSGRenderItem2D &item2D,
1073 PickResultList &outIntersectionResultList)
1075 const auto &globalTransform = layer.renderData->getGlobalTransform(item2D);
1078 const QVector3D p0 = QSSGRenderNode::getGlobalPos(globalTransform);
1079 const QVector3D normal = -QSSGRenderNode::getDirection(globalTransform);
1081 const float d = QVector3D::dotProduct(inRay.direction, normal);
1082 float intersectionTime = 0;
1084 const QVector3D p0l0 = p0 - inRay.origin;
1085 intersectionTime = QVector3D::dotProduct(p0l0, normal) / d;
1086 if (intersectionTime >= 0) {
1088 const QVector3D intersectionPoint = inRay.origin + inRay.direction * intersectionTime;
1089 const QMatrix4x4 inverseGlobalTransform = globalTransform.inverted();
1090 const QVector3D localIntersectionPoint = QSSGUtils::mat44::transform(inverseGlobalTransform, intersectionPoint);
1091 const QVector2D qmlCoordinate(localIntersectionPoint.x(), -localIntersectionPoint.y());
1092 outIntersectionResultList.push_back(QSSGRenderPickResult { &item2D,
1093 intersectionTime * intersectionTime,
1096 localIntersectionPoint,
1097 -normal, -normal });
1102QSSGRhiShaderPipelinePtr QSSGRendererPrivate::getShaderPipelineForDefaultMaterial(QSSGRenderer &renderer,
1103 QSSGSubsetRenderable &inRenderable,
1104 const QSSGShaderFeatures &inFeatureSet,
1105 const QSSGUserShaderAugmentation &shaderAugmentation)
1107 auto *m_currentLayer = renderer.m_currentLayer;
1108 QSSG_ASSERT(m_currentLayer !=
nullptr,
return {});
1117 auto &shaderMap = m_currentLayer->shaderMap;
1119 QElapsedTimer timer;
1122 QSSGRhiShaderPipelinePtr shaderPipeline;
1127 QByteArray name = shaderAugmentation.preamble + shaderAugmentation.body;
1128 for (
const auto &def : shaderAugmentation.defines)
1129 name.append(def.name).append(def.value);
1130 QSSGShaderMapKey skey = QSSGShaderMapKey(name,
1132 inRenderable.shaderDescription);
1133 auto it = shaderMap.find(skey);
1134 if (it == shaderMap.end()) {
1135 Q_TRACE_SCOPE(QSSG_generateShader);
1136 Q_QUICK3D_PROFILE_START(QQuick3DProfiler::Quick3DGenerateShader);
1137 shaderPipeline = QSSGRendererPrivate::generateRhiShaderPipeline(renderer, inRenderable, inFeatureSet, shaderAugmentation);
1138 Q_QUICK3D_PROFILE_END_WITH_ID(QQuick3DProfiler::Quick3DGenerateShader, 0, inRenderable.material.profilingId);
1142 shaderMap.insert(skey, shaderPipeline);
1144 shaderPipeline = it.value();
1147 if (shaderPipeline !=
nullptr) {
1148 if (m_currentLayer && !m_currentLayer->renderedCameras.isEmpty())
1149 m_currentLayer->ensureCachedCameraDatas();
1152 const auto &rhiContext = renderer.m_contextInterface->rhiContext();
1153 QSSGRhiContextStats::get(*rhiContext).registerMaterialShaderGenerationTime(timer.elapsed());
1155 return shaderPipeline;
1158QList<
const QSSGRenderNode *> QSSGRendererPrivate::syncPickInFrustum(
const QSSGRenderContextInterface &ctx,
1159 const QSSGRenderLayer &layer,
1160 const QSSGFrustum &frustum)
1162 if (!layer.renderData)
1165 auto &bufferManager = ctx.bufferManager();
1166 const bool pickEverything = QSSGRendererPrivate::isGlobalPickingEnabled(*ctx.renderer());
1168 RenderableList nodes;
1169 for (
const auto &child : layer.children)
1170 getPickableRecursive(child, nodes, pickEverything);
1172 QList<
const QSSGRenderNode *> ret;
1173 for (
const auto node : nodes) {
1174 auto aabb = node->getBounds(*bufferManager);
1175 if (!aabb.isEmpty()) {
1176 const auto &transform = layer.renderData->getGlobalTransform(*node);
1177 aabb.transform(transform);
1178 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()