9#include <QtQuick3DRuntimeRender/private/qssgrenderloadedtexture_p.h>
11#include <QtQuick3DRuntimeRender/private/qssgruntimerenderlogging_p.h>
12#include <QtQuick3DUtils/private/qssgmeshbvhbuilder_p.h>
13#include <QtQuick3DUtils/private/qssgbounds3_p.h>
14#include <QtQuick3DUtils/private/qssgassert_p.h>
16#include <QtQuick/QSGTexture>
19#include <QtGui/private/qimage_p.h>
20#include <QtQuick/private/qsgtexture_p.h>
21#include <QtQuick/private/qsgcompressedtexture_p.h>
24#include "../utils/qssgrenderbasetypes_p.h"
25#include <QtQuick3DRuntimeRender/private/qssgrendergeometry_p.h>
26#include <QtQuick3DRuntimeRender/private/qssgrendermodel_p.h>
27#include <QtQuick3DRuntimeRender/private/qssgrenderimage_p.h>
28#include <QtQuick3DRuntimeRender/private/qssgrendertexturedata_p.h>
29#include "../qssgrendercontextcore.h"
30#include <QtQuick3DRuntimeRender/private/qssglightmapper_p.h>
31#include <QtQuick3DRuntimeRender/private/qssgrenderresourceloader_p.h>
32#include <qtquick3d_tracepoints_p.h>
33#include "../extensionapi/qssgrenderextensions.h"
38using namespace Qt::StringLiterals;
84using AssetMeshMap = QHash<QString, MeshStorageRef>;
86Q_GLOBAL_STATIC(AssetMeshMap, g_assetMeshMap)
89QString QSSGBufferManager::runtimeMeshSourceName(
const QString &assetId, qsizetype meshId)
91 return QString::fromUtf16(u"!%1@%2").arg(QString::number(meshId), assetId);
94using MeshIdxNamePair = QPair<qsizetype, QString>;
97 const auto &path = rpath.path();
98 Q_ASSERT(path.startsWith(u'!'));
99 const auto strings = path.mid(1).split(u'@');
100 const bool hasData = (strings.size() == 2) && !strings[0].isEmpty() && !strings[1].isEmpty();
104 idx = strings.at(0).toLongLong(&ok);
106 return (ok) ? qMakePair(idx, strings.at(1)) : qMakePair(qsizetype(-1), QString());
113 const char *primitive;
121 {
"#Rectangle",
"/Rectangle.mesh"},
122 {
"#Sphere",
"/Sphere.mesh"},
123 {
"#Cube",
"/Cube.mesh"},
124 {
"#Cone",
"/Cone.mesh"},
125 {
"#Cylinder",
"/Cylinder.mesh"},
132 return QSize(qMax(1, baseLevelSize.width() >> mipLevel), qMax(1, baseLevelSize.height() >> mipLevel));
137 QPair<QSSGMesh::Mesh, QString> retVal;
139 if (lightmapPath.isEmpty() || lightmapKey.isEmpty())
142 if (
const auto io = QSSGLightmapLoader::open(lightmapPath)) {
144 if (metadata.isEmpty())
147 const QString meshKey = metadata[QStringLiteral(
"mesh_key")].toString();
148 if (meshKey.isEmpty())
152 if (meshData.isEmpty())
155 QBuffer buffer(&meshData);
156 buffer.open(QIODevice::ReadOnly);
157 retVal.first = QSSGMesh::Mesh::loadMesh(&buffer, 1);
158 retVal.second = QFileInfo(lightmapPath).fileName() +
" ["_L1 + lightmapKey + u']';
164QSSGBufferManager::QSSGBufferManager()
168QSSGBufferManager::~QSSGBufferManager()
171 m_contextInterface =
nullptr;
174void QSSGBufferManager::setRenderContextInterface(QSSGRenderContextInterface *ctx)
176 m_contextInterface = ctx;
179void QSSGBufferManager::releaseCachedResources()
184void QSSGBufferManager::releaseResourcesForLayer(QSSGRenderLayer *layer)
189 resetUsageCounters(frameResetIndex + 1, layer);
190 cleanupUnreferencedBuffers(frameResetIndex + 1, layer);
193QSSGRenderImageTexture QSSGBufferManager::loadRenderImage(
const QSSGRenderImage *image,
195 LoadRenderImageFlags flags)
197 if (inMipMode == MipModeFollowRenderImage)
198 inMipMode = image->m_generateMipmaps ? MipModeEnable : MipModeDisable;
200 const auto &context = m_contextInterface->rhiContext();
201 QSSGRenderImageTexture result;
202 if (image->m_qsgTexture) {
203 QRhi *rhi = context->rhi();
204 QSGTexture *qsgTexture = image->m_qsgTexture;
205 QRhiTexture *rhiTex = qsgTexture->rhiTexture();
206 if (!rhiTex || rhiTex->rhi() == rhi) {
210 QRhiResourceUpdateBatch *rub = rhi->nextResourceUpdateBatch();
211 if (qsgTexture->isAtlasTexture()) {
218 qsgTexture = qsgTexture->removedFromAtlas(rub);
220 qsgTexture->commitTextureOperations(rhi, rub);
221 context->commandBuffer()->resourceUpdate(rub);
222 auto theImage = qsgImageMap.find(qsgTexture);
223 if (theImage == qsgImageMap.end())
224 theImage = qsgImageMap.insert(qsgTexture, ImageData());
225 theImage.value().renderImageTexture.m_texture = qsgTexture->rhiTexture();
226 theImage.value().renderImageTexture.m_flags.setHasTransparency(qsgTexture->hasAlphaChannel());
228 theImage.value().renderImageTexture.m_flags.setPreMultipliedAlpha(qsgTexture->hasAlphaChannel());
229 theImage.value().usageCounts[currentLayer]++;
230 result = theImage.value().renderImageTexture;
236 if (inMipMode == MipModeBsdf)
237 qWarning(
"Cannot use QSGTexture from Texture.sourceItem as light probe.");
239 qWarning(
"Cannot use QSGTexture (presumably from Texture.sourceItem) created in another "
240 "window that was using a different graphics device/context. "
241 "Avoid using View3D.importScene between multiple windows.");
244 }
else if (image->m_rawTextureData) {
245 Q_TRACE_SCOPE(QSSG_textureLoad);
246 Q_QUICK3D_PROFILE_START(QQuick3DProfiler::Quick3DTextureLoad);
247 result = loadTextureData(image->m_rawTextureData, inMipMode);
248 Q_QUICK3D_PROFILE_END_WITH_ID(QQuick3DProfiler::Quick3DTextureLoad, stats.imageDataSize, image->profilingId);
249 }
else if (!image->m_imagePath.isEmpty()) {
251 const ImageCacheKey imageKey = { image->m_imagePath, inMipMode,
int(image->type), QString() };
252 auto foundIt = imageMap.find(imageKey);
253 if (foundIt != imageMap.cend()) {
254 result = foundIt.value().renderImageTexture;
256 Q_QUICK3D_PROFILE_START(QQuick3DProfiler::Quick3DTextureLoad);
257 QScopedPointer<QSSGLoadedTexture> theLoadedTexture;
258 const auto &path = image->m_imagePath.path();
259 const bool flipY = flags.testFlag(LoadWithFlippedY);
260 Q_TRACE_SCOPE(QSSG_textureLoadPath, path);
261 theLoadedTexture.reset(QSSGLoadedTexture::load(path, image->m_format, flipY));
262 if (theLoadedTexture) {
263 foundIt = imageMap.insert(imageKey, ImageData());
264 CreateRhiTextureFlags rhiTexFlags = ScanForTransparency;
265 if (image->type == QSSGRenderGraphObject::Type::ImageCube)
266 rhiTexFlags |= CubeMap;
267 if (!setRhiTexture(foundIt.value().renderImageTexture, theLoadedTexture.data(), inMipMode, rhiTexFlags, QFileInfo(path).fileName())) {
268 foundIt.value() = ImageData();
269 }
else if (QSSGBufferManagerStat::enabled(QSSGBufferManagerStat::Level::Debug)) {
270 qDebug() <<
"+ uploadTexture: " << image->m_imagePath.path() << currentLayer;
272 result = foundIt.value().renderImageTexture;
273 increaseMemoryStat(result.m_texture);
278 foundIt = imageMap.insert(imageKey, ImageData());
279 qCWarning(WARNING,
"Failed to load image: %s", qPrintable(path));
281 Q_QUICK3D_PROFILE_END_WITH_STRING(QQuick3DProfiler::Quick3DTextureLoad, stats.imageDataSize, path.toUtf8());
283 foundIt.value().usageCounts[currentLayer]++;
284 }
else if (image->m_extensionsSource) {
285 auto it = renderExtensionTexture.find(image->m_extensionsSource);
286 if (it != renderExtensionTexture.end()) {
287 it->usageCounts[currentLayer]++;
288 result = it->renderImageTexture;
294QSSGRenderImageTexture QSSGBufferManager::loadTextureData(QSSGRenderTextureData *data, MipMode inMipMode)
296 QSSG_ASSERT(data !=
nullptr,
return {});
298 const CustomImageCacheKey imageKey = { data, data->size(), inMipMode };
299 auto theImageData = customTextureMap.find(imageKey);
300 if (theImageData == customTextureMap.end()) {
301 theImageData = customTextureMap.insert(imageKey, ImageData{{}, {}, data->version()});
302 }
else if (data->version() == theImageData->version) {
304 theImageData.value().usageCounts[currentLayer]++;
305 return theImageData.value().renderImageTexture;
309 theImageData->version = data->version();
313 QScopedPointer<QSSGLoadedTexture> theLoadedTexture;
314 if (!data->textureData().isNull()) {
315 theLoadedTexture.reset(QSSGLoadedTexture::loadTextureData(data));
316 theLoadedTexture->ownsData =
false;
317 CreateRhiTextureFlags rhiTexFlags = {};
318 if (theLoadedTexture->depth > 0)
319 rhiTexFlags |= Texture3D;
321 bool wasTextureCreated =
false;
323 if (setRhiTexture(theImageData.value().renderImageTexture, theLoadedTexture.data(), inMipMode, rhiTexFlags, data->debugObjectName, &wasTextureCreated)) {
324 if (wasTextureCreated) {
325 if (QSSGBufferManagerStat::enabled(QSSGBufferManagerStat::Level::Debug))
326 qDebug() <<
"+ uploadTexture: " << data << theImageData.value().renderImageTexture.m_texture << currentLayer;
327 increaseMemoryStat(theImageData.value().renderImageTexture.m_texture);
330 theImageData.value() = ImageData();
334 theImageData.value().usageCounts[currentLayer]++;
335 return theImageData.value().renderImageTexture;
338QSSGRenderImageTexture QSSGBufferManager::loadLightmap(
const QSSGRenderModel &model)
340 Q_ASSERT(currentLayer);
342 if (model.lightmapKey.isEmpty() || currentlyLightmapBaking || !validateLightmap())
345 Q_ASSERT(!lightmapSource.isEmpty());
346 static const QSSGRenderTextureFormat format = QSSGRenderTextureFormat::RGBA16F;
347 QSSGRenderImageTexture result;
348 const ImageCacheKey imageKey = { QSSGRenderPath(lightmapSource), MipModeDisable,
int(QSSGRenderGraphObject::Type::Image2D), model.lightmapKey };
349 auto foundIt = imageMap.find(imageKey);
350 if (foundIt != imageMap.end()) {
351 result = foundIt.value().renderImageTexture;
353 Q_QUICK3D_PROFILE_START(QQuick3DProfiler::Quick3DTextureLoad);
354 Q_TRACE_SCOPE(QSSG_textureLoadPath, lightmapSource);
355 QScopedPointer<QSSGLoadedTexture> theLoadedTexture;
356 theLoadedTexture.reset(QSSGLoadedTexture::loadLightmapImage(lightmapSource, format, model.lightmapKey));
357 if (!theLoadedTexture) {
358 qCWarning(WARNING,
"Failed to load lightmap image for %s", qPrintable(model.lightmapKey));
360 foundIt = imageMap.insert(imageKey, ImageData());
361 if (theLoadedTexture) {
362 const QString debugOjbectName = lightmapSource + QStringLiteral(
" [%1]").arg(model.lightmapKey);
363 if (!setRhiTexture(foundIt.value().renderImageTexture, theLoadedTexture.data(), MipModeDisable, {}, debugOjbectName))
364 foundIt.value() = ImageData();
365 else if (QSSGBufferManagerStat::enabled(QSSGBufferManagerStat::Level::Debug))
366 qDebug() <<
"+ uploadTexture: " << debugOjbectName << currentLayer;
367 result = foundIt.value().renderImageTexture;
369 increaseMemoryStat(result.m_texture);
370 Q_QUICK3D_PROFILE_END_WITH_STRING(QQuick3DProfiler::Quick3DTextureLoad, stats.imageDataSize, lightmapSource.toUtf8());
372 foundIt.value().usageCounts[currentLayer]++;
376QSSGRenderImageTexture QSSGBufferManager::loadSkinmap(QSSGRenderTextureData *skin)
378 return loadTextureData(skin, MipModeDisable);
381QSSGRenderMesh *QSSGBufferManager::getMeshForPicking(
const QSSGRenderModel &model,
const QString &lightmap)
const
383 if (!model.meshPath.isNull()) {
384 auto key = std::make_pair(model.meshPath, lightmap);
385 const auto foundIt = meshMap.constFind(key);
386 if (foundIt != meshMap.constEnd())
387 return foundIt->mesh;
390 if (model.geometry) {
391 auto key = std::make_pair(model.geometry, lightmap);
392 const auto foundIt = customMeshMap.constFind(key);
393 if (foundIt != customMeshMap.constEnd())
394 return foundIt->mesh;
400QRhiTexture::Format QSSGBufferManager::toRhiFormat(
const QSSGRenderTextureFormat format)
402 switch (format.format) {
404 case QSSGRenderTextureFormat::RGBA8:
405 return QRhiTexture::RGBA8;
406 case QSSGRenderTextureFormat::R8:
407 return QRhiTexture::R8;
408 case QSSGRenderTextureFormat::Luminance16:
409 case QSSGRenderTextureFormat::R16:
410 return QRhiTexture::R16;
411 case QSSGRenderTextureFormat::LuminanceAlpha8:
412 case QSSGRenderTextureFormat::Luminance8:
413 case QSSGRenderTextureFormat::Alpha8:
414 return QRhiTexture::RED_OR_ALPHA8;
415 case QSSGRenderTextureFormat::RGBA16F:
416 return QRhiTexture::RGBA16F;
417 case QSSGRenderTextureFormat::RGBA32F:
418 return QRhiTexture::RGBA32F;
419 case QSSGRenderTextureFormat::R16F:
420 return QRhiTexture::R16F;
421 case QSSGRenderTextureFormat::R32F:
422 return QRhiTexture::R32F;
423 case QSSGRenderTextureFormat::RGBE8:
424 return QRhiTexture::RGBA8;
425 case QSSGRenderTextureFormat::R32UI:
426 return QRhiTexture::R32UI;
427 case QSSGRenderTextureFormat::RGBA32UI:
428 return QRhiTexture::RGBA32UI;
429 case QSSGRenderTextureFormat::RGB_DXT1:
430 return QRhiTexture::BC1;
431 case QSSGRenderTextureFormat::RGBA_DXT3:
432 return QRhiTexture::BC2;
433 case QSSGRenderTextureFormat::RGBA_DXT5:
434 return QRhiTexture::BC3;
435 case QSSGRenderTextureFormat::RGBA8_ETC2_EAC:
436 return QRhiTexture::ETC2_RGBA8;
437 case QSSGRenderTextureFormat::RGBA_ASTC_4x4:
438 case QSSGRenderTextureFormat::SRGB8_Alpha8_ASTC_4x4:
439 return QRhiTexture::ASTC_4x4;
440 case QSSGRenderTextureFormat::RGBA_ASTC_5x4:
441 case QSSGRenderTextureFormat::SRGB8_Alpha8_ASTC_5x4:
442 return QRhiTexture::ASTC_5x4;
443 case QSSGRenderTextureFormat::RGBA_ASTC_5x5:
444 case QSSGRenderTextureFormat::SRGB8_Alpha8_ASTC_5x5:
445 return QRhiTexture::ASTC_5x5;
446 case QSSGRenderTextureFormat::RGBA_ASTC_6x5:
447 case QSSGRenderTextureFormat::SRGB8_Alpha8_ASTC_6x5:
448 return QRhiTexture::ASTC_6x5;
449 case QSSGRenderTextureFormat::RGBA_ASTC_6x6:
450 case QSSGRenderTextureFormat::SRGB8_Alpha8_ASTC_6x6:
451 return QRhiTexture::ASTC_6x6;
452 case QSSGRenderTextureFormat::RGBA_ASTC_8x5:
453 case QSSGRenderTextureFormat::SRGB8_Alpha8_ASTC_8x5:
454 return QRhiTexture::ASTC_8x5;
455 case QSSGRenderTextureFormat::RGBA_ASTC_8x6:
456 case QSSGRenderTextureFormat::SRGB8_Alpha8_ASTC_8x6:
457 return QRhiTexture::ASTC_8x6;
458 case QSSGRenderTextureFormat::RGBA_ASTC_8x8:
459 case QSSGRenderTextureFormat::SRGB8_Alpha8_ASTC_8x8:
460 return QRhiTexture::ASTC_8x8;
461 case QSSGRenderTextureFormat::RGBA_ASTC_10x5:
462 case QSSGRenderTextureFormat::SRGB8_Alpha8_ASTC_10x5:
463 return QRhiTexture::ASTC_10x5;
464 case QSSGRenderTextureFormat::RGBA_ASTC_10x6:
465 case QSSGRenderTextureFormat::SRGB8_Alpha8_ASTC_10x6:
466 return QRhiTexture::ASTC_10x6;
467 case QSSGRenderTextureFormat::RGBA_ASTC_10x8:
468 case QSSGRenderTextureFormat::SRGB8_Alpha8_ASTC_10x8:
469 return QRhiTexture::ASTC_10x8;
470 case QSSGRenderTextureFormat::RGBA_ASTC_10x10:
471 case QSSGRenderTextureFormat::SRGB8_Alpha8_ASTC_10x10:
472 return QRhiTexture::ASTC_10x10;
473 case QSSGRenderTextureFormat::RGBA_ASTC_12x10:
474 case QSSGRenderTextureFormat::SRGB8_Alpha8_ASTC_12x10:
475 return QRhiTexture::ASTC_12x10;
476 case QSSGRenderTextureFormat::RGBA_ASTC_12x12:
477 case QSSGRenderTextureFormat::SRGB8_Alpha8_ASTC_12x12:
478 return QRhiTexture::ASTC_12x12;
480 case QSSGRenderTextureFormat::Depth16:
481 return QRhiTexture::D16;
482 case QSSGRenderTextureFormat::Depth24:
483 return QRhiTexture::D24;
484 case QSSGRenderTextureFormat::Depth32:
485 return QRhiTexture::D32F;
486 case QSSGRenderTextureFormat::Depth24Stencil8:
487 return QRhiTexture::D24S8;
489 case QSSGRenderTextureFormat::SRGB8A8:
490 return QRhiTexture::RGBA8;
493 qWarning() <<
"Unsupported texture format" << format.format;
494 return QRhiTexture::UnknownFormat;
586bool QSSGBufferManager::createEnvironmentMap(
const QSSGLoadedTexture *inImage, QSSGRenderImageTexture *outTexture,
const QString &debugObjectName)
612 const auto &context = m_contextInterface->rhiContext();
613 auto *rhi = context->rhi();
615 int suggestedSize = inImage->height * 0.5f;
616 suggestedSize = qMax(512, suggestedSize);
617 const QSize environmentMapSize(suggestedSize, suggestedSize);
618 const bool isRGBE = inImage->format.format == QSSGRenderTextureFormat::Format::RGBE8;
619 const QRhiTexture::Format sourceTextureFormat = toRhiFormat(inImage->format.format);
621 if (!rhi->isTextureFormatSupported(sourceTextureFormat))
624 QRhiTexture::Format cubeTextureFormat = inImage->format.isCompressedTextureFormat()
625 ? QRhiTexture::RGBA16F
626 : sourceTextureFormat;
633 if (cubeTextureFormat == QRhiTexture::RGBA32F)
634 cubeTextureFormat = QRhiTexture::RGBA16F;
636 const int colorSpace = inImage->isSRGB ? 1 : 0;
639 QRhiTexture *envCubeMap = rhi->newTexture(cubeTextureFormat, environmentMapSize, 1,
640 QRhiTexture::RenderTarget | QRhiTexture::CubeMap | QRhiTexture::MipMapped | QRhiTexture::UsedWithGenerateMips);
641 if (!envCubeMap->create()) {
642 qWarning(
"Failed to create Environment Cube Map");
645 envCubeMap->deleteLater();
648 QRhiRenderBuffer *envMapRenderBuffer = rhi->newRenderBuffer(QRhiRenderBuffer::Color, environmentMapSize);
649 if (!envMapRenderBuffer->create()) {
650 qWarning(
"Failed to create Environment Map Render Buffer");
653 envMapRenderBuffer->deleteLater();
655 const QByteArray rtName = debugObjectName.toLatin1();
658 QVarLengthArray<QRhiTextureRenderTarget *, 6> renderTargets;
659 QRhiRenderPassDescriptor *renderPassDesc =
nullptr;
660 for (
const auto face : QSSGRenderTextureCubeFaces) {
661 QRhiColorAttachment att(envCubeMap);
662 att.setLayer(quint8(face));
663 QRhiTextureRenderTargetDescription rtDesc;
664 rtDesc.setColorAttachments({att});
665 auto renderTarget = rhi->newTextureRenderTarget(rtDesc);
666 renderTarget->setName(rtName + QByteArrayLiteral(
" env cube face: ") + QSSGBaseTypeHelpers::displayName(face));
667 renderTarget->setDescription(rtDesc);
669 renderPassDesc = renderTarget->newCompatibleRenderPassDescriptor();
670 renderTarget->setRenderPassDescriptor(renderPassDesc);
671 if (!renderTarget->create()) {
672 qWarning(
"Failed to build env map render target");
675 renderTarget->deleteLater();
676 renderTargets << renderTarget;
678 renderPassDesc->deleteLater();
681 QSize size(inImage->width, inImage->height);
682 auto *sourceTexture = rhi->newTexture(sourceTextureFormat, size, 1);
683 if (!sourceTexture->create()) {
684 qWarning(
"failed to create source env map texture");
687 sourceTexture->deleteLater();
690 const auto desc = inImage->textureFileData.isValid()
691 ? QRhiTextureUploadDescription(
692 { 0, 0, QRhiTextureSubresourceUploadDescription(inImage->textureFileData.getDataView().toByteArray()) })
693 : QRhiTextureUploadDescription({ 0, 0, { inImage->data, inImage->dataSizeInBytes } });
695 auto *rub = rhi->nextResourceUpdateBatch();
696 rub->uploadTexture(sourceTexture, desc);
698 const QSSGRhiSamplerDescription samplerDesc {
702 QRhiSampler::ClampToEdge,
703 QRhiSampler::ClampToEdge,
706 QRhiSampler *sampler = context->sampler(samplerDesc);
709 const auto &shaderCache = m_contextInterface->shaderCache();
710 const auto &envMapShaderStages = shaderCache->getBuiltInRhiShaders().getRhiEnvironmentmapShader();
713 QRhiBuffer *vertexBuffer = rhi->newBuffer(QRhiBuffer::Immutable, QRhiBuffer::VertexBuffer,
sizeof(cube));
714 vertexBuffer->create();
715 vertexBuffer->deleteLater();
716 rub->uploadStaticBuffer(vertexBuffer, cube);
719 int ubufElementSize = rhi->ubufAligned(128);
720 QRhiBuffer *uBuf = rhi->newBuffer(QRhiBuffer::Dynamic, QRhiBuffer::UniformBuffer, ubufElementSize * 6);
724 int ubufEnvMapElementSize = rhi->ubufAligned(4);
725 QRhiBuffer *uBufEnvMap = rhi->newBuffer(QRhiBuffer::Dynamic, QRhiBuffer::UniformBuffer, ubufEnvMapElementSize * 6);
726 uBufEnvMap->create();
727 uBufEnvMap->deleteLater();
730 QRhiShaderResourceBindings *envMapSrb = rhi->newShaderResourceBindings();
731 envMapSrb->setBindings({
732 QRhiShaderResourceBinding::uniformBufferWithDynamicOffset(0, QRhiShaderResourceBinding::VertexStage, uBuf, 128),
733 QRhiShaderResourceBinding::uniformBufferWithDynamicOffset(2, QRhiShaderResourceBinding::FragmentStage, uBufEnvMap, ubufEnvMapElementSize),
734 QRhiShaderResourceBinding::sampledTexture(1, QRhiShaderResourceBinding::FragmentStage, sourceTexture, sampler)
737 envMapSrb->deleteLater();
740 QRhiGraphicsPipeline *envMapPipeline = rhi->newGraphicsPipeline();
741 envMapPipeline->setCullMode(QRhiGraphicsPipeline::Front);
742 envMapPipeline->setFrontFace(QRhiGraphicsPipeline::CCW);
743 envMapPipeline->setShaderStages({
744 *envMapShaderStages->vertexStage(),
745 *envMapShaderStages->fragmentStage()
748 QRhiVertexInputLayout inputLayout;
749 inputLayout.setBindings({
750 { 3 *
sizeof(
float) }
752 inputLayout.setAttributes({
753 { 0, 0, QRhiVertexInputAttribute::Float3, 0 }
756 envMapPipeline->setVertexInputLayout(inputLayout);
757 envMapPipeline->setShaderResourceBindings(envMapSrb);
758 envMapPipeline->setRenderPassDescriptor(renderPassDesc);
759 if (!envMapPipeline->create()) {
760 qWarning(
"failed to create source env map pipeline state");
763 envMapPipeline->deleteLater();
766 auto *cb = context->commandBuffer();
767 cb->debugMarkBegin(
"Environment Cubemap Generation");
768 Q_QUICK3D_PROFILE_START(QQuick3DProfiler::Quick3DRenderPass);
769 Q_TRACE(QSSG_renderPass_entry, QStringLiteral(
"Environment Cubemap Generation"));
770 const QRhiCommandBuffer::VertexInput vbufBinding(vertexBuffer, 0);
773 QMatrix4x4 mvp = rhi->clipSpaceCorrMatrix();
774 mvp.perspective(90.0f, 1.0f, 0.1f, 10.0f);
776 auto lookAt = [](
const QVector3D &eye,
const QVector3D ¢er,
const QVector3D &up) {
777 QMatrix4x4 viewMatrix;
778 viewMatrix.lookAt(eye, center, up);
781 QVarLengthArray<QMatrix4x4, 6> views;
782 views.append(lookAt(QVector3D(0.0f, 0.0f, 0.0f), QVector3D(1.0, 0.0, 0.0), QVector3D(0.0f, -1.0f, 0.0f)));
783 views.append(lookAt(QVector3D(0.0f, 0.0f, 0.0f), QVector3D(-1.0, 0.0, 0.0), QVector3D(0.0f, -1.0f, 0.0f)));
784 if (rhi->isYUpInFramebuffer()) {
785 views.append(lookAt(QVector3D(0.0f, 0.0f, 0.0f), QVector3D(0.0, 1.0, 0.0), QVector3D(0.0f, 0.0f, 1.0f)));
786 views.append(lookAt(QVector3D(0.0f, 0.0f, 0.0f), QVector3D(0.0, -1.0, 0.0), QVector3D(0.0f, 0.0f, -1.0f)));
788 views.append(lookAt(QVector3D(0.0f, 0.0f, 0.0f), QVector3D(0.0, -1.0, 0.0), QVector3D(0.0f, 0.0f, -1.0f)));
789 views.append(lookAt(QVector3D(0.0f, 0.0f, 0.0f), QVector3D(0.0, 1.0, 0.0), QVector3D(0.0f, 0.0f, 1.0f)));
791 views.append(lookAt(QVector3D(0.0f, 0.0f, 0.0f), QVector3D(0.0, 0.0, 1.0), QVector3D(0.0f, -1.0f, 0.0f)));
792 views.append(lookAt(QVector3D(0.0f, 0.0f, 0.0f), QVector3D(0.0, 0.0, -1.0), QVector3D(0.0f, -1.0f, 0.0f)));
793 for (
const auto face : QSSGRenderTextureCubeFaces) {
794 rub->updateDynamicBuffer(uBuf, quint8(face) * ubufElementSize, 64, mvp.constData());
795 rub->updateDynamicBuffer(uBuf, quint8(face) * ubufElementSize + 64, 64, views[quint8(face)].constData());
796 rub->updateDynamicBuffer(uBufEnvMap, quint8(face) * ubufEnvMapElementSize, 4, &colorSpace);
798 cb->resourceUpdate(rub);
800 for (
const auto face : QSSGRenderTextureCubeFaces) {
801 cb->beginPass(renderTargets[quint8(face)], QColor(0, 0, 0, 1), { 1.0f, 0 },
nullptr, context->commonPassFlags());
802 Q_QUICK3D_PROFILE_START(QQuick3DProfiler::Quick3DRenderPass);
803 QSSGRHICTX_STAT(context, beginRenderPass(renderTargets[quint8(face)]));
806 cb->setGraphicsPipeline(envMapPipeline);
807 cb->setVertexInput(0, 1, &vbufBinding);
808 cb->setViewport(QRhiViewport(0, 0, environmentMapSize.width(), environmentMapSize.height()));
809 QVector<QPair<
int, quint32>> dynamicOffset = {
810 { 0, quint32(ubufElementSize * quint8(face)) },
811 { 2, quint32(ubufEnvMapElementSize * quint8(face) )}
813 cb->setShaderResources(envMapSrb, 2, dynamicOffset.constData());
814 Q_QUICK3D_PROFILE_START(QQuick3DProfiler::Quick3DRenderCall);
816 QSSGRHICTX_STAT(context, draw(36, 1));
817 Q_QUICK3D_PROFILE_END_WITH_STRING(QQuick3DProfiler::Quick3DRenderCall, 36llu | (1llu << 32), QByteArrayLiteral(
"environment_map"));
820 QSSGRHICTX_STAT(context, endRenderPass());
821 Q_QUICK3D_PROFILE_END_WITH_STRING(QQuick3DProfiler::Quick3DRenderPass, 0, QSSG_RENDERPASS_NAME(
"environment_map", 0, face));
824 Q_QUICK3D_PROFILE_END_WITH_STRING(QQuick3DProfiler::Quick3DRenderPass, 0, QByteArrayLiteral(
"environment_cube_generation"));
825 Q_TRACE(QSSG_renderPass_exit);
829 rub = rhi->nextResourceUpdateBatch();
830 rub->generateMips(envCubeMap);
831 cb->resourceUpdate(rub);
835 cb->debugMarkBegin(
"Pre-filtered Environment Cubemap Generation");
836 Q_QUICK3D_PROFILE_START(QQuick3DProfiler::Quick3DRenderPass);
837 Q_TRACE(QSSG_renderPass_entry, QStringLiteral(
"Pre-filtered Environment Cubemap Generation"));
838 QRhiTexture *preFilteredEnvCubeMap = rhi->newTexture(cubeTextureFormat, environmentMapSize, 1, QRhiTexture::RenderTarget | QRhiTexture::CubeMap| QRhiTexture::MipMapped);
839 if (!preFilteredEnvCubeMap->create())
840 qWarning(
"Failed to create Pre-filtered Environment Cube Map");
841 preFilteredEnvCubeMap->setName(rtName);
845 const int fullMipCount = rhi->mipLevelsForSize(environmentMapSize);
846 const int mipmapCount = qMin(fullMipCount, 6);
847 QMap<
int, QSize> mipLevelSizes;
848 QMap<
int, QVarLengthArray<QRhiTextureRenderTarget *, 6>> renderTargetsMap;
849 QRhiRenderPassDescriptor *renderPassDescriptorPhase2 =
nullptr;
852 for (
int mipLevel = 0; mipLevel < mipmapCount; ++mipLevel) {
853 const QSize levelSize = QSize(environmentMapSize.width() * std::pow(0.5, mipLevel),
854 environmentMapSize.height() * std::pow(0.5, mipLevel));
855 mipLevelSizes.insert(mipLevel, levelSize);
857 QVarLengthArray<QRhiTextureRenderTarget *, 6> renderTargets;
858 for (
const auto face : QSSGRenderTextureCubeFaces) {
859 QRhiColorAttachment att(preFilteredEnvCubeMap);
860 att.setLayer(quint8(face));
861 att.setLevel(mipLevel);
862 QRhiTextureRenderTargetDescription rtDesc;
863 rtDesc.setColorAttachments({att});
864 auto renderTarget = rhi->newTextureRenderTarget(rtDesc);
865 renderTarget->setName(rtName + QByteArrayLiteral(
" env prefilter mip/face: ")
866 + QByteArray::number(mipLevel) + QByteArrayLiteral(
"/") + QSSGBaseTypeHelpers::displayName(face));
867 renderTarget->setDescription(rtDesc);
868 if (!renderPassDescriptorPhase2)
869 renderPassDescriptorPhase2 = renderTarget->newCompatibleRenderPassDescriptor();
870 renderTarget->setRenderPassDescriptor(renderPassDescriptorPhase2);
871 if (!renderTarget->create())
872 qWarning(
"Failed to build prefilter env map render target");
873 renderTarget->deleteLater();
874 renderTargets << renderTarget;
876 renderTargetsMap.insert(mipLevel, renderTargets);
877 renderPassDescriptorPhase2->deleteLater();
881 const auto &prefilterShaderStages = shaderCache->getBuiltInRhiShaders().getRhienvironmentmapPreFilterShader(isRGBE);
884 const QSSGRhiSamplerDescription samplerMipMapDesc {
888 QRhiSampler::ClampToEdge,
889 QRhiSampler::ClampToEdge,
893 QRhiSampler *envMapCubeSampler =
nullptr;
896 envMapCubeSampler = context->sampler(samplerMipMapDesc);
898 envMapCubeSampler = sampler;
910 int ubufPrefilterElementSize = rhi->ubufAligned(20);
911 QRhiBuffer *uBufPrefilter = rhi->newBuffer(QRhiBuffer::Dynamic, QRhiBuffer::UniformBuffer, ubufPrefilterElementSize * mipmapCount);
912 uBufPrefilter->create();
913 uBufPrefilter->deleteLater();
916 QRhiShaderResourceBindings *preFilterSrb = rhi->newShaderResourceBindings();
917 preFilterSrb->setBindings({
918 QRhiShaderResourceBinding::uniformBufferWithDynamicOffset(0, QRhiShaderResourceBinding::VertexStage, uBuf, 128),
919 QRhiShaderResourceBinding::uniformBufferWithDynamicOffset(2, QRhiShaderResourceBinding::FragmentStage, uBufPrefilter, 20),
920 QRhiShaderResourceBinding::sampledTexture(1, QRhiShaderResourceBinding::FragmentStage, envCubeMap, envMapCubeSampler)
922 preFilterSrb->create();
923 preFilterSrb->deleteLater();
926 QRhiGraphicsPipeline *prefilterPipeline = rhi->newGraphicsPipeline();
927 prefilterPipeline->setCullMode(QRhiGraphicsPipeline::Front);
928 prefilterPipeline->setFrontFace(QRhiGraphicsPipeline::CCW);
929 prefilterPipeline->setDepthOp(QRhiGraphicsPipeline::LessOrEqual);
930 prefilterPipeline->setShaderStages({
931 *prefilterShaderStages->vertexStage(),
932 *prefilterShaderStages->fragmentStage()
935 prefilterPipeline->setVertexInputLayout(inputLayout);
936 prefilterPipeline->setShaderResourceBindings(preFilterSrb);
937 prefilterPipeline->setRenderPassDescriptor(renderPassDescriptorPhase2);
938 if (!prefilterPipeline->create()) {
939 qWarning(
"failed to create pre-filter env map pipeline state");
942 prefilterPipeline->deleteLater();
946 rub = rhi->nextResourceUpdateBatch();
947 const float resolution = environmentMapSize.width();
948 const float lodBias = 0.0f;
949 const int sampleCount = 1024;
950 for (
int mipLevel = 0; mipLevel < mipmapCount; ++mipLevel) {
951 Q_ASSERT(mipmapCount - 2);
952 const float roughness =
float(mipLevel) /
float(mipmapCount - 2);
953 const int distribution = mipLevel == (mipmapCount - 1) ? 0 : 1;
954 rub->updateDynamicBuffer(uBufPrefilter, mipLevel * ubufPrefilterElementSize, 4, &roughness);
955 rub->updateDynamicBuffer(uBufPrefilter, mipLevel * ubufPrefilterElementSize + 4, 4, &resolution);
956 rub->updateDynamicBuffer(uBufPrefilter, mipLevel * ubufPrefilterElementSize + 4 + 4, 4, &lodBias);
957 rub->updateDynamicBuffer(uBufPrefilter, mipLevel * ubufPrefilterElementSize + 4 + 4 + 4, 4, &sampleCount);
958 rub->updateDynamicBuffer(uBufPrefilter, mipLevel * ubufPrefilterElementSize + 4 + 4 + 4 + 4, 4, &distribution);
961 cb->resourceUpdate(rub);
964 for (
int mipLevel = 0; mipLevel < mipmapCount; ++mipLevel) {
965 for (
const auto face : QSSGRenderTextureCubeFaces) {
966 cb->beginPass(renderTargetsMap[mipLevel][quint8(face)], QColor(0, 0, 0, 1), { 1.0f, 0 },
nullptr, context->commonPassFlags());
967 QSSGRHICTX_STAT(context, beginRenderPass(renderTargetsMap[mipLevel][quint8(face)]));
968 Q_QUICK3D_PROFILE_START(QQuick3DProfiler::Quick3DRenderPass);
969 cb->setGraphicsPipeline(prefilterPipeline);
970 cb->setVertexInput(0, 1, &vbufBinding);
971 cb->setViewport(QRhiViewport(0, 0, mipLevelSizes[mipLevel].width(), mipLevelSizes[mipLevel].height()));
972 QVector<QPair<
int, quint32>> dynamicOffsets = {
973 { 0, quint32(ubufElementSize * quint8(face)) },
974 { 2, quint32(ubufPrefilterElementSize * mipLevel) }
976 Q_QUICK3D_PROFILE_START(QQuick3DProfiler::Quick3DRenderCall);
978 cb->setShaderResources(preFilterSrb, 2, dynamicOffsets.constData());
980 QSSGRHICTX_STAT(context, draw(36, 1));
981 Q_QUICK3D_PROFILE_END_WITH_STRING(QQuick3DProfiler::Quick3DRenderCall, 36llu | (1llu << 32), QByteArrayLiteral(
"environment_map"));
983 QSSGRHICTX_STAT(context, endRenderPass());
984 Q_QUICK3D_PROFILE_END_WITH_STRING(QQuick3DProfiler::Quick3DRenderPass, 0, QSSG_RENDERPASS_NAME(
"environment_map", mipLevel, face));
993 for (
int mipLevel = mipmapCount; mipLevel < fullMipCount; ++mipLevel) {
994 for (
const auto face : QSSGRenderTextureCubeFaces) {
995 QRhiColorAttachment att(preFilteredEnvCubeMap);
996 att.setLayer(quint8(face));
997 att.setLevel(mipLevel);
998 QRhiTextureRenderTargetDescription rtDesc;
999 rtDesc.setColorAttachments({att});
1000 auto renderTarget = rhi->newTextureRenderTarget(rtDesc);
1001 renderTarget->setDescription(rtDesc);
1002 renderTarget->setRenderPassDescriptor(renderPassDescriptorPhase2);
1003 if (!renderTarget->create()) {
1004 qWarning(
"Failed to build prefilter env map render target for unused mip level");
1005 delete renderTarget;
1008 renderTarget->deleteLater();
1009 cb->beginPass(renderTarget, QColor(0, 0, 0, 1), { 1.0f, 0 },
nullptr, context->commonPassFlags());
1010 QSSGRHICTX_STAT(context, beginRenderPass(renderTarget));
1012 QSSGRHICTX_STAT(context, endRenderPass());
1017 Q_QUICK3D_PROFILE_END_WITH_STRING(QQuick3DProfiler::Quick3DRenderPass, 0, QByteArrayLiteral(
"environment_cube_prefilter"));
1018 Q_TRACE(QSSG_renderPass_exit);
1020 outTexture->m_texture = preFilteredEnvCubeMap;
1021 outTexture->m_mipmapCount = mipmapCount;
1025bool QSSGBufferManager::setRhiTexture(QSSGRenderImageTexture &texture,
1026 const QSSGLoadedTexture *inTexture,
1028 CreateRhiTextureFlags inFlags,
1029 const QString &debugObjectName,
1030 bool *wasTextureCreated)
1032 Q_ASSERT(inMipMode != MipModeFollowRenderImage);
1033 QVarLengthArray<QRhiTextureUploadEntry, 16> textureUploads;
1034 int textureSampleCount = 1;
1035 QRhiTexture::Flags textureFlags;
1036 const bool checkTransp = inFlags.testFlag(ScanForTransparency);
1037 bool hasTransp =
false;
1039 const auto &context = m_contextInterface->rhiContext();
1040 QSSGRhiContextPrivate *rhiCtxD = QSSGRhiContextPrivate::get(context.get());
1041 auto *rhi = context->rhi();
1042 QRhiTexture::Format rhiFormat = toRhiFormat(inTexture->format.format);
1043 const QTextureFileData &texFileData = inTexture->textureFileData;
1044 QSize size = texFileData.isValid() ? texFileData.size() : QSize(inTexture->width, inTexture->height);
1045 int mipmapCount = texFileData.isValid() ? texFileData.numLevels() : 1;
1046 int depth = inFlags.testFlag(Texture3D) ? inTexture->depth : 0;
1047 bool generateMipmaps =
false;
1049 if (size.isEmpty()) {
1050 qWarning() <<
"Could not use 0 sized texture";
1052 }
else if (!rhi->isTextureFormatSupported(rhiFormat)) {
1053 qWarning() <<
"Unsupported texture format" << rhiFormat;
1058 if (wasTextureCreated)
1059 *wasTextureCreated =
false;
1061 if (texture.m_texture ==
nullptr) {
1062 if (inTexture->format.format == QSSGRenderTextureFormat::Format::RGBE8)
1063 texture.m_flags.setRgbe8(
true);
1064 if (!inTexture->isSRGB)
1065 texture.m_flags.setLinear(
true);
1066 if (inMipMode == MipModeBsdf && (inTexture->data || texFileData.isValid())) {
1069 if (texFileData.isValid() && texFileData.keyValueMetadata().contains(
"QT_IBL_BAKER_VERSION")) {
1070 Q_ASSERT(texFileData.numFaces() == 6);
1071 Q_ASSERT(texFileData.numLevels() >= 5);
1073 QRhiTexture *environmentCubeMap = rhi->newTexture(rhiFormat, size, 1, QRhiTexture::CubeMap | QRhiTexture::MipMapped);
1074 environmentCubeMap->setName(debugObjectName.toLatin1());
1075 environmentCubeMap->create();
1076 texture.m_texture = environmentCubeMap;
1077 rhiCtxD->registerTexture(texture.m_texture);
1078 if (wasTextureCreated)
1079 *wasTextureCreated =
true;
1081 }
else if (createEnvironmentMap(inTexture, &texture, debugObjectName)) {
1082 rhiCtxD->registerTexture(texture.m_texture);
1083 if (wasTextureCreated)
1084 *wasTextureCreated =
true;
1087 qWarning() <<
"Failed to create environment map";
1091 if (inMipMode == MipModeEnable && mipmapCount == 1) {
1092 textureFlags |= QRhiTexture::Flag::UsedWithGenerateMips;
1093 generateMipmaps =
true;
1094 mipmapCount = rhi->mipLevelsForSize(size);
1097 if (mipmapCount > 1)
1098 textureFlags |= QRhiTexture::Flag::MipMapped;
1100 if (inFlags.testFlag(CubeMap))
1101 textureFlags |= QRhiTexture::CubeMap;
1103 if (inFlags.testFlag(Texture3D) && depth > 0)
1104 texture.m_texture = rhi->newTexture(rhiFormat, size.width(), size.height(), depth, textureSampleCount, textureFlags);
1106 texture.m_texture = rhi->newTexture(rhiFormat, size, textureSampleCount, textureFlags);
1108 texture.m_texture->setName(debugObjectName.toLatin1());
1109 texture.m_texture->create();
1110 rhiCtxD->registerTexture(texture.m_texture);
1111 if (wasTextureCreated)
1112 *wasTextureCreated =
true;
1117 if (inMipMode == MipModeBsdf && (inTexture->data || texFileData.isValid())) {
1118 if (texFileData.isValid() && texFileData.keyValueMetadata().contains(
"QT_IBL_BAKER_VERSION")) {
1119 const int faceCount = texFileData.numFaces();
1120 for (
int layer = 0; layer < faceCount; ++layer) {
1121 for (
int level = 0; level < mipmapCount; ++level) {
1122 QRhiTextureSubresourceUploadDescription subDesc;
1123 subDesc.setSourceSize(sizeForMipLevel(level, size));
1124 subDesc.setData(texFileData.getDataView(level, layer).toByteArray());
1125 textureUploads << QRhiTextureUploadEntry { layer, level, subDesc };
1129 QRhiTextureUploadDescription uploadDescription;
1130 uploadDescription.setEntries(textureUploads.cbegin(), textureUploads.cend());
1131 auto *rub = rhi->nextResourceUpdateBatch();
1132 rub->uploadTexture(texture.m_texture, uploadDescription);
1133 context->commandBuffer()->resourceUpdate(rub);
1134 texture.m_mipmapCount = mipmapCount;
1137 }
else if (texFileData.isValid()) {
1142 if (texFileData.numFaces() == 6 && inFlags.testFlag(CubeMap))
1145 for (
int level = 0; level < texFileData.numLevels(); ++level) {
1146 QRhiTextureSubresourceUploadDescription subDesc;
1147 subDesc.setSourceSize(sizeForMipLevel(level, size));
1148 for (
int face = 0; face < numFaces; ++face) {
1149 subDesc.setData(texFileData.getDataView(level, face).toByteArray());
1150 textureUploads << QRhiTextureUploadEntry{ face, level, subDesc };
1154 auto glFormat = texFileData.glInternalFormat() ? texFileData.glInternalFormat() : texFileData.glFormat();
1155 hasTransp = !QSGCompressedTexture::formatIsOpaque(glFormat);
1157 }
else if (inFlags.testFlag(Texture3D)) {
1159 quint32 formatSize = (quint32)inTexture->format.getSizeofFormat();
1160 quint32 size2D = inTexture->width * inTexture->height * formatSize;
1161 if (inTexture->dataSizeInBytes >= (quint32)(size2D * inTexture->depth)) {
1162 for (
int slice = 0; slice < inTexture->depth; ++slice) {
1163 QRhiTextureSubresourceUploadDescription sliceUpload((
char *)inTexture->data + slice * size2D, size2D);
1164 textureUploads << QRhiTextureUploadEntry(slice, 0, sliceUpload);
1167 qWarning() <<
"Texture size set larger than the data";
1170 QRhiTextureSubresourceUploadDescription subDesc;
1171 if (!inTexture->image.isNull()) {
1172 subDesc.setImage(inTexture->image);
1174 hasTransp = QImageData::get(inTexture->image)->checkForAlphaPixels();
1175 }
else if (inTexture->data) {
1176 QByteArray buf(
static_cast<
const char *>(inTexture->data), qMax(0,
int(inTexture->dataSizeInBytes)));
1177 subDesc.setData(buf);
1179 hasTransp = inTexture->scanForTransparency();
1181 subDesc.setSourceSize(size);
1182 if (!subDesc.data().isEmpty() || !subDesc.image().isNull())
1183 textureUploads << QRhiTextureUploadEntry{0, 0, subDesc};
1186 static const auto textureSizeWarning = [](QSize requestedSize, qsizetype maxSize) {
1187 return QStringLiteral(
"Requested texture width and height (%1x%2) exceeds the maximum allowed size (%3)!")
1188 .arg(requestedSize.width()).arg(requestedSize.height()).arg(maxSize);
1190 static auto maxTextureSize = rhi->resourceLimit(QRhi::ResourceLimit::TextureSizeMax);
1191 const auto validTexSize = size.width() <= maxTextureSize && size.height() <= maxTextureSize;
1192 QSSG_ASSERT_X(validTexSize, qPrintable(textureSizeWarning(size, maxTextureSize)),
return false);
1194 QSSG_ASSERT(texture.m_texture !=
nullptr,
return false);
1197 texture.m_flags.setHasTransparency(hasTransp);
1199 QRhiTextureUploadDescription uploadDescription;
1200 uploadDescription.setEntries(textureUploads.cbegin(), textureUploads.cend());
1201 auto *rub = rhi->nextResourceUpdateBatch();
1202 rub->uploadTexture(texture.m_texture, uploadDescription);
1203 if (generateMipmaps)
1204 rub->generateMips(texture.m_texture);
1205 context->commandBuffer()->resourceUpdate(rub);
1207 texture.m_mipmapCount = mipmapCount;
1211QString QSSGBufferManager::primitivePath(
const QString &primitive)
1213 QByteArray theName = primitive.toUtf8();
1214 for (size_t idx = 0; idx < nPrimitives; ++idx) {
1215 if (primitives[idx].primitive == theName) {
1216 QString pathBuilder = QString::fromLatin1(primitivesDirectory);
1217 pathBuilder += QLatin1String(primitives[idx].file);
1224QMutex *QSSGBufferManager::meshUpdateMutex()
1226 return &meshBufferMutex;
1229QSSGMesh::Mesh QSSGBufferManager::loadPrimitive(
const QString &inRelativePath)
1231 QString path = primitivePath(inRelativePath);
1232 const quint32 id = 1;
1233 QSharedPointer<QIODevice> device(QSSGInputUtil::getStreamForFile(path));
1235 QSSGMesh::Mesh mesh = QSSGMesh::Mesh::loadMesh(device.data(), id);
1240 qCCritical(INTERNAL_ERROR,
"Unable to find mesh primitive %s", qPrintable(path));
1241 return QSSGMesh::Mesh();
1244QSSGRenderMesh *QSSGBufferManager::loadMesh(
const QSSGRenderModel &model)
1248 QSSGMeshProcessingOptions options;
1249 QSSGRenderMesh *theMesh =
nullptr;
1251 if (model.hasLightmap() && !currentlyLightmapBaking && validateLightmap()) {
1252 options.lightmapPath = lightmapSource;
1253 options.lightmapKey = model.lightmapKey;
1256 if (model.meshPath.isNull() && model.geometry) {
1257 theMesh = loadRenderMesh(model.geometry, options);
1259 theMesh = loadRenderMesh(model.meshPath, options);
1265QSSGMesh::Mesh QSSGBufferManager::loadLightmapMesh(
const QSSGRenderModel &model)
1268 if (model.hasLightmap() && !currentlyLightmapBaking && validateLightmap() ) {
1269 auto [meshLightmap, _] = loadFromLightmapFile(lightmapSource, model.lightmapKey);
1270 if (meshLightmap.isValid())
1271 return meshLightmap;
1277QSSGBounds3 QSSGBufferManager::getModelBounds(
const QSSGRenderModel *model)
1281 if (model->hasLightmap() && !currentlyLightmapBaking && validateLightmap()) {
1282 auto [meshLightmap, _] = loadFromLightmapFile(lightmapSource, model->lightmapKey);
1283 if (meshLightmap.isValid()) {
1284 const QVector<QSSGMesh::Mesh::Subset> subsets = meshLightmap.subsets();
1285 for (
const QSSGMesh::Mesh::Subset &subset : std::as_const(subsets)) {
1286 retval.include(QSSGBounds3(subset.bounds.min, subset.bounds.max));
1290 qWarning() <<
"Could not load lightmap" << lightmapSource << model->lightmapKey;
1295 if (model->geometry) {
1296 retval = QSSGBounds3(model->geometry->boundsMin(), model->geometry->boundsMax());
1297 }
else if (!model->meshPath.isNull()){
1299 QSSGRenderMesh *theMesh =
nullptr;
1300 auto key = std::make_pair(model->meshPath, QString());
1301 auto meshItr = meshMap.constFind(key);
1302 if (meshItr != meshMap.cend())
1303 theMesh = meshItr.value().mesh;
1307 const auto &subSets = theMesh->subsets;
1308 for (
const auto &subSet : subSets)
1309 retval.include(subSet.bounds);
1313 QSSGMesh::Mesh mesh = loadMeshData(model->meshPath);
1314 if (mesh.isValid()) {
1315 auto const &subsets = mesh.subsets();
1316 for (
const auto &subset : subsets)
1317 retval.include(QSSGBounds3(subset.bounds.min, subset.bounds.max));
1324QSSGRenderMesh *QSSGBufferManager::createRenderMesh(
const QSSGMesh::Mesh &mesh,
const QString &debugObjectName)
1326 QSSGRenderMesh *newMesh =
new QSSGRenderMesh(QSSGRenderDrawMode(mesh.drawMode()),
1327 QSSGRenderWinding(mesh.winding()));
1328 const QSSGMesh::Mesh::VertexBuffer vertexBuffer = mesh.vertexBuffer();
1329 const QSSGMesh::Mesh::IndexBuffer indexBuffer = mesh.indexBuffer();
1330 const QSSGMesh::Mesh::TargetBuffer targetBuffer = mesh.targetBuffer();
1332 QSSGRenderComponentType indexBufComponentType = QSSGRenderComponentType::UnsignedInt16;
1333 QRhiCommandBuffer::IndexFormat rhiIndexFormat = QRhiCommandBuffer::IndexUInt16;
1334 if (!indexBuffer.data.isEmpty()) {
1335 indexBufComponentType = QSSGRenderComponentType(indexBuffer.componentType);
1336 const quint32 sizeofType = quint32(QSSGBaseTypeHelpers::getSizeOfType(indexBufComponentType));
1337 if (sizeofType == 2 || sizeofType == 4) {
1339 if (indexBufComponentType == QSSGRenderComponentType::Int16)
1340 indexBufComponentType = QSSGRenderComponentType::UnsignedInt16;
1341 if (indexBufComponentType == QSSGRenderComponentType::Int32)
1342 indexBufComponentType = QSSGRenderComponentType::UnsignedInt32;
1343 rhiIndexFormat = indexBufComponentType == QSSGRenderComponentType::UnsignedInt32
1344 ? QRhiCommandBuffer::IndexUInt32 : QRhiCommandBuffer::IndexUInt16;
1351 QSSGRhiBufferPtr vertexBuffer;
1352 QSSGRhiBufferPtr indexBuffer;
1353 QSSGRhiInputAssemblerState ia;
1354 QRhiTexture *targetsTexture =
nullptr;
1357 QRhiResourceUpdateBatch *rub = meshBufferUpdateBatch();
1358 const auto &context = m_contextInterface->rhiContext();
1359 QSSGRhiContextPrivate *rhiCtxD = QSSGRhiContextPrivate::get(context.get());
1360 rhi.vertexBuffer = std::make_shared<QSSGRhiBuffer>(*context.get(),
1362 QRhiBuffer::VertexBuffer,
1363 vertexBuffer.stride,
1364 vertexBuffer.data.size());
1365 rhi.vertexBuffer->buffer()->setName(debugObjectName.toLatin1());
1366 rub->uploadStaticBuffer(rhi.vertexBuffer->buffer(), vertexBuffer.data);
1368 if (!indexBuffer.data.isEmpty()) {
1369 rhi.indexBuffer = std::make_shared<QSSGRhiBuffer>(*context.get(),
1371 QRhiBuffer::IndexBuffer,
1373 indexBuffer.data.size(),
1375 rub->uploadStaticBuffer(rhi.indexBuffer->buffer(), indexBuffer.data);
1378 if (!targetBuffer.data.isEmpty()) {
1379 const int arraySize = targetBuffer.entries.size() * targetBuffer.numTargets;
1380 const int numTexels = (targetBuffer.data.size() / arraySize) >> 4;
1381 const int texWidth = qCeil(qSqrt(numTexels));
1382 const QSize texSize(texWidth, texWidth);
1383 if (!rhi.targetsTexture) {
1384 rhi.targetsTexture = context->rhi()->newTextureArray(QRhiTexture::RGBA32F, arraySize, texSize);
1385 rhi.targetsTexture->create();
1386 rhiCtxD->registerTexture(rhi.targetsTexture);
1387 }
else if (rhi.targetsTexture->pixelSize() != texSize
1388 || rhi.targetsTexture->arraySize() != arraySize) {
1389 rhi.targetsTexture->setPixelSize(texSize);
1390 rhi.targetsTexture->setArraySize(arraySize);
1391 rhi.targetsTexture->create();
1394 const quint32 layerSize = texWidth * texWidth * 4 * 4;
1395 for (
int arrayId = 0; arrayId < arraySize; ++arrayId) {
1396 QRhiTextureSubresourceUploadDescription targetDesc(targetBuffer.data + arrayId * layerSize, layerSize);
1397 QRhiTextureUploadDescription desc(QRhiTextureUploadEntry(arrayId, 0, targetDesc));
1398 rub->uploadTexture(rhi.targetsTexture, desc);
1401 for (quint32 entryIdx = 0, entryEnd = targetBuffer.entries.size(); entryIdx < entryEnd; ++entryIdx) {
1402 const char *nameStr = targetBuffer.entries[entryIdx].name.constData();
1403 if (!strcmp(nameStr, QSSGMesh::MeshInternal::getPositionAttrName())) {
1404 rhi.ia.targetOffsets[QSSGRhiInputAssemblerState::PositionSemantic] = entryIdx * targetBuffer.numTargets;
1405 }
else if (!strcmp(nameStr, QSSGMesh::MeshInternal::getNormalAttrName())) {
1406 rhi.ia.targetOffsets[QSSGRhiInputAssemblerState::NormalSemantic] = entryIdx * targetBuffer.numTargets;
1407 }
else if (!strcmp(nameStr, QSSGMesh::MeshInternal::getUV0AttrName())) {
1408 rhi.ia.targetOffsets[QSSGRhiInputAssemblerState::TexCoord0Semantic] = entryIdx * targetBuffer.numTargets;
1409 }
else if (!strcmp(nameStr, QSSGMesh::MeshInternal::getUV1AttrName())) {
1410 rhi.ia.targetOffsets[QSSGRhiInputAssemblerState::TexCoord1Semantic] = entryIdx * targetBuffer.numTargets;
1411 }
else if (!strcmp(nameStr, QSSGMesh::MeshInternal::getTexTanAttrName())) {
1412 rhi.ia.targetOffsets[QSSGRhiInputAssemblerState::TangentSemantic] = entryIdx * targetBuffer.numTargets;
1413 }
else if (!strcmp(nameStr, QSSGMesh::MeshInternal::getTexBinormalAttrName())) {
1414 rhi.ia.targetOffsets[QSSGRhiInputAssemblerState::BinormalSemantic] = entryIdx * targetBuffer.numTargets;
1415 }
else if (!strcmp(nameStr, QSSGMesh::MeshInternal::getColorAttrName())) {
1416 rhi.ia.targetOffsets[QSSGRhiInputAssemblerState::ColorSemantic] = entryIdx * targetBuffer.numTargets;
1419 rhi.ia.targetCount = targetBuffer.numTargets;
1420 }
else if (rhi.targetsTexture) {
1421 rhiCtxD->releaseTexture(rhi.targetsTexture);
1422 rhi.targetsTexture =
nullptr;
1423 rhi.ia.targetOffsets = { UINT8_MAX, UINT8_MAX, UINT8_MAX, UINT8_MAX,
1424 UINT8_MAX, UINT8_MAX, UINT8_MAX };
1425 rhi.ia.targetCount = 0;
1428 QVector<QSSGRenderVertexBufferEntry> entryBuffer;
1429 entryBuffer.resize(vertexBuffer.entries.size());
1430 for (quint32 entryIdx = 0, entryEnd = vertexBuffer.entries.size(); entryIdx < entryEnd; ++entryIdx)
1431 entryBuffer[entryIdx] = vertexBuffer.entries[entryIdx].toRenderVertexBufferEntry();
1433 QVarLengthArray<QRhiVertexInputAttribute, 4> inputAttrs;
1434 for (quint32 entryIdx = 0, entryEnd = entryBuffer.size(); entryIdx < entryEnd; ++entryIdx) {
1435 const QSSGRenderVertexBufferEntry &vbe(entryBuffer[entryIdx]);
1436 const int binding = 0;
1437 const int location = 0;
1438 const QRhiVertexInputAttribute::Format format = QSSGRhiHelpers::toVertexInputFormat(vbe.m_componentType, vbe.m_numComponents);
1439 const int offset =
int(vbe.m_firstItemOffset);
1442 const char *nameStr = vbe.m_name.constData();
1443 if (!strcmp(nameStr, QSSGMesh::MeshInternal::getPositionAttrName())) {
1444 rhi.ia.inputs << QSSGRhiInputAssemblerState::PositionSemantic;
1445 }
else if (!strcmp(nameStr, QSSGMesh::MeshInternal::getNormalAttrName())) {
1446 rhi.ia.inputs << QSSGRhiInputAssemblerState::NormalSemantic;
1447 }
else if (!strcmp(nameStr, QSSGMesh::MeshInternal::getUV0AttrName())) {
1448 rhi.ia.inputs << QSSGRhiInputAssemblerState::TexCoord0Semantic;
1449 }
else if (!strcmp(nameStr, QSSGMesh::MeshInternal::getUV1AttrName())) {
1450 rhi.ia.inputs << QSSGRhiInputAssemblerState::TexCoord1Semantic;
1451 }
else if (!strcmp(nameStr, QSSGMesh::MeshInternal::getLightmapUVAttrName())) {
1452 rhi.ia.inputs << QSSGRhiInputAssemblerState::TexCoordLightmapSemantic;
1453 }
else if (!strcmp(nameStr, QSSGMesh::MeshInternal::getTexTanAttrName())) {
1454 rhi.ia.inputs << QSSGRhiInputAssemblerState::TangentSemantic;
1455 }
else if (!strcmp(nameStr, QSSGMesh::MeshInternal::getTexBinormalAttrName())) {
1456 rhi.ia.inputs << QSSGRhiInputAssemblerState::BinormalSemantic;
1457 }
else if (!strcmp(nameStr, QSSGMesh::MeshInternal::getColorAttrName())) {
1458 rhi.ia.inputs << QSSGRhiInputAssemblerState::ColorSemantic;
1459 }
else if (!strcmp(nameStr, QSSGMesh::MeshInternal::getJointAttrName())) {
1460 rhi.ia.inputs << QSSGRhiInputAssemblerState::JointSemantic;
1461 }
else if (!strcmp(nameStr, QSSGMesh::MeshInternal::getWeightAttrName())) {
1462 rhi.ia.inputs << QSSGRhiInputAssemblerState::WeightSemantic;
1464 qWarning(
"Unknown vertex input %s in mesh", nameStr);
1468 QRhiVertexInputAttribute inputAttr(binding, location, format, offset);
1469 inputAttrs.append(inputAttr);
1472 rhi.ia.inputLayout.setAttributes(inputAttrs.cbegin(), inputAttrs.cend());
1473 rhi.ia.inputLayout.setBindings({ vertexBuffer.stride });
1474 rhi.ia.topology = QSSGRhiHelpers::toTopology(QSSGRenderDrawMode(mesh.drawMode()));
1476 if (rhi.ia.topology == QRhiGraphicsPipeline::TriangleFan && !context->rhi()->isFeatureSupported(QRhi::TriangleFanTopology))
1477 qWarning(
"Mesh topology is TriangleFan but this is not supported with the active graphics API. Rendering will be incorrect.");
1479 QVector<QSSGMesh::Mesh::Subset> meshSubsets = mesh.subsets();
1480 for (quint32 subsetIdx = 0, subsetEnd = meshSubsets.size(); subsetIdx < subsetEnd; ++subsetIdx) {
1481 QSSGRenderSubset subset;
1482 const QSSGMesh::Mesh::Subset &source(meshSubsets[subsetIdx]);
1483 subset.bounds = QSSGBounds3(source.bounds.min, source.bounds.max);
1484 subset.count = source.count;
1485 subset.offset = source.offset;
1486 for (
auto &lod : source.lods)
1487 subset.lods.append(QSSGRenderSubset::Lod({lod.count, lod.offset, lod.distance}));
1490 if (rhi.vertexBuffer) {
1491 subset.rhi.vertexBuffer = rhi.vertexBuffer;
1492 subset.rhi.ia = rhi.ia;
1494 if (rhi.indexBuffer)
1495 subset.rhi.indexBuffer = rhi.indexBuffer;
1496 if (rhi.targetsTexture)
1497 subset.rhi.targetsTexture = rhi.targetsTexture;
1499 newMesh->subsets.push_back(subset);
1502 if (!meshSubsets.isEmpty())
1503 newMesh->lightmapSizeHint = meshSubsets.first().lightmapSizeHint;
1508void QSSGBufferManager::releaseGeometry(QSSGRenderGeometry *geometry,
const QString &lightmapSource)
1510 QMutexLocker meshMutexLocker(&meshBufferMutex);
1511 auto key = std::make_pair(geometry, lightmapSource);
1512 const auto meshItr = customMeshMap.constFind(key);
1513 if (meshItr != customMeshMap.cend()) {
1514 if (QSSGBufferManagerStat::enabled(QSSGBufferManagerStat::Level::Debug))
1515 qDebug() <<
"- releaseGeometry: " << geometry << currentLayer;
1516 Q_QUICK3D_PROFILE_START(QQuick3DProfiler::Quick3DCustomMeshLoad);
1517 Q_TRACE_SCOPE(QSSG_customMeshUnload);
1518 decreaseMemoryStat(meshItr.value().mesh);
1519 QSSGRhiContextPrivate *rhiCtxD = QSSGRhiContextPrivate::get(m_contextInterface->rhiContext().get());
1520 rhiCtxD->releaseMesh(meshItr.value().mesh);
1521 customMeshMap.erase(meshItr);
1522 Q_QUICK3D_PROFILE_END_WITH_ID(QQuick3DProfiler::Quick3DCustomMeshLoad,
1523 stats.meshDataSize, geometry->profilingId);
1527void QSSGBufferManager::releaseTextureData(
const QSSGRenderTextureData *data)
1529 QVarLengthArray<CustomImageCacheKey, 4> keys;
1530 for (
auto it = customTextureMap.cbegin(), end = customTextureMap.cend(); it != end; ++it) {
1531 if (it.key().data == data)
1532 keys.append(it.key());
1534 for (
const CustomImageCacheKey &key : keys)
1535 releaseTextureData(key);
1538void QSSGBufferManager::releaseTextureData(
const CustomImageCacheKey &key)
1540 const auto textureDataItr = customTextureMap.constFind(key);
1541 if (textureDataItr != customTextureMap.cend()) {
1542 auto rhiTexture = textureDataItr.value().renderImageTexture.m_texture;
1544 if (QSSGBufferManagerStat::enabled(QSSGBufferManagerStat::Level::Debug))
1545 qDebug() <<
"- releaseTextureData: " << rhiTexture << currentLayer;
1546 Q_QUICK3D_PROFILE_START(QQuick3DProfiler::Quick3DTextureLoad);
1547 Q_TRACE_SCOPE(QSSG_textureUnload);
1548 decreaseMemoryStat(rhiTexture);
1549 QSSGRhiContextPrivate *rhiCtxD = QSSGRhiContextPrivate::get(m_contextInterface->rhiContext().get());
1550 rhiCtxD->releaseTexture(rhiTexture);
1551 Q_QUICK3D_PROFILE_END_WITH_ID(QQuick3DProfiler::Quick3DTextureLoad,
1552 stats.imageDataSize, 0);
1555 customTextureMap.erase(textureDataItr);
1559void QSSGBufferManager::releaseExtensionResult(
const QSSGRenderExtension &rext)
1561 renderExtensionTexture.remove(&rext);
1564void QSSGBufferManager::releaseUserRenderPass(
const QSSGRenderUserPass &upass)
1567 std::vector<QSSGUserRenderPassManagerWeakPtr> activeManagers;
1568 activeManagers.reserve(userRenderPassManagers.size());
1569 for (
const auto &wmptr : std::as_const(userRenderPassManagers)) {
1570 if (
const auto &mng = wmptr.lock()) {
1571 mng->unscheduleUserPass(&upass);
1572 activeManagers.push_back(wmptr);
1576 userRenderPassManagers = std::move(activeManagers);
1579void QSSGBufferManager::releaseMesh(
const QSSGRenderPath &inSourcePath)
1581 QMutexLocker meshMutexLocker(&meshBufferMutex);
1582 auto key = std::make_pair(inSourcePath, lightmapSource);
1583 const auto meshItr = meshMap.constFind(key);
1584 if (meshItr != meshMap.cend()) {
1585 if (QSSGBufferManagerStat::enabled(QSSGBufferManagerStat::Level::Debug))
1586 qDebug() <<
"- releaseMesh: " << inSourcePath.path() << currentLayer;
1587 Q_QUICK3D_PROFILE_START(QQuick3DProfiler::Quick3DMeshLoad);
1588 Q_TRACE_SCOPE(QSSG_meshUnload);
1589 decreaseMemoryStat(meshItr.value().mesh);
1590 QSSGRhiContextPrivate *rhiCtxD = QSSGRhiContextPrivate::get(m_contextInterface->rhiContext().get());
1591 rhiCtxD->releaseMesh(meshItr.value().mesh);
1592 meshMap.erase(meshItr);
1593 Q_QUICK3D_PROFILE_END_WITH_STRING(QQuick3DProfiler::Quick3DMeshLoad,
1594 stats.meshDataSize, inSourcePath.path().toUtf8());
1598void QSSGBufferManager::releaseImage(
const ImageCacheKey &key)
1600 const auto imageItr = imageMap.constFind(key);
1601 if (imageItr != imageMap.cend()) {
1602 auto rhiTexture = imageItr.value().renderImageTexture.m_texture;
1604 if (QSSGBufferManagerStat::enabled(QSSGBufferManagerStat::Level::Debug))
1605 qDebug() <<
"- releaseTexture: " << key.path.path() << currentLayer;
1606 Q_QUICK3D_PROFILE_START(QQuick3DProfiler::Quick3DTextureLoad);
1607 Q_TRACE_SCOPE(QSSG_textureUnload);
1608 decreaseMemoryStat(rhiTexture);
1609 QSSGRhiContextPrivate *rhiCtxD = QSSGRhiContextPrivate::get(m_contextInterface->rhiContext().get());
1610 rhiCtxD->releaseTexture(rhiTexture);
1611 Q_QUICK3D_PROFILE_END_WITH_STRING(QQuick3DProfiler::Quick3DTextureLoad,
1612 stats.imageDataSize, key.path.path().toUtf8());
1614 imageMap.erase(imageItr);
1618bool QSSGBufferManager::validateLightmap()
1620 if (lightmapSourceDirty) {
1621 lightmapSourceDirty =
false;
1622 if (lightmapSource.isEmpty()) {
1623 lightmapFileValid =
false;
1625 QSharedPointer<QSSGLightmapLoader> loader = QSSGLightmapLoader::open(lightmapSource);
1626 lightmapFileValid = loader !=
nullptr;
1627 if (!lightmapFileValid)
1628 qCWarning(WARNING,
"Lightmaps are disabled.");
1631 return lightmapFileValid;
1634void QSSGBufferManager::cleanupUnreferencedBuffers(quint32 frameId, QSSGRenderLayer *currentLayer)
1636 Q_UNUSED(currentLayer);
1638 QSSGRhiContextPrivate *rhiCtxD = QSSGRhiContextPrivate::get(m_contextInterface->rhiContext().get());
1641 if (frameId == frameCleanupIndex)
1644 auto isUnused = [] (
const QHash<QSSGRenderLayer*, uint32_t> &usages) ->
bool {
1645 for (
const auto &value : std::as_const(usages))
1652 QMutexLocker meshMutexLocker(&meshBufferMutex);
1654 auto meshIterator = meshMap.cbegin();
1655 while (meshIterator != meshMap.cend()) {
1656 if (isUnused(meshIterator.value().usageCounts)) {
1657 if (QSSGBufferManagerStat::enabled(QSSGBufferManagerStat::Level::Debug))
1658 qDebug() <<
"- releaseGeometry: " << meshIterator.key().first.path() << currentLayer;
1659 decreaseMemoryStat(meshIterator.value().mesh);
1660 rhiCtxD->releaseMesh(meshIterator.value().mesh);
1661 meshIterator = meshMap.erase(meshIterator);
1668 auto customMeshIterator = customMeshMap.cbegin();
1669 while (customMeshIterator != customMeshMap.cend()) {
1670 if (isUnused(customMeshIterator.value().usageCounts)) {
1671 if (QSSGBufferManagerStat::enabled(QSSGBufferManagerStat::Level::Debug))
1672 qDebug() <<
"- releaseGeometry: " << customMeshIterator.key() << currentLayer;
1673 decreaseMemoryStat(customMeshIterator.value().mesh);
1674 rhiCtxD->releaseMesh(customMeshIterator.value().mesh);
1675 customMeshIterator = customMeshMap.erase(customMeshIterator);
1677 ++customMeshIterator;
1683 auto sgIterator = qsgImageMap.cbegin();
1684 while (sgIterator != qsgImageMap.cend()) {
1685 if (isUnused(sgIterator.value().usageCounts)) {
1689 sgIterator = qsgImageMap.erase(sgIterator);
1696 auto imageKeyIterator = imageMap.cbegin();
1697 while (imageKeyIterator != imageMap.cend()) {
1698 if (isUnused(imageKeyIterator.value().usageCounts)) {
1699 auto rhiTexture = imageKeyIterator.value().renderImageTexture.m_texture;
1701 if (QSSGBufferManagerStat::enabled(QSSGBufferManagerStat::Level::Debug))
1702 qDebug() <<
"- releaseTexture: " << imageKeyIterator.key().path.path() << currentLayer;
1703 decreaseMemoryStat(rhiTexture);
1704 rhiCtxD->releaseTexture(rhiTexture);
1706 imageKeyIterator = imageMap.erase(imageKeyIterator);
1713 auto textureDataIterator = customTextureMap.cbegin();
1714 while (textureDataIterator != customTextureMap.cend()) {
1715 if (isUnused(textureDataIterator.value().usageCounts)) {
1716 auto rhiTexture = textureDataIterator.value().renderImageTexture.m_texture;
1718 if (QSSGBufferManagerStat::enabled(QSSGBufferManagerStat::Level::Debug))
1719 qDebug() <<
"- releaseTextureData: " << rhiTexture << currentLayer;
1720 decreaseMemoryStat(rhiTexture);
1721 rhiCtxD->releaseTexture(rhiTexture);
1723 textureDataIterator = customTextureMap.erase(textureDataIterator);
1725 ++textureDataIterator;
1730 auto renderExtensionTextureKeyIterator = renderExtensionTexture.cbegin();
1731 while (renderExtensionTextureKeyIterator != renderExtensionTexture.cend()) {
1735 auto rhiTexture = renderExtensionTextureKeyIterator.value().renderImageTexture.m_texture;
1737 renderExtensionTextureKeyIterator = renderExtensionTexture.erase(renderExtensionTextureKeyIterator);
1739 ++renderExtensionTextureKeyIterator;
1743 frameCleanupIndex = frameId;
1744 if (QSSGBufferManagerStat::enabled(QSSGBufferManagerStat::Level::Usage)) {
1745 qDebug() <<
"QSSGBufferManager::cleanupUnreferencedBuffers()" <<
this <<
"frame:" << frameCleanupIndex << currentLayer;
1746 qDebug() <<
"Textures(by path): " << imageMap.count();
1747 qDebug() <<
"Textures(custom): " << customTextureMap.count();
1748 qDebug() <<
"Textures(Extension)" << renderExtensionTexture.count();
1749 qDebug() <<
"Textures(qsg): " << qsgImageMap.count();
1750 qDebug() <<
"Geometry(by path): " << meshMap.count();
1751 qDebug() <<
"Geometry(custom): " << customMeshMap.count();
1755void QSSGBufferManager::resetUsageCounters(quint32 frameId, QSSGRenderLayer *layer)
1757 currentLayer = layer;
1758 if (frameResetIndex == frameId)
1762 for (
auto &imageData : qsgImageMap)
1763 imageData.usageCounts[layer] = 0;
1766 for (
auto &imageData : imageMap)
1767 imageData.usageCounts[layer] = 0;
1770 for (
auto &imageData : customTextureMap)
1771 imageData.usageCounts[layer] = 0;
1773 for (
auto &meshData : meshMap)
1774 meshData.usageCounts[layer] = 0;
1777 for (
auto &meshData : customMeshMap)
1778 meshData.usageCounts[layer] = 0;
1783 for (
auto &retData : renderExtensionTexture) {
1784 const bool hasTexture = (retData.renderImageTexture.m_texture !=
nullptr);
1785 retData.usageCounts[layer] = uint32_t(hasTexture) * 1;
1788 frameResetIndex = frameId;
1791void QSSGBufferManager::registerMeshData(
const QString &assetId,
const QVector<QSSGMesh::Mesh> &meshData)
1793 auto it = g_assetMeshMap->find(assetId);
1794 if (it != g_assetMeshMap->end())
1797 g_assetMeshMap->insert(assetId, { meshData, 1 });
1800void QSSGBufferManager::unregisterMeshData(
const QString &assetId)
1802 auto it = g_assetMeshMap->find(assetId);
1803 if (it != g_assetMeshMap->end() && (--it->ref == 0))
1804 g_assetMeshMap->erase(AssetMeshMap::const_iterator(it));
1807QSSGRenderMesh *QSSGBufferManager::loadRenderMesh(
const QSSGRenderPath &inMeshPath, QSSGMeshProcessingOptions options)
1809 if (inMeshPath.isNull())
1812 auto key = std::make_pair(inMeshPath, options.lightmapPath);
1815 auto meshItr = meshMap.find(key);
1816 if (meshItr != meshMap.cend()) {
1817 if (options.isCompatible(meshItr.value().options)) {
1818 meshItr.value().usageCounts[currentLayer]++;
1819 return meshItr.value().mesh;
1823 auto *mesh = meshItr->mesh;
1824 meshMap.erase(meshItr);
1825 auto keyReaped = std::make_pair(QSSGRenderPath(inMeshPath.path() + u"@reaped"), QString());
1826 meshMap.insert(keyReaped, { mesh, { { currentLayer, 0 } }, 0, { } });
1830 Q_QUICK3D_PROFILE_START(QQuick3DProfiler::Quick3DMeshLoad);
1831 Q_TRACE_SCOPE(QSSG_meshLoadPath, inMeshPath.path());
1833 auto [mesh, debugObjectName] = loadFromLightmapFile(options.lightmapPath, options.lightmapKey);
1835 if (!mesh.isValid()) {
1836 mesh = loadMeshData(inMeshPath);
1837 debugObjectName = QFileInfo(inMeshPath.path()).fileName();
1840 if (!mesh.isValid()) {
1841 qCWarning(WARNING,
"Failed to load mesh: %s", qPrintable(inMeshPath.path()));
1842 Q_QUICK3D_PROFILE_END_WITH_PAYLOAD(QQuick3DProfiler::Quick3DMeshLoad,
1843 stats.meshDataSize);
1846 if (QSSGBufferManagerStat::enabled(QSSGBufferManagerStat::Level::Debug))
1847 qDebug() <<
"+ uploadGeometry: " << inMeshPath.path() << currentLayer;
1849 auto ret = createRenderMesh(mesh, debugObjectName);
1850 meshMap.insert(key, { ret, { { currentLayer, 1 } }, 0, options });
1851 QSSGRhiContextPrivate *rhiCtxD = QSSGRhiContextPrivate::get(m_contextInterface->rhiContext().get());
1852 rhiCtxD->registerMesh(ret);
1853 increaseMemoryStat(ret);
1854 Q_QUICK3D_PROFILE_END_WITH_STRING(QQuick3DProfiler::Quick3DMeshLoad,
1855 stats.meshDataSize, inMeshPath.path().toUtf8());
1859QSSGRenderMesh *QSSGBufferManager::loadRenderMesh(QSSGRenderGeometry *geometry, QSSGMeshProcessingOptions options)
1861 QSSGRhiContextPrivate *rhiCtxD = QSSGRhiContextPrivate::get(m_contextInterface->rhiContext().get());
1862 auto key = std::make_pair(geometry, options.lightmapPath);
1863 auto meshIterator = customMeshMap.find(key);
1864 if (meshIterator == customMeshMap.end()) {
1865 meshIterator = customMeshMap.insert(key, MeshData());
1866 }
else if (geometry->generationId() != meshIterator->generationId || !options.isCompatible(meshIterator->options)) {
1868 releaseGeometry(geometry, options.lightmapPath);
1869 meshIterator = customMeshMap.insert(key, MeshData());
1872 meshIterator.value().usageCounts[currentLayer]++;
1873 return meshIterator.value().mesh;
1876 Q_QUICK3D_PROFILE_START(QQuick3DProfiler::Quick3DCustomMeshLoad);
1877 Q_TRACE_SCOPE(QSSG_customMeshLoad);
1879 auto [mesh, debugObjectName] = loadFromLightmapFile(options.lightmapPath, options.lightmapKey);
1882 if (!geometry->meshData().m_vertexBuffer.isEmpty() || mesh.isValid()) {
1885 if (!mesh.isValid()) {
1886 mesh = QSSGMesh::Mesh::fromRuntimeData(geometry->meshData(), &error);
1887 debugObjectName = geometry->debugObjectName;
1890 if (mesh.isValid()) {
1891 if (QSSGBufferManagerStat::enabled(QSSGBufferManagerStat::Level::Debug))
1892 qDebug() <<
"+ uploadGeometry: " << geometry << currentLayer;
1893 meshIterator->mesh = createRenderMesh(mesh, debugObjectName);
1894 meshIterator->usageCounts[currentLayer] = 1;
1895 meshIterator->generationId = geometry->generationId();
1896 meshIterator->options = options;
1897 rhiCtxD->registerMesh(meshIterator->mesh);
1898 increaseMemoryStat(meshIterator->mesh);
1900 qWarning(
"Mesh building failed: %s", qPrintable(error));
1905 Q_QUICK3D_PROFILE_END_WITH_ID(QQuick3DProfiler::Quick3DCustomMeshLoad,
1906 stats.meshDataSize, geometry->profilingId);
1907 return meshIterator->mesh;
1910std::unique_ptr<QSSGMeshBVH> QSSGBufferManager::loadMeshBVH(
const QSSGRenderPath &inSourcePath)
1912 const QSSGMesh::Mesh mesh = loadMeshData(inSourcePath);
1913 if (!mesh.isValid()) {
1914 qCWarning(WARNING,
"Failed to load mesh: %s", qPrintable(inSourcePath.path()));
1917 QSSGMeshBVHBuilder meshBVHBuilder(mesh);
1918 return meshBVHBuilder.buildTree();
1921std::unique_ptr<QSSGMeshBVH> QSSGBufferManager::loadMeshBVH(QSSGRenderGeometry *geometry)
1927 if (geometry->primitiveType() != QSSGMesh::Mesh::DrawMode::Triangles)
1931 bool hasIndexBuffer =
false;
1932 QSSGRenderComponentType indexBufferFormat = QSSGRenderComponentType::UnsignedInt32;
1937 for (
int i = 0; i < geometry->attributeCount(); ++i) {
1938 auto attribute = geometry->attribute(i);
1939 if (attribute.semantic == QSSGMesh::RuntimeMeshData::Attribute::PositionSemantic) {
1940 posOffset = attribute.offset;
1941 }
else if (attribute.semantic == QSSGMesh::RuntimeMeshData::Attribute::TexCoord0Semantic) {
1943 uvOffset = attribute.offset;
1944 }
else if (!hasUV && attribute.semantic == QSSGMesh::RuntimeMeshData::Attribute::TexCoord1Semantic) {
1946 uvOffset = attribute.offset;
1947 }
else if (attribute.semantic == QSSGMesh::RuntimeMeshData::Attribute::IndexSemantic) {
1948 hasIndexBuffer =
true;
1949 indexBufferFormat = attribute.componentType;
1950 if (indexBufferFormat != QSSGRenderComponentType::Int16
1951 && indexBufferFormat != QSSGRenderComponentType::Int32
1952 && indexBufferFormat != QSSGRenderComponentType::UnsignedInt16
1953 && indexBufferFormat != QSSGRenderComponentType::UnsignedInt32) {
1954 qWarning() <<
"Unsupported index buffer format for geometry";
1960 QSSGMeshBVHBuilder meshBVHBuilder(geometry->vertexBuffer(),
1966 geometry->indexBuffer(),
1968 return meshBVHBuilder.buildTree();
1971std::unique_ptr<QSSGMeshBVH> QSSGBufferManager::loadMeshBVH(
const QSSGMesh::Mesh &mesh)
1973 QSSGMeshBVHBuilder meshBVHBuilder(mesh);
1974 return meshBVHBuilder.buildTree();
1977QSSGMesh::Mesh QSSGBufferManager::loadMeshData(
const QSSGRenderPath &inMeshPath)
1979 QSSGMesh::Mesh result;
1982 if (inMeshPath.path().startsWith(QChar::fromLatin1(
'#')))
1983 result = loadPrimitive(inMeshPath.path());
1986 if (!result.isValid() && inMeshPath.path().startsWith(u'!')) {
1987 const auto &[idx, assetId] = splitRuntimeMeshPath(inMeshPath);
1989 const auto ait = g_assetMeshMap->constFind(assetId);
1990 if (ait != g_assetMeshMap->constEnd()) {
1991 const auto &meshes = ait->meshes;
1992 if (idx < meshes.size())
1993 result = ait->meshes.at(idx);
1996 qWarning(
"Unexpected mesh path!");
2001 if (!result.isValid()) {
2002 QString pathBuilder = inMeshPath.path();
2003 int poundIndex = pathBuilder.lastIndexOf(QChar::fromLatin1(
'#'));
2005 if (poundIndex != -1) {
2006 id = QStringView(pathBuilder).mid(poundIndex + 1).toUInt();
2007 pathBuilder = pathBuilder.left(poundIndex);
2009 if (!pathBuilder.isEmpty()) {
2010 QSharedPointer<QIODevice> device(QSSGInputUtil::getStreamForFile(pathBuilder));
2012 QSSGMesh::Mesh mesh = QSSGMesh::Mesh::loadMesh(device.data(), id);
2022QSSGMesh::Mesh QSSGBufferManager::loadMeshData(
const QSSGRenderGeometry *geometry)
2025 QSSGMesh::Mesh mesh = QSSGMesh::Mesh::fromRuntimeData(geometry->meshData(), &error);
2026 if (!mesh.isValid())
2027 qWarning(
"loadMeshDataForCustomMeshUncached failed: %s", qPrintable(error));
2032void QSSGBufferManager::registerExtensionResult(
const QSSGRenderExtension &extensions,
2033 QRhiTexture *texture)
2036 const bool isMipMapped = texture->flags().testFlag(QRhiTexture::Flag::MipMapped);
2037 const auto mipLevels = isMipMapped ? QRhi::mipLevelsForSize(texture->pixelSize()) : 0;
2038 QSSGRenderImageTextureFlags flags;
2039 const bool isSRGB = texture->flags().testFlag(QRhiTexture::Flag::sRGB);
2040 flags.setLinear(!isSRGB);
2041 const bool isRGBA8 = (texture->format() == QRhiTexture::Format::RGBA8);
2042 flags.setRgbe8(isRGBA8);
2043 renderExtensionTexture.insert(&extensions, ImageData { QSSGRenderImageTexture{ texture, mipLevels, flags }, {}, 0 });
2045 renderExtensionTexture.insert(&extensions, {});
2049void QSSGBufferManager::clear()
2051 QSSGRhiContextPrivate *rhiCtxD = QSSGRhiContextPrivate::get(m_contextInterface->rhiContext().get());
2053 if (meshBufferUpdates) {
2054 meshBufferUpdates->release();
2055 meshBufferUpdates =
nullptr;
2059 QMutexLocker meshMutexLocker(&meshBufferMutex);
2061 auto meshMapCopy = meshMap;
2062 meshMapCopy.detach();
2063 for (
auto iter = meshMapCopy.begin(), end = meshMapCopy.end(); iter != end; ++iter) {
2064 QSSGRenderMesh *theMesh = iter.value().mesh;
2066 if (QSSGBufferManagerStat::enabled(QSSGBufferManagerStat::Level::Debug))
2067 qDebug() <<
"- releaseGeometry: " << iter.key().first.path() << currentLayer;
2068 decreaseMemoryStat(theMesh);
2069 rhiCtxD->releaseMesh(theMesh);
2075 auto customMeshMapCopy = customMeshMap;
2076 customMeshMapCopy.detach();
2077 for (
auto iter = customMeshMapCopy.begin(), end = customMeshMapCopy.end(); iter != end; ++iter) {
2078 QSSGRenderMesh *theMesh = iter.value().mesh;
2080 if (QSSGBufferManagerStat::enabled(QSSGBufferManagerStat::Level::Debug))
2081 qDebug() <<
"- releaseGeometry: " << iter.key() << currentLayer;
2082 decreaseMemoryStat(theMesh);
2083 rhiCtxD->releaseMesh(theMesh);
2086 customMeshMap.clear();
2090 for (
auto it = imageMap.constBegin(), end = imageMap.constEnd(); it != end; ++it)
2091 releaseImage(it.key());
2096 for (
auto it = customTextureMap.cbegin(), end = customTextureMap.cend(); it != end; ++it)
2097 releaseTextureData(it.key());
2099 customTextureMap.clear();
2103 qsgImageMap.clear();
2106 lightmapSourceDirty =
true;
2109QRhiResourceUpdateBatch *QSSGBufferManager::meshBufferUpdateBatch()
2111 if (!meshBufferUpdates)
2112 meshBufferUpdates = m_contextInterface->rhiContext()->rhi()->nextResourceUpdateBatch();
2113 return meshBufferUpdates;
2116void QSSGBufferManager::commitBufferResourceUpdates()
2118 if (meshBufferUpdates) {
2119 m_contextInterface->rhiContext()->commandBuffer()->resourceUpdate(meshBufferUpdates);
2120 meshBufferUpdates =
nullptr;
2124void QSSGBufferManager::processResourceLoader(
const QSSGRenderResourceLoader *loader)
2126 for (
auto &mesh : std::as_const(loader->meshes))
2127 loadRenderMesh(mesh, {});
2129 for (
auto customMesh : std::as_const(loader->geometries))
2130 loadRenderMesh(
static_cast<QSSGRenderGeometry*>(customMesh), {});
2132 for (
auto texture : std::as_const(loader->textures)) {
2133 const auto image =
static_cast<QSSGRenderImage *>(texture);
2134 loadRenderImage(image);
2138 commitBufferResourceUpdates();
2144 case QRhiTexture::UnknownFormat:
2146 case QRhiTexture::RGBA8:
2148 case QRhiTexture::BGRA8:
2150 case QRhiTexture::R8:
2152 case QRhiTexture::RG8:
2154 case QRhiTexture::R16:
2156 case QRhiTexture::RG16:
2158 case QRhiTexture::RED_OR_ALPHA8:
2161 case QRhiTexture::RGBA16F:
2163 case QRhiTexture::RGBA32F:
2165 case QRhiTexture::R16F:
2167 case QRhiTexture::R32F:
2170 case QRhiTexture::RGB10A2:
2173 case QRhiTexture::R8SI:
2175 case QRhiTexture::R32SI:
2177 case QRhiTexture::RG32SI:
2179 case QRhiTexture::RGBA32SI:
2182 case QRhiTexture::R8UI:
2184 case QRhiTexture::R32UI:
2186 case QRhiTexture::RG32UI:
2188 case QRhiTexture::RGBA32UI:
2191 case QRhiTexture::D16:
2193 case QRhiTexture::D24:
2195 case QRhiTexture::D24S8:
2197 case QRhiTexture::D32F:
2199 case QRhiTexture::D32FS8:
2202 case QRhiTexture::BC1:
2204 case QRhiTexture::BC2:
2206 case QRhiTexture::BC3:
2208 case QRhiTexture::BC4:
2210 case QRhiTexture::BC5:
2212 case QRhiTexture::BC6H:
2214 case QRhiTexture::BC7:
2217 case QRhiTexture::ETC2_RGB8:
2219 case QRhiTexture::ETC2_RGB8A1:
2221 case QRhiTexture::ETC2_RGBA8:
2224 case QRhiTexture::ASTC_4x4:
2225 case QRhiTexture::ASTC_5x4:
2226 case QRhiTexture::ASTC_5x5:
2227 case QRhiTexture::ASTC_6x5:
2228 case QRhiTexture::ASTC_6x6:
2229 case QRhiTexture::ASTC_8x5:
2230 case QRhiTexture::ASTC_8x6:
2231 case QRhiTexture::ASTC_8x8:
2232 case QRhiTexture::ASTC_10x5:
2233 case QRhiTexture::ASTC_10x6:
2234 case QRhiTexture::ASTC_10x8:
2235 case QRhiTexture::ASTC_10x10:
2236 case QRhiTexture::ASTC_12x10:
2237 case QRhiTexture::ASTC_12x12:
2240 Q_UNREACHABLE_RETURN(0);
2245 return (format >= QRhiTexture::BC1);
2254 auto format = texture->format();
2255 if (format == QRhiTexture::UnknownFormat)
2258 s = texture->pixelSize().width() * texture->pixelSize().height();
2260 const quint32 bytesPerPixel = textureFormatSize(format);
2261 QSSG_ASSERT_X(bytesPerPixel > 0,
"Invalid texture format size",
return 0);
2263 if (!isCompressedTextureFormat(format)) {
2269 if (texture->flags() & QRhiTexture::MipMapped)
2271 if (texture->flags() & QRhiTexture::CubeMap)
2281 s = buffer->buffer()->size();
2285void QSSGBufferManager::increaseMemoryStat(QRhiTexture *texture)
2287 stats.imageDataSize += textureMemorySize(texture);
2288 QSSGRhiContextStats::get(*m_contextInterface->rhiContext()).imageDataSizeChanges(stats.imageDataSize);
2291void QSSGBufferManager::decreaseMemoryStat(QRhiTexture *texture)
2293 stats.imageDataSize = qMax(0u, stats.imageDataSize - textureMemorySize(texture));
2294 QSSGRhiContextStats::get(*m_contextInterface->rhiContext()).imageDataSizeChanges(stats.imageDataSize);
2297void QSSGBufferManager::increaseMemoryStat(QSSGRenderMesh *mesh)
2299 stats.meshDataSize += bufferMemorySize(mesh->subsets.at(0).rhi.vertexBuffer)
2300 + bufferMemorySize(mesh->subsets.at(0).rhi.indexBuffer);
2301 QSSGRhiContextStats::get(*m_contextInterface->rhiContext()).meshDataSizeChanges(stats.meshDataSize);
2304void QSSGBufferManager::decreaseMemoryStat(QSSGRenderMesh *mesh)
2308 s = bufferMemorySize(mesh->subsets.at(0).rhi.vertexBuffer)
2309 + bufferMemorySize(mesh->subsets.at(0).rhi.indexBuffer);
2310 stats.meshDataSize = qMax(0u, stats.meshDataSize - s);
2311 QSSGRhiContextStats::get(*m_contextInterface->rhiContext()).meshDataSizeChanges(stats.meshDataSize);
2314void QSSGBufferManager::setLightmapSource(
const QString &source)
2316 if (lightmapSource != source) {
2317 lightmapSource = source;
2318 lightmapSourceDirty =
true;
2322void QSSGBufferManager::setCurrentlyLightmapBaking(
bool value)
2324 currentlyLightmapBaking = value;
2327void QSSGBufferManager::registerUserRenderPassManager(
const QSSGUserRenderPassManagerPtr &userPassManager)
2329 userRenderPassManagers.push_back(userPassManager);
2332size_t qHash(
const QSSGBufferManager::CustomImageCacheKey &k, size_t seed)
noexcept
2336 using MipMap_t = std::underlying_type_t<QSSGBufferManager::MipMode>;
2337 return qHash(*k.data, seed) ^ MipMap_t(k.mipMode);
Combined button and popup list for selecting options.
Q_TRACE_POINT(qtcore, QCoreApplication_postEvent_exit)
Q_TRACE_POINT(qtcore, QFactoryLoader_update, const QString &fileName)
constexpr size_t qHash(const QSize &s, size_t seed=0) noexcept
static quint32 textureFormatSize(QRhiTexture::Format format)
static const PrimitiveEntry primitives[nPrimitives]
static constexpr QSize sizeForMipLevel(int mipLevel, const QSize &baseLevelSize)
static const int nPrimitives
static quint64 textureMemorySize(QRhiTexture *texture)
static MeshIdxNamePair splitRuntimeMeshPath(const QSSGRenderPath &rpath)
static const float cube[]
static bool isCompressedTextureFormat(QRhiTexture::Format format)
static const char * primitivesDirectory
static quint64 bufferMemorySize(const QSSGRhiBufferPtr &buffer)
static QPair< QSSGMesh::Mesh, QString > loadFromLightmapFile(const QString &lightmapPath, const QString &lightmapKey)
QVector< QSSGMesh::Mesh > meshes
static constexpr bool enabled(Level)