Qt
Internal/Contributor docs for the Qt SDK. Note: These are NOT official API docs; those are found at https://doc.qt.io/
Loading...
Searching...
No Matches
qssgrendershadowmap.cpp
Go to the documentation of this file.
1// Copyright (C) 2008-2012 NVIDIA Corporation.
2// Copyright (C) 2019 The Qt Company Ltd.
3// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
4// Qt-Security score:significant reason:default
5
6
7#include <QtQuick3DRuntimeRender/private/qssgrenderlayer_p.h>
8#include <QtQuick3DRuntimeRender/private/qssgrendershadowmap_p.h>
9#include <QtQuick3DRuntimeRender/private/qssglayerrenderdata_p.h>
11
13
14namespace AtlasHelpers {
15
17 struct Shelf {
18 int y;
19 int height;
20 int curX;
21 };
22
23 struct ShelfPage {
25 int width;
26 int height;
27 int curY;
29 };
30
32 bool success;
34 int x;
35 int y;
36 };
37 const int pageWidth;
38 const int pageHeight;
40
41 ShelfPacker(int pageWidth, int pageHeight)
42 : pageWidth(pageWidth)
43 , pageHeight(pageHeight)
44 {
45 }
46
47 AtlasPlacement addRectangle(int sizeNeeded) {
48 // Try each page in turn
49 for (auto &page : pages) {
50 AtlasPlacement placement = placeOnPage(page, sizeNeeded);
51 if (placement.success)
52 return placement;
53 }
54
55 // If we get here, we need a new page
56 ShelfPage newPage;
57 newPage.pageIndex = int(pages.size());
58 newPage.width = pageWidth;
59 newPage.height = pageHeight;
60 newPage.curY = 0;
61 newPage.shelves.clear();
62 pages.push_back(newPage);
63
64 // now place in the new page
65 return placeOnPage(pages.back(), sizeNeeded);
66 }
67
68 AtlasPlacement placeOnPage(ShelfPage &page, int sizeNeeded)
69 {
70 AtlasPlacement result;
71 result.success = false;
72
73 // Iterate over shelves to see if we can place the rect
74 for (auto &shelf : page.shelves) {
75 // check if it fits horizontally
76 if (shelf.curX + sizeNeeded <= page.width) {
77 // also check if the shelf's height is enough
78 // but here we store the shelf's "height" as the max of the rectangles' heights
79 // If each rect is the same "sizeNeeded," we just need to see if there's room in page.height
80
81 // not enough vertical space
82 if (shelf.y + shelf.height >= page.height)
83 continue;
84
85 // place here
86 result.success = true;
87 result.pageIndex = page.pageIndex;
88 result.x = shelf.curX;
89 result.y = shelf.y;
90 // update shelf
91 shelf.curX += sizeNeeded;
92 if (sizeNeeded > shelf.height)
93 shelf.height = sizeNeeded;
94
95 return result;
96 }
97 }
98
99 // No existing shelf had space, so start a new shelf
100 int newShelfY = 0;
101 if (!page.shelves.empty()) {
102 const Shelf &lastShelf = page.shelves.back();
103 newShelfY = lastShelf.y + lastShelf.height;
104 }
105
106 // check if there's enough space left in the page vertically
107 if (newShelfY + sizeNeeded > page.height) {
108 // no space in this page
109 return result; // success = false
110 }
111
112 // create a new shelf
113 Shelf newShelf;
114 newShelf.y = newShelfY;
115 newShelf.height = sizeNeeded;
116 newShelf.curX = 0;
117 page.shelves.push_back(newShelf);
118
119 // place rect at top-left of this shelf
120 Shelf &shelfRef = page.shelves.back();
121 result.success = true;
122 result.pageIndex = page.pageIndex;
123 result.x = shelfRef.curX;
124 result.y = shelfRef.y;
125 shelfRef.curX += sizeNeeded;
126
127 return result;
128 }
129
130 int pagesNeeded() const { return int(pages.size()); }
131};
132
133}
134
135static QRhiTexture *allocateRhiShadowTexture(QRhi *rhi, QRhiTexture::Format format, const QSize &size, quint32 numLayers, QRhiTexture::Flags flags)
136{
137 auto texture = rhi->newTexture(format, size, 1, flags);
138 if (flags & QRhiTexture::TextureArray)
139 texture->setArraySize(numLayers);
140 if (!texture->create())
141 qWarning("Failed to create shadow map texture of size %dx%d", size.width(), size.height());
142 return texture;
143}
144
145static QRhiRenderBuffer *allocateRhiShadowRenderBuffer(QRhi *rhi, QRhiRenderBuffer::Type type, const QSize &size)
146{
147 auto renderBuffer = rhi->newRenderBuffer(type, size, 1);
148 if (!renderBuffer->create())
149 qWarning("Failed to build depth-stencil buffer of size %dx%d", size.width(), size.height());
150 return renderBuffer;
151}
152
153
154static bool checkCompatibility(QSSGShadowMapEntry *entry, const QVector<QSSGShadowMapEntry::AtlasEntry> &atlasEntries) {
155 if (!entry)
156 return false;
157
158 for (int i = 0; i < atlasEntries.size(); ++i) {
159 const auto &atlasEntry = atlasEntries.at(i);
160
161 if (!(entry->m_atlasInfo[i].layerIndex == atlasEntry.layerIndex &&
162 qFuzzyCompare(entry->m_atlasInfo[i].uOffset, atlasEntry.uOffset) &&
163 qFuzzyCompare(entry->m_atlasInfo[i].vOffset, atlasEntry.vOffset) &&
164 qFuzzyCompare(entry->m_atlasInfo[i].uvScale, atlasEntry.uvScale)))
165 return false;
166 }
167
168 return true;
169}
170
171static QVector<QSSGShadowMapEntry::AtlasEntry> createAtlasEntries(const QVarLengthArray<AtlasHelpers::ShelfPacker::AtlasPlacement, 4> &atlasPlacements, int entrySize, int atlasPageSize) {
172 QVector<QSSGShadowMapEntry::AtlasEntry> atlasEntries;
173 atlasEntries.reserve(atlasPlacements.size());
174 for (int i = 0; i < atlasPlacements.size(); ++i) {
175 const auto &placement = atlasPlacements.at(i);
176 const float x = float(placement.x) / float(atlasPageSize);
177 const float y = float(placement.y) / float(atlasPageSize);
178 const float uvScale = float(entrySize) / float(atlasPageSize);
179 atlasEntries.append({placement.pageIndex, x, y, uvScale});
180 }
181 return atlasEntries;
182}
183
184
185QSSGRenderShadowMap::QSSGRenderShadowMap(const QSSGRenderContextInterface &inContext)
186 : m_context(inContext)
187{
188}
189
190QSSGRenderShadowMap::~QSSGRenderShadowMap()
191{
192 releaseCachedResources();
193}
194
195void QSSGRenderShadowMap::releaseCachedResources()
196{
197 for (QSSGShadowMapEntry &entry : m_shadowMapList)
198 entry.destroyRhiResources();
199
200 if (m_shadowMapAtlasTexture)
201 m_shadowMapAtlasTexture.reset();
202
203 if (m_shadowMapBlueNoiseTexture)
204 m_shadowMapBlueNoiseTexture.reset();
205
206 qDeleteAll(m_layerDepthStencilBuffers);
207 m_layerDepthStencilBuffers.clear();
208 qDeleteAll(m_layerRenderTargets);
209 m_layerRenderTargets.clear();
210 qDeleteAll(m_layerRenderPassDescriptors);
211 m_layerRenderPassDescriptors.clear();
212
213 m_shadowMapList.clear();
214}
215
216void QSSGRenderShadowMap::addShadowMaps(const QSSGShaderLightList &renderableLights)
217{
218 QRhi *rhi = m_context.rhiContext()->rhi();
219 // Bail out if there is no QRhi, since we can't add entries without it
220 if (!rhi)
221 return;
222
223 // Handle blue noise texture
224 if (!m_shadowMapBlueNoiseTexture) {
225 // The blue noise image is based on two random blue noise images with values v0 and v1.
226 // Then the cos and sin of these are stored to avoid trigonmoetric functions
227 // in the shadow mapping shader.
228 // R = cos(v0)
229 // G = sin(v0)
230 // B = cos(v1)
231 // A = sin(v1)
232 QImage blueNoiseImage(QStringLiteral(":/res/textures/blue_noise.png"));
233 if (blueNoiseImage.isNull()) {
234 qWarning("Failed to load blue noise texture!");
235 } else {
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");
242 } else {
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);
249 }
250 }
251 }
252
253 const quint32 numLights = renderableLights.size();
254 qsizetype numShadows = 0;
255 const bool supports32BitTextures = rhi->isTextureFormatSupported(QRhiTexture::R32F);
256 QRhiTexture::Format format = QRhiTexture::R16F;
257 quint32 mapSize = 0;
258 QHash<quint32, QVector<QSSGShadowMapEntry::AtlasEntry>> lightIndexToAtlasEntries;
259
260 // Get format and maximum texture size needed
261 for (quint32 lightIndex = 0; lightIndex < numLights; ++lightIndex) {
262 const QSSGShaderLight &shaderLight = renderableLights.at(lightIndex);
263 if (!shaderLight.shadows)
264 continue;
265
266 // Force R32F format if any light requests it
267 if (shaderLight.light->m_use32BitShadowmap && supports32BitTextures)
268 format = QRhiTexture::R32F;
269
270 // Find the largest shadow map size needed
271 if (mapSize < shaderLight.light->m_shadowMapRes)
272 mapSize = shaderLight.light->m_shadowMapRes;
273
274 numShadows += 1;
275 }
276
277 auto atlasPacker = AtlasHelpers::ShelfPacker(mapSize, mapSize);
278
279
280 // Figure out the number of layers needed for the atlas
281 // including where everthing will go in the atlas
282 for (quint32 lightIndex = 0; lightIndex < numLights; ++lightIndex) {
283 const QSSGShaderLight &shaderLight = renderableLights.at(lightIndex);
284 if (!shaderLight.shadows)
285 continue;
286
287 quint8 mapsNeeded = 0;
288
289 if (shaderLight.light->type == QSSGRenderLight::Type::DirectionalLight)
290 mapsNeeded = shaderLight.light->m_csmNumSplits + 1;
291 else if (shaderLight.light->type == QSSGRenderLight::Type::SpotLight)
292 mapsNeeded = 1;
293 else if (shaderLight.light->type == QSSGRenderLight::Type::PointLight)
294 mapsNeeded = 2;
295
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);
300 if (result.success)
301 atlasPlacements.push_back(result);
302 }
303 lightIndexToAtlasEntries.insert(lightIndex, createAtlasEntries(atlasPlacements, shaderLight.light->m_shadowMapRes, mapSize));
304 }
305 const QSize texSize = QSize(mapSize, mapSize);
306 const quint32 layersNeeded = atlasPacker.pagesNeeded();
307
308 // Check if we need a rebuild
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) {
314
315 // If we need a new texture as well
316 if (m_shadowMapAtlasTexture) {
317 m_shadowMapAtlasTexture.reset();
318 }
319
320 m_shadowMapAtlasTexture.reset(allocateRhiShadowTexture(rhi, format, texSize, layersNeeded, QRhiTexture::RenderTarget | QRhiTexture::TextureArray));
321
322 qDeleteAll(m_layerDepthStencilBuffers);
323 m_layerDepthStencilBuffers.clear();
324 qDeleteAll(m_layerRenderTargets);
325 m_layerRenderTargets.clear();
326 qDeleteAll(m_layerRenderPassDescriptors);
327 m_layerRenderPassDescriptors.clear();
328
329 for (quint32 i = 0; i < layersNeeded; ++i) {
330 // Recreate per layer RenderBuffers, TextureRenderTarget, and RenderPassDescriptors
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); // Don't clear between passes since this is an atlas
340 QRhiRenderPassDescriptor *rpDesc = rt->newCompatibleRenderPassDescriptor();
341 rt->setRenderPassDescriptor(rpDesc);
342 if (!rt->create())
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);
348 }
349
350 needsRebuild = true;
351 }
352
353 // If we need to allocate the RHI resources
354 if (!m_sharedFrontCubeToAtlasUniformBuffer || !m_sharedBackCubeToAtlasUniformBuffer) {
355 const quint32 uniformValueFront = 0;
356 const quint32 uniformValueBack = 1;
357
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);
363 }
364
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);
369 }
370 QRhiCommandBuffer *cb = m_context.rhiContext()->commandBuffer();
371 cb->resourceUpdate(rub);
372 }
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();
376 }
377 if (!m_shadowClearSrb) {
378 m_shadowClearSrb.reset(rhi->newShaderResourceBindings());
379 m_shadowClearSrb->create();
380 }
381
382
383 if (!needsRebuild) {
384 // Check if relevant shadow properties has changed
385 for (quint32 lightIndex = 0; lightIndex < numLights; ++lightIndex) {
386 const QSSGShaderLight &shaderLight = renderableLights.at(lightIndex);
387 if (!shaderLight.shadows)
388 continue;
389 QSSGShadowMapEntry *pEntry = shadowMapEntry(lightIndex);
390 if (!pEntry) {
391 needsRebuild = true;
392 break;
393 }
394
395 const auto &atlasEntires = lightIndexToAtlasEntries.value(lightIndex);
396 if (!checkCompatibility(pEntry, atlasEntires)) {
397 needsRebuild = true;
398 break;
399 }
400 }
401 }
402
403 if (!needsRebuild)
404 return;
405
406 // Rebuild then
407 for (QSSGShadowMapEntry &entry : m_shadowMapList)
408 entry.destroyRhiResources();
409 m_shadowMapList.clear();
410
411 for (quint32 lightIndex = 0; lightIndex < numLights; ++lightIndex) {
412 const QSSGShaderLight &shaderLight = renderableLights.at(lightIndex);
413 if (!shaderLight.shadows)
414 continue;
415 addShadowMap(lightIndex,
416 texSize,
417 lightIndexToAtlasEntries.value(lightIndex),
418 shaderLight.light->m_csmNumSplits,
419 format,
420 shaderLight.light->type == QSSGRenderLight::Type::PointLight,
421 shaderLight.light->debugObjectName);
422 }
423
424}
425
426QSSGShadowMapEntry *QSSGRenderShadowMap::addShadowMap(quint32 lightIdx,
427 QSize size,
428 QVector<QSSGShadowMapEntry::AtlasEntry> atlasEntries,
429 quint32 csmNumSplits,
430 QRhiTexture::Format rhiFormat,
431 bool isPointLight,
432 const QString &renderNodeObjName)
433{
434 QRhi *rhi = m_context.rhiContext()->rhi();
435 QSSGShadowMapEntry *pEntry = shadowMapEntry(lightIdx);
436
437 Q_ASSERT(rhi);
438 Q_ASSERT(!pEntry);
439 Q_ASSERT(!atlasEntries.isEmpty());
440
441 m_shadowMapList.push_back(QSSGShadowMapEntry::withAtlas(lightIdx));
442
443 pEntry = &m_shadowMapList.back();
444 pEntry->m_csmNumSplits = csmNumSplits;
445
446 if (isPointLight) {
447 const QSize localSize = size * atlasEntries.first().uvScale;
448 // First pass renders to depth cube map
449 const QByteArray rtName = renderNodeObjName.toLatin1();
450
451 pEntry->m_rhiDepthCube = allocateRhiShadowTexture(rhi, rhiFormat, localSize, 0, QRhiTexture::RenderTarget | QRhiTexture::CubeMap);
452 pEntry->m_rhiDepthStencilCube = allocateRhiShadowRenderBuffer(rhi, QRhiRenderBuffer::DepthStencil, localSize);
453
454
455 for (const auto face : QSSGRenderTextureCubeFaces) {
456 QRhiTextureRenderTarget *&rt(pEntry->m_rhiRenderTargetCube[quint8(face)]);
457 Q_ASSERT(!rt);
458 QRhiColorAttachment att(pEntry->m_rhiDepthCube);
459 att.setLayer(quint8(face)); // 6 render targets, each referencing one face of the cubemap
460 QRhiTextureRenderTargetDescription rtDesc;
461 rtDesc.setColorAttachments({ att });
462 rtDesc.setDepthStencilBuffer(pEntry->m_rhiDepthStencilCube);
463 rt = rhi->newTextureRenderTarget(rtDesc);
464 rt->setDescription(rtDesc);
465 if (!pEntry->m_rhiRenderPassDescCube)
466 pEntry->m_rhiRenderPassDescCube = rt->newCompatibleRenderPassDescriptor();
467 rt->setRenderPassDescriptor(pEntry->m_rhiRenderPassDescCube);
468 if (!rt->create())
469 qWarning("Failed to build shadow map render target");
470 rt->setName(rtName + QByteArrayLiteral(" shadow cube face: ") + QSSGBaseTypeHelpers::displayName(face));
471 }
472
473 if (!pEntry->m_cubeToAtlasFrontSrb) {
474 pEntry->m_cubeToAtlasFrontSrb = rhi->newShaderResourceBindings();
475 pEntry->m_cubeToAtlasFrontSrb->setBindings({
476 QRhiShaderResourceBinding::uniformBuffer(0, QRhiShaderResourceBinding::FragmentStage, m_sharedFrontCubeToAtlasUniformBuffer.get()),
477 QRhiShaderResourceBinding::sampledTexture(1, QRhiShaderResourceBinding::FragmentStage, pEntry->m_rhiDepthCube, m_sharedCubeToAtlasSampler.get())
478 });
479 pEntry->m_cubeToAtlasFrontSrb->create();
480 }
481
482 if (!pEntry->m_cubeToAtlasBackSrb) {
483 pEntry->m_cubeToAtlasBackSrb = rhi->newShaderResourceBindings();
484 pEntry->m_cubeToAtlasBackSrb->setBindings({
485 QRhiShaderResourceBinding::uniformBuffer(0, QRhiShaderResourceBinding::FragmentStage, m_sharedBackCubeToAtlasUniformBuffer.get()),
486 QRhiShaderResourceBinding::sampledTexture(1, QRhiShaderResourceBinding::FragmentStage, pEntry->m_rhiDepthCube, m_sharedCubeToAtlasSampler.get())
487 });
488 pEntry->m_cubeToAtlasBackSrb->create();
489 }
490 }
491
492
493 // Additional graphics resources: samplers, render targets.
494 const quint32 entriesCount = atlasEntries.size();
495 for (quint32 splitIndex = 0; splitIndex < entriesCount; splitIndex++) {
496 pEntry->m_atlasInfo[splitIndex].layerIndex = atlasEntries.at(splitIndex).layerIndex;
497 pEntry->m_atlasInfo[splitIndex].uOffset = atlasEntries.at(splitIndex).uOffset;
498 pEntry->m_atlasInfo[splitIndex].vOffset = atlasEntries.at(splitIndex).vOffset;
499 pEntry->m_atlasInfo[splitIndex].uvScale = atlasEntries.at(splitIndex).uvScale;
500
501 quint32 layerId = atlasEntries.at(splitIndex).layerIndex;
502 pEntry->m_rhiRenderTargets[splitIndex] = m_layerRenderTargets[layerId];
503 pEntry->m_rhiRenderPassDesc[splitIndex] = m_layerRenderPassDescriptors[layerId];
504 }
505 pEntry->m_lightIndex = lightIdx;
506
507 return pEntry;
508}
509
510QSSGShadowMapEntry *QSSGRenderShadowMap::shadowMapEntry(int lightIdx)
511{
512 Q_ASSERT(lightIdx >= 0);
513
514 for (int i = 0; i < m_shadowMapList.size(); i++) {
515 QSSGShadowMapEntry *pEntry = &m_shadowMapList[i];
516 if (pEntry->m_lightIndex == quint32(lightIdx))
517 return pEntry;
518 }
519
520 return nullptr;
521}
522
523QRhiTextureRenderTarget *QSSGRenderShadowMap::layerRenderTarget(int layerIndex)
524{
525 if (layerIndex < 0 || layerIndex >= m_layerRenderTargets.size())
526 return nullptr;
527 return m_layerRenderTargets.at(layerIndex);
528}
529
530QRhiRenderPassDescriptor *QSSGRenderShadowMap::layerRenderPassDescriptor(int layerIndex)
531{
532 if (layerIndex < 0 || layerIndex >= m_layerRenderPassDescriptors.size())
533 return nullptr;
534 return m_layerRenderPassDescriptors.at(layerIndex);
535}
536
537QRhiTexture *QSSGRenderShadowMap::shadowMapAtlasTexture() const
538{
539 return m_shadowMapAtlasTexture.get();
540}
541
542QRhiTexture *QSSGRenderShadowMap::shadowMapBlueNoiseTexture() const
543{
544 return m_shadowMapBlueNoiseTexture.get();
545}
546
547QSSGShadowMapEntry::QSSGShadowMapEntry()
548 : m_lightIndex(std::numeric_limits<quint32>::max())
549{
550}
551
552QSSGShadowMapEntry QSSGShadowMapEntry::withAtlas(quint32 lightIdx)
553{
554 QSSGShadowMapEntry e;
555 e.m_lightIndex = lightIdx;
556 return e;
557}
558
559void QSSGShadowMapEntry::destroyRhiResources()
560{
561 delete m_rhiDepthCube;
562 m_rhiDepthCube = nullptr;
563 delete m_rhiDepthStencilCube;
564 m_rhiDepthStencilCube = nullptr;
565 qDeleteAll(m_rhiRenderTargetCube);
566 m_rhiRenderTargetCube.fill(nullptr);
567 delete m_rhiRenderPassDescCube;
568 m_rhiRenderPassDescCube = nullptr;
569 delete m_cubeToAtlasFrontSrb;
570 m_cubeToAtlasFrontSrb = nullptr;
571 delete m_cubeToAtlasBackSrb;
572 m_cubeToAtlasBackSrb = nullptr;
573
574 // un-owned references
575 m_rhiRenderTargets.fill(nullptr);
576 m_rhiRenderPassDesc.fill(nullptr);
577}
578
579QT_END_NAMESPACE
Combined button and popup list for selecting options.
static QVector< QSSGShadowMapEntry::AtlasEntry > createAtlasEntries(const QVarLengthArray< AtlasHelpers::ShelfPacker::AtlasPlacement, 4 > &atlasPlacements, int entrySize, int atlasPageSize)
static bool checkCompatibility(QSSGShadowMapEntry *entry, const QVector< QSSGShadowMapEntry::AtlasEntry > &atlasEntries)
static QRhiTexture * allocateRhiShadowTexture(QRhi *rhi, QRhiTexture::Format format, const QSize &size, quint32 numLayers, QRhiTexture::Flags flags)
static QRhiRenderBuffer * allocateRhiShadowRenderBuffer(QRhi *rhi, QRhiRenderBuffer::Type type, const QSize &size)
AtlasPlacement addRectangle(int sizeNeeded)
AtlasPlacement placeOnPage(ShelfPage &page, int sizeNeeded)
std::vector< ShelfPage > pages
ShelfPacker(int pageWidth, int pageHeight)