5#include <QtQuick3DRuntimeRender/private/qssgrenderskymaterialmanager_p.h>
7#include <QtQuick3DRuntimeRender/private/qssgrenderskymaterial_p.h>
8#include <QtQuick3DRuntimeRender/private/qssglayerrenderdata_p.h>
9#include <QtQuick3DUtils/private/qssgassert_p.h>
15#include <QtCore/qmath.h>
19using namespace Qt::StringLiterals;
31 int upper = qNextPowerOfTwo(v);
32 int lower = upper >> 1;
33 return (v - lower < upper - v) ? lower : upper;
38 auto lookAt = [](
const QVector3D &eye,
const QVector3D ¢er,
const QVector3D &up) {
39 QMatrix4x4 viewMatrix;
40 viewMatrix.lookAt(eye, center, up);
44 QVarLengthArray<QMatrix4x4, 6> views;
45 views.append(lookAt(QVector3D(0.0f, 0.0f, 0.0f), QVector3D(1.0, 0.0, 0.0), QVector3D(0.0f, -1.0f, 0.0f)));
46 views.append(lookAt(QVector3D(0.0f, 0.0f, 0.0f), QVector3D(-1.0, 0.0, 0.0), QVector3D(0.0f, -1.0f, 0.0f)));
47 if (rhi->isYUpInFramebuffer()) {
48 views.append(lookAt(QVector3D(0.0f, 0.0f, 0.0f), QVector3D(0.0, 1.0, 0.0), QVector3D(0.0f, 0.0f, 1.0f)));
49 views.append(lookAt(QVector3D(0.0f, 0.0f, 0.0f), QVector3D(0.0, -1.0, 0.0), QVector3D(0.0f, 0.0f, -1.0f)));
51 views.append(lookAt(QVector3D(0.0f, 0.0f, 0.0f), QVector3D(0.0, -1.0, 0.0), QVector3D(0.0f, 0.0f, -1.0f)));
52 views.append(lookAt(QVector3D(0.0f, 0.0f, 0.0f), QVector3D(0.0, 1.0, 0.0), QVector3D(0.0f, 0.0f, 1.0f)));
54 views.append(lookAt(QVector3D(0.0f, 0.0f, 0.0f), QVector3D(0.0, 0.0, 1.0), QVector3D(0.0f, -1.0f, 0.0f)));
55 views.append(lookAt(QVector3D(0.0f, 0.0f, 0.0f), QVector3D(0.0, 0.0, -1.0), QVector3D(0.0f, -1.0f, 0.0f)));
68 const QByteArray &namePrefix,
69 QSSGSkyIblFaceTargets *outTargets,
70 bool preserveColorContents =
false)
73 const QRhiTextureRenderTarget::Flags rtFlags = preserveColorContents
74 ? QRhiTextureRenderTarget::Flags(QRhiTextureRenderTarget::PreserveColorContents)
75 : QRhiTextureRenderTarget::Flags();
76 for (
const auto face : QSSGRenderTextureCubeFaces) {
77 QRhiColorAttachment att(texture);
78 att.setLayer(quint8(face));
81 QRhiTextureRenderTargetDescription rtDesc;
82 rtDesc.setColorAttachments({ att });
83 auto renderTarget = rhi->newTextureRenderTarget(rtDesc, rtFlags);
84 renderTarget->setName(namePrefix +
"/"_ba + QSSGBaseTypeHelpers::displayName(face));
85 renderTarget->setDescription(rtDesc);
86 if (!outTargets->renderPassDesc)
87 outTargets->renderPassDesc = renderTarget->newCompatibleRenderPassDescriptor();
88 renderTarget->setRenderPassDescriptor(outTargets->renderPassDesc);
89 if (!renderTarget->create()) {
90 qWarning(
"Failed to build sky IBL env map render target");
93 outTargets->renderTargets << renderTarget;
100 const QSize &environmentMapSize,
101 const QByteArray &namePrefix,
102 QSSGSkyIblPrefilterTargets *outTargets,
103 bool preserveColorContents =
false)
105 Q_ASSERT(outTargets);
106 const bool hasMips = texture->flags().testFlag(QRhiTexture::MipMapped);
107 outTargets->mipmapCount = hasMips ? qMin(rhi->mipLevelsForSize(environmentMapSize), 6) : 1;
108 outTargets->mipLevelSizes.resize(outTargets->mipmapCount);
109 outTargets->mipRenderTargetsMap.resize(outTargets->mipmapCount);
111 const QRhiTextureRenderTarget::Flags rtFlags = preserveColorContents
112 ? QRhiTextureRenderTarget::Flags(QRhiTextureRenderTarget::PreserveColorContents)
113 : QRhiTextureRenderTarget::Flags();
115 auto cleanup = [outTargets](QRhiTextureRenderTarget *failed,
const QSSGSkyIblCubeFaceRenderTargets &partial) {
117 for (
auto *rt : partial)
119 for (
const QSSGSkyIblCubeFaceRenderTargets &rts : std::as_const(outTargets->mipRenderTargetsMap)) {
123 delete outTargets->renderPassDesc;
124 outTargets->renderPassDesc =
nullptr;
125 outTargets->mipRenderTargetsMap.clear();
126 outTargets->mipLevelSizes.clear();
127 outTargets->mipmapCount = 0;
130 for (
int mipLevel = 0; mipLevel < outTargets->mipmapCount; ++mipLevel) {
131 QSSGSkyIblCubeFaceRenderTargets renderTargets;
132 for (
const auto face : QSSGRenderTextureCubeFaces) {
133 QRhiColorAttachment att(texture);
134 att.setLayer(quint8(face));
135 att.setLevel(mipLevel);
136 QRhiTextureRenderTargetDescription rtDesc;
137 rtDesc.setColorAttachments({ att });
138 auto renderTarget = rhi->newTextureRenderTarget(rtDesc, rtFlags);
139 renderTarget->setName(namePrefix + QByteArrayLiteral(
"/m") + QByteArray::number(mipLevel)
140 + QByteArrayLiteral(
"/") + QSSGBaseTypeHelpers::displayName(face));
141 renderTarget->setDescription(rtDesc);
142 if (!outTargets->renderPassDesc)
143 outTargets->renderPassDesc = renderTarget->newCompatibleRenderPassDescriptor();
144 renderTarget->setRenderPassDescriptor(outTargets->renderPassDesc);
145 if (!renderTarget->create()) {
146 qWarning(
"Failed to build sky IBL prefilter env map render target");
147 cleanup(renderTarget, renderTargets);
150 renderTargets << renderTarget;
152 const QSize levelSize(environmentMapSize.width() *
std::pow(0.5, mipLevel),
153 environmentMapSize.height() *
std::pow(0.5, mipLevel));
154 outTargets->mipLevelSizes[mipLevel] = levelSize;
155 outTargets->mipRenderTargetsMap[mipLevel] = renderTargets;
161 QRhiCommandBuffer *cb,
162 QSSGRhiContext *context,
163 QRhiTexture *texture,
165 int mipCountExclusive,
166 const QByteArray &debugObjectName)
168 if (firstMip >= mipCountExclusive)
170 QRhiRenderPassDescriptor *rpDesc =
nullptr;
171 for (
int mipLevel = firstMip; mipLevel < mipCountExclusive; ++mipLevel) {
172 for (
const auto face : QSSGRenderTextureCubeFaces) {
173 QRhiColorAttachment att(texture);
174 att.setLayer(quint8(face));
175 att.setLevel(mipLevel);
176 QRhiTextureRenderTargetDescription rtDesc;
177 rtDesc.setColorAttachments({ att });
178 auto *rt = rhi->newTextureRenderTarget(rtDesc);
179 rt->setName(debugObjectName +
"/init/m"_ba + QByteArray::number(mipLevel) +
"/"_ba
180 + QSSGBaseTypeHelpers::displayName(face));
181 rt->setDescription(rtDesc);
183 rpDesc = rt->newCompatibleRenderPassDescriptor();
184 rt->setRenderPassDescriptor(rpDesc);
186 qWarning(
"Failed to create sky IBL init render target");
191 cb->beginPass(rt, QColor(0, 0, 0, 1), { 1.0f, 0 },
nullptr, context->commonPassFlags());
196 rpDesc->deleteLater();
200 -1.0f, -1.0f, -1.0f, -1.0f, -1.0f, 1.0f, -1.0f, 1.0f, 1.0f, -1.0f, 1.0f, 1.0f,
201 -1.0f, 1.0f, -1.0f, -1.0f, -1.0f, -1.0f,
203 -1.0f, -1.0f, -1.0f, 1.0f, 1.0f, -1.0f, 1.0f, -1.0f, -1.0f, -1.0f, -1.0f, -1.0f,
204 -1.0f, 1.0f, -1.0f, 1.0f, 1.0f, -1.0f,
206 -1.0f, -1.0f, -1.0f, 1.0f, -1.0f, -1.0f, 1.0f, -1.0f, 1.0f, -1.0f, -1.0f, -1.0f,
207 1.0f, -1.0f, 1.0f, -1.0f, -1.0f, 1.0f,
209 -1.0f, 1.0f, -1.0f, -1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, -1.0f, 1.0f, -1.0f,
210 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, -1.0f,
212 1.0f, 1.0f, -1.0f, 1.0f, 1.0f, 1.0f, 1.0f, -1.0f, 1.0f, 1.0f, -1.0f, 1.0f,
213 1.0f, -1.0f, -1.0f, 1.0f, 1.0f, -1.0f,
215 -1.0f, 1.0f, 1.0f, -1.0f, -1.0f, 1.0f, 1.0f, 1.0f, 1.0f, -1.0f, -1.0f, 1.0f,
216 1.0f, -1.0f, 1.0f, 1.0f, 1.0f, 1.0f,
218 0.0f, 1.0f, 1.0f, 1.0f, 1.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f,
220 1.0f, 1.0f, 0.0f, 0.0f, 0.0f, 1.0f, 1.0f, 1.0f, 1.0f, 0.0f, 0.0f, 0.0f,
222 1.0f, 0.0f, 1.0f, 1.0f, 0.0f, 1.0f, 1.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f,
224 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 1.0f, 0.0f, 0.0f, 1.0f, 1.0f, 1.0f,
226 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 1.0f, 1.0f, 1.0f, 1.0f, 0.0f,
228 0.0f, 0.0f, 0.0f, 1.0f, 1.0f, 0.0f, 0.0f, 1.0f, 1.0f, 1.0f, 1.0f, 0.0f,
235 dst = rhi->newBuffer(QRhiBuffer::Dynamic, QRhiBuffer::UniformBuffer, size);
236 if (!dst->create()) {
237 qWarning(
"%s", errorMessage);
245static bool ensureSrb(QRhi *rhi, QRhiShaderResourceBindings *&dst, std::initializer_list<QRhiShaderResourceBinding> bindings,
const char *errorMessage)
249 dst = rhi->newShaderResourceBindings();
250 dst->setBindings(bindings);
251 if (!dst->create()) {
252 qWarning(
"%s", errorMessage);
269 const QRhiVertexInputLayout &inputLayout,
271 const char *errorMessage)
273 auto *pipeline = rhi->newGraphicsPipeline();
274 pipeline->setCullMode(QRhiGraphicsPipeline::Front);
275 pipeline->setFrontFace(QRhiGraphicsPipeline::CCW);
276 pipeline->setDepthOp(QRhiGraphicsPipeline::LessOrEqual);
277 pipeline->setShaderStages({ *cfg
.shader->vertexStage(), *cfg
.shader->fragmentStage() });
278 pipeline->setVertexInputLayout(inputLayout);
279 pipeline->setShaderResourceBindings(cfg.srb);
280 pipeline->setRenderPassDescriptor(cfg.rpd);
281 pipeline->setFlags(QRhiGraphicsPipeline::UsesScissor);
283 QRhiGraphicsPipeline::TargetBlend addBlend;
284 addBlend.enable =
true;
285 addBlend.srcColor = QRhiGraphicsPipeline::One;
286 addBlend.dstColor = QRhiGraphicsPipeline::One;
287 addBlend.opColor = QRhiGraphicsPipeline::Add;
288 addBlend.srcAlpha = QRhiGraphicsPipeline::One;
289 addBlend.dstAlpha = QRhiGraphicsPipeline::One;
290 addBlend.opAlpha = QRhiGraphicsPipeline::Add;
291 pipeline->setTargetBlends({ addBlend });
293 if (!pipeline->create()) {
294 qWarning(
"%s", errorMessage);
303 QRhiTextureRenderTarget *rt,
305 QRhiGraphicsPipeline *pipeline,
306 QRhiShaderResourceBindings *srb,
307 const QRhiCommandBuffer::VertexInput &vbufBinding,
308 const QVector<QPair<
int, quint32>> &dynamicOffsets,
309 const QByteArray &profilerLabel,
310 const QByteArray &passDebugLabel,
311 const QColor &clearColor = QColor(0, 0, 0, 1))
313 cb->beginPass(rt, clearColor, { 1.0f, 0 },
nullptr, ctx->commonPassFlags());
315 Q_QUICK3D_PROFILE_START(QQuick3DProfiler::Quick3DRenderPass);
316 cb->setViewport(QRhiViewport(0, 0, viewport.width(), viewport.height()));
317 cb->setScissor(QRhiScissor(0, 0, viewport.width(), viewport.height()));
318 cb->setGraphicsPipeline(pipeline);
319 cb->setShaderResources(srb, dynamicOffsets.size(), dynamicOffsets.constData());
320 cb->setVertexInput(0, 1, &vbufBinding);
321 Q_QUICK3D_PROFILE_START(QQuick3DProfiler::Quick3DRenderCall);
324 Q_QUICK3D_PROFILE_END_WITH_STRING(QQuick3DProfiler::Quick3DRenderCall, 36llu | (1llu << 32), profilerLabel);
327 Q_QUICK3D_PROFILE_END_WITH_STRING(QQuick3DProfiler::Quick3DRenderPass, 0, passDebugLabel);
333struct QSSGRenderSkyMaterialManager::FrameState
336 QSize environmentMapSize;
337 int totalSamples = 0;
338 bool enableIBL =
false;
339 bool needCreateEnv =
false;
340 bool inProgressTimeSlice =
false;
341 bool envContentDirty =
false;
342 bool deferEnvRefresh =
false;
343 bool needRenderEnv =
false;
346 bool prefilteredJustCreated =
false;
347 int prefilterTotalMipCount = 0;
348 int prefilterSpecularMipCount = 0;
349 int prefilterRoughnessDenom = 1;
350 float resolution = 0.0f;
353 bool multiFrame =
false;
354 bool prefilterIsConverged =
false;
355 bool runPrefilterSlice =
false;
356 int perFrameBudget = 0;
357 int sliceSamplesThisFrame = 0;
358 int sliceSampleStart = 0;
359 int sliceSampleEnd = 0;
360 bool sliceCompletesCycle =
false;
361 bool writePrefilteredCubeThisFrame =
false;
362 bool runIrradiancePass =
false;
363 bool haveConvergedResultEntering =
false;
364 bool isFirstSlice =
false;
367 QRhiVertexInputLayout inputLayout;
369 QVarLengthArray<QMatrix4x4, 6> views;
370 int ubufElementSize = 0;
371 QRhiCommandBuffer::VertexInput vbufBinding {
nullptr, 0 };
374QSSGRenderSkyMaterialManager::QSSGRenderSkyMaterialManager(
const QSSGRenderContextInterface &inContext)
375 : m_context(inContext)
379QSSGRenderSkyMaterialManager::~QSSGRenderSkyMaterialManager()
381 releaseCachedResources();
384void QSSGRenderSkyMaterialManager::clearPrefilterCache()
386 auto releasePrefilterTargets = [](QSSGSkyIblPrefilterTargets &t) {
387 for (
const QSSGSkyIblCubeFaceRenderTargets &renderTargets : std::as_const(t.mipRenderTargetsMap)) {
388 for (QRhiTextureRenderTarget *renderTarget : renderTargets)
391 delete t.renderPassDesc;
392 t.renderPassDesc =
nullptr;
393 t.mipRenderTargetsMap.clear();
394 t.mipLevelSizes.clear();
398 auto releaseFaceTargets = [](QSSGSkyIblFaceTargets &t) {
399 for (QRhiTextureRenderTarget *rt : t.renderTargets)
401 delete t.renderPassDesc;
402 t.renderPassDesc =
nullptr;
403 t.renderTargets.clear();
406 auto safeDelete = [](
auto *&res) {
411 safeDelete(m_cache.vertexBuffer);
412 safeDelete(m_cache.uBuf);
413 safeDelete(m_cache.uBufSlice);
414 safeDelete(m_cache.uBufNormalize);
415 safeDelete(m_cache.uBufIrradiance);
417 releaseFaceTargets(m_cache.envFaceTargets);
418 releasePrefilterTargets(m_cache.prefilterTargets);
421 for (QSSGSkyIblFaceTargets &t : m_cache.accumPreserveFaceTargets)
422 releaseFaceTargets(t);
423 m_cache.accumPreserveFaceTargets.clear();
425 for (QSSGSkyIblFaceTargets &t : m_cache.accumClearFaceTargets)
426 releaseFaceTargets(t);
427 m_cache.accumClearFaceTargets.clear();
430 for (QRhiShaderResourceBindings *srb : std::as_const(m_cache.normalizeSrbs))
432 m_cache.normalizeSrbs.clear();
434 safeDelete(m_cache.sliceSrb);
435 safeDelete(m_cache.irradianceSrb);
437 safeDelete(m_cache.envMapPipeline);
438 safeDelete(m_cache.slicePipeline);
439 safeDelete(m_cache.normalizeCubePipeline);
440 safeDelete(m_cache.irradiancePipeline);
442 m_cache.environmentMapSize = { };
443 m_cache.enableIBL =
false;
444 m_cache.prefilterTotalMipCount = 0;
445 m_cache.envCubeMap =
nullptr;
446 m_cache.prefilteredCubeMap =
nullptr;
447 m_cache.prefilterAccumulators.clear();
448 m_cache.envShaderPipeline =
nullptr;
451void QSSGRenderSkyMaterialManager::releaseCachedResources()
453 clearPrefilterCache();
454 if (
auto rhiCtx = m_context.rhiContext().get(); QSSG_GUARD(rhiCtx && rhiCtx->isValid())) {
455 QSSGRhiContextPrivate *rhiCtxD = QSSGRhiContextPrivate::get(rhiCtx);
457 rhiCtxD->releaseTexture(m_envCubeMap);
458 if (m_prefilteredCubeMap)
459 rhiCtxD->releaseTexture(m_prefilteredCubeMap);
460 for (QRhiTexture *t : std::as_const(m_prefilterAccumulators))
461 rhiCtxD->releaseTexture(t);
463 m_envCubeMap =
nullptr;
464 m_prefilteredCubeMap =
nullptr;
465 m_prefilterAccumulators.clear();
466 m_skyIblTexture = { };
468 m_prefilteredMipCount = 0;
469 m_accumulatedSamples = 0;
470 m_accumIblSampleCount = 0;
471 m_haveConvergedResult =
false;
472 m_envTailMipsInitialized =
false;
473 m_prefilteredTailMipsInitialized =
false;
474 m_finalizeIblPending =
false;
477QSSGRenderImageTexture QSSGRenderSkyMaterialManager::resolve(QSSGRenderSkyMaterial *settings)
479 const auto &rhiCtx = m_context.rhiContext();
480 if (!QSSG_GUARD(rhiCtx && rhiCtx->isValid() && rhiCtx->rhi()->isRecordingFrame()))
483 if (!ensureEnvironmentMap(settings)) {
487 const bool pendingAccumulation = settings->enableIBL && m_accumulatedSamples < settings->iblSampleCount;
488 settings->wantsMoreFrames = pendingAccumulation || settings->isDirty || m_finalizeIblPending;
490 return m_skyIblTexture;
493bool QSSGRenderSkyMaterialManager::ensureEnvironmentMap(QSSGRenderSkyMaterial *inSky)
495 const auto &context = m_context.rhiContext();
496 if (!context->rhi()->isTextureFormatSupported(cTextureFormat)) {
497 static bool warningPrinted =
false;
498 if (Q_UNLIKELY(!warningPrinted)) {
499 qWarning() <<
"SkyMaterial not supported due to missing RGBA16F texture format support.";
500 warningPrinted =
true;
505 auto *cb = context->commandBuffer();
508 if (!computeFrameState(inSky, fs))
511 if (!ensureTextures(fs))
514 deriveCycleState(inSky, fs);
516 QSSGRhiShaderPipelinePtr shaderPipeline;
517 if (fs.needRenderEnv) {
518 shaderPipeline = inSky->ensurePipeline(m_context);
519 if (!shaderPipeline) {
523 QSSGRhiShaderPipeline *envShaderPipelineKey = fs.needRenderEnv ? shaderPipeline.get() : m_cache.envShaderPipeline;
525 validateAndUpdateCacheKey(fs, envShaderPipelineKey);
527 if (!ensureSharedResources(fs, cb))
530 auto *rub = context->rhi()->nextResourceUpdateBatch();
531 for (
const auto face : QSSGRenderTextureCubeFaces) {
532 rub->updateDynamicBuffer(m_cache.uBuf, quint8(face) * fs.ubufElementSize, 64, fs.mvp.constData());
533 rub->updateDynamicBuffer(m_cache.uBuf, quint8(face) * fs.ubufElementSize + 64, 64, fs.views[quint8(face)].constData());
536 if (fs.runIrradiancePass) {
537 struct IrradianceData
545 irradianceData.roughness = 0.0f;
546 irradianceData.resolution = fs.resolution;
547 irradianceData.lodBias = 0.0f;
548 irradianceData.distribution = 0;
549 irradianceData.sampleCount = qMax(
int(fs.resolution / 4.0f), 1);
550 rub->updateDynamicBuffer(m_cache.uBufIrradiance, 0,
sizeof(IrradianceData), &irradianceData);
553 if (fs.needRenderEnv) {
554 if (!renderEnvironmentCube(inSky, fs, shaderPipeline, cb, rub))
559 if (fs.enableIBL && fs.needRenderEnv)
560 runEnvironmentMipChain(fs, cb);
563 rub = context->rhi()->nextResourceUpdateBatch();
565 cb->debugMarkBegin(
"Sky IBL Pre-filtered Environment Cubemap Generation");
566 if (!runPrefilterCycle(inSky, fs, cb, rub))
569 cb->resourceUpdate(rub);
574 m_prefilteredMipCount = m_cache.prefilterTargets.mipmapCount;
575 initializeTailMips(fs, cb);
577 m_skyIblTexture.m_texture = m_prefilteredCubeMap;
578 m_skyIblTexture.m_mipmapCount = m_prefilteredMipCount;
579 m_skyIblTexture.m_flags.setLinear(
true);
580 m_skyIblTexture.m_flags.setRgbe8(
false);
584bool QSSGRenderSkyMaterialManager::computeFrameState(QSSGRenderSkyMaterial *inSky, FrameState &fs)
586 fs.enableIBL = inSky->enableIBL;
587 fs.totalSamples = qBound(1, inSky->iblSampleCount, 1024);
589 const int radianceMapSize = qBound(8, nearestPowerOfTwo(inSky->radianceMapSize), 2048);
590 fs.environmentMapSize = QSize(radianceMapSize, radianceMapSize);
592 const bool envWantsMips = fs.enableIBL;
593 const bool envHasMips = m_envCubeMap && m_envCubeMap->flags().testFlag(QRhiTexture::MipMapped);
594 fs.needCreateEnv = !m_envCubeMap || m_cubeMapSize != fs.environmentMapSize || envHasMips != envWantsMips;
596 fs.inProgressTimeSlice = fs.enableIBL && m_accumulatedSamples > 0 && m_accumulatedSamples < m_accumIblSampleCount;
597 fs.envContentDirty = inSky->isDirty && !fs.needCreateEnv;
598 fs.deferEnvRefresh = fs.envContentDirty && (fs.inProgressTimeSlice || m_finalizeIblPending);
599 fs.needRenderEnv = fs.needCreateEnv || (fs.envContentDirty && !fs.deferEnvRefresh);
603bool QSSGRenderSkyMaterialManager::ensureTextures(FrameState &fs)
605 const auto &context = m_context.rhiContext();
606 auto *rhi = context->rhi();
607 QSSGRhiContextPrivate *rhiCtxD = QSSGRhiContextPrivate::get(context.get());
610 if (fs.needCreateEnv) {
612 rhiCtxD->releaseTexture(m_envCubeMap);
613 m_envCubeMap =
nullptr;
616 if (m_prefilteredCubeMap) {
617 rhiCtxD->releaseTexture(m_prefilteredCubeMap);
618 m_prefilteredCubeMap =
nullptr;
619 m_prefilteredMipCount = 0;
621 m_envTailMipsInitialized =
false;
622 m_prefilteredTailMipsInitialized =
false;
624 QRhiTexture::Flags envFlags = QRhiTexture::RenderTarget | QRhiTexture::CubeMap;
626 envFlags |= QRhiTexture::MipMapped | QRhiTexture::UsedWithGenerateMips;
627 m_envCubeMap = rhi->newTexture(cTextureFormat, fs.environmentMapSize, 1, envFlags);
628 if (!m_envCubeMap->create()) {
629 qWarning(
"Failed to create Sky IBL environment cube map");
631 m_envCubeMap =
nullptr;
634 m_envCubeMap->setName(
"SkyMaterialLightProbe procEnvCube"_ba);
635 rhiCtxD->registerTexture(m_envCubeMap);
636 m_cubeMapSize = fs.environmentMapSize;
639 if (!m_prefilteredCubeMap) {
640 const QRhiTexture::Flags pfFlags = QRhiTexture::RenderTarget | QRhiTexture::CubeMap | QRhiTexture::MipMapped;
641 m_prefilteredCubeMap = rhi->newTexture(cTextureFormat, fs.environmentMapSize, 1, pfFlags);
642 if (!m_prefilteredCubeMap->create()) {
643 qWarning(
"Failed to create Sky IBL pre-filtered environment cube map");
644 delete m_prefilteredCubeMap;
645 m_prefilteredCubeMap =
nullptr;
648 m_prefilteredCubeMap->setName(
"SkyMaterialLightProbe"_ba);
649 rhiCtxD->registerTexture(m_prefilteredCubeMap);
650 fs.prefilteredJustCreated =
true;
651 m_haveConvergedResult =
false;
652 m_prefilteredTailMipsInitialized =
false;
655 fs.prefilterTotalMipCount = m_prefilteredCubeMap->flags().testFlag(QRhiTexture::MipMapped)
656 ? qMin(rhi->mipLevelsForSize(fs.environmentMapSize), 6)
658 fs.prefilterSpecularMipCount = fs.enableIBL ? fs.prefilterTotalMipCount - 1 : 1;
659 fs.prefilterRoughnessDenom = qMax(fs.prefilterSpecularMipCount - 1, 1);
660 fs.resolution =
float(fs.environmentMapSize.width());
664 const bool needCreateAccum = m_prefilterAccumulators.isEmpty() || m_prefilterAccumulators.size() != fs.prefilterSpecularMipCount
665 || (!m_prefilterAccumulators.isEmpty() && m_prefilterAccumulators[0]->pixelSize() != fs.environmentMapSize);
667 if (needCreateAccum) {
668 for (QRhiTexture *t : std::as_const(m_prefilterAccumulators))
669 rhiCtxD->releaseTexture(t);
670 m_prefilterAccumulators.clear();
671 m_accumulatedSamples = 0;
682 for (
int mip = 0; mip < fs.prefilterSpecularMipCount; ++mip) {
683 const QSize mipSize(qMax(1, fs.environmentMapSize.width() >> mip), qMax(1, fs.environmentMapSize.height() >> mip));
685 auto *t = rhi->newTextureArray(cTextureFormat, 6, mipSize, 1, QRhiTexture::RenderTarget);
687 qWarning(
"Failed to create Sky IBL prefilter accumulator mip %d", mip);
692 t->setName(
"SkyMaterialLightProbe procEnvPfAccum m"_ba + QByteArray::number(mip));
693 rhiCtxD->registerTexture(t);
694 m_prefilterAccumulators.append(t);
698 for (QRhiTexture *t : std::as_const(m_prefilterAccumulators))
699 rhiCtxD->releaseTexture(t);
700 m_prefilterAccumulators.clear();
711void QSSGRenderSkyMaterialManager::deriveCycleState(QSSGRenderSkyMaterial *inSky, FrameState &fs)
714 if (m_finalizeIblPending && !fs.needRenderEnv && !fs.prefilteredJustCreated && m_accumIblSampleCount == fs.totalSamples) {
715 m_finalizeIblPending =
false;
716 m_haveConvergedResult =
true;
717 fs.runPrefilterSlice =
false;
718 fs.writePrefilteredCubeThisFrame =
true;
719 fs.runIrradiancePass = fs.enableIBL;
720 fs.sliceSamplesThisFrame = 0;
721 fs.sliceSampleStart = m_accumulatedSamples;
722 fs.sliceSampleEnd = m_accumulatedSamples;
723 fs.isFirstSlice =
false;
724 fs.sliceCompletesCycle =
false;
725 fs.haveConvergedResultEntering =
false;
726 fs.multiFrame =
false;
730 fs.multiFrame = fs.enableIBL && inSky->iblSamplesPerFrame > 0 && inSky->iblSamplesPerFrame < fs.totalSamples;
734 if (!m_prefilterAccumulators.isEmpty()) {
735 const bool resetAccumulation = fs.needRenderEnv || fs.prefilteredJustCreated || m_accumIblSampleCount != fs.totalSamples;
736 if (resetAccumulation) {
737 m_accumulatedSamples = 0;
740 m_finalizeIblPending =
false;
742 m_accumIblSampleCount = fs.totalSamples;
745 fs.prefilterIsConverged = !m_prefilterAccumulators.isEmpty() && m_accumulatedSamples >= fs.totalSamples;
746 fs.runPrefilterSlice = !m_prefilterAccumulators.isEmpty() && !fs.prefilterIsConverged;
751 fs.perFrameBudget = (inSky->iblSamplesPerFrame > 0 && fs.enableIBL) ? inSky->iblSamplesPerFrame
752 : (fs.totalSamples - m_accumulatedSamples);
753 fs.sliceSamplesThisFrame = fs.runPrefilterSlice ? qMin(fs.perFrameBudget, fs.totalSamples - m_accumulatedSamples) : 0;
754 fs.sliceCompletesCycle = fs.runPrefilterSlice && (m_accumulatedSamples + fs.sliceSamplesThisFrame >= fs.totalSamples);
756 fs.sliceSampleStart = m_accumulatedSamples;
757 fs.sliceSampleEnd = fs.runPrefilterSlice ? qMin(m_accumulatedSamples + fs.perFrameBudget, fs.totalSamples) : 0;
758 fs.isFirstSlice = m_accumulatedSamples == 0;
759 fs.haveConvergedResultEntering = m_haveConvergedResult;
761 if (inSky->iblRenderFrames >= 1) {
768 fs.writePrefilteredCubeThisFrame =
false;
769 fs.runIrradiancePass =
false;
773 fs.writePrefilteredCubeThisFrame = fs.runPrefilterSlice && (fs.sliceCompletesCycle || !m_haveConvergedResult);
774 fs.runIrradiancePass = fs.enableIBL && fs.writePrefilteredCubeThisFrame;
778void QSSGRenderSkyMaterialManager::validateAndUpdateCacheKey(
const FrameState &fs, QSSGRhiShaderPipeline *envShaderPipelineKey)
780 const bool cacheValid = m_cache.environmentMapSize == fs.environmentMapSize && m_cache.enableIBL == fs.enableIBL
781 && m_cache.prefilterTotalMipCount == fs.prefilterTotalMipCount && m_cache.envCubeMap == m_envCubeMap
782 && m_cache.prefilteredCubeMap == m_prefilteredCubeMap && m_cache.prefilterAccumulators == m_prefilterAccumulators
783 && m_cache.envShaderPipeline == envShaderPipelineKey;
786 clearPrefilterCache();
787 m_cache.environmentMapSize = fs.environmentMapSize;
788 m_cache.enableIBL = fs.enableIBL;
789 m_cache.prefilterTotalMipCount = fs.prefilterTotalMipCount;
790 m_cache.envCubeMap = m_envCubeMap;
791 m_cache.prefilteredCubeMap = m_prefilteredCubeMap;
792 m_cache.prefilterAccumulators = m_prefilterAccumulators;
793 m_cache.envShaderPipeline = envShaderPipelineKey;
796bool QSSGRenderSkyMaterialManager::ensureSharedResources(FrameState &fs, QRhiCommandBuffer *cb)
798 const auto &context = m_context.rhiContext();
799 auto *rhi = context->rhi();
801 fs.inputLayout.setBindings({ { 3 *
sizeof(
float) } });
802 fs.inputLayout.setAttributes({ { 0, 0, QRhiVertexInputAttribute::Float3, 0 } });
804 fs.mvp = rhi->clipSpaceCorrMatrix();
805 fs.mvp.perspective(90.0f, 1.0f, 0.1f, 10.0f);
806 fs.views = skyIblEnvironmentMapViews(rhi);
808 fs.ubufElementSize = rhi->ubufAligned(128);
810 if (!m_cache.vertexBuffer) {
811 m_cache.vertexBuffer = rhi->newBuffer(QRhiBuffer::Immutable, QRhiBuffer::VertexBuffer,
sizeof(skyIblCubeVerts));
812 if (!m_cache.vertexBuffer->create()) {
813 qWarning(
"Failed to create sky IBL vertex buffer");
814 delete m_cache.vertexBuffer;
815 m_cache.vertexBuffer =
nullptr;
818 auto *initRub = rhi->nextResourceUpdateBatch();
819 initRub->uploadStaticBuffer(m_cache.vertexBuffer, skyIblCubeVerts);
820 cb->resourceUpdate(initRub);
822 if (!ensureDynamicUBuf(rhi, m_cache.uBuf, fs.ubufElementSize * 6,
"Failed to create sky IBL view uniform buffer"))
824 fs.vbufBinding = QRhiCommandBuffer::VertexInput(m_cache.vertexBuffer, 0);
826 if (!m_cache.prefilterTargets.renderPassDesc) {
827 if (!skyIblCreatePrefilterTargets(rhi, m_prefilteredCubeMap, fs.environmentMapSize,
"SkyMaterialLightProbe procEnvPf"_ba, &m_cache.prefilterTargets)) {
832 if (!ensureDynamicUBuf(rhi, m_cache.uBufIrradiance, rhi->ubufAligned(20),
"Failed to create sky IBL irradiance uniform buffer"))
835 const QSSGRhiSamplerDescription samplerNoMipDesc { QRhiSampler::Linear, QRhiSampler::Linear,
836 QRhiSampler::None, QRhiSampler::ClampToEdge,
837 QRhiSampler::ClampToEdge, QRhiSampler::Repeat };
838 QRhiSampler *envMapCubeNoMipSampler = context->sampler(samplerNoMipDesc);
841 m_cache.irradianceSrb,
842 { QRhiShaderResourceBinding::uniformBufferWithDynamicOffset(0, QRhiShaderResourceBinding::VertexStage, m_cache.uBuf, 128),
843 QRhiShaderResourceBinding::uniformBufferWithDynamicOffset(2, QRhiShaderResourceBinding::FragmentStage, m_cache.uBufIrradiance, 20),
844 QRhiShaderResourceBinding::sampledTexture(1, QRhiShaderResourceBinding::FragmentStage, m_envCubeMap, envMapCubeNoMipSampler) },
845 "Failed to create sky IBL irradiance SRB"))
848 if (!m_cache.irradiancePipeline) {
849 const auto &shader = m_context.shaderCache()->getBuiltInRhiShaders().getRhienvironmentmapPreFilterShader(
false);
850 m_cache.irradiancePipeline = createPrefilterPipeline(rhi,
853 m_cache.irradianceSrb,
854 m_cache.prefilterTargets.renderPassDesc,
856 "Failed to create sky IBL realtime irradiance pipeline "
858 if (!m_cache.irradiancePipeline)
864bool QSSGRenderSkyMaterialManager::renderEnvironmentCube(QSSGRenderSkyMaterial *inSky,
865 const FrameState &fs,
866 const QSSGRhiShaderPipelinePtr &shaderPipeline,
867 QRhiCommandBuffer *cb,
868 QRhiResourceUpdateBatch *rub)
870 const auto &context = m_context.rhiContext();
871 auto *rhi = context->rhi();
872 QSSGRhiContextPrivate *rhiCtxD = QSSGRhiContextPrivate::get(context.get());
874 const quint32 skyElementSize = inSky->updateUniforms(m_context, fs.mvp, fs.views);
876 if (!m_cache.envFaceTargets.renderPassDesc) {
877 if (!skyIblCreateFaceTargets(rhi, m_envCubeMap,
"SkyMaterialLightProbe procEnvCube"_ba, &m_cache.envFaceTargets))
881 QRhiShaderResourceBindings *envSrb = rhiCtxD->srb(inSky->bindings);
884 if (!m_cache.envMapPipeline) {
885 m_cache.envMapPipeline = createPrefilterPipeline(rhi,
887 { shaderPipeline.get(), envSrb, m_cache.envFaceTargets.renderPassDesc,
false },
888 "Failed to create sky IBL env map pipeline state");
889 if (!m_cache.envMapPipeline)
893 cb->resourceUpdate(rub);
895 cb->debugMarkBegin(
"Sky IBL Procedural Environment Cubemap Generation");
896 for (
const auto face : QSSGRenderTextureCubeFaces) {
897 const QVector<QPair<
int, quint32>> offsets = { { 0, quint32(skyElementSize * quint8(face)) } };
900 m_cache.envFaceTargets.renderTargets[quint8(face)],
901 fs.environmentMapSize,
902 m_cache.envMapPipeline,
906 QByteArrayLiteral(
"sky_ibl_procedural_environment_map"),
907 QSSG_RENDERPASS_NAME(
"sky_ibl_procedural_environment_map", 0, face));
911 inSky->isDirty =
false;
915void QSSGRenderSkyMaterialManager::runEnvironmentMipChain(
const FrameState &fs, QRhiCommandBuffer *cb)
917 const auto &context = m_context.rhiContext();
918 auto *rhi = context->rhi();
920 if (!m_envTailMipsInitialized) {
921 const int envFullMipCount = rhi->mipLevelsForSize(fs.environmentMapSize);
922 skyIblInitializeUnrenderedMips(rhi, cb, context.get(), m_envCubeMap, 1, envFullMipCount,
"SkyMaterialLightProbe procEnvCube"_ba);
923 m_envTailMipsInitialized =
true;
926 auto *rubMip = rhi->nextResourceUpdateBatch();
927 rubMip->generateMips(m_envCubeMap);
928 cb->resourceUpdate(rubMip);
931bool QSSGRenderSkyMaterialManager::runPrefilterCycle(QSSGRenderSkyMaterial *inSky,
932 const FrameState &fs,
933 QRhiCommandBuffer *cb,
934 QRhiResourceUpdateBatch *&rub)
937 if (!fs.runPrefilterSlice && !fs.writePrefilteredCubeThisFrame) {
938 cb->resourceUpdate(rub);
943 const auto &context = m_context.rhiContext();
944 auto *rhi = context->rhi();
956 if (m_cache.accumPreserveFaceTargets.size() != fs.prefilterSpecularMipCount) {
958 for (QSSGSkyIblFaceTargets &t : m_cache.accumPreserveFaceTargets) {
959 for (QRhiTextureRenderTarget *rt : t.renderTargets)
961 delete t.renderPassDesc;
963 m_cache.accumPreserveFaceTargets.clear();
964 m_cache.accumPreserveFaceTargets.resize(fs.prefilterSpecularMipCount);
966 for (
int mip = 0; mip < fs.prefilterSpecularMipCount; ++mip) {
967 if (!skyIblCreateFaceTargets(rhi,
968 m_prefilterAccumulators[mip],
969 "SkyMaterialLightProbe procEnvPfAccum/m"_ba + QByteArray::number(mip),
970 &m_cache.accumPreserveFaceTargets[mip],
977 if (m_cache.accumClearFaceTargets.size() != fs.prefilterSpecularMipCount) {
978 for (QSSGSkyIblFaceTargets &t : m_cache.accumClearFaceTargets) {
979 for (QRhiTextureRenderTarget *rt : t.renderTargets)
981 delete t.renderPassDesc;
983 m_cache.accumClearFaceTargets.clear();
984 m_cache.accumClearFaceTargets.resize(fs.prefilterSpecularMipCount);
986 for (
int mip = 0; mip < fs.prefilterSpecularMipCount; ++mip) {
987 if (!skyIblCreateFaceTargets(rhi,
988 m_prefilterAccumulators[mip],
989 "SkyMaterialLightProbe procEnvPfAccumClear/m"_ba + QByteArray::number(mip),
990 &m_cache.accumClearFaceTargets[mip],
997 QSSGSkyIblPrefilterTargets &prefilterTargets = m_cache.prefilterTargets;
999 constexpr int uBufSliceSize = 32;
1000 constexpr int uBufNormalizeSize = 16;
1001 const int uBufSliceElementSize = rhi->ubufAligned(uBufSliceSize);
1002 const int uBufNormalizeElementSize = rhi->ubufAligned(uBufNormalizeSize);
1003 const int uBufNormalizeEntryCount = qMax(fs.prefilterSpecularMipCount, 1) * 6;
1005 if (!ensureDynamicUBuf(rhi, m_cache.uBufSlice, uBufSliceElementSize * qMax(fs.prefilterSpecularMipCount, 1),
"Failed to create sky IBL slice uniform buffer"))
1007 if (!ensureDynamicUBuf(rhi, m_cache.uBufNormalize, uBufNormalizeElementSize * uBufNormalizeEntryCount,
"Failed to create sky IBL normalize uniform buffer"))
1010 const QSSGRhiSamplerDescription mipSamplerDesc { QRhiSampler::Linear, QRhiSampler::Linear,
1011 QRhiSampler::Linear, QRhiSampler::ClampToEdge,
1012 QRhiSampler::ClampToEdge, QRhiSampler::Repeat };
1013 const QSSGRhiSamplerDescription noMipLinearSamplerDesc { QRhiSampler::Linear, QRhiSampler::Linear,
1014 QRhiSampler::None, QRhiSampler::ClampToEdge,
1015 QRhiSampler::ClampToEdge, QRhiSampler::Repeat };
1016 const QSSGRhiSamplerDescription nearestSamplerDesc { QRhiSampler::Nearest, QRhiSampler::Nearest,
1017 QRhiSampler::None, QRhiSampler::ClampToEdge,
1018 QRhiSampler::ClampToEdge, QRhiSampler::Repeat };
1019 QRhiSampler *envMapCubeSampler = context->sampler(fs.enableIBL ? mipSamplerDesc : noMipLinearSamplerDesc);
1020 QRhiSampler *accumReadSampler = context->sampler(nearestSamplerDesc);
1024 { QRhiShaderResourceBinding::uniformBufferWithDynamicOffset(0, QRhiShaderResourceBinding::VertexStage, m_cache.uBuf, 128),
1025 QRhiShaderResourceBinding::uniformBufferWithDynamicOffset(2, QRhiShaderResourceBinding::FragmentStage, m_cache.uBufSlice, uBufSliceSize),
1026 QRhiShaderResourceBinding::sampledTexture(1, QRhiShaderResourceBinding::FragmentStage, m_envCubeMap, envMapCubeSampler) },
1027 "Failed to create sky IBL slice SRB"))
1035 if (m_cache.normalizeSrbs.size() != fs.prefilterSpecularMipCount) {
1036 for (QRhiShaderResourceBindings *srb : std::as_const(m_cache.normalizeSrbs))
1038 m_cache.normalizeSrbs.clear();
1039 m_cache.normalizeSrbs.resize(fs.prefilterSpecularMipCount,
nullptr);
1041 for (
int mip = 0; mip < fs.prefilterSpecularMipCount; ++mip) {
1043 m_cache.normalizeSrbs[mip],
1044 { QRhiShaderResourceBinding::uniformBufferWithDynamicOffset(0, QRhiShaderResourceBinding::VertexStage, m_cache.uBuf, 128),
1045 QRhiShaderResourceBinding::uniformBufferWithDynamicOffset(2, QRhiShaderResourceBinding::FragmentStage, m_cache.uBufNormalize, uBufNormalizeSize),
1046 QRhiShaderResourceBinding::sampledTexture(1, QRhiShaderResourceBinding::FragmentStage, m_prefilterAccumulators[mip], accumReadSampler) },
1047 "Failed to create sky IBL normalize SRB"))
1051 if (!m_cache.slicePipeline) {
1052 const auto &shader = m_context.shaderCache()->getBuiltInRhiShaders().getRhiSkyIblPreFilterShader();
1053 m_cache.slicePipeline = createPrefilterPipeline(rhi,
1057 m_cache.accumPreserveFaceTargets[0].renderPassDesc,
1059 "Failed to create sky IBL slice pipeline state");
1060 if (!m_cache.slicePipeline)
1063 if (!m_cache.normalizeCubePipeline) {
1065 const auto &shader = m_context.shaderCache()->getBuiltInRhiShaders().getRhiSkyIblPreFilterNormalizeShader();
1066 m_cache.normalizeCubePipeline = createPrefilterPipeline(rhi,
1069 m_cache.normalizeSrbs[0],
1070 prefilterTargets.renderPassDesc,
1072 "Failed to create sky IBL normalize-to-cube pipeline "
1074 if (!m_cache.normalizeCubePipeline)
1078 for (
int mipLevel = 0; mipLevel < fs.prefilterSpecularMipCount; ++mipLevel) {
1083 quint32 sampleStart;
1085 quint32 totalSampleCount;
1090 sliceData.roughness =
float(mipLevel) /
float(fs.prefilterRoughnessDenom);
1091 sliceData.resolution = fs.resolution;
1092 sliceData.sampleStart = quint32(fs.sliceSampleStart);
1093 sliceData.sampleEnd = quint32(fs.sliceSampleEnd);
1094 sliceData.totalSampleCount = quint32(fs.totalSamples);
1095 sliceData._pad0 = sliceData._pad1 = sliceData._pad2 = 0;
1096 rub->updateDynamicBuffer(m_cache.uBufSlice, mipLevel * uBufSliceElementSize,
sizeof(SliceData), &sliceData);
1098 for (
const auto face : QSSGRenderTextureCubeFaces) {
1099 struct NormalizeData
1106 normalizeData.faceIndex = quint8(face);
1107 const int entryIndex = mipLevel * 6 + quint8(face);
1108 rub->updateDynamicBuffer(m_cache.uBufNormalize, entryIndex * uBufNormalizeElementSize,
sizeof(NormalizeData), &normalizeData);
1112 cb->resourceUpdate(rub);
1125 if (fs.runPrefilterSlice) {
1126 for (
int mipLevel = 0; mipLevel < fs.prefilterSpecularMipCount; ++mipLevel) {
1127 QSSGSkyIblFaceTargets &sliceTargets = fs.isFirstSlice ? m_cache.accumClearFaceTargets[mipLevel]
1128 : m_cache.accumPreserveFaceTargets[mipLevel];
1129 const QSize mipSize(qMax(1, fs.environmentMapSize.width() >> mipLevel),
1130 qMax(1, fs.environmentMapSize.height() >> mipLevel));
1132 for (
const auto face : QSSGRenderTextureCubeFaces) {
1133 const QVector<QPair<
int, quint32>> offsets = { { 0, quint32(fs.ubufElementSize * quint8(face)) },
1134 { 2, quint32(uBufSliceElementSize * mipLevel) } };
1137 sliceTargets.renderTargets[quint8(face)],
1139 m_cache.slicePipeline,
1143 QByteArrayLiteral(
"sky_ibl_prefilter_slice"),
1144 QSSG_RENDERPASS_NAME(
"sky_ibl_prefilter_slice", mipLevel, face),
1145 QColor(0, 0, 0, 0));
1148 m_accumulatedSamples = fs.sliceSampleEnd;
1149 if (fs.sliceCompletesCycle) {
1150 if (inSky->iblRenderFrames >= 1) {
1154 m_finalizeIblPending =
true;
1157 m_haveConvergedResult =
true;
1165 if (fs.writePrefilteredCubeThisFrame) {
1166 for (
int mipLevel = 0; mipLevel < fs.prefilterSpecularMipCount; ++mipLevel) {
1167 for (
const auto face : QSSGRenderTextureCubeFaces) {
1168 const int normalizeEntryIndex = mipLevel * 6 + quint8(face);
1169 const QVector<QPair<
int, quint32>> offsets = { { 0, quint32(fs.ubufElementSize * quint8(face)) },
1170 { 2, quint32(uBufNormalizeElementSize * normalizeEntryIndex) } };
1173 prefilterTargets.mipRenderTargetsMap[mipLevel][quint8(face)],
1174 prefilterTargets.mipLevelSizes[mipLevel],
1175 m_cache.normalizeCubePipeline,
1176 m_cache.normalizeSrbs[mipLevel],
1179 QByteArrayLiteral(
"sky_ibl_prefilter_normalize"),
1180 QSSG_RENDERPASS_NAME(
"sky_ibl_prefilter_normalize", mipLevel, face));
1185 if (fs.runIrradiancePass) {
1186 const int irradianceMip = prefilterTargets.mipmapCount - 1;
1187 for (
const auto face : QSSGRenderTextureCubeFaces) {
1188 const QVector<QPair<
int, quint32>> offsets = { { 0, quint32(fs.ubufElementSize * quint8(face)) }, { 2, 0u } };
1191 prefilterTargets.mipRenderTargetsMap[irradianceMip][quint8(face)],
1192 prefilterTargets.mipLevelSizes[irradianceMip],
1193 m_cache.irradiancePipeline,
1194 m_cache.irradianceSrb,
1197 QByteArrayLiteral(
"sky_ibl_irradiance"),
1198 QSSG_RENDERPASS_NAME(
"sky_ibl_irradiance", irradianceMip, face));
1205void QSSGRenderSkyMaterialManager::initializeTailMips(
const FrameState &fs, QRhiCommandBuffer *cb)
1207 if (!m_prefilteredCubeMap->flags().testFlag(QRhiTexture::MipMapped) || m_prefilteredTailMipsInitialized)
1209 const auto &context = m_context.rhiContext();
1210 auto *rhi = context->rhi();
1211 const int prefilteredFullMipCount = rhi->mipLevelsForSize(fs.environmentMapSize);
1219 const int firstUnwrittenMip = fs.enableIBL ? m_cache.prefilterTargets.mipmapCount : 1;
1220 skyIblInitializeUnrenderedMips(rhi, cb, context.get(), m_prefilteredCubeMap, firstUnwrittenMip, prefilteredFullMipCount,
"SkyMaterialLightProbe"_ba);
1221 m_prefilteredTailMipsInitialized =
true;
static bool skyIblCreatePrefilterTargets(QRhi *rhi, QRhiTexture *texture, const QSize &environmentMapSize, const QByteArray &namePrefix, QSSGSkyIblPrefilterTargets *outTargets, bool preserveColorContents=false)
static constexpr QRhiTexture::Format cTextureFormat
static bool ensureSrb(QRhi *rhi, QRhiShaderResourceBindings *&dst, std::initializer_list< QRhiShaderResourceBinding > bindings, const char *errorMessage)
static bool skyIblCreateFaceTargets(QRhi *rhi, QRhiTexture *texture, const QByteArray &namePrefix, QSSGSkyIblFaceTargets *outTargets, bool preserveColorContents=false)
static QRhiGraphicsPipeline * createPrefilterPipeline(QRhi *rhi, const QRhiVertexInputLayout &inputLayout, const PrefilterPipelineConfig &cfg, const char *errorMessage)
static void drawCubeFace(QRhiCommandBuffer *cb, QSSGRhiContext *ctx, QRhiTextureRenderTarget *rt, QSize viewport, QRhiGraphicsPipeline *pipeline, QRhiShaderResourceBindings *srb, const QRhiCommandBuffer::VertexInput &vbufBinding, const QVector< QPair< int, quint32 > > &dynamicOffsets, const QByteArray &profilerLabel, const QByteArray &passDebugLabel, const QColor &clearColor=QColor(0, 0, 0, 1))
static void skyIblInitializeUnrenderedMips(QRhi *rhi, QRhiCommandBuffer *cb, QSSGRhiContext *context, QRhiTexture *texture, int firstMip, int mipCountExclusive, const QByteArray &debugObjectName)
static bool ensureDynamicUBuf(QRhi *rhi, QRhiBuffer *&dst, int size, const char *errorMessage)
static QVarLengthArray< QMatrix4x4, 6 > skyIblEnvironmentMapViews(QRhi *rhi)
static const float skyIblCubeVerts[]
static int nearestPowerOfTwo(int v)
#define QSSGRHICTX_STAT(ctx, f)
QSSGRhiShaderPipeline * shader
QRhiRenderPassDescriptor * rpd
QRhiShaderResourceBindings * srb