125bool QSSGRenderer::prepareLayerForRender(QSSGRenderLayer &inLayer)
127 QSSGLayerRenderData *theRenderData = getOrCreateLayerRenderData(inLayer);
128 Q_ASSERT(theRenderData);
132 Q_ASSERT(inLayer.rootNode);
133 if (inLayer.rootNode->isDirty(QSSGRenderRoot::DirtyFlag::TreeDirty))
134 inLayer.rootNode->reindex();
136 beginLayerRender(*theRenderData);
137 theRenderData->resetForFrame();
138 theRenderData->prepareForRender();
140 return theRenderData->layerPrepResult.getFlags().wasDirty();
144void QSSGRenderer::rhiPrepare(QSSGRenderLayer &inLayer)
146 QSSGLayerRenderData *theRenderData = getOrCreateLayerRenderData(inLayer);
147 QSSG_ASSERT(theRenderData && !theRenderData->renderedCameras.isEmpty(),
return);
149 const auto layerPrepResult = theRenderData->layerPrepResult;
150 if (layerPrepResult.isLayerVisible()) {
152 QSSGRhiContext *rhiCtx = contextInterface()->rhiContext().get();
153 QSSG_ASSERT(rhiCtx->isValid() && rhiCtx->rhi()->isRecordingFrame(),
return);
154 beginLayerRender(*theRenderData);
155 theRenderData->maybeProcessLightmapBaking();
160 const auto &activePasses = theRenderData->activePasses;
161 for (
const auto &pass : activePasses) {
162 pass->renderPrep(*
this, *theRenderData);
163 if (pass->passType() == QSSGRenderPass::Type::Standalone)
164 pass->renderPass(*
this);
190 const auto &rhiCtx = rci.rhiContext();
191 if (!rhiCtx->isValid())
194 const auto &bufferManager = rci.bufferManager();
196 for (
const auto &resource : resources) {
197 if (resource->type == QSSGRenderGraphObject::Type::Geometry) {
198 auto geometry =
static_cast<QSSGRenderGeometry*>(resource);
199 bufferManager->releaseGeometry(geometry);
200 }
else if (resource->type == QSSGRenderGraphObject::Type::Model) {
201 auto model =
static_cast<QSSGRenderModel*>(resource);
202 QSSGRhiContextPrivate::get(rhiCtx.get())->cleanupDrawCallData(model);
203 delete model->particleBuffer;
204 }
else if (resource->type == QSSGRenderGraphObject::Type::TextureData || resource->type == QSSGRenderGraphObject::Type::Skin) {
205 static_assert(std::is_base_of_v<QSSGRenderTextureData, QSSGRenderSkin>,
"QSSGRenderSkin is expected to be a QSSGRenderTextureData type!");
206 auto textureData =
static_cast<QSSGRenderTextureData *>(resource);
207 bufferManager->releaseTextureData(textureData);
208 }
else if (resource->type == QSSGRenderGraphObject::Type::RenderExtension) {
209 auto *rext =
static_cast<QSSGRenderExtension *>(resource);
210 bufferManager->releaseExtensionResult(*rext);
211 }
else if (resource->type == QSSGRenderGraphObject::Type::ModelInstance) {
212 auto *rhiCtxD = QSSGRhiContextPrivate::get(rhiCtx.get());
213 auto *table =
static_cast<QSSGRenderInstanceTable *>(resource);
214 rhiCtxD->releaseInstanceBuffer(table);
215 }
else if (resource->type == QSSGRenderGraphObject::Type::Item2D) {
216 auto *item2D =
static_cast<QSSGRenderItem2D *>(resource);
217 rci.renderer()->releaseItem2DData(*item2D);
254QSSGRhiShaderPipelinePtr QSSGRendererPrivate::generateRhiShaderPipelineImpl(QSSGSubsetRenderable &renderable,
255 QSSGShaderLibraryManager &shaderLibraryManager,
256 QSSGShaderCache &shaderCache,
257 QSSGProgramGenerator &shaderProgramGenerator,
258 const QSSGShaderDefaultMaterialKeyProperties &shaderKeyProperties,
259 const QSSGShaderFeatures &featureSet,
260 QByteArray &shaderString)
262 shaderString = logPrefix();
263 QSSGShaderDefaultMaterialKey theKey(renderable.shaderDescription);
269 theKey.toString(shaderString, shaderKeyProperties);
275 if (
const auto &maybePipeline = shaderCache.tryGetRhiShaderPipeline(shaderString, featureSet))
276 return maybePipeline;
279 const QByteArray qsbcKey = QQsbCollection::EntryDesc::generateSha(shaderString, QQsbCollection::toFeatureSet(featureSet));
280 const QQsbCollection::EntryMap &pregenEntries = shaderLibraryManager.m_preGeneratedShaderEntries;
281 if (!pregenEntries.isEmpty()) {
282 const auto foundIt = pregenEntries.constFind(QQsbCollection::Entry(qsbcKey));
283 if (foundIt != pregenEntries.cend())
284 return shaderCache.newPipelineFromPregenerated(shaderString, featureSet, *foundIt, renderable.material);
288 if (
const auto &maybePipeline = shaderCache.tryNewPipelineFromPersistentCache(qsbcKey, shaderString, featureSet))
289 return maybePipeline;
293 const auto &material =
static_cast<
const QSSGRenderDefaultMaterial &>(renderable.getMaterial());
294 QSSGMaterialVertexPipeline vertexPipeline(shaderProgramGenerator,
298 return QSSGMaterialShaderGenerator::generateMaterialRhiShader(logPrefix(),
300 renderable.shaderDescription,
304 renderable.firstImage,
305 shaderLibraryManager,
309QSSGRhiShaderPipelinePtr QSSGRendererPrivate::generateRhiShaderPipeline(QSSGRenderer &renderer,
310 QSSGSubsetRenderable &inRenderable,
311 const QSSGShaderFeatures &inFeatureSet)
313 auto *currentLayer = renderer.m_currentLayer;
314 auto &generatedShaderString = currentLayer->generatedShaderString;
315 const auto &m_contextInterface = renderer.m_contextInterface;
316 const auto &theCache = m_contextInterface->shaderCache();
317 const auto &shaderProgramGenerator = m_contextInterface->shaderProgramGenerator();
318 const auto &shaderLibraryManager = m_contextInterface->shaderLibraryManager();
319 return QSSGRendererPrivate::generateRhiShaderPipelineImpl(inRenderable, *shaderLibraryManager, *theCache, *shaderProgramGenerator, currentLayer->defaultMaterialShaderKeyProperties, inFeatureSet, generatedShaderString);
332bool QSSGRenderer::endFrame(QSSGRenderLayer &layer,
bool allowRecursion)
334 const bool executeEndFrame = !(allowRecursion && (--m_activeFrameRef != 0));
335 if (executeEndFrame) {
336 cleanupUnreferencedBuffers(&layer);
339 for (
auto *matObj : std::as_const(m_materialClearDirty)) {
340 if (matObj->type == QSSGRenderGraphObject::Type::CustomMaterial) {
341 static_cast<QSSGRenderCustomMaterial *>(matObj)->clearDirty();
342 }
else if (matObj->type == QSSGRenderGraphObject::Type::DefaultMaterial ||
343 matObj->type == QSSGRenderGraphObject::Type::PrincipledMaterial ||
344 matObj->type == QSSGRenderGraphObject::Type::SpecularGlossyMaterial) {
345 static_cast<QSSGRenderDefaultMaterial *>(matObj)->clearDirty();
348 m_materialClearDirty.clear();
350 QSSGRHICTX_STAT(m_contextInterface->rhiContext().get(), stop(&layer));
355 return executeEndFrame;
358QSSGRendererPrivate::PickResultList QSSGRendererPrivate::syncPickAll(
const QSSGRenderContextInterface &ctx,
359 const QSSGRenderLayer &layer,
360 const QSSGRenderRay &ray)
362 const auto &bufferManager = ctx.bufferManager();
363 const bool isGlobalPickingEnabled = QSSGRendererPrivate::isGlobalPickingEnabled(*ctx.renderer());
364 PickResultList pickResults;
365 Q_ASSERT(layer.getGlobalState(QSSGRenderNode::GlobalState::Active));
366 getLayerHitObjectList(layer, *bufferManager, ray, isGlobalPickingEnabled, pickResults);
368 std::stable_sort(pickResults.begin(), pickResults.end(), [](
const QSSGRenderPickResult &lhs,
const QSSGRenderPickResult &rhs) {
369 return lhs.m_distanceSq < rhs.m_distanceSq;
374QSSGRendererPrivate::PickResultList QSSGRendererPrivate::syncPick(
const QSSGRenderContextInterface &ctx,
375 const QSSGRenderLayer &layer,
376 const QSSGRenderRay &ray,
377 QSSGRenderNode *target)
379 const auto &bufferManager = ctx.bufferManager();
380 const bool isGlobalPickingEnabled = QSSGRendererPrivate::isGlobalPickingEnabled(*ctx.renderer());
382 Q_ASSERT(layer.getGlobalState(QSSGRenderNode::GlobalState::Active));
383 PickResultList pickResults;
385 intersectRayWithSubsetRenderable(layer, *bufferManager, ray, *target, pickResults);
387 getLayerHitObjectList(layer, *bufferManager, ray, isGlobalPickingEnabled, pickResults);
389 std::stable_sort(pickResults.begin(), pickResults.end(), [](
const QSSGRenderPickResult &lhs,
const QSSGRenderPickResult &rhs) {
390 return lhs.m_distanceSq < rhs.m_distanceSq;
395QSSGRendererPrivate::PickResultList QSSGRendererPrivate::syncPickSubset(
const QSSGRenderLayer &layer,
396 QSSGBufferManager &bufferManager,
397 const QSSGRenderRay &ray,
398 QVarLengthArray<QSSGRenderNode*> subset)
400 QSSGRendererPrivate::PickResultList pickResults;
401 Q_ASSERT(layer.getGlobalState(QSSGRenderNode::GlobalState::Active));
403 for (
auto target : subset)
404 intersectRayWithSubsetRenderable(layer, bufferManager, ray, *target, pickResults);
406 std::stable_sort(pickResults.begin(), pickResults.end(), [](
const QSSGRenderPickResult &lhs,
const QSSGRenderPickResult &rhs) {
407 return lhs.m_distanceSq < rhs.m_distanceSq;
480void QSSGRendererPrivate::getLayerHitObjectList(
const QSSGRenderLayer &layer,
481 QSSGBufferManager &bufferManager,
482 const QSSGRenderRay &ray,
483 bool inPickEverything,
484 PickResultList &outIntersectionResult)
486 RenderableList renderables;
487 for (
const auto &childNode : layer.children)
488 dfs(childNode, renderables);
490 for (
int idx = renderables.size() - 1; idx >= 0; --idx) {
491 const auto &pickableObject = renderables.at(idx);
492 if (inPickEverything || pickableObject->getLocalState(QSSGRenderNode::LocalState::Pickable))
493 intersectRayWithSubsetRenderable(layer, bufferManager, ray, *pickableObject, outIntersectionResult);
497void QSSGRendererPrivate::intersectRayWithSubsetRenderable(
const QSSGRenderLayer &layer,
498 QSSGBufferManager &bufferManager,
499 const QSSGRenderRay &inRay,
500 const QSSGRenderNode &node,
501 PickResultList &outIntersectionResultList)
503 if (!layer.renderData)
506 const auto *renderData = layer.renderData;
509 if (node.type == QSSGRenderGraphObject::Type::Item2D) {
510 const QSSGRenderItem2D &item2D =
static_cast<
const QSSGRenderItem2D &>(node);
511 intersectRayWithItem2D(layer, inRay, item2D, outIntersectionResultList);
515 if (node.type != QSSGRenderGraphObject::Type::Model)
518 const QSSGRenderModel &model =
static_cast<
const QSSGRenderModel &>(node);
525 QMutexLocker mutexLocker(bufferManager.meshUpdateMutex());
526 auto mesh = bufferManager.getMeshForPicking(model);
530 const auto &subMeshes = mesh->subsets;
531 QSSGBounds3 modelBounds;
532 for (
const auto &subMesh : subMeshes)
533 modelBounds.include(subMesh.bounds);
535 if (modelBounds.isEmpty())
538 const bool instancing = model.instancing();
539 int instanceCount = instancing ? model.instanceTable->count() : 1;
541 for (
int instanceIndex = 0; instanceIndex < instanceCount; ++instanceIndex) {
543 QMatrix4x4 modelTransform;
545 const auto &[localInstanceTransform, globalInstanceTransform] = renderData->getInstanceTransforms(model);
546 modelTransform = globalInstanceTransform * model.instanceTable->getTransform(instanceIndex) * localInstanceTransform;
548 modelTransform = renderData->getGlobalTransform(model);
550 auto rayData = QSSGRenderRay::createRayData(modelTransform, inRay);
552 auto hit = QSSGRenderRay::intersectWithAABBv2(rayData, modelBounds);
555 if (!hit.intersects())
559 float minRayLength = std::numeric_limits<
float>::max();
560 QSSGRenderRay::IntersectionResult intersectionResult;
561 QVector<QSSGRenderRay::IntersectionResult> results;
564 int resultSubset = 0;
565 for (
const auto &subMesh : subMeshes) {
566 QSSGRenderRay::IntersectionResult result;
567 if (!subMesh.bvhRoot.isNull()) {
568 hit = QSSGRenderRay::intersectWithAABBv2(rayData, subMesh.bvhRoot->boundingData);
569 if (hit.intersects()) {
571 inRay.intersectWithBVH(rayData,
static_cast<
const QSSGMeshBVHNode *>(subMesh.bvhRoot), mesh, results);
572 float subMeshMinRayLength = std::numeric_limits<
float>::max();
573 for (
const auto &subMeshResult : std::as_const(results)) {
574 if (subMeshResult.rayLengthSquared < subMeshMinRayLength) {
575 result = subMeshResult;
576 subMeshMinRayLength = result.rayLengthSquared;
581 hit = QSSGRenderRay::intersectWithAABBv2(rayData, subMesh.bounds);
582 if (hit.intersects())
583 result = QSSGRenderRay::createIntersectionResult(rayData, hit);
585 if (result.intersects && result.rayLengthSquared < minRayLength) {
586 intersectionResult = result;
587 minRayLength = intersectionResult.rayLengthSquared;
588 resultSubset = subset;
593 if (intersectionResult.intersects)
594 outIntersectionResultList.push_back(QSSGRenderPickResult { &model,
595 intersectionResult.rayLengthSquared,
596 intersectionResult.relXY,
597 intersectionResult.scenePosition,
598 intersectionResult.localPosition,
599 intersectionResult.faceNormal,
600 intersectionResult.sceneFaceNormal,
607void QSSGRendererPrivate::intersectRayWithItem2D(
const QSSGRenderLayer &layer,
608 const QSSGRenderRay &inRay,
609 const QSSGRenderItem2D &item2D,
610 PickResultList &outIntersectionResultList)
612 const auto &globalTransform = layer.renderData->getGlobalTransform(item2D);
615 const QVector3D p0 = QSSGRenderNode::getGlobalPos(globalTransform);
616 const QVector3D normal = -QSSGRenderNode::getDirection(globalTransform);
618 const float d = QVector3D::dotProduct(inRay.direction, normal);
619 float intersectionTime = 0;
621 const QVector3D p0l0 = p0 - inRay.origin;
622 intersectionTime = QVector3D::dotProduct(p0l0, normal) / d;
623 if (intersectionTime >= 0) {
625 const QVector3D intersectionPoint = inRay.origin + inRay.direction * intersectionTime;
626 const QMatrix4x4 inverseGlobalTransform = globalTransform.inverted();
627 const QVector3D localIntersectionPoint = QSSGUtils::mat44::transform(inverseGlobalTransform, intersectionPoint);
628 const QVector2D qmlCoordinate(localIntersectionPoint.x(), -localIntersectionPoint.y());
629 outIntersectionResultList.push_back(QSSGRenderPickResult { &item2D,
630 intersectionTime * intersectionTime,
633 localIntersectionPoint,
639QSSGRhiShaderPipelinePtr QSSGRendererPrivate::getShaderPipelineForDefaultMaterial(QSSGRenderer &renderer,
640 QSSGSubsetRenderable &inRenderable,
641 const QSSGShaderFeatures &inFeatureSet)
643 auto *m_currentLayer = renderer.m_currentLayer;
644 QSSG_ASSERT(m_currentLayer !=
nullptr,
return {});
653 auto &shaderMap = m_currentLayer->shaderMap;
658 QSSGRhiShaderPipelinePtr shaderPipeline;
662 QSSGShaderMapKey skey = QSSGShaderMapKey(QByteArray(),
664 inRenderable.shaderDescription);
665 auto it = shaderMap.find(skey);
666 if (it == shaderMap.end()) {
667 Q_TRACE_SCOPE(QSSG_generateShader);
668 Q_QUICK3D_PROFILE_START(QQuick3DProfiler::Quick3DGenerateShader);
669 shaderPipeline = QSSGRendererPrivate::generateRhiShaderPipeline(renderer, inRenderable, inFeatureSet);
670 Q_QUICK3D_PROFILE_END_WITH_ID(QQuick3DProfiler::Quick3DGenerateShader, 0, inRenderable.material.profilingId);
674 shaderMap.insert(skey, shaderPipeline);
676 shaderPipeline = it.value();
679 if (shaderPipeline !=
nullptr) {
680 if (m_currentLayer && !m_currentLayer->renderedCameras.isEmpty())
681 m_currentLayer->ensureCachedCameraDatas();
684 const auto &rhiContext = renderer.m_contextInterface->rhiContext();
685 QSSGRhiContextStats::get(*rhiContext).registerMaterialShaderGenerationTime(timer.elapsed());
687 return shaderPipeline;