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;
476QSSGRenderImageTexture QSSGRenderSkyMaterialManager::resolve(QSSGRenderSkyMaterial *settings)
478 const auto &rhiCtx = m_context.rhiContext();
479 if (!QSSG_GUARD(rhiCtx && rhiCtx->isValid() && rhiCtx->rhi()->isRecordingFrame()))
482 if (!ensureEnvironmentMap(settings)) {
486 const bool pendingAccumulation = settings->enableIBL && m_accumulatedSamples < settings->iblSampleCount;
487 settings->wantsMoreFrames = pendingAccumulation || settings->isDirty;
489 return m_skyIblTexture;
492bool QSSGRenderSkyMaterialManager::ensureEnvironmentMap(QSSGRenderSkyMaterial *inSky)
494 const auto &context = m_context.rhiContext();
495 if (!context->rhi()->isTextureFormatSupported(cTextureFormat)) {
496 static bool warningPrinted =
false;
497 if (Q_UNLIKELY(!warningPrinted)) {
498 qWarning() <<
"SkyMaterial not supported due to missing RGBA16F texture format support.";
499 warningPrinted =
true;
504 auto *cb = context->commandBuffer();
507 if (!computeFrameState(inSky, fs))
510 if (!ensureTextures(fs))
513 deriveCycleState(inSky, fs);
515 QSSGRhiShaderPipelinePtr shaderPipeline;
516 if (fs.needRenderEnv) {
517 shaderPipeline = inSky->ensurePipeline(m_context);
518 if (!shaderPipeline) {
522 QSSGRhiShaderPipeline *envShaderPipelineKey = fs.needRenderEnv ? shaderPipeline.get() : m_cache.envShaderPipeline;
524 validateAndUpdateCacheKey(fs, envShaderPipelineKey);
526 if (!ensureSharedResources(fs, cb))
529 auto *rub = context->rhi()->nextResourceUpdateBatch();
530 for (
const auto face : QSSGRenderTextureCubeFaces) {
531 rub->updateDynamicBuffer(m_cache.uBuf, quint8(face) * fs.ubufElementSize, 64, fs.mvp.constData());
532 rub->updateDynamicBuffer(m_cache.uBuf, quint8(face) * fs.ubufElementSize + 64, 64, fs.views[quint8(face)].constData());
535 if (fs.runIrradiancePass) {
536 struct IrradianceData
544 irradianceData.roughness = 0.0f;
545 irradianceData.resolution = fs.resolution;
546 irradianceData.lodBias = 0.0f;
547 irradianceData.distribution = 0;
548 irradianceData.sampleCount = qMax(
int(fs.resolution / 4.0f), 1);
549 rub->updateDynamicBuffer(m_cache.uBufIrradiance, 0,
sizeof(IrradianceData), &irradianceData);
552 if (fs.needRenderEnv) {
553 if (!renderEnvironmentCube(inSky, fs, shaderPipeline, cb, rub))
558 if (fs.enableIBL && fs.needRenderEnv)
559 runEnvironmentMipChain(fs, cb);
562 rub = context->rhi()->nextResourceUpdateBatch();
564 cb->debugMarkBegin(
"Sky IBL Pre-filtered Environment Cubemap Generation");
565 if (!runPrefilterCycle(inSky, fs, cb, rub))
568 cb->resourceUpdate(rub);
573 m_prefilteredMipCount = m_cache.prefilterTargets.mipmapCount;
574 initializeTailMips(fs, cb);
576 m_skyIblTexture.m_texture = m_prefilteredCubeMap;
577 m_skyIblTexture.m_mipmapCount = m_prefilteredMipCount;
578 m_skyIblTexture.m_flags.setLinear(
true);
579 m_skyIblTexture.m_flags.setRgbe8(
false);
583bool QSSGRenderSkyMaterialManager::computeFrameState(QSSGRenderSkyMaterial *inSky, FrameState &fs)
585 fs.enableIBL = inSky->enableIBL;
586 fs.totalSamples = qBound(1, inSky->iblSampleCount, 1024);
588 const int radianceMapSize = qBound(8, nearestPowerOfTwo(inSky->radianceMapSize), 2048);
589 fs.environmentMapSize = QSize(radianceMapSize, radianceMapSize);
591 const bool envWantsMips = fs.enableIBL;
592 const bool envHasMips = m_envCubeMap && m_envCubeMap->flags().testFlag(QRhiTexture::MipMapped);
593 fs.needCreateEnv = !m_envCubeMap || m_cubeMapSize != fs.environmentMapSize || envHasMips != envWantsMips;
595 fs.inProgressTimeSlice = fs.enableIBL && m_accumulatedSamples > 0 && m_accumulatedSamples < m_accumIblSampleCount;
596 fs.envContentDirty = inSky->isDirty && !fs.needCreateEnv;
597 fs.deferEnvRefresh = fs.envContentDirty && fs.inProgressTimeSlice;
598 fs.needRenderEnv = fs.needCreateEnv || (fs.envContentDirty && !fs.deferEnvRefresh);
602bool QSSGRenderSkyMaterialManager::ensureTextures(FrameState &fs)
604 const auto &context = m_context.rhiContext();
605 auto *rhi = context->rhi();
606 QSSGRhiContextPrivate *rhiCtxD = QSSGRhiContextPrivate::get(context.get());
609 if (fs.needCreateEnv) {
611 rhiCtxD->releaseTexture(m_envCubeMap);
612 m_envCubeMap =
nullptr;
615 if (m_prefilteredCubeMap) {
616 rhiCtxD->releaseTexture(m_prefilteredCubeMap);
617 m_prefilteredCubeMap =
nullptr;
618 m_prefilteredMipCount = 0;
620 m_envTailMipsInitialized =
false;
621 m_prefilteredTailMipsInitialized =
false;
623 QRhiTexture::Flags envFlags = QRhiTexture::RenderTarget | QRhiTexture::CubeMap;
625 envFlags |= QRhiTexture::MipMapped | QRhiTexture::UsedWithGenerateMips;
626 m_envCubeMap = rhi->newTexture(cTextureFormat, fs.environmentMapSize, 1, envFlags);
627 if (!m_envCubeMap->create()) {
628 qWarning(
"Failed to create Sky IBL environment cube map");
630 m_envCubeMap =
nullptr;
633 m_envCubeMap->setName(
"SkyMaterialLightProbe procEnvCube"_ba);
634 rhiCtxD->registerTexture(m_envCubeMap);
635 m_cubeMapSize = fs.environmentMapSize;
638 if (!m_prefilteredCubeMap) {
639 const QRhiTexture::Flags pfFlags = QRhiTexture::RenderTarget | QRhiTexture::CubeMap | QRhiTexture::MipMapped;
640 m_prefilteredCubeMap = rhi->newTexture(cTextureFormat, fs.environmentMapSize, 1, pfFlags);
641 if (!m_prefilteredCubeMap->create()) {
642 qWarning(
"Failed to create Sky IBL pre-filtered environment cube map");
643 delete m_prefilteredCubeMap;
644 m_prefilteredCubeMap =
nullptr;
647 m_prefilteredCubeMap->setName(
"SkyMaterialLightProbe"_ba);
648 rhiCtxD->registerTexture(m_prefilteredCubeMap);
649 fs.prefilteredJustCreated =
true;
650 m_haveConvergedResult =
false;
651 m_prefilteredTailMipsInitialized =
false;
654 fs.prefilterTotalMipCount = m_prefilteredCubeMap->flags().testFlag(QRhiTexture::MipMapped)
655 ? qMin(rhi->mipLevelsForSize(fs.environmentMapSize), 6)
657 fs.prefilterSpecularMipCount = fs.enableIBL ? fs.prefilterTotalMipCount - 1 : 1;
658 fs.prefilterRoughnessDenom = qMax(fs.prefilterSpecularMipCount - 1, 1);
659 fs.resolution =
float(fs.environmentMapSize.width());
663 const bool needCreateAccum = m_prefilterAccumulators.isEmpty() || m_prefilterAccumulators.size() != fs.prefilterSpecularMipCount
664 || (!m_prefilterAccumulators.isEmpty() && m_prefilterAccumulators[0]->pixelSize() != fs.environmentMapSize);
666 if (needCreateAccum) {
667 for (QRhiTexture *t : std::as_const(m_prefilterAccumulators))
668 rhiCtxD->releaseTexture(t);
669 m_prefilterAccumulators.clear();
670 m_accumulatedSamples = 0;
681 for (
int mip = 0; mip < fs.prefilterSpecularMipCount; ++mip) {
682 const QSize mipSize(qMax(1, fs.environmentMapSize.width() >> mip), qMax(1, fs.environmentMapSize.height() >> mip));
684 auto *t = rhi->newTextureArray(cTextureFormat, 6, mipSize, 1, QRhiTexture::RenderTarget);
686 qWarning(
"Failed to create Sky IBL prefilter accumulator mip %d", mip);
691 t->setName(
"SkyMaterialLightProbe procEnvPfAccum m"_ba + QByteArray::number(mip));
692 rhiCtxD->registerTexture(t);
693 m_prefilterAccumulators.append(t);
697 for (QRhiTexture *t : std::as_const(m_prefilterAccumulators))
698 rhiCtxD->releaseTexture(t);
699 m_prefilterAccumulators.clear();
710void QSSGRenderSkyMaterialManager::deriveCycleState(QSSGRenderSkyMaterial *inSky, FrameState &fs)
712 fs.multiFrame = fs.enableIBL && inSky->iblSamplesPerFrame > 0 && inSky->iblSamplesPerFrame < fs.totalSamples;
716 if (!m_prefilterAccumulators.isEmpty()) {
717 const bool resetAccumulation = fs.needRenderEnv || fs.prefilteredJustCreated || m_accumIblSampleCount != fs.totalSamples;
718 if (resetAccumulation)
719 m_accumulatedSamples = 0;
720 m_accumIblSampleCount = fs.totalSamples;
723 fs.prefilterIsConverged = !m_prefilterAccumulators.isEmpty() && m_accumulatedSamples >= fs.totalSamples;
724 fs.runPrefilterSlice = !m_prefilterAccumulators.isEmpty() && !fs.prefilterIsConverged;
729 fs.perFrameBudget = (inSky->iblSamplesPerFrame > 0 && fs.enableIBL) ? inSky->iblSamplesPerFrame
730 : (fs.totalSamples - m_accumulatedSamples);
731 fs.sliceSamplesThisFrame = fs.runPrefilterSlice ? qMin(fs.perFrameBudget, fs.totalSamples - m_accumulatedSamples) : 0;
732 fs.sliceCompletesCycle = fs.runPrefilterSlice && (m_accumulatedSamples + fs.sliceSamplesThisFrame >= fs.totalSamples);
734 fs.sliceSampleStart = m_accumulatedSamples;
735 fs.sliceSampleEnd = fs.runPrefilterSlice ? qMin(m_accumulatedSamples + fs.perFrameBudget, fs.totalSamples) : 0;
736 fs.isFirstSlice = m_accumulatedSamples == 0;
737 fs.haveConvergedResultEntering = m_haveConvergedResult;
741 fs.writePrefilteredCubeThisFrame = fs.runPrefilterSlice && (fs.sliceCompletesCycle || !m_haveConvergedResult);
742 fs.runIrradiancePass = fs.enableIBL && fs.writePrefilteredCubeThisFrame;
745void QSSGRenderSkyMaterialManager::validateAndUpdateCacheKey(
const FrameState &fs, QSSGRhiShaderPipeline *envShaderPipelineKey)
747 const bool cacheValid = m_cache.environmentMapSize == fs.environmentMapSize && m_cache.enableIBL == fs.enableIBL
748 && m_cache.prefilterTotalMipCount == fs.prefilterTotalMipCount && m_cache.envCubeMap == m_envCubeMap
749 && m_cache.prefilteredCubeMap == m_prefilteredCubeMap && m_cache.prefilterAccumulators == m_prefilterAccumulators
750 && m_cache.envShaderPipeline == envShaderPipelineKey;
753 clearPrefilterCache();
754 m_cache.environmentMapSize = fs.environmentMapSize;
755 m_cache.enableIBL = fs.enableIBL;
756 m_cache.prefilterTotalMipCount = fs.prefilterTotalMipCount;
757 m_cache.envCubeMap = m_envCubeMap;
758 m_cache.prefilteredCubeMap = m_prefilteredCubeMap;
759 m_cache.prefilterAccumulators = m_prefilterAccumulators;
760 m_cache.envShaderPipeline = envShaderPipelineKey;
763bool QSSGRenderSkyMaterialManager::ensureSharedResources(FrameState &fs, QRhiCommandBuffer *cb)
765 const auto &context = m_context.rhiContext();
766 auto *rhi = context->rhi();
768 fs.inputLayout.setBindings({ { 3 *
sizeof(
float) } });
769 fs.inputLayout.setAttributes({ { 0, 0, QRhiVertexInputAttribute::Float3, 0 } });
771 fs.mvp = rhi->clipSpaceCorrMatrix();
772 fs.mvp.perspective(90.0f, 1.0f, 0.1f, 10.0f);
773 fs.views = skyIblEnvironmentMapViews(rhi);
775 fs.ubufElementSize = rhi->ubufAligned(128);
777 if (!m_cache.vertexBuffer) {
778 m_cache.vertexBuffer = rhi->newBuffer(QRhiBuffer::Immutable, QRhiBuffer::VertexBuffer,
sizeof(skyIblCubeVerts));
779 if (!m_cache.vertexBuffer->create()) {
780 qWarning(
"Failed to create sky IBL vertex buffer");
781 delete m_cache.vertexBuffer;
782 m_cache.vertexBuffer =
nullptr;
785 auto *initRub = rhi->nextResourceUpdateBatch();
786 initRub->uploadStaticBuffer(m_cache.vertexBuffer, skyIblCubeVerts);
787 cb->resourceUpdate(initRub);
789 if (!ensureDynamicUBuf(rhi, m_cache.uBuf, fs.ubufElementSize * 6,
"Failed to create sky IBL view uniform buffer"))
791 fs.vbufBinding = QRhiCommandBuffer::VertexInput(m_cache.vertexBuffer, 0);
793 if (!m_cache.prefilterTargets.renderPassDesc) {
794 if (!skyIblCreatePrefilterTargets(rhi, m_prefilteredCubeMap, fs.environmentMapSize,
"SkyMaterialLightProbe procEnvPf"_ba, &m_cache.prefilterTargets)) {
799 if (!ensureDynamicUBuf(rhi, m_cache.uBufIrradiance, rhi->ubufAligned(20),
"Failed to create sky IBL irradiance uniform buffer"))
802 const QSSGRhiSamplerDescription samplerNoMipDesc { QRhiSampler::Linear, QRhiSampler::Linear,
803 QRhiSampler::None, QRhiSampler::ClampToEdge,
804 QRhiSampler::ClampToEdge, QRhiSampler::Repeat };
805 QRhiSampler *envMapCubeNoMipSampler = context->sampler(samplerNoMipDesc);
808 m_cache.irradianceSrb,
809 { QRhiShaderResourceBinding::uniformBufferWithDynamicOffset(0, QRhiShaderResourceBinding::VertexStage, m_cache.uBuf, 128),
810 QRhiShaderResourceBinding::uniformBufferWithDynamicOffset(2, QRhiShaderResourceBinding::FragmentStage, m_cache.uBufIrradiance, 20),
811 QRhiShaderResourceBinding::sampledTexture(1, QRhiShaderResourceBinding::FragmentStage, m_envCubeMap, envMapCubeNoMipSampler) },
812 "Failed to create sky IBL irradiance SRB"))
815 if (!m_cache.irradiancePipeline) {
816 const auto &shader = m_context.shaderCache()->getBuiltInRhiShaders().getRhienvironmentmapPreFilterShader(
false);
817 m_cache.irradiancePipeline = createPrefilterPipeline(rhi,
820 m_cache.irradianceSrb,
821 m_cache.prefilterTargets.renderPassDesc,
823 "Failed to create sky IBL realtime irradiance pipeline "
825 if (!m_cache.irradiancePipeline)
831bool QSSGRenderSkyMaterialManager::renderEnvironmentCube(QSSGRenderSkyMaterial *inSky,
832 const FrameState &fs,
833 const QSSGRhiShaderPipelinePtr &shaderPipeline,
834 QRhiCommandBuffer *cb,
835 QRhiResourceUpdateBatch *rub)
837 const auto &context = m_context.rhiContext();
838 auto *rhi = context->rhi();
839 QSSGRhiContextPrivate *rhiCtxD = QSSGRhiContextPrivate::get(context.get());
841 const quint32 skyElementSize = inSky->updateUniforms(m_context, fs.mvp, fs.views);
843 if (!m_cache.envFaceTargets.renderPassDesc) {
844 if (!skyIblCreateFaceTargets(rhi, m_envCubeMap,
"SkyMaterialLightProbe procEnvCube"_ba, &m_cache.envFaceTargets))
848 QRhiShaderResourceBindings *envSrb = rhiCtxD->srb(inSky->bindings);
851 if (!m_cache.envMapPipeline) {
852 m_cache.envMapPipeline = createPrefilterPipeline(rhi,
854 { shaderPipeline.get(), envSrb, m_cache.envFaceTargets.renderPassDesc,
false },
855 "Failed to create sky IBL env map pipeline state");
856 if (!m_cache.envMapPipeline)
860 cb->resourceUpdate(rub);
862 cb->debugMarkBegin(
"Sky IBL Procedural Environment Cubemap Generation");
863 for (
const auto face : QSSGRenderTextureCubeFaces) {
864 const QVector<QPair<
int, quint32>> offsets = { { 0, quint32(skyElementSize * quint8(face)) } };
867 m_cache.envFaceTargets.renderTargets[quint8(face)],
868 fs.environmentMapSize,
869 m_cache.envMapPipeline,
873 QByteArrayLiteral(
"sky_ibl_procedural_environment_map"),
874 QSSG_RENDERPASS_NAME(
"sky_ibl_procedural_environment_map", 0, face));
878 inSky->isDirty =
false;
882void QSSGRenderSkyMaterialManager::runEnvironmentMipChain(
const FrameState &fs, QRhiCommandBuffer *cb)
884 const auto &context = m_context.rhiContext();
885 auto *rhi = context->rhi();
887 if (!m_envTailMipsInitialized) {
888 const int envFullMipCount = rhi->mipLevelsForSize(fs.environmentMapSize);
889 skyIblInitializeUnrenderedMips(rhi, cb, context.get(), m_envCubeMap, 1, envFullMipCount,
"SkyMaterialLightProbe procEnvCube"_ba);
890 m_envTailMipsInitialized =
true;
893 auto *rubMip = rhi->nextResourceUpdateBatch();
894 rubMip->generateMips(m_envCubeMap);
895 cb->resourceUpdate(rubMip);
898bool QSSGRenderSkyMaterialManager::runPrefilterCycle(QSSGRenderSkyMaterial *inSky,
899 const FrameState &fs,
900 QRhiCommandBuffer *cb,
901 QRhiResourceUpdateBatch *&rub)
904 if (!fs.runPrefilterSlice) {
905 cb->resourceUpdate(rub);
910 const auto &context = m_context.rhiContext();
911 auto *rhi = context->rhi();
923 if (m_cache.accumPreserveFaceTargets.size() != fs.prefilterSpecularMipCount) {
925 for (QSSGSkyIblFaceTargets &t : m_cache.accumPreserveFaceTargets) {
926 for (QRhiTextureRenderTarget *rt : t.renderTargets)
928 delete t.renderPassDesc;
930 m_cache.accumPreserveFaceTargets.clear();
931 m_cache.accumPreserveFaceTargets.resize(fs.prefilterSpecularMipCount);
933 for (
int mip = 0; mip < fs.prefilterSpecularMipCount; ++mip) {
934 if (!skyIblCreateFaceTargets(rhi,
935 m_prefilterAccumulators[mip],
936 "SkyMaterialLightProbe procEnvPfAccum/m"_ba + QByteArray::number(mip),
937 &m_cache.accumPreserveFaceTargets[mip],
944 if (m_cache.accumClearFaceTargets.size() != fs.prefilterSpecularMipCount) {
945 for (QSSGSkyIblFaceTargets &t : m_cache.accumClearFaceTargets) {
946 for (QRhiTextureRenderTarget *rt : t.renderTargets)
948 delete t.renderPassDesc;
950 m_cache.accumClearFaceTargets.clear();
951 m_cache.accumClearFaceTargets.resize(fs.prefilterSpecularMipCount);
953 for (
int mip = 0; mip < fs.prefilterSpecularMipCount; ++mip) {
954 if (!skyIblCreateFaceTargets(rhi,
955 m_prefilterAccumulators[mip],
956 "SkyMaterialLightProbe procEnvPfAccumClear/m"_ba + QByteArray::number(mip),
957 &m_cache.accumClearFaceTargets[mip],
964 QSSGSkyIblPrefilterTargets &prefilterTargets = m_cache.prefilterTargets;
966 constexpr int uBufSliceSize = 32;
967 constexpr int uBufNormalizeSize = 16;
968 const int uBufSliceElementSize = rhi->ubufAligned(uBufSliceSize);
969 const int uBufNormalizeElementSize = rhi->ubufAligned(uBufNormalizeSize);
970 const int uBufNormalizeEntryCount = qMax(fs.prefilterSpecularMipCount, 1) * 6;
972 if (!ensureDynamicUBuf(rhi, m_cache.uBufSlice, uBufSliceElementSize * qMax(fs.prefilterSpecularMipCount, 1),
"Failed to create sky IBL slice uniform buffer"))
974 if (!ensureDynamicUBuf(rhi, m_cache.uBufNormalize, uBufNormalizeElementSize * uBufNormalizeEntryCount,
"Failed to create sky IBL normalize uniform buffer"))
977 const QSSGRhiSamplerDescription mipSamplerDesc { QRhiSampler::Linear, QRhiSampler::Linear,
978 QRhiSampler::Linear, QRhiSampler::ClampToEdge,
979 QRhiSampler::ClampToEdge, QRhiSampler::Repeat };
980 const QSSGRhiSamplerDescription noMipLinearSamplerDesc { QRhiSampler::Linear, QRhiSampler::Linear,
981 QRhiSampler::None, QRhiSampler::ClampToEdge,
982 QRhiSampler::ClampToEdge, QRhiSampler::Repeat };
983 const QSSGRhiSamplerDescription nearestSamplerDesc { QRhiSampler::Nearest, QRhiSampler::Nearest,
984 QRhiSampler::None, QRhiSampler::ClampToEdge,
985 QRhiSampler::ClampToEdge, QRhiSampler::Repeat };
986 QRhiSampler *envMapCubeSampler = context->sampler(fs.enableIBL ? mipSamplerDesc : noMipLinearSamplerDesc);
987 QRhiSampler *accumReadSampler = context->sampler(nearestSamplerDesc);
991 { QRhiShaderResourceBinding::uniformBufferWithDynamicOffset(0, QRhiShaderResourceBinding::VertexStage, m_cache.uBuf, 128),
992 QRhiShaderResourceBinding::uniformBufferWithDynamicOffset(2, QRhiShaderResourceBinding::FragmentStage, m_cache.uBufSlice, uBufSliceSize),
993 QRhiShaderResourceBinding::sampledTexture(1, QRhiShaderResourceBinding::FragmentStage, m_envCubeMap, envMapCubeSampler) },
994 "Failed to create sky IBL slice SRB"))
1002 if (m_cache.normalizeSrbs.size() != fs.prefilterSpecularMipCount) {
1003 for (QRhiShaderResourceBindings *srb : std::as_const(m_cache.normalizeSrbs))
1005 m_cache.normalizeSrbs.clear();
1006 m_cache.normalizeSrbs.resize(fs.prefilterSpecularMipCount,
nullptr);
1008 for (
int mip = 0; mip < fs.prefilterSpecularMipCount; ++mip) {
1010 m_cache.normalizeSrbs[mip],
1011 { QRhiShaderResourceBinding::uniformBufferWithDynamicOffset(0, QRhiShaderResourceBinding::VertexStage, m_cache.uBuf, 128),
1012 QRhiShaderResourceBinding::uniformBufferWithDynamicOffset(2, QRhiShaderResourceBinding::FragmentStage, m_cache.uBufNormalize, uBufNormalizeSize),
1013 QRhiShaderResourceBinding::sampledTexture(1, QRhiShaderResourceBinding::FragmentStage, m_prefilterAccumulators[mip], accumReadSampler) },
1014 "Failed to create sky IBL normalize SRB"))
1018 if (!m_cache.slicePipeline) {
1019 const auto &shader = m_context.shaderCache()->getBuiltInRhiShaders().getRhiSkyIblPreFilterShader();
1020 m_cache.slicePipeline = createPrefilterPipeline(rhi,
1024 m_cache.accumPreserveFaceTargets[0].renderPassDesc,
1026 "Failed to create sky IBL slice pipeline state");
1027 if (!m_cache.slicePipeline)
1030 if (!m_cache.normalizeCubePipeline) {
1032 const auto &shader = m_context.shaderCache()->getBuiltInRhiShaders().getRhiSkyIblPreFilterNormalizeShader();
1033 m_cache.normalizeCubePipeline = createPrefilterPipeline(rhi,
1036 m_cache.normalizeSrbs[0],
1037 prefilterTargets.renderPassDesc,
1039 "Failed to create sky IBL normalize-to-cube pipeline "
1041 if (!m_cache.normalizeCubePipeline)
1045 for (
int mipLevel = 0; mipLevel < fs.prefilterSpecularMipCount; ++mipLevel) {
1050 quint32 sampleStart;
1052 quint32 totalSampleCount;
1057 sliceData.roughness =
float(mipLevel) /
float(fs.prefilterRoughnessDenom);
1058 sliceData.resolution = fs.resolution;
1059 sliceData.sampleStart = quint32(fs.sliceSampleStart);
1060 sliceData.sampleEnd = quint32(fs.sliceSampleEnd);
1061 sliceData.totalSampleCount = quint32(fs.totalSamples);
1062 sliceData._pad0 = sliceData._pad1 = sliceData._pad2 = 0;
1063 rub->updateDynamicBuffer(m_cache.uBufSlice, mipLevel * uBufSliceElementSize,
sizeof(SliceData), &sliceData);
1065 for (
const auto face : QSSGRenderTextureCubeFaces) {
1066 struct NormalizeData
1073 normalizeData.faceIndex = quint8(face);
1074 const int entryIndex = mipLevel * 6 + quint8(face);
1075 rub->updateDynamicBuffer(m_cache.uBufNormalize, entryIndex * uBufNormalizeElementSize,
sizeof(NormalizeData), &normalizeData);
1079 cb->resourceUpdate(rub);
1092 for (
int mipLevel = 0; mipLevel < fs.prefilterSpecularMipCount; ++mipLevel) {
1093 QSSGSkyIblFaceTargets &sliceTargets = fs.isFirstSlice ? m_cache.accumClearFaceTargets[mipLevel]
1094 : m_cache.accumPreserveFaceTargets[mipLevel];
1095 const QSize mipSize(qMax(1, fs.environmentMapSize.width() >> mipLevel), qMax(1, fs.environmentMapSize.height() >> mipLevel));
1097 for (
const auto face : QSSGRenderTextureCubeFaces) {
1098 const QVector<QPair<
int, quint32>> offsets = { { 0, quint32(fs.ubufElementSize * quint8(face)) },
1099 { 2, quint32(uBufSliceElementSize * mipLevel) } };
1102 sliceTargets.renderTargets[quint8(face)],
1104 m_cache.slicePipeline,
1108 QByteArrayLiteral(
"sky_ibl_prefilter_slice"),
1109 QSSG_RENDERPASS_NAME(
"sky_ibl_prefilter_slice", mipLevel, face),
1110 QColor(0, 0, 0, 0));
1114 if (fs.sliceCompletesCycle || !fs.haveConvergedResultEntering) {
1115 for (
int mipLevel = 0; mipLevel < fs.prefilterSpecularMipCount; ++mipLevel) {
1116 for (
const auto face : QSSGRenderTextureCubeFaces) {
1117 const int normalizeEntryIndex = mipLevel * 6 + quint8(face);
1118 const QVector<QPair<
int, quint32>> offsets = { { 0, quint32(fs.ubufElementSize * quint8(face)) },
1119 { 2, quint32(uBufNormalizeElementSize * normalizeEntryIndex) } };
1122 prefilterTargets.mipRenderTargetsMap[mipLevel][quint8(face)],
1123 prefilterTargets.mipLevelSizes[mipLevel],
1124 m_cache.normalizeCubePipeline,
1125 m_cache.normalizeSrbs[mipLevel],
1128 QByteArrayLiteral(
"sky_ibl_prefilter_normalize"),
1129 QSSG_RENDERPASS_NAME(
"sky_ibl_prefilter_normalize", mipLevel, face));
1134 if (fs.enableIBL && (fs.sliceCompletesCycle || !fs.haveConvergedResultEntering)) {
1135 const int irradianceMip = prefilterTargets.mipmapCount - 1;
1136 for (
const auto face : QSSGRenderTextureCubeFaces) {
1137 const QVector<QPair<
int, quint32>> offsets = { { 0, quint32(fs.ubufElementSize * quint8(face)) }, { 2, 0u } };
1140 prefilterTargets.mipRenderTargetsMap[irradianceMip][quint8(face)],
1141 prefilterTargets.mipLevelSizes[irradianceMip],
1142 m_cache.irradiancePipeline,
1143 m_cache.irradianceSrb,
1146 QByteArrayLiteral(
"sky_ibl_irradiance"),
1147 QSSG_RENDERPASS_NAME(
"sky_ibl_irradiance", irradianceMip, face));
1151 m_accumulatedSamples = fs.sliceSampleEnd;
1152 if (fs.sliceCompletesCycle)
1153 m_haveConvergedResult =
true;
1158void QSSGRenderSkyMaterialManager::initializeTailMips(
const FrameState &fs, QRhiCommandBuffer *cb)
1160 if (!m_prefilteredCubeMap->flags().testFlag(QRhiTexture::MipMapped) || m_prefilteredTailMipsInitialized)
1162 const auto &context = m_context.rhiContext();
1163 auto *rhi = context->rhi();
1164 const int prefilteredFullMipCount = rhi->mipLevelsForSize(fs.environmentMapSize);
1172 const int firstUnwrittenMip = fs.enableIBL ? m_cache.prefilterTargets.mipmapCount : 1;
1173 skyIblInitializeUnrenderedMips(rhi, cb, context.get(), m_prefilteredCubeMap, firstUnwrittenMip, prefilteredFullMipCount,
"SkyMaterialLightProbe"_ba);
1174 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