216void QSSGRenderShadowMap::addShadowMaps(
const QSSGShaderLightList &renderableLights)
218 QRhi *rhi = m_context.rhiContext()->rhi();
224 if (!m_shadowMapBlueNoiseTexture) {
232 QImage blueNoiseImage(QStringLiteral(
":/res/textures/blue_noise.png"));
233 if (blueNoiseImage.isNull()) {
234 qWarning(
"Failed to load blue noise texture!");
236 Q_ASSERT(blueNoiseImage.size() == QSize(64, 64));
237 Q_ASSERT(blueNoiseImage.format() == QImage::Format_ARGB32);
238 blueNoiseImage = blueNoiseImage.convertToFormat(QImage::Format_RGBA8888);
239 m_shadowMapBlueNoiseTexture.reset(allocateRhiShadowTexture(rhi, QRhiTexture::RGBA8, QSize(64, 64), 0, {}));
240 if (!m_shadowMapBlueNoiseTexture->create()) {
241 qWarning(
"Failed to create blue noise texture");
243 QRhiResourceUpdateBatch *rub = rhi->nextResourceUpdateBatch();
244 QRhiTextureSubresourceUploadDescription subresDesc(blueNoiseImage);
245 rub->uploadTexture(m_shadowMapBlueNoiseTexture.get(),
246 QRhiTextureUploadDescription({ QRhiTextureUploadEntry(0, 0, subresDesc) }));
247 QRhiCommandBuffer *cb = m_context.rhiContext()->commandBuffer();
248 cb->resourceUpdate(rub);
253 const quint32 numLights = renderableLights.size();
254 qsizetype numShadows = 0;
255 const bool supports32BitTextures = rhi->isTextureFormatSupported(QRhiTexture::R32F);
256 QRhiTexture::Format format = QRhiTexture::R16F;
258 QHash<quint32, QVector<QSSGShadowMapEntry::AtlasEntry>> lightIndexToAtlasEntries;
261 for (quint32 lightIndex = 0; lightIndex < numLights; ++lightIndex) {
262 const QSSGShaderLight &shaderLight = renderableLights.at(lightIndex);
263 if (!shaderLight.shadows)
267 if (shaderLight.light->m_use32BitShadowmap && supports32BitTextures)
268 format = QRhiTexture::R32F;
271 if (mapSize < shaderLight.light->m_shadowMapRes)
272 mapSize = shaderLight.light->m_shadowMapRes;
277 auto atlasPacker = AtlasHelpers::ShelfPacker(mapSize, mapSize);
282 for (quint32 lightIndex = 0; lightIndex < numLights; ++lightIndex) {
283 const QSSGShaderLight &shaderLight = renderableLights.at(lightIndex);
284 if (!shaderLight.shadows)
287 quint8 mapsNeeded = 0;
289 if (shaderLight.light->type == QSSGRenderLight::Type::DirectionalLight)
290 mapsNeeded = shaderLight.light->m_csmNumSplits + 1;
291 else if (shaderLight.light->type == QSSGRenderLight::Type::SpotLight)
293 else if (shaderLight.light->type == QSSGRenderLight::Type::PointLight)
296 QVarLengthArray<AtlasHelpers::ShelfPacker::AtlasPlacement, 4> atlasPlacements;
297 for (quint8 i = 0; i < mapsNeeded; ++i) {
298 const int size = shaderLight.light->m_shadowMapRes;
299 auto result = atlasPacker.addRectangle(size);
301 atlasPlacements.push_back(result);
303 lightIndexToAtlasEntries.insert(lightIndex, createAtlasEntries(atlasPlacements, shaderLight.light->m_shadowMapRes, mapSize));
305 const QSize texSize = QSize(mapSize, mapSize);
306 const quint32 layersNeeded = atlasPacker.pagesNeeded();
309 bool needsRebuild = numShadows != shadowMapEntryCount();
310 if (!m_shadowMapAtlasTexture ||
311 m_shadowMapAtlasTexture->pixelSize() != texSize ||
312 m_shadowMapAtlasTexture->arraySize() !=
int(layersNeeded) ||
313 m_shadowMapAtlasTexture->format() != format) {
316 if (m_shadowMapAtlasTexture) {
317 m_shadowMapAtlasTexture.reset();
320 m_shadowMapAtlasTexture.reset(allocateRhiShadowTexture(rhi, format, texSize, layersNeeded, QRhiTexture::RenderTarget | QRhiTexture::TextureArray));
322 qDeleteAll(m_layerDepthStencilBuffers);
323 m_layerDepthStencilBuffers.clear();
324 qDeleteAll(m_layerRenderTargets);
325 m_layerRenderTargets.clear();
326 qDeleteAll(m_layerRenderPassDescriptors);
327 m_layerRenderPassDescriptors.clear();
329 for (quint32 i = 0; i < layersNeeded; ++i) {
331 QRhiRenderBuffer *depthStencilBuffer = allocateRhiShadowRenderBuffer(rhi, QRhiRenderBuffer::DepthStencil, texSize);
332 QRhiTextureRenderTargetDescription rtDesc;
333 QRhiColorAttachment attachment(m_shadowMapAtlasTexture.get());
334 attachment.setLayer(i);
335 rtDesc.setColorAttachments({ attachment });
336 rtDesc.setDepthStencilBuffer(depthStencilBuffer);
337 QRhiTextureRenderTarget *rt = rhi->newTextureRenderTarget(rtDesc);
338 rt->setDescription(rtDesc);
339 rt->setFlags(QRhiTextureRenderTarget::PreserveColorContents);
340 QRhiRenderPassDescriptor *rpDesc = rt->newCompatibleRenderPassDescriptor();
341 rt->setRenderPassDescriptor(rpDesc);
343 qWarning(
"Failed to build shadow map render target");
344 rt->setName(QByteArrayLiteral(
"shadow map atlas layer") + QByteArray::number(i));
345 m_layerDepthStencilBuffers.append(depthStencilBuffer);
346 m_layerRenderTargets.append(rt);
347 m_layerRenderPassDescriptors.append(rpDesc);
354 if (!m_sharedFrontCubeToAtlasUniformBuffer || !m_sharedBackCubeToAtlasUniformBuffer) {
355 const quint32 uniformValueFront = 0;
356 const quint32 uniformValueBack = 1;
358 QRhiResourceUpdateBatch *rub = rhi->nextResourceUpdateBatch();
359 if (!m_sharedFrontCubeToAtlasUniformBuffer) {
360 m_sharedFrontCubeToAtlasUniformBuffer.reset(rhi->newBuffer(QRhiBuffer::Dynamic, QRhiBuffer::UniformBuffer,
sizeof(uint32_t)));
361 m_sharedFrontCubeToAtlasUniformBuffer->create();
362 rub->updateDynamicBuffer(m_sharedFrontCubeToAtlasUniformBuffer.get(), 0,
sizeof(quint32), &uniformValueFront);
365 if (!m_sharedBackCubeToAtlasUniformBuffer) {
366 m_sharedBackCubeToAtlasUniformBuffer.reset(rhi->newBuffer(QRhiBuffer::Dynamic, QRhiBuffer::UniformBuffer,
sizeof(uint32_t)));
367 m_sharedBackCubeToAtlasUniformBuffer->create();
368 rub->updateDynamicBuffer(m_sharedBackCubeToAtlasUniformBuffer.get(), 0,
sizeof(quint32), &uniformValueBack);
370 QRhiCommandBuffer *cb = m_context.rhiContext()->commandBuffer();
371 cb->resourceUpdate(rub);
373 if (!m_sharedCubeToAtlasSampler) {
374 m_sharedCubeToAtlasSampler.reset(rhi->newSampler(QRhiSampler::Linear, QRhiSampler::Linear, QRhiSampler::None, QRhiSampler::ClampToEdge, QRhiSampler::ClampToEdge, QRhiSampler::ClampToEdge));
375 m_sharedCubeToAtlasSampler->create();
377 if (!m_shadowClearSrb) {
378 m_shadowClearSrb.reset(rhi->newShaderResourceBindings());
379 m_shadowClearSrb->create();
385 for (quint32 lightIndex = 0; lightIndex < numLights; ++lightIndex) {
386 const QSSGShaderLight &shaderLight = renderableLights.at(lightIndex);
387 if (!shaderLight.shadows)
389 QSSGShadowMapEntry *pEntry = shadowMapEntry(lightIndex);
395 const auto &atlasEntires = lightIndexToAtlasEntries.value(lightIndex);
396 if (!checkCompatibility(pEntry, atlasEntires)) {
407 for (QSSGShadowMapEntry &entry : m_shadowMapList)
408 entry.destroyRhiResources();
409 m_shadowMapList.clear();
411 for (quint32 lightIndex = 0; lightIndex < numLights; ++lightIndex) {
412 const QSSGShaderLight &shaderLight = renderableLights.at(lightIndex);
413 if (!shaderLight.shadows)
415 addShadowMap(lightIndex,
417 lightIndexToAtlasEntries.value(lightIndex),
418 shaderLight.light->m_csmNumSplits,
420 shaderLight.light->type == QSSGRenderLight::Type::PointLight,
421 shaderLight.light->debugObjectName);