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, QSSGPassKey passKey)
479 const auto &rhiCtx = m_context.rhiContext();
480 if (!QSSG_GUARD(rhiCtx && rhiCtx->isValid() && rhiCtx->rhi()->isRecordingFrame()))
483 if (!ensureEnvironmentMap(settings, passKey)) {
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, QSSGPassKey passKey)
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, passKey, fs, shaderPipeline, cb, rub)) {
562 if (fs.enableIBL && fs.needRenderEnv)
563 runEnvironmentMipChain(fs, cb);
566 rub = context->rhi()->nextResourceUpdateBatch();
568 cb->debugMarkBegin(
"Sky IBL Pre-filtered Environment Cubemap Generation");
569 if (!runPrefilterCycle(inSky, fs, cb, rub)) {
576 cb->resourceUpdate(rub);
581 m_prefilteredMipCount = m_cache.prefilterTargets.mipmapCount;
582 initializeTailMips(fs, cb);
584 m_skyIblTexture.m_texture = m_prefilteredCubeMap;
585 m_skyIblTexture.m_mipmapCount = m_prefilteredMipCount;
586 m_skyIblTexture.m_flags.setLinear(
true);
587 m_skyIblTexture.m_flags.setRgbe8(
false);
591bool QSSGRenderSkyMaterialManager::computeFrameState(QSSGRenderSkyMaterial *inSky, FrameState &fs)
593 fs.enableIBL = inSky->enableIBL;
594 fs.totalSamples = qBound(1, inSky->iblSampleCount, 1024);
596 const int radianceMapSize = qBound(8, nearestPowerOfTwo(inSky->radianceMapSize), 2048);
597 fs.environmentMapSize = QSize(radianceMapSize, radianceMapSize);
599 const bool envWantsMips = fs.enableIBL;
600 const bool envHasMips = m_envCubeMap && m_envCubeMap->flags().testFlag(QRhiTexture::MipMapped);
601 fs.needCreateEnv = !m_envCubeMap || m_cubeMapSize != fs.environmentMapSize || envHasMips != envWantsMips;
603 fs.inProgressTimeSlice = fs.enableIBL && m_accumulatedSamples > 0 && m_accumulatedSamples < m_accumIblSampleCount;
604 fs.envContentDirty = inSky->isDirty && !fs.needCreateEnv;
605 fs.deferEnvRefresh = fs.envContentDirty && (fs.inProgressTimeSlice || m_finalizeIblPending);
606 fs.needRenderEnv = fs.needCreateEnv || (fs.envContentDirty && !fs.deferEnvRefresh);
610bool QSSGRenderSkyMaterialManager::ensureTextures(FrameState &fs)
612 const auto &context = m_context.rhiContext();
613 auto *rhi = context->rhi();
614 QSSGRhiContextPrivate *rhiCtxD = QSSGRhiContextPrivate::get(context.get());
617 if (fs.needCreateEnv) {
619 rhiCtxD->releaseTexture(m_envCubeMap);
620 m_envCubeMap =
nullptr;
623 if (m_prefilteredCubeMap) {
624 rhiCtxD->releaseTexture(m_prefilteredCubeMap);
625 m_prefilteredCubeMap =
nullptr;
626 m_prefilteredMipCount = 0;
628 m_envTailMipsInitialized =
false;
629 m_prefilteredTailMipsInitialized =
false;
631 QRhiTexture::Flags envFlags = QRhiTexture::RenderTarget | QRhiTexture::CubeMap;
633 envFlags |= QRhiTexture::MipMapped | QRhiTexture::UsedWithGenerateMips;
634 m_envCubeMap = rhi->newTexture(cTextureFormat, fs.environmentMapSize, 1, envFlags);
635 if (!m_envCubeMap->create()) {
636 qWarning(
"Failed to create Sky IBL environment cube map");
638 m_envCubeMap =
nullptr;
641 m_envCubeMap->setName(
"SkyMaterialLightProbe procEnvCube"_ba);
642 rhiCtxD->registerTexture(m_envCubeMap);
643 m_cubeMapSize = fs.environmentMapSize;
646 if (!m_prefilteredCubeMap) {
647 const QRhiTexture::Flags pfFlags = QRhiTexture::RenderTarget | QRhiTexture::CubeMap | QRhiTexture::MipMapped;
648 m_prefilteredCubeMap = rhi->newTexture(cTextureFormat, fs.environmentMapSize, 1, pfFlags);
649 if (!m_prefilteredCubeMap->create()) {
650 qWarning(
"Failed to create Sky IBL pre-filtered environment cube map");
651 delete m_prefilteredCubeMap;
652 m_prefilteredCubeMap =
nullptr;
655 m_prefilteredCubeMap->setName(
"SkyMaterialLightProbe"_ba);
656 rhiCtxD->registerTexture(m_prefilteredCubeMap);
657 fs.prefilteredJustCreated =
true;
658 m_haveConvergedResult =
false;
659 m_prefilteredTailMipsInitialized =
false;
662 fs.prefilterTotalMipCount = m_prefilteredCubeMap->flags().testFlag(QRhiTexture::MipMapped)
663 ? qMin(rhi->mipLevelsForSize(fs.environmentMapSize), 6)
665 fs.prefilterSpecularMipCount = fs.enableIBL ? fs.prefilterTotalMipCount - 1 : 1;
666 fs.prefilterRoughnessDenom = qMax(fs.prefilterSpecularMipCount - 1, 1);
667 fs.resolution =
float(fs.environmentMapSize.width());
671 const bool needCreateAccum = m_prefilterAccumulators.isEmpty() || m_prefilterAccumulators.size() != fs.prefilterSpecularMipCount
672 || (!m_prefilterAccumulators.isEmpty() && m_prefilterAccumulators[0]->pixelSize() != fs.environmentMapSize);
674 if (needCreateAccum) {
675 for (QRhiTexture *t : std::as_const(m_prefilterAccumulators))
676 rhiCtxD->releaseTexture(t);
677 m_prefilterAccumulators.clear();
678 m_accumulatedSamples = 0;
689 for (
int mip = 0; mip < fs.prefilterSpecularMipCount; ++mip) {
690 const QSize mipSize(qMax(1, fs.environmentMapSize.width() >> mip), qMax(1, fs.environmentMapSize.height() >> mip));
692 auto *t = rhi->newTextureArray(cTextureFormat, 6, mipSize, 1, QRhiTexture::RenderTarget);
694 qWarning(
"Failed to create Sky IBL prefilter accumulator mip %d", mip);
699 t->setName(
"SkyMaterialLightProbe procEnvPfAccum m"_ba + QByteArray::number(mip));
700 rhiCtxD->registerTexture(t);
701 m_prefilterAccumulators.append(t);
705 for (QRhiTexture *t : std::as_const(m_prefilterAccumulators))
706 rhiCtxD->releaseTexture(t);
707 m_prefilterAccumulators.clear();
718void QSSGRenderSkyMaterialManager::deriveCycleState(QSSGRenderSkyMaterial *inSky, FrameState &fs)
721 if (m_finalizeIblPending && !fs.needRenderEnv && !fs.prefilteredJustCreated && m_accumIblSampleCount == fs.totalSamples) {
722 m_finalizeIblPending =
false;
723 m_haveConvergedResult =
true;
724 fs.runPrefilterSlice =
false;
725 fs.writePrefilteredCubeThisFrame =
true;
726 fs.runIrradiancePass = fs.enableIBL;
727 fs.sliceSamplesThisFrame = 0;
728 fs.sliceSampleStart = m_accumulatedSamples;
729 fs.sliceSampleEnd = m_accumulatedSamples;
730 fs.isFirstSlice =
false;
731 fs.sliceCompletesCycle =
false;
732 fs.haveConvergedResultEntering =
false;
733 fs.multiFrame =
false;
737 fs.multiFrame = fs.enableIBL && inSky->iblSamplesPerFrame > 0 && inSky->iblSamplesPerFrame < fs.totalSamples;
741 if (!m_prefilterAccumulators.isEmpty()) {
742 const bool resetAccumulation = fs.needRenderEnv || fs.prefilteredJustCreated || m_accumIblSampleCount != fs.totalSamples;
743 if (resetAccumulation) {
744 m_accumulatedSamples = 0;
747 m_finalizeIblPending =
false;
749 m_accumIblSampleCount = fs.totalSamples;
752 fs.prefilterIsConverged = !m_prefilterAccumulators.isEmpty() && m_accumulatedSamples >= fs.totalSamples;
753 fs.runPrefilterSlice = !m_prefilterAccumulators.isEmpty() && !fs.prefilterIsConverged;
758 fs.perFrameBudget = (inSky->iblSamplesPerFrame > 0 && fs.enableIBL) ? inSky->iblSamplesPerFrame
759 : (fs.totalSamples - m_accumulatedSamples);
760 fs.sliceSamplesThisFrame = fs.runPrefilterSlice ? qMin(fs.perFrameBudget, fs.totalSamples - m_accumulatedSamples) : 0;
761 fs.sliceCompletesCycle = fs.runPrefilterSlice && (m_accumulatedSamples + fs.sliceSamplesThisFrame >= fs.totalSamples);
763 fs.sliceSampleStart = m_accumulatedSamples;
764 fs.sliceSampleEnd = fs.runPrefilterSlice ? qMin(m_accumulatedSamples + fs.perFrameBudget, fs.totalSamples) : 0;
765 fs.isFirstSlice = m_accumulatedSamples == 0;
766 fs.haveConvergedResultEntering = m_haveConvergedResult;
768 if (inSky->iblRenderFrames >= 1) {
775 fs.writePrefilteredCubeThisFrame =
false;
776 fs.runIrradiancePass =
false;
780 fs.writePrefilteredCubeThisFrame = fs.runPrefilterSlice && (fs.sliceCompletesCycle || !m_haveConvergedResult);
781 fs.runIrradiancePass = fs.enableIBL && fs.writePrefilteredCubeThisFrame;
785void QSSGRenderSkyMaterialManager::validateAndUpdateCacheKey(
const FrameState &fs, QSSGRhiShaderPipeline *envShaderPipelineKey)
787 const bool cacheValid = m_cache.environmentMapSize == fs.environmentMapSize && m_cache.enableIBL == fs.enableIBL
788 && m_cache.prefilterTotalMipCount == fs.prefilterTotalMipCount && m_cache.envCubeMap == m_envCubeMap
789 && m_cache.prefilteredCubeMap == m_prefilteredCubeMap && m_cache.prefilterAccumulators == m_prefilterAccumulators
790 && m_cache.envShaderPipeline == envShaderPipelineKey;
793 clearPrefilterCache();
794 m_cache.environmentMapSize = fs.environmentMapSize;
795 m_cache.enableIBL = fs.enableIBL;
796 m_cache.prefilterTotalMipCount = fs.prefilterTotalMipCount;
797 m_cache.envCubeMap = m_envCubeMap;
798 m_cache.prefilteredCubeMap = m_prefilteredCubeMap;
799 m_cache.prefilterAccumulators = m_prefilterAccumulators;
800 m_cache.envShaderPipeline = envShaderPipelineKey;
803bool QSSGRenderSkyMaterialManager::ensureSharedResources(FrameState &fs, QRhiCommandBuffer *cb)
805 const auto &context = m_context.rhiContext();
806 auto *rhi = context->rhi();
808 fs.inputLayout.setBindings({ { 3 *
sizeof(
float) } });
809 fs.inputLayout.setAttributes({ { 0, 0, QRhiVertexInputAttribute::Float3, 0 } });
811 fs.mvp = rhi->clipSpaceCorrMatrix();
812 fs.mvp.perspective(90.0f, 1.0f, 0.1f, 10.0f);
813 fs.views = skyIblEnvironmentMapViews(rhi);
815 fs.ubufElementSize = rhi->ubufAligned(128);
817 if (!m_cache.vertexBuffer) {
818 m_cache.vertexBuffer = rhi->newBuffer(QRhiBuffer::Immutable, QRhiBuffer::VertexBuffer,
sizeof(skyIblCubeVerts));
819 if (!m_cache.vertexBuffer->create()) {
820 qWarning(
"Failed to create sky IBL vertex buffer");
821 delete m_cache.vertexBuffer;
822 m_cache.vertexBuffer =
nullptr;
825 auto *initRub = rhi->nextResourceUpdateBatch();
826 initRub->uploadStaticBuffer(m_cache.vertexBuffer, skyIblCubeVerts);
827 cb->resourceUpdate(initRub);
829 if (!ensureDynamicUBuf(rhi, m_cache.uBuf, fs.ubufElementSize * 6,
"Failed to create sky IBL view uniform buffer"))
831 fs.vbufBinding = QRhiCommandBuffer::VertexInput(m_cache.vertexBuffer, 0);
833 if (!m_cache.prefilterTargets.renderPassDesc) {
834 if (!skyIblCreatePrefilterTargets(rhi, m_prefilteredCubeMap, fs.environmentMapSize,
"SkyMaterialLightProbe procEnvPf"_ba, &m_cache.prefilterTargets)) {
839 if (!ensureDynamicUBuf(rhi, m_cache.uBufIrradiance, rhi->ubufAligned(20),
"Failed to create sky IBL irradiance uniform buffer"))
842 const QSSGRhiSamplerDescription samplerNoMipDesc { QRhiSampler::Linear, QRhiSampler::Linear,
843 QRhiSampler::None, QRhiSampler::ClampToEdge,
844 QRhiSampler::ClampToEdge, QRhiSampler::Repeat };
845 QRhiSampler *envMapCubeNoMipSampler = context->sampler(samplerNoMipDesc);
848 m_cache.irradianceSrb,
849 { QRhiShaderResourceBinding::uniformBufferWithDynamicOffset(0, QRhiShaderResourceBinding::VertexStage, m_cache.uBuf, 128),
850 QRhiShaderResourceBinding::uniformBufferWithDynamicOffset(2, QRhiShaderResourceBinding::FragmentStage, m_cache.uBufIrradiance, 20),
851 QRhiShaderResourceBinding::sampledTexture(1, QRhiShaderResourceBinding::FragmentStage, m_envCubeMap, envMapCubeNoMipSampler) },
852 "Failed to create sky IBL irradiance SRB"))
855 if (!m_cache.irradiancePipeline) {
856 const auto &shader = m_context.shaderCache()->getBuiltInRhiShaders().getRhienvironmentmapPreFilterShader(
false);
857 m_cache.irradiancePipeline = createPrefilterPipeline(rhi,
860 m_cache.irradianceSrb,
861 m_cache.prefilterTargets.renderPassDesc,
863 "Failed to create sky IBL realtime irradiance pipeline "
865 if (!m_cache.irradiancePipeline)
871bool QSSGRenderSkyMaterialManager::renderEnvironmentCube(QSSGRenderSkyMaterial *inSky,
873 const FrameState &fs,
874 const QSSGRhiShaderPipelinePtr &shaderPipeline,
875 QRhiCommandBuffer *cb,
876 QRhiResourceUpdateBatch *rub)
878 const auto &context = m_context.rhiContext();
879 auto *rhi = context->rhi();
880 QSSGRhiContextPrivate *rhiCtxD = QSSGRhiContextPrivate::get(context.get());
882 const quint32 skyElementSize = inSky->updateUniforms(m_context, passKey, fs.mvp, fs.views);
884 if (!m_cache.envFaceTargets.renderPassDesc) {
885 if (!skyIblCreateFaceTargets(rhi, m_envCubeMap,
"SkyMaterialLightProbe procEnvCube"_ba, &m_cache.envFaceTargets))
889 QRhiShaderResourceBindings *envSrb = rhiCtxD->srb(inSky->bindings);
892 if (!m_cache.envMapPipeline) {
893 m_cache.envMapPipeline = createPrefilterPipeline(rhi,
895 { shaderPipeline.get(), envSrb, m_cache.envFaceTargets.renderPassDesc,
false },
896 "Failed to create sky IBL env map pipeline state");
897 if (!m_cache.envMapPipeline)
901 cb->resourceUpdate(rub);
903 cb->debugMarkBegin(
"Sky IBL Procedural Environment Cubemap Generation");
904 for (
const auto face : QSSGRenderTextureCubeFaces) {
905 const QVector<QPair<
int, quint32>> offsets = { { 0, quint32(skyElementSize * quint8(face)) } };
908 m_cache.envFaceTargets.renderTargets[quint8(face)],
909 fs.environmentMapSize,
910 m_cache.envMapPipeline,
914 QByteArrayLiteral(
"sky_ibl_procedural_environment_map"),
915 QSSG_RENDERPASS_NAME(
"sky_ibl_procedural_environment_map", 0, face));
919 inSky->isDirty =
false;
923void QSSGRenderSkyMaterialManager::runEnvironmentMipChain(
const FrameState &fs, QRhiCommandBuffer *cb)
925 const auto &context = m_context.rhiContext();
926 auto *rhi = context->rhi();
928 if (!m_envTailMipsInitialized) {
929 const int envFullMipCount = rhi->mipLevelsForSize(fs.environmentMapSize);
930 skyIblInitializeUnrenderedMips(rhi, cb, context.get(), m_envCubeMap, 1, envFullMipCount,
"SkyMaterialLightProbe procEnvCube"_ba);
931 m_envTailMipsInitialized =
true;
934 auto *rubMip = rhi->nextResourceUpdateBatch();
935 rubMip->generateMips(m_envCubeMap);
936 cb->resourceUpdate(rubMip);
939bool QSSGRenderSkyMaterialManager::runPrefilterCycle(QSSGRenderSkyMaterial *inSky,
940 const FrameState &fs,
941 QRhiCommandBuffer *cb,
942 QRhiResourceUpdateBatch *&rub)
945 if (!fs.runPrefilterSlice && !fs.writePrefilteredCubeThisFrame) {
946 cb->resourceUpdate(rub);
951 const auto &context = m_context.rhiContext();
952 auto *rhi = context->rhi();
964 if (m_cache.accumPreserveFaceTargets.size() != fs.prefilterSpecularMipCount) {
966 for (QSSGSkyIblFaceTargets &t : m_cache.accumPreserveFaceTargets) {
967 for (QRhiTextureRenderTarget *rt : t.renderTargets)
969 delete t.renderPassDesc;
971 m_cache.accumPreserveFaceTargets.clear();
972 m_cache.accumPreserveFaceTargets.resize(fs.prefilterSpecularMipCount);
974 for (
int mip = 0; mip < fs.prefilterSpecularMipCount; ++mip) {
975 if (!skyIblCreateFaceTargets(rhi,
976 m_prefilterAccumulators[mip],
977 "SkyMaterialLightProbe procEnvPfAccum/m"_ba + QByteArray::number(mip),
978 &m_cache.accumPreserveFaceTargets[mip],
985 if (m_cache.accumClearFaceTargets.size() != fs.prefilterSpecularMipCount) {
986 for (QSSGSkyIblFaceTargets &t : m_cache.accumClearFaceTargets) {
987 for (QRhiTextureRenderTarget *rt : t.renderTargets)
989 delete t.renderPassDesc;
991 m_cache.accumClearFaceTargets.clear();
992 m_cache.accumClearFaceTargets.resize(fs.prefilterSpecularMipCount);
994 for (
int mip = 0; mip < fs.prefilterSpecularMipCount; ++mip) {
995 if (!skyIblCreateFaceTargets(rhi,
996 m_prefilterAccumulators[mip],
997 "SkyMaterialLightProbe procEnvPfAccumClear/m"_ba + QByteArray::number(mip),
998 &m_cache.accumClearFaceTargets[mip],
1005 QSSGSkyIblPrefilterTargets &prefilterTargets = m_cache.prefilterTargets;
1007 constexpr int uBufSliceSize = 32;
1008 constexpr int uBufNormalizeSize = 16;
1009 const int uBufSliceElementSize = rhi->ubufAligned(uBufSliceSize);
1010 const int uBufNormalizeElementSize = rhi->ubufAligned(uBufNormalizeSize);
1011 const int uBufNormalizeEntryCount = qMax(fs.prefilterSpecularMipCount, 1) * 6;
1013 if (!ensureDynamicUBuf(rhi, m_cache.uBufSlice, uBufSliceElementSize * qMax(fs.prefilterSpecularMipCount, 1),
"Failed to create sky IBL slice uniform buffer"))
1015 if (!ensureDynamicUBuf(rhi, m_cache.uBufNormalize, uBufNormalizeElementSize * uBufNormalizeEntryCount,
"Failed to create sky IBL normalize uniform buffer"))
1018 const QSSGRhiSamplerDescription mipSamplerDesc { QRhiSampler::Linear, QRhiSampler::Linear,
1019 QRhiSampler::Linear, QRhiSampler::ClampToEdge,
1020 QRhiSampler::ClampToEdge, QRhiSampler::Repeat };
1021 const QSSGRhiSamplerDescription noMipLinearSamplerDesc { QRhiSampler::Linear, QRhiSampler::Linear,
1022 QRhiSampler::None, QRhiSampler::ClampToEdge,
1023 QRhiSampler::ClampToEdge, QRhiSampler::Repeat };
1024 const QSSGRhiSamplerDescription nearestSamplerDesc { QRhiSampler::Nearest, QRhiSampler::Nearest,
1025 QRhiSampler::None, QRhiSampler::ClampToEdge,
1026 QRhiSampler::ClampToEdge, QRhiSampler::Repeat };
1027 QRhiSampler *envMapCubeSampler = context->sampler(fs.enableIBL ? mipSamplerDesc : noMipLinearSamplerDesc);
1028 QRhiSampler *accumReadSampler = context->sampler(nearestSamplerDesc);
1032 { QRhiShaderResourceBinding::uniformBufferWithDynamicOffset(0, QRhiShaderResourceBinding::VertexStage, m_cache.uBuf, 128),
1033 QRhiShaderResourceBinding::uniformBufferWithDynamicOffset(2, QRhiShaderResourceBinding::FragmentStage, m_cache.uBufSlice, uBufSliceSize),
1034 QRhiShaderResourceBinding::sampledTexture(1, QRhiShaderResourceBinding::FragmentStage, m_envCubeMap, envMapCubeSampler) },
1035 "Failed to create sky IBL slice SRB"))
1043 if (m_cache.normalizeSrbs.size() != fs.prefilterSpecularMipCount) {
1044 for (QRhiShaderResourceBindings *srb : std::as_const(m_cache.normalizeSrbs))
1046 m_cache.normalizeSrbs.clear();
1047 m_cache.normalizeSrbs.resize(fs.prefilterSpecularMipCount,
nullptr);
1049 for (
int mip = 0; mip < fs.prefilterSpecularMipCount; ++mip) {
1051 m_cache.normalizeSrbs[mip],
1052 { QRhiShaderResourceBinding::uniformBufferWithDynamicOffset(0, QRhiShaderResourceBinding::VertexStage, m_cache.uBuf, 128),
1053 QRhiShaderResourceBinding::uniformBufferWithDynamicOffset(2, QRhiShaderResourceBinding::FragmentStage, m_cache.uBufNormalize, uBufNormalizeSize),
1054 QRhiShaderResourceBinding::sampledTexture(1, QRhiShaderResourceBinding::FragmentStage, m_prefilterAccumulators[mip], accumReadSampler) },
1055 "Failed to create sky IBL normalize SRB"))
1059 if (!m_cache.slicePipeline) {
1060 const auto &shader = m_context.shaderCache()->getBuiltInRhiShaders().getRhiSkyIblPreFilterShader();
1061 m_cache.slicePipeline = createPrefilterPipeline(rhi,
1065 m_cache.accumPreserveFaceTargets[0].renderPassDesc,
1067 "Failed to create sky IBL slice pipeline state");
1068 if (!m_cache.slicePipeline)
1071 if (!m_cache.normalizeCubePipeline) {
1073 const auto &shader = m_context.shaderCache()->getBuiltInRhiShaders().getRhiSkyIblPreFilterNormalizeShader();
1074 m_cache.normalizeCubePipeline = createPrefilterPipeline(rhi,
1077 m_cache.normalizeSrbs[0],
1078 prefilterTargets.renderPassDesc,
1080 "Failed to create sky IBL normalize-to-cube pipeline "
1082 if (!m_cache.normalizeCubePipeline)
1086 for (
int mipLevel = 0; mipLevel < fs.prefilterSpecularMipCount; ++mipLevel) {
1091 quint32 sampleStart;
1093 quint32 totalSampleCount;
1098 sliceData.roughness =
float(mipLevel) /
float(fs.prefilterRoughnessDenom);
1099 sliceData.resolution = fs.resolution;
1100 sliceData.sampleStart = quint32(fs.sliceSampleStart);
1101 sliceData.sampleEnd = quint32(fs.sliceSampleEnd);
1102 sliceData.totalSampleCount = quint32(fs.totalSamples);
1103 sliceData._pad0 = sliceData._pad1 = sliceData._pad2 = 0;
1104 rub->updateDynamicBuffer(m_cache.uBufSlice, mipLevel * uBufSliceElementSize,
sizeof(SliceData), &sliceData);
1106 for (
const auto face : QSSGRenderTextureCubeFaces) {
1107 struct NormalizeData
1114 normalizeData.faceIndex = quint8(face);
1115 const int entryIndex = mipLevel * 6 + quint8(face);
1116 rub->updateDynamicBuffer(m_cache.uBufNormalize, entryIndex * uBufNormalizeElementSize,
sizeof(NormalizeData), &normalizeData);
1120 cb->resourceUpdate(rub);
1133 if (fs.runPrefilterSlice) {
1134 for (
int mipLevel = 0; mipLevel < fs.prefilterSpecularMipCount; ++mipLevel) {
1135 QSSGSkyIblFaceTargets &sliceTargets = fs.isFirstSlice ? m_cache.accumClearFaceTargets[mipLevel]
1136 : m_cache.accumPreserveFaceTargets[mipLevel];
1137 const QSize mipSize(qMax(1, fs.environmentMapSize.width() >> mipLevel),
1138 qMax(1, fs.environmentMapSize.height() >> mipLevel));
1140 for (
const auto face : QSSGRenderTextureCubeFaces) {
1141 const QVector<QPair<
int, quint32>> offsets = { { 0, quint32(fs.ubufElementSize * quint8(face)) },
1142 { 2, quint32(uBufSliceElementSize * mipLevel) } };
1145 sliceTargets.renderTargets[quint8(face)],
1147 m_cache.slicePipeline,
1151 QByteArrayLiteral(
"sky_ibl_prefilter_slice"),
1152 QSSG_RENDERPASS_NAME(
"sky_ibl_prefilter_slice", mipLevel, face),
1153 QColor(0, 0, 0, 0));
1156 m_accumulatedSamples = fs.sliceSampleEnd;
1157 if (fs.sliceCompletesCycle) {
1158 if (inSky->iblRenderFrames >= 1) {
1162 m_finalizeIblPending =
true;
1165 m_haveConvergedResult =
true;
1173 if (fs.writePrefilteredCubeThisFrame) {
1174 for (
int mipLevel = 0; mipLevel < fs.prefilterSpecularMipCount; ++mipLevel) {
1175 for (
const auto face : QSSGRenderTextureCubeFaces) {
1176 const int normalizeEntryIndex = mipLevel * 6 + quint8(face);
1177 const QVector<QPair<
int, quint32>> offsets = { { 0, quint32(fs.ubufElementSize * quint8(face)) },
1178 { 2, quint32(uBufNormalizeElementSize * normalizeEntryIndex) } };
1181 prefilterTargets.mipRenderTargetsMap[mipLevel][quint8(face)],
1182 prefilterTargets.mipLevelSizes[mipLevel],
1183 m_cache.normalizeCubePipeline,
1184 m_cache.normalizeSrbs[mipLevel],
1187 QByteArrayLiteral(
"sky_ibl_prefilter_normalize"),
1188 QSSG_RENDERPASS_NAME(
"sky_ibl_prefilter_normalize", mipLevel, face));
1193 if (fs.runIrradiancePass) {
1194 const int irradianceMip = prefilterTargets.mipmapCount - 1;
1195 for (
const auto face : QSSGRenderTextureCubeFaces) {
1196 const QVector<QPair<
int, quint32>> offsets = { { 0, quint32(fs.ubufElementSize * quint8(face)) }, { 2, 0u } };
1199 prefilterTargets.mipRenderTargetsMap[irradianceMip][quint8(face)],
1200 prefilterTargets.mipLevelSizes[irradianceMip],
1201 m_cache.irradiancePipeline,
1202 m_cache.irradianceSrb,
1205 QByteArrayLiteral(
"sky_ibl_irradiance"),
1206 QSSG_RENDERPASS_NAME(
"sky_ibl_irradiance", irradianceMip, face));
1213void QSSGRenderSkyMaterialManager::initializeTailMips(
const FrameState &fs, QRhiCommandBuffer *cb)
1215 if (!m_prefilteredCubeMap->flags().testFlag(QRhiTexture::MipMapped) || m_prefilteredTailMipsInitialized)
1217 const auto &context = m_context.rhiContext();
1218 auto *rhi = context->rhi();
1219 const int prefilteredFullMipCount = rhi->mipLevelsForSize(fs.environmentMapSize);
1227 const int firstUnwrittenMip = fs.enableIBL ? m_cache.prefilterTargets.mipmapCount : 1;
1228 skyIblInitializeUnrenderedMips(rhi, cb, context.get(), m_prefilteredCubeMap, firstUnwrittenMip, prefilteredFullMipCount,
"SkyMaterialLightProbe"_ba);
1229 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