7#include <QtQuick3DRuntimeRender/private/qssgrenderloadedtexture_p.h>
9#include <QtQuick3DRuntimeRender/private/qssgruntimerenderlogging_p.h>
10#include <QtQuick3DUtils/private/qssgmeshbvhbuilder_p.h>
11#include <QtQuick3DUtils/private/qssgbounds3_p.h>
12#include <QtQuick3DUtils/private/qssgassert_p.h>
14#include <QtQuick/QSGTexture>
17#include <QtGui/private/qimage_p.h>
18#include <QtQuick/private/qsgtexture_p.h>
19#include <QtQuick/private/qsgcompressedtexture_p.h>
22#include "../utils/qssgrenderbasetypes_p.h"
23#include <QtQuick3DRuntimeRender/private/qssgrendergeometry_p.h>
24#include <QtQuick3DRuntimeRender/private/qssgrendermodel_p.h>
25#include <QtQuick3DRuntimeRender/private/qssgrenderimage_p.h>
26#include <QtQuick3DRuntimeRender/private/qssgrendertexturedata_p.h>
27#include "../qssgrendercontextcore.h"
28#include <QtQuick3DRuntimeRender/private/qssglightmapper_p.h>
29#include <QtQuick3DRuntimeRender/private/qssgrenderresourceloader_p.h>
30#include <qtquick3d_tracepoints_p.h>
31#include "../extensionapi/qssgrenderextensions.h"
36using namespace Qt::StringLiterals;
82using AssetMeshMap = QHash<QString, MeshStorageRef>;
84Q_GLOBAL_STATIC(AssetMeshMap, g_assetMeshMap)
87QString QSSGBufferManager::runtimeMeshSourceName(
const QString &assetId, qsizetype meshId)
89 return QString::fromUtf16(u"!%1@%2").arg(QString::number(meshId), assetId);
92using MeshIdxNamePair = QPair<qsizetype, QString>;
95 const auto &path = rpath.path();
96 Q_ASSERT(path.startsWith(u'!'));
97 const auto strings = path.mid(1).split(u'@');
98 const bool hasData = (strings.size() == 2) && !strings[0].isEmpty() && !strings[1].isEmpty();
102 idx = strings.at(0).toLongLong(&ok);
104 return (ok) ? qMakePair(idx, strings.at(1)) : qMakePair(qsizetype(-1), QString());
111 const char *primitive;
119 {
"#Rectangle",
"/Rectangle.mesh"},
120 {
"#Sphere",
"/Sphere.mesh"},
121 {
"#Cube",
"/Cube.mesh"},
122 {
"#Cone",
"/Cone.mesh"},
123 {
"#Cylinder",
"/Cylinder.mesh"},
130 return QSize(qMax(1, baseLevelSize.width() >> mipLevel), qMax(1, baseLevelSize.height() >> mipLevel));
135 QPair<QSSGMesh::Mesh, QString> retVal;
137 if (lightmapPath.isEmpty() || lightmapKey.isEmpty())
140 if (
const auto io = QSSGLightmapLoader::open(lightmapPath)) {
142 if (metadata.isEmpty())
145 const QString meshKey = metadata[QStringLiteral(
"mesh_key")].toString();
146 if (meshKey.isEmpty())
150 if (meshData.isEmpty())
153 QBuffer buffer(&meshData);
154 buffer.open(QIODevice::ReadOnly);
155 retVal.first = QSSGMesh::Mesh::loadMesh(&buffer, 1);
156 retVal.second = QFileInfo(lightmapPath).fileName() +
" ["_L1 + lightmapKey + u']';
162QSSGBufferManager::QSSGBufferManager()
166QSSGBufferManager::~QSSGBufferManager()
169 m_contextInterface =
nullptr;
172void QSSGBufferManager::setRenderContextInterface(QSSGRenderContextInterface *ctx)
174 m_contextInterface = ctx;
177void QSSGBufferManager::releaseCachedResources()
182void QSSGBufferManager::releaseResourcesForLayer(QSSGRenderLayer *layer)
187 resetUsageCounters(frameResetIndex + 1, layer);
188 cleanupUnreferencedBuffers(frameResetIndex + 1, layer);
191QSSGRenderImageTexture QSSGBufferManager::loadRenderImage(
const QSSGRenderImage *image,
193 LoadRenderImageFlags flags)
195 if (inMipMode == MipModeFollowRenderImage)
196 inMipMode = image->m_generateMipmaps ? MipModeEnable : MipModeDisable;
198 const auto &context = m_contextInterface->rhiContext();
199 QSSGRenderImageTexture result;
200 if (image->m_qsgTexture) {
201 QRhi *rhi = context->rhi();
202 QSGTexture *qsgTexture = image->m_qsgTexture;
203 QRhiTexture *rhiTex = qsgTexture->rhiTexture();
204 if (!rhiTex || rhiTex->rhi() == rhi) {
208 QRhiResourceUpdateBatch *rub = rhi->nextResourceUpdateBatch();
209 if (qsgTexture->isAtlasTexture()) {
216 qsgTexture = qsgTexture->removedFromAtlas(rub);
218 qsgTexture->commitTextureOperations(rhi, rub);
219 context->commandBuffer()->resourceUpdate(rub);
220 auto theImage = qsgImageMap.find(qsgTexture);
221 if (theImage == qsgImageMap.end())
222 theImage = qsgImageMap.insert(qsgTexture, ImageData());
223 theImage.value().renderImageTexture.m_texture = qsgTexture->rhiTexture();
224 theImage.value().renderImageTexture.m_flags.setHasTransparency(qsgTexture->hasAlphaChannel());
225 theImage.value().usageCounts[currentLayer]++;
226 result = theImage.value().renderImageTexture;
232 if (inMipMode == MipModeBsdf)
233 qWarning(
"Cannot use QSGTexture from Texture.sourceItem as light probe.");
235 qWarning(
"Cannot use QSGTexture (presumably from Texture.sourceItem) created in another "
236 "window that was using a different graphics device/context. "
237 "Avoid using View3D.importScene between multiple windows.");
240 }
else if (image->m_rawTextureData) {
241 Q_TRACE_SCOPE(QSSG_textureLoad);
242 Q_QUICK3D_PROFILE_START(QQuick3DProfiler::Quick3DTextureLoad);
243 result = loadTextureData(image->m_rawTextureData, inMipMode);
244 Q_QUICK3D_PROFILE_END_WITH_ID(QQuick3DProfiler::Quick3DTextureLoad, stats.imageDataSize, image->profilingId);
245 }
else if (!image->m_imagePath.isEmpty()) {
247 const ImageCacheKey imageKey = { image->m_imagePath, inMipMode,
int(image->type), QString() };
248 auto foundIt = imageMap.find(imageKey);
249 if (foundIt != imageMap.cend()) {
250 result = foundIt.value().renderImageTexture;
252 Q_QUICK3D_PROFILE_START(QQuick3DProfiler::Quick3DTextureLoad);
253 QScopedPointer<QSSGLoadedTexture> theLoadedTexture;
254 const auto &path = image->m_imagePath.path();
255 const bool flipY = flags.testFlag(LoadWithFlippedY);
256 Q_TRACE_SCOPE(QSSG_textureLoadPath, path);
257 theLoadedTexture.reset(QSSGLoadedTexture::load(path, image->m_format, flipY));
258 if (theLoadedTexture) {
259 foundIt = imageMap.insert(imageKey, ImageData());
260 CreateRhiTextureFlags rhiTexFlags = ScanForTransparency;
261 if (image->type == QSSGRenderGraphObject::Type::ImageCube)
262 rhiTexFlags |= CubeMap;
263 if (!setRhiTexture(foundIt.value().renderImageTexture, theLoadedTexture.data(), inMipMode, rhiTexFlags, QFileInfo(path).fileName())) {
264 foundIt.value() = ImageData();
265 }
else if (QSSGBufferManagerStat::enabled(QSSGBufferManagerStat::Level::Debug)) {
266 qDebug() <<
"+ uploadTexture: " << image->m_imagePath.path() << currentLayer;
268 result = foundIt.value().renderImageTexture;
269 increaseMemoryStat(result.m_texture);
274 foundIt = imageMap.insert(imageKey, ImageData());
275 qCWarning(WARNING,
"Failed to load image: %s", qPrintable(path));
277 Q_QUICK3D_PROFILE_END_WITH_STRING(QQuick3DProfiler::Quick3DTextureLoad, stats.imageDataSize, path.toUtf8());
279 foundIt.value().usageCounts[currentLayer]++;
280 }
else if (image->m_extensionsSource) {
281 auto it = renderExtensionTexture.find(image->m_extensionsSource);
282 if (it != renderExtensionTexture.end()) {
283 it->usageCounts[currentLayer]++;
284 result = it->renderImageTexture;
290QSSGRenderImageTexture QSSGBufferManager::loadTextureData(QSSGRenderTextureData *data, MipMode inMipMode)
292 QSSG_ASSERT(data !=
nullptr,
return {});
294 const CustomImageCacheKey imageKey = { data, data->size(), inMipMode };
295 auto theImageData = customTextureMap.find(imageKey);
296 if (theImageData == customTextureMap.end()) {
297 theImageData = customTextureMap.insert(imageKey, ImageData{{}, {}, data->version()});
298 }
else if (data->version() == theImageData->version) {
300 theImageData.value().usageCounts[currentLayer]++;
301 return theImageData.value().renderImageTexture;
305 theImageData->version = data->version();
309 QScopedPointer<QSSGLoadedTexture> theLoadedTexture;
310 if (!data->textureData().isNull()) {
311 theLoadedTexture.reset(QSSGLoadedTexture::loadTextureData(data));
312 theLoadedTexture->ownsData =
false;
313 CreateRhiTextureFlags rhiTexFlags = {};
314 if (theLoadedTexture->depth > 0)
315 rhiTexFlags |= Texture3D;
317 bool wasTextureCreated =
false;
319 if (setRhiTexture(theImageData.value().renderImageTexture, theLoadedTexture.data(), inMipMode, rhiTexFlags, data->debugObjectName, &wasTextureCreated)) {
320 if (wasTextureCreated) {
321 if (QSSGBufferManagerStat::enabled(QSSGBufferManagerStat::Level::Debug))
322 qDebug() <<
"+ uploadTexture: " << data << theImageData.value().renderImageTexture.m_texture << currentLayer;
323 increaseMemoryStat(theImageData.value().renderImageTexture.m_texture);
326 theImageData.value() = ImageData();
330 theImageData.value().usageCounts[currentLayer]++;
331 return theImageData.value().renderImageTexture;
334QSSGRenderImageTexture QSSGBufferManager::loadLightmap(
const QSSGRenderModel &model)
336 Q_ASSERT(currentLayer);
338 if (model.lightmapKey.isEmpty() || currentlyLightmapBaking || !validateLightmap())
341 Q_ASSERT(!lightmapSource.isEmpty());
342 static const QSSGRenderTextureFormat format = QSSGRenderTextureFormat::RGBA16F;
343 QSSGRenderImageTexture result;
344 const ImageCacheKey imageKey = { QSSGRenderPath(lightmapSource), MipModeDisable,
int(QSSGRenderGraphObject::Type::Image2D), model.lightmapKey };
345 auto foundIt = imageMap.find(imageKey);
346 if (foundIt != imageMap.end()) {
347 result = foundIt.value().renderImageTexture;
349 Q_QUICK3D_PROFILE_START(QQuick3DProfiler::Quick3DTextureLoad);
350 Q_TRACE_SCOPE(QSSG_textureLoadPath, lightmapSource);
351 QScopedPointer<QSSGLoadedTexture> theLoadedTexture;
352 theLoadedTexture.reset(QSSGLoadedTexture::loadLightmapImage(lightmapSource, format, model.lightmapKey));
353 if (!theLoadedTexture) {
354 qCWarning(WARNING,
"Failed to load lightmap image for %s", qPrintable(model.lightmapKey));
356 foundIt = imageMap.insert(imageKey, ImageData());
357 if (theLoadedTexture) {
358 const QString debugOjbectName = lightmapSource + QStringLiteral(
" [%1]").arg(model.lightmapKey);
359 if (!setRhiTexture(foundIt.value().renderImageTexture, theLoadedTexture.data(), MipModeDisable, {}, debugOjbectName))
360 foundIt.value() = ImageData();
361 else if (QSSGBufferManagerStat::enabled(QSSGBufferManagerStat::Level::Debug))
362 qDebug() <<
"+ uploadTexture: " << debugOjbectName << currentLayer;
363 result = foundIt.value().renderImageTexture;
365 increaseMemoryStat(result.m_texture);
366 Q_QUICK3D_PROFILE_END_WITH_STRING(QQuick3DProfiler::Quick3DTextureLoad, stats.imageDataSize, lightmapSource.toUtf8());
368 foundIt.value().usageCounts[currentLayer]++;
372QSSGRenderImageTexture QSSGBufferManager::loadSkinmap(QSSGRenderTextureData *skin)
374 return loadTextureData(skin, MipModeDisable);
377QSSGRenderMesh *QSSGBufferManager::getMeshForPicking(
const QSSGRenderModel &model)
const
379 if (!model.meshPath.isNull()) {
380 const auto foundIt = meshMap.constFind(model.meshPath);
381 if (foundIt != meshMap.constEnd())
382 return foundIt->mesh;
385 if (model.geometry) {
386 const auto foundIt = customMeshMap.constFind(model.geometry);
387 if (foundIt != customMeshMap.constEnd())
388 return foundIt->mesh;
394QRhiTexture::Format QSSGBufferManager::toRhiFormat(
const QSSGRenderTextureFormat format)
396 switch (format.format) {
398 case QSSGRenderTextureFormat::RGBA8:
399 return QRhiTexture::RGBA8;
400 case QSSGRenderTextureFormat::R8:
401 return QRhiTexture::R8;
402 case QSSGRenderTextureFormat::Luminance16:
403 case QSSGRenderTextureFormat::R16:
404 return QRhiTexture::R16;
405 case QSSGRenderTextureFormat::LuminanceAlpha8:
406 case QSSGRenderTextureFormat::Luminance8:
407 case QSSGRenderTextureFormat::Alpha8:
408 return QRhiTexture::RED_OR_ALPHA8;
409 case QSSGRenderTextureFormat::RGBA16F:
410 return QRhiTexture::RGBA16F;
411 case QSSGRenderTextureFormat::RGBA32F:
412 return QRhiTexture::RGBA32F;
413 case QSSGRenderTextureFormat::R16F:
414 return QRhiTexture::R16F;
415 case QSSGRenderTextureFormat::R32F:
416 return QRhiTexture::R32F;
417 case QSSGRenderTextureFormat::RGBE8:
418 return QRhiTexture::RGBA8;
419 case QSSGRenderTextureFormat::R32UI:
420 return QRhiTexture::R32UI;
421 case QSSGRenderTextureFormat::RGBA32UI:
422 return QRhiTexture::RGBA32UI;
423 case QSSGRenderTextureFormat::RGB_DXT1:
424 return QRhiTexture::BC1;
425 case QSSGRenderTextureFormat::RGBA_DXT3:
426 return QRhiTexture::BC2;
427 case QSSGRenderTextureFormat::RGBA_DXT5:
428 return QRhiTexture::BC3;
429 case QSSGRenderTextureFormat::RGBA8_ETC2_EAC:
430 return QRhiTexture::ETC2_RGBA8;
431 case QSSGRenderTextureFormat::RGBA_ASTC_4x4:
432 case QSSGRenderTextureFormat::SRGB8_Alpha8_ASTC_4x4:
433 return QRhiTexture::ASTC_4x4;
434 case QSSGRenderTextureFormat::RGBA_ASTC_5x4:
435 case QSSGRenderTextureFormat::SRGB8_Alpha8_ASTC_5x4:
436 return QRhiTexture::ASTC_5x4;
437 case QSSGRenderTextureFormat::RGBA_ASTC_5x5:
438 case QSSGRenderTextureFormat::SRGB8_Alpha8_ASTC_5x5:
439 return QRhiTexture::ASTC_5x5;
440 case QSSGRenderTextureFormat::RGBA_ASTC_6x5:
441 case QSSGRenderTextureFormat::SRGB8_Alpha8_ASTC_6x5:
442 return QRhiTexture::ASTC_6x5;
443 case QSSGRenderTextureFormat::RGBA_ASTC_6x6:
444 case QSSGRenderTextureFormat::SRGB8_Alpha8_ASTC_6x6:
445 return QRhiTexture::ASTC_6x6;
446 case QSSGRenderTextureFormat::RGBA_ASTC_8x5:
447 case QSSGRenderTextureFormat::SRGB8_Alpha8_ASTC_8x5:
448 return QRhiTexture::ASTC_8x5;
449 case QSSGRenderTextureFormat::RGBA_ASTC_8x6:
450 case QSSGRenderTextureFormat::SRGB8_Alpha8_ASTC_8x6:
451 return QRhiTexture::ASTC_8x6;
452 case QSSGRenderTextureFormat::RGBA_ASTC_8x8:
453 case QSSGRenderTextureFormat::SRGB8_Alpha8_ASTC_8x8:
454 return QRhiTexture::ASTC_8x8;
455 case QSSGRenderTextureFormat::RGBA_ASTC_10x5:
456 case QSSGRenderTextureFormat::SRGB8_Alpha8_ASTC_10x5:
457 return QRhiTexture::ASTC_10x5;
458 case QSSGRenderTextureFormat::RGBA_ASTC_10x6:
459 case QSSGRenderTextureFormat::SRGB8_Alpha8_ASTC_10x6:
460 return QRhiTexture::ASTC_10x6;
461 case QSSGRenderTextureFormat::RGBA_ASTC_10x8:
462 case QSSGRenderTextureFormat::SRGB8_Alpha8_ASTC_10x8:
463 return QRhiTexture::ASTC_10x8;
464 case QSSGRenderTextureFormat::RGBA_ASTC_10x10:
465 case QSSGRenderTextureFormat::SRGB8_Alpha8_ASTC_10x10:
466 return QRhiTexture::ASTC_10x10;
467 case QSSGRenderTextureFormat::RGBA_ASTC_12x10:
468 case QSSGRenderTextureFormat::SRGB8_Alpha8_ASTC_12x10:
469 return QRhiTexture::ASTC_12x10;
470 case QSSGRenderTextureFormat::RGBA_ASTC_12x12:
471 case QSSGRenderTextureFormat::SRGB8_Alpha8_ASTC_12x12:
472 return QRhiTexture::ASTC_12x12;
475 case QSSGRenderTextureFormat::SRGB8A8:
476 return QRhiTexture::RGBA8;
479 qWarning() <<
"Unsupported texture format" << format.format;
480 return QRhiTexture::UnknownFormat;
572bool QSSGBufferManager::createEnvironmentMap(
const QSSGLoadedTexture *inImage, QSSGRenderImageTexture *outTexture,
const QString &debugObjectName)
598 const auto &context = m_contextInterface->rhiContext();
599 auto *rhi = context->rhi();
601 int suggestedSize = inImage->height * 0.5f;
602 suggestedSize = qMax(512, suggestedSize);
603 const QSize environmentMapSize(suggestedSize, suggestedSize);
604 const bool isRGBE = inImage->format.format == QSSGRenderTextureFormat::Format::RGBE8;
605 const QRhiTexture::Format sourceTextureFormat = toRhiFormat(inImage->format.format);
607 if (!rhi->isTextureFormatSupported(sourceTextureFormat))
610 QRhiTexture::Format cubeTextureFormat = inImage->format.isCompressedTextureFormat()
611 ? QRhiTexture::RGBA16F
612 : sourceTextureFormat;
615 if (cubeTextureFormat == QRhiTexture::RGBA32F)
616 cubeTextureFormat = QRhiTexture::RGBA16F;
619 const int colorSpace = inImage->isSRGB ? 1 : 0;
622 QRhiTexture *envCubeMap = rhi->newTexture(cubeTextureFormat, environmentMapSize, 1,
623 QRhiTexture::RenderTarget | QRhiTexture::CubeMap | QRhiTexture::MipMapped | QRhiTexture::UsedWithGenerateMips);
624 if (!envCubeMap->create()) {
625 qWarning(
"Failed to create Environment Cube Map");
628 envCubeMap->deleteLater();
631 QRhiRenderBuffer *envMapRenderBuffer = rhi->newRenderBuffer(QRhiRenderBuffer::Color, environmentMapSize);
632 if (!envMapRenderBuffer->create()) {
633 qWarning(
"Failed to create Environment Map Render Buffer");
636 envMapRenderBuffer->deleteLater();
638 const QByteArray rtName = debugObjectName.toLatin1();
641 QVarLengthArray<QRhiTextureRenderTarget *, 6> renderTargets;
642 QRhiRenderPassDescriptor *renderPassDesc =
nullptr;
643 for (
const auto face : QSSGRenderTextureCubeFaces) {
644 QRhiColorAttachment att(envCubeMap);
645 att.setLayer(quint8(face));
646 QRhiTextureRenderTargetDescription rtDesc;
647 rtDesc.setColorAttachments({att});
648 auto renderTarget = rhi->newTextureRenderTarget(rtDesc);
649 renderTarget->setName(rtName + QByteArrayLiteral(
" env cube face: ") + QSSGBaseTypeHelpers::displayName(face));
650 renderTarget->setDescription(rtDesc);
652 renderPassDesc = renderTarget->newCompatibleRenderPassDescriptor();
653 renderTarget->setRenderPassDescriptor(renderPassDesc);
654 if (!renderTarget->create()) {
655 qWarning(
"Failed to build env map render target");
658 renderTarget->deleteLater();
659 renderTargets << renderTarget;
661 renderPassDesc->deleteLater();
664 QSize size(inImage->width, inImage->height);
665 auto *sourceTexture = rhi->newTexture(sourceTextureFormat, size, 1);
666 if (!sourceTexture->create()) {
667 qWarning(
"failed to create source env map texture");
670 sourceTexture->deleteLater();
673 const auto desc = inImage->textureFileData.isValid()
674 ? QRhiTextureUploadDescription(
675 { 0, 0, QRhiTextureSubresourceUploadDescription(inImage->textureFileData.getDataView().toByteArray()) })
676 : QRhiTextureUploadDescription({ 0, 0, { inImage->data, inImage->dataSizeInBytes } });
678 auto *rub = rhi->nextResourceUpdateBatch();
679 rub->uploadTexture(sourceTexture, desc);
681 const QSSGRhiSamplerDescription samplerDesc {
685 QRhiSampler::ClampToEdge,
686 QRhiSampler::ClampToEdge,
689 QRhiSampler *sampler = context->sampler(samplerDesc);
692 const auto &shaderCache = m_contextInterface->shaderCache();
693 const auto &envMapShaderStages = shaderCache->getBuiltInRhiShaders().getRhiEnvironmentmapShader();
696 QRhiBuffer *vertexBuffer = rhi->newBuffer(QRhiBuffer::Immutable, QRhiBuffer::VertexBuffer,
sizeof(cube));
697 vertexBuffer->create();
698 vertexBuffer->deleteLater();
699 rub->uploadStaticBuffer(vertexBuffer, cube);
702 int ubufElementSize = rhi->ubufAligned(128);
703 QRhiBuffer *uBuf = rhi->newBuffer(QRhiBuffer::Dynamic, QRhiBuffer::UniformBuffer, ubufElementSize * 6);
707 int ubufEnvMapElementSize = rhi->ubufAligned(4);
708 QRhiBuffer *uBufEnvMap = rhi->newBuffer(QRhiBuffer::Dynamic, QRhiBuffer::UniformBuffer, ubufEnvMapElementSize * 6);
709 uBufEnvMap->create();
710 uBufEnvMap->deleteLater();
713 QRhiShaderResourceBindings *envMapSrb = rhi->newShaderResourceBindings();
714 envMapSrb->setBindings({
715 QRhiShaderResourceBinding::uniformBufferWithDynamicOffset(0, QRhiShaderResourceBinding::VertexStage, uBuf, 128),
716 QRhiShaderResourceBinding::uniformBufferWithDynamicOffset(2, QRhiShaderResourceBinding::FragmentStage, uBufEnvMap, ubufEnvMapElementSize),
717 QRhiShaderResourceBinding::sampledTexture(1, QRhiShaderResourceBinding::FragmentStage, sourceTexture, sampler)
720 envMapSrb->deleteLater();
723 QRhiGraphicsPipeline *envMapPipeline = rhi->newGraphicsPipeline();
724 envMapPipeline->setCullMode(QRhiGraphicsPipeline::Front);
725 envMapPipeline->setFrontFace(QRhiGraphicsPipeline::CCW);
726 envMapPipeline->setShaderStages({
727 *envMapShaderStages->vertexStage(),
728 *envMapShaderStages->fragmentStage()
731 QRhiVertexInputLayout inputLayout;
732 inputLayout.setBindings({
733 { 3 *
sizeof(
float) }
735 inputLayout.setAttributes({
736 { 0, 0, QRhiVertexInputAttribute::Float3, 0 }
739 envMapPipeline->setVertexInputLayout(inputLayout);
740 envMapPipeline->setShaderResourceBindings(envMapSrb);
741 envMapPipeline->setRenderPassDescriptor(renderPassDesc);
742 if (!envMapPipeline->create()) {
743 qWarning(
"failed to create source env map pipeline state");
746 envMapPipeline->deleteLater();
749 auto *cb = context->commandBuffer();
750 cb->debugMarkBegin(
"Environment Cubemap Generation");
751 Q_QUICK3D_PROFILE_START(QQuick3DProfiler::Quick3DRenderPass);
752 Q_TRACE(QSSG_renderPass_entry, QStringLiteral(
"Environment Cubemap Generation"));
753 const QRhiCommandBuffer::VertexInput vbufBinding(vertexBuffer, 0);
756 QMatrix4x4 mvp = rhi->clipSpaceCorrMatrix();
757 mvp.perspective(90.0f, 1.0f, 0.1f, 10.0f);
759 auto lookAt = [](
const QVector3D &eye,
const QVector3D ¢er,
const QVector3D &up) {
760 QMatrix4x4 viewMatrix;
761 viewMatrix.lookAt(eye, center, up);
764 QVarLengthArray<QMatrix4x4, 6> views;
765 views.append(lookAt(QVector3D(0.0f, 0.0f, 0.0f), QVector3D(1.0, 0.0, 0.0), QVector3D(0.0f, -1.0f, 0.0f)));
766 views.append(lookAt(QVector3D(0.0f, 0.0f, 0.0f), QVector3D(-1.0, 0.0, 0.0), QVector3D(0.0f, -1.0f, 0.0f)));
767 if (rhi->isYUpInFramebuffer()) {
768 views.append(lookAt(QVector3D(0.0f, 0.0f, 0.0f), QVector3D(0.0, 1.0, 0.0), QVector3D(0.0f, 0.0f, 1.0f)));
769 views.append(lookAt(QVector3D(0.0f, 0.0f, 0.0f), QVector3D(0.0, -1.0, 0.0), QVector3D(0.0f, 0.0f, -1.0f)));
771 views.append(lookAt(QVector3D(0.0f, 0.0f, 0.0f), QVector3D(0.0, -1.0, 0.0), QVector3D(0.0f, 0.0f, -1.0f)));
772 views.append(lookAt(QVector3D(0.0f, 0.0f, 0.0f), QVector3D(0.0, 1.0, 0.0), QVector3D(0.0f, 0.0f, 1.0f)));
774 views.append(lookAt(QVector3D(0.0f, 0.0f, 0.0f), QVector3D(0.0, 0.0, 1.0), QVector3D(0.0f, -1.0f, 0.0f)));
775 views.append(lookAt(QVector3D(0.0f, 0.0f, 0.0f), QVector3D(0.0, 0.0, -1.0), QVector3D(0.0f, -1.0f, 0.0f)));
776 for (
const auto face : QSSGRenderTextureCubeFaces) {
777 rub->updateDynamicBuffer(uBuf, quint8(face) * ubufElementSize, 64, mvp.constData());
778 rub->updateDynamicBuffer(uBuf, quint8(face) * ubufElementSize + 64, 64, views[quint8(face)].constData());
779 rub->updateDynamicBuffer(uBufEnvMap, quint8(face) * ubufEnvMapElementSize, 4, &colorSpace);
781 cb->resourceUpdate(rub);
783 for (
const auto face : QSSGRenderTextureCubeFaces) {
784 cb->beginPass(renderTargets[quint8(face)], QColor(0, 0, 0, 1), { 1.0f, 0 },
nullptr, context->commonPassFlags());
785 Q_QUICK3D_PROFILE_START(QQuick3DProfiler::Quick3DRenderPass);
786 QSSGRHICTX_STAT(context, beginRenderPass(renderTargets[quint8(face)]));
789 cb->setGraphicsPipeline(envMapPipeline);
790 cb->setVertexInput(0, 1, &vbufBinding);
791 cb->setViewport(QRhiViewport(0, 0, environmentMapSize.width(), environmentMapSize.height()));
792 QVector<QPair<
int, quint32>> dynamicOffset = {
793 { 0, quint32(ubufElementSize * quint8(face)) },
794 { 2, quint32(ubufEnvMapElementSize * quint8(face) )}
796 cb->setShaderResources(envMapSrb, 2, dynamicOffset.constData());
797 Q_QUICK3D_PROFILE_START(QQuick3DProfiler::Quick3DRenderCall);
799 QSSGRHICTX_STAT(context, draw(36, 1));
800 Q_QUICK3D_PROFILE_END_WITH_STRING(QQuick3DProfiler::Quick3DRenderCall, 36llu | (1llu << 32), QByteArrayLiteral(
"environment_map"));
803 QSSGRHICTX_STAT(context, endRenderPass());
804 Q_QUICK3D_PROFILE_END_WITH_STRING(QQuick3DProfiler::Quick3DRenderPass, 0, QSSG_RENDERPASS_NAME(
"environment_map", 0, face));
807 Q_QUICK3D_PROFILE_END_WITH_STRING(QQuick3DProfiler::Quick3DRenderPass, 0, QByteArrayLiteral(
"environment_cube_generation"));
808 Q_TRACE(QSSG_renderPass_exit);
812 rub = rhi->nextResourceUpdateBatch();
813 rub->generateMips(envCubeMap);
814 cb->resourceUpdate(rub);
818 cb->debugMarkBegin(
"Pre-filtered Environment Cubemap Generation");
819 Q_QUICK3D_PROFILE_START(QQuick3DProfiler::Quick3DRenderPass);
820 Q_TRACE(QSSG_renderPass_entry, QStringLiteral(
"Pre-filtered Environment Cubemap Generation"));
821 QRhiTexture *preFilteredEnvCubeMap = rhi->newTexture(cubeTextureFormat, environmentMapSize, 1, QRhiTexture::RenderTarget | QRhiTexture::CubeMap| QRhiTexture::MipMapped);
822 if (!preFilteredEnvCubeMap->create())
823 qWarning(
"Failed to create Pre-filtered Environment Cube Map");
824 preFilteredEnvCubeMap->setName(rtName);
825 int mipmapCount = rhi->mipLevelsForSize(environmentMapSize);
826 mipmapCount = qMin(mipmapCount, 6);
827 QMap<
int, QSize> mipLevelSizes;
828 QMap<
int, QVarLengthArray<QRhiTextureRenderTarget *, 6>> renderTargetsMap;
829 QRhiRenderPassDescriptor *renderPassDescriptorPhase2 =
nullptr;
832 for (
int mipLevel = 0; mipLevel < mipmapCount; ++mipLevel) {
833 const QSize levelSize = QSize(environmentMapSize.width() * std::pow(0.5, mipLevel),
834 environmentMapSize.height() * std::pow(0.5, mipLevel));
835 mipLevelSizes.insert(mipLevel, levelSize);
837 QVarLengthArray<QRhiTextureRenderTarget *, 6> renderTargets;
838 for (
const auto face : QSSGRenderTextureCubeFaces) {
839 QRhiColorAttachment att(preFilteredEnvCubeMap);
840 att.setLayer(quint8(face));
841 att.setLevel(mipLevel);
842 QRhiTextureRenderTargetDescription rtDesc;
843 rtDesc.setColorAttachments({att});
844 auto renderTarget = rhi->newTextureRenderTarget(rtDesc);
845 renderTarget->setName(rtName + QByteArrayLiteral(
" env prefilter mip/face: ")
846 + QByteArray::number(mipLevel) + QByteArrayLiteral(
"/") + QSSGBaseTypeHelpers::displayName(face));
847 renderTarget->setDescription(rtDesc);
848 if (!renderPassDescriptorPhase2)
849 renderPassDescriptorPhase2 = renderTarget->newCompatibleRenderPassDescriptor();
850 renderTarget->setRenderPassDescriptor(renderPassDescriptorPhase2);
851 if (!renderTarget->create())
852 qWarning(
"Failed to build prefilter env map render target");
853 renderTarget->deleteLater();
854 renderTargets << renderTarget;
856 renderTargetsMap.insert(mipLevel, renderTargets);
857 renderPassDescriptorPhase2->deleteLater();
861 const auto &prefilterShaderStages = shaderCache->getBuiltInRhiShaders().getRhienvironmentmapPreFilterShader(isRGBE);
864 const QSSGRhiSamplerDescription samplerMipMapDesc {
868 QRhiSampler::ClampToEdge,
869 QRhiSampler::ClampToEdge,
873 QRhiSampler *envMapCubeSampler =
nullptr;
876 envMapCubeSampler = context->sampler(samplerMipMapDesc);
878 envMapCubeSampler = sampler;
890 int ubufPrefilterElementSize = rhi->ubufAligned(20);
891 QRhiBuffer *uBufPrefilter = rhi->newBuffer(QRhiBuffer::Dynamic, QRhiBuffer::UniformBuffer, ubufPrefilterElementSize * mipmapCount);
892 uBufPrefilter->create();
893 uBufPrefilter->deleteLater();
896 QRhiShaderResourceBindings *preFilterSrb = rhi->newShaderResourceBindings();
897 preFilterSrb->setBindings({
898 QRhiShaderResourceBinding::uniformBufferWithDynamicOffset(0, QRhiShaderResourceBinding::VertexStage, uBuf, 128),
899 QRhiShaderResourceBinding::uniformBufferWithDynamicOffset(2, QRhiShaderResourceBinding::FragmentStage, uBufPrefilter, 20),
900 QRhiShaderResourceBinding::sampledTexture(1, QRhiShaderResourceBinding::FragmentStage, envCubeMap, envMapCubeSampler)
902 preFilterSrb->create();
903 preFilterSrb->deleteLater();
906 QRhiGraphicsPipeline *prefilterPipeline = rhi->newGraphicsPipeline();
907 prefilterPipeline->setCullMode(QRhiGraphicsPipeline::Front);
908 prefilterPipeline->setFrontFace(QRhiGraphicsPipeline::CCW);
909 prefilterPipeline->setDepthOp(QRhiGraphicsPipeline::LessOrEqual);
910 prefilterPipeline->setShaderStages({
911 *prefilterShaderStages->vertexStage(),
912 *prefilterShaderStages->fragmentStage()
915 prefilterPipeline->setVertexInputLayout(inputLayout);
916 prefilterPipeline->setShaderResourceBindings(preFilterSrb);
917 prefilterPipeline->setRenderPassDescriptor(renderPassDescriptorPhase2);
918 if (!prefilterPipeline->create()) {
919 qWarning(
"failed to create pre-filter env map pipeline state");
922 prefilterPipeline->deleteLater();
926 rub = rhi->nextResourceUpdateBatch();
927 const float resolution = environmentMapSize.width();
928 const float lodBias = 0.0f;
929 const int sampleCount = 1024;
930 for (
int mipLevel = 0; mipLevel < mipmapCount; ++mipLevel) {
931 Q_ASSERT(mipmapCount - 2);
932 const float roughness =
float(mipLevel) /
float(mipmapCount - 2);
933 const int distribution = mipLevel == (mipmapCount - 1) ? 0 : 1;
934 rub->updateDynamicBuffer(uBufPrefilter, mipLevel * ubufPrefilterElementSize, 4, &roughness);
935 rub->updateDynamicBuffer(uBufPrefilter, mipLevel * ubufPrefilterElementSize + 4, 4, &resolution);
936 rub->updateDynamicBuffer(uBufPrefilter, mipLevel * ubufPrefilterElementSize + 4 + 4, 4, &lodBias);
937 rub->updateDynamicBuffer(uBufPrefilter, mipLevel * ubufPrefilterElementSize + 4 + 4 + 4, 4, &sampleCount);
938 rub->updateDynamicBuffer(uBufPrefilter, mipLevel * ubufPrefilterElementSize + 4 + 4 + 4 + 4, 4, &distribution);
941 cb->resourceUpdate(rub);
944 for (
int mipLevel = 0; mipLevel < mipmapCount; ++mipLevel) {
945 for (
const auto face : QSSGRenderTextureCubeFaces) {
946 cb->beginPass(renderTargetsMap[mipLevel][quint8(face)], QColor(0, 0, 0, 1), { 1.0f, 0 },
nullptr, context->commonPassFlags());
947 QSSGRHICTX_STAT(context, beginRenderPass(renderTargetsMap[mipLevel][quint8(face)]));
948 Q_QUICK3D_PROFILE_START(QQuick3DProfiler::Quick3DRenderPass);
949 cb->setGraphicsPipeline(prefilterPipeline);
950 cb->setVertexInput(0, 1, &vbufBinding);
951 cb->setViewport(QRhiViewport(0, 0, mipLevelSizes[mipLevel].width(), mipLevelSizes[mipLevel].height()));
952 QVector<QPair<
int, quint32>> dynamicOffsets = {
953 { 0, quint32(ubufElementSize * quint8(face)) },
954 { 2, quint32(ubufPrefilterElementSize * mipLevel) }
956 Q_QUICK3D_PROFILE_START(QQuick3DProfiler::Quick3DRenderCall);
958 cb->setShaderResources(preFilterSrb, 2, dynamicOffsets.constData());
960 QSSGRHICTX_STAT(context, draw(36, 1));
961 Q_QUICK3D_PROFILE_END_WITH_STRING(QQuick3DProfiler::Quick3DRenderCall, 36llu | (1llu << 32), QByteArrayLiteral(
"environment_map"));
963 QSSGRHICTX_STAT(context, endRenderPass());
964 Q_QUICK3D_PROFILE_END_WITH_STRING(QQuick3DProfiler::Quick3DRenderPass, 0, QSSG_RENDERPASS_NAME(
"environment_map", mipLevel, face));
968 Q_QUICK3D_PROFILE_END_WITH_STRING(QQuick3DProfiler::Quick3DRenderPass, 0, QByteArrayLiteral(
"environment_cube_prefilter"));
969 Q_TRACE(QSSG_renderPass_exit);
971 outTexture->m_texture = preFilteredEnvCubeMap;
972 outTexture->m_mipmapCount = mipmapCount;
976bool QSSGBufferManager::setRhiTexture(QSSGRenderImageTexture &texture,
977 const QSSGLoadedTexture *inTexture,
979 CreateRhiTextureFlags inFlags,
980 const QString &debugObjectName,
981 bool *wasTextureCreated)
983 Q_ASSERT(inMipMode != MipModeFollowRenderImage);
984 QVarLengthArray<QRhiTextureUploadEntry, 16> textureUploads;
985 int textureSampleCount = 1;
986 QRhiTexture::Flags textureFlags;
987 const bool checkTransp = inFlags.testFlag(ScanForTransparency);
988 bool hasTransp =
false;
990 const auto &context = m_contextInterface->rhiContext();
991 QSSGRhiContextPrivate *rhiCtxD = QSSGRhiContextPrivate::get(context.get());
992 auto *rhi = context->rhi();
993 QRhiTexture::Format rhiFormat = toRhiFormat(inTexture->format.format);
994 const QTextureFileData &texFileData = inTexture->textureFileData;
995 QSize size = texFileData.isValid() ? texFileData.size() : QSize(inTexture->width, inTexture->height);
996 int mipmapCount = texFileData.isValid() ? texFileData.numLevels() : 1;
997 int depth = inFlags.testFlag(Texture3D) ? inTexture->depth : 0;
998 bool generateMipmaps =
false;
1000 if (size.isEmpty()) {
1001 qWarning() <<
"Could not use 0 sized texture";
1003 }
else if (!rhi->isTextureFormatSupported(rhiFormat)) {
1004 qWarning() <<
"Unsupported texture format" << rhiFormat;
1009 if (wasTextureCreated)
1010 *wasTextureCreated =
false;
1012 if (texture.m_texture ==
nullptr) {
1013 if (inTexture->format.format == QSSGRenderTextureFormat::Format::RGBE8)
1014 texture.m_flags.setRgbe8(
true);
1015 if (!inTexture->isSRGB)
1016 texture.m_flags.setLinear(
true);
1017 if (inMipMode == MipModeBsdf && (inTexture->data || texFileData.isValid())) {
1020 if (texFileData.isValid() && texFileData.keyValueMetadata().contains(
"QT_IBL_BAKER_VERSION")) {
1021 Q_ASSERT(texFileData.numFaces() == 6);
1022 Q_ASSERT(texFileData.numLevels() >= 5);
1024 QRhiTexture *environmentCubeMap = rhi->newTexture(rhiFormat, size, 1, QRhiTexture::CubeMap | QRhiTexture::MipMapped);
1025 environmentCubeMap->setName(debugObjectName.toLatin1());
1026 environmentCubeMap->create();
1027 texture.m_texture = environmentCubeMap;
1028 rhiCtxD->registerTexture(texture.m_texture);
1029 if (wasTextureCreated)
1030 *wasTextureCreated =
true;
1032 }
else if (createEnvironmentMap(inTexture, &texture, debugObjectName)) {
1033 rhiCtxD->registerTexture(texture.m_texture);
1034 if (wasTextureCreated)
1035 *wasTextureCreated =
true;
1038 qWarning() <<
"Failed to create environment map";
1042 if (inMipMode == MipModeEnable && mipmapCount == 1) {
1043 textureFlags |= QRhiTexture::Flag::UsedWithGenerateMips;
1044 generateMipmaps =
true;
1045 mipmapCount = rhi->mipLevelsForSize(size);
1048 if (mipmapCount > 1)
1049 textureFlags |= QRhiTexture::Flag::MipMapped;
1051 if (inFlags.testFlag(CubeMap))
1052 textureFlags |= QRhiTexture::CubeMap;
1054 if (inFlags.testFlag(Texture3D) && depth > 0)
1055 texture.m_texture = rhi->newTexture(rhiFormat, size.width(), size.height(), depth, textureSampleCount, textureFlags);
1057 texture.m_texture = rhi->newTexture(rhiFormat, size, textureSampleCount, textureFlags);
1059 texture.m_texture->setName(debugObjectName.toLatin1());
1060 texture.m_texture->create();
1061 rhiCtxD->registerTexture(texture.m_texture);
1062 if (wasTextureCreated)
1063 *wasTextureCreated =
true;
1068 if (inMipMode == MipModeBsdf && (inTexture->data || texFileData.isValid())) {
1069 if (texFileData.isValid() && texFileData.keyValueMetadata().contains(
"QT_IBL_BAKER_VERSION")) {
1070 const int faceCount = texFileData.numFaces();
1071 for (
int layer = 0; layer < faceCount; ++layer) {
1072 for (
int level = 0; level < mipmapCount; ++level) {
1073 QRhiTextureSubresourceUploadDescription subDesc;
1074 subDesc.setSourceSize(sizeForMipLevel(level, size));
1075 subDesc.setData(texFileData.getDataView(level, layer).toByteArray());
1076 textureUploads << QRhiTextureUploadEntry { layer, level, subDesc };
1080 QRhiTextureUploadDescription uploadDescription;
1081 uploadDescription.setEntries(textureUploads.cbegin(), textureUploads.cend());
1082 auto *rub = rhi->nextResourceUpdateBatch();
1083 rub->uploadTexture(texture.m_texture, uploadDescription);
1084 context->commandBuffer()->resourceUpdate(rub);
1085 texture.m_mipmapCount = mipmapCount;
1088 }
else if (texFileData.isValid()) {
1093 if (texFileData.numFaces() == 6 && inFlags.testFlag(CubeMap))
1096 for (
int level = 0; level < texFileData.numLevels(); ++level) {
1097 QRhiTextureSubresourceUploadDescription subDesc;
1098 subDesc.setSourceSize(sizeForMipLevel(level, size));
1099 for (
int face = 0; face < numFaces; ++face) {
1100 subDesc.setData(texFileData.getDataView(level, face).toByteArray());
1101 textureUploads << QRhiTextureUploadEntry{ face, level, subDesc };
1105 auto glFormat = texFileData.glInternalFormat() ? texFileData.glInternalFormat() : texFileData.glFormat();
1106 hasTransp = !QSGCompressedTexture::formatIsOpaque(glFormat);
1108 }
else if (inFlags.testFlag(Texture3D)) {
1110 quint32 formatSize = (quint32)inTexture->format.getSizeofFormat();
1111 quint32 size2D = inTexture->width * inTexture->height * formatSize;
1112 if (inTexture->dataSizeInBytes >= (quint32)(size2D * inTexture->depth)) {
1113 for (
int slice = 0; slice < inTexture->depth; ++slice) {
1114 QRhiTextureSubresourceUploadDescription sliceUpload((
char *)inTexture->data + slice * size2D, size2D);
1115 textureUploads << QRhiTextureUploadEntry(slice, 0, sliceUpload);
1118 qWarning() <<
"Texture size set larger than the data";
1121 QRhiTextureSubresourceUploadDescription subDesc;
1122 if (!inTexture->image.isNull()) {
1123 subDesc.setImage(inTexture->image);
1125 hasTransp = QImageData::get(inTexture->image)->checkForAlphaPixels();
1126 }
else if (inTexture->data) {
1127 QByteArray buf(
static_cast<
const char *>(inTexture->data), qMax(0,
int(inTexture->dataSizeInBytes)));
1128 subDesc.setData(buf);
1130 hasTransp = inTexture->scanForTransparency();
1132 subDesc.setSourceSize(size);
1133 if (!subDesc.data().isEmpty() || !subDesc.image().isNull())
1134 textureUploads << QRhiTextureUploadEntry{0, 0, subDesc};
1137 static const auto textureSizeWarning = [](QSize requestedSize, qsizetype maxSize) {
1138 return QStringLiteral(
"Requested texture width and height (%1x%2) exceeds the maximum allowed size (%3)!")
1139 .arg(requestedSize.width()).arg(requestedSize.height()).arg(maxSize);
1141 static auto maxTextureSize = rhi->resourceLimit(QRhi::ResourceLimit::TextureSizeMax);
1142 const auto validTexSize = size.width() <= maxTextureSize && size.height() <= maxTextureSize;
1143 QSSG_ASSERT_X(validTexSize, qPrintable(textureSizeWarning(size, maxTextureSize)),
return false);
1145 QSSG_ASSERT(texture.m_texture !=
nullptr,
return false);
1148 texture.m_flags.setHasTransparency(hasTransp);
1150 QRhiTextureUploadDescription uploadDescription;
1151 uploadDescription.setEntries(textureUploads.cbegin(), textureUploads.cend());
1152 auto *rub = rhi->nextResourceUpdateBatch();
1153 rub->uploadTexture(texture.m_texture, uploadDescription);
1154 if (generateMipmaps)
1155 rub->generateMips(texture.m_texture);
1156 context->commandBuffer()->resourceUpdate(rub);
1158 texture.m_mipmapCount = mipmapCount;
1162QString QSSGBufferManager::primitivePath(
const QString &primitive)
1164 QByteArray theName = primitive.toUtf8();
1165 for (size_t idx = 0; idx < nPrimitives; ++idx) {
1166 if (primitives[idx].primitive == theName) {
1167 QString pathBuilder = QString::fromLatin1(primitivesDirectory);
1168 pathBuilder += QLatin1String(primitives[idx].file);
1175QMutex *QSSGBufferManager::meshUpdateMutex()
1177 return &meshBufferMutex;
1180QSSGMesh::Mesh QSSGBufferManager::loadPrimitive(
const QString &inRelativePath)
1182 QString path = primitivePath(inRelativePath);
1183 const quint32 id = 1;
1184 QSharedPointer<QIODevice> device(QSSGInputUtil::getStreamForFile(path));
1186 QSSGMesh::Mesh mesh = QSSGMesh::Mesh::loadMesh(device.data(), id);
1191 qCCritical(INTERNAL_ERROR,
"Unable to find mesh primitive %s", qPrintable(path));
1192 return QSSGMesh::Mesh();
1195QSSGRenderMesh *QSSGBufferManager::loadMesh(
const QSSGRenderModel &model)
1199 QSSGMeshProcessingOptions options;
1200 QSSGRenderMesh *theMesh =
nullptr;
1202 if (model.hasLightmap() && !currentlyLightmapBaking && validateLightmap()) {
1203 options.lightmapPath = lightmapSource;
1204 options.lightmapKey = model.lightmapKey;
1207 if (model.meshPath.isNull() && model.geometry) {
1208 theMesh = loadRenderMesh(model.geometry, options);
1210 theMesh = loadRenderMesh(model.meshPath, options);
1216QSSGMesh::Mesh QSSGBufferManager::loadLightmapMesh(
const QSSGRenderModel &model)
1219 if (model.hasLightmap() && !currentlyLightmapBaking && validateLightmap() ) {
1220 auto [meshLightmap, _] = loadFromLightmapFile(lightmapSource, model.lightmapKey);
1221 if (meshLightmap.isValid())
1222 return meshLightmap;
1228QSSGBounds3 QSSGBufferManager::getModelBounds(
const QSSGRenderModel *model)
1232 if (model->hasLightmap() && !currentlyLightmapBaking && validateLightmap()) {
1233 auto [meshLightmap, _] = loadFromLightmapFile(lightmapSource, model->lightmapKey);
1234 if (meshLightmap.isValid()) {
1235 const QVector<QSSGMesh::Mesh::Subset> subsets = meshLightmap.subsets();
1236 for (
const QSSGMesh::Mesh::Subset &subset : std::as_const(subsets)) {
1237 retval.include(QSSGBounds3(subset.bounds.min, subset.bounds.max));
1241 qWarning() <<
"Could not load lightmap" << lightmapSource << model->lightmapKey;
1246 if (model->geometry) {
1247 retval = QSSGBounds3(model->geometry->boundsMin(), model->geometry->boundsMax());
1248 }
else if (!model->meshPath.isNull()){
1250 QSSGRenderMesh *theMesh =
nullptr;
1251 auto meshItr = meshMap.constFind(model->meshPath);
1252 if (meshItr != meshMap.cend())
1253 theMesh = meshItr.value().mesh;
1257 const auto &subSets = theMesh->subsets;
1258 for (
const auto &subSet : subSets)
1259 retval.include(subSet.bounds);
1263 QSSGMesh::Mesh mesh = loadMeshData(model->meshPath);
1264 if (mesh.isValid()) {
1265 auto const &subsets = mesh.subsets();
1266 for (
const auto &subset : subsets)
1267 retval.include(QSSGBounds3(subset.bounds.min, subset.bounds.max));
1274QSSGRenderMesh *QSSGBufferManager::createRenderMesh(
const QSSGMesh::Mesh &mesh,
const QString &debugObjectName)
1276 QSSGRenderMesh *newMesh =
new QSSGRenderMesh(QSSGRenderDrawMode(mesh.drawMode()),
1277 QSSGRenderWinding(mesh.winding()));
1278 const QSSGMesh::Mesh::VertexBuffer vertexBuffer = mesh.vertexBuffer();
1279 const QSSGMesh::Mesh::IndexBuffer indexBuffer = mesh.indexBuffer();
1280 const QSSGMesh::Mesh::TargetBuffer targetBuffer = mesh.targetBuffer();
1282 QSSGRenderComponentType indexBufComponentType = QSSGRenderComponentType::UnsignedInt16;
1283 QRhiCommandBuffer::IndexFormat rhiIndexFormat = QRhiCommandBuffer::IndexUInt16;
1284 if (!indexBuffer.data.isEmpty()) {
1285 indexBufComponentType = QSSGRenderComponentType(indexBuffer.componentType);
1286 const quint32 sizeofType = quint32(QSSGBaseTypeHelpers::getSizeOfType(indexBufComponentType));
1287 if (sizeofType == 2 || sizeofType == 4) {
1289 if (indexBufComponentType == QSSGRenderComponentType::Int16)
1290 indexBufComponentType = QSSGRenderComponentType::UnsignedInt16;
1291 if (indexBufComponentType == QSSGRenderComponentType::Int32)
1292 indexBufComponentType = QSSGRenderComponentType::UnsignedInt32;
1293 rhiIndexFormat = indexBufComponentType == QSSGRenderComponentType::UnsignedInt32
1294 ? QRhiCommandBuffer::IndexUInt32 : QRhiCommandBuffer::IndexUInt16;
1301 QSSGRhiBufferPtr vertexBuffer;
1302 QSSGRhiBufferPtr indexBuffer;
1303 QSSGRhiInputAssemblerState ia;
1304 QRhiTexture *targetsTexture =
nullptr;
1307 QRhiResourceUpdateBatch *rub = meshBufferUpdateBatch();
1308 const auto &context = m_contextInterface->rhiContext();
1309 QSSGRhiContextPrivate *rhiCtxD = QSSGRhiContextPrivate::get(context.get());
1310 rhi.vertexBuffer = std::make_shared<QSSGRhiBuffer>(*context.get(),
1312 QRhiBuffer::VertexBuffer,
1313 vertexBuffer.stride,
1314 vertexBuffer.data.size());
1315 rhi.vertexBuffer->buffer()->setName(debugObjectName.toLatin1());
1316 rub->uploadStaticBuffer(rhi.vertexBuffer->buffer(), vertexBuffer.data);
1318 if (!indexBuffer.data.isEmpty()) {
1319 rhi.indexBuffer = std::make_shared<QSSGRhiBuffer>(*context.get(),
1321 QRhiBuffer::IndexBuffer,
1323 indexBuffer.data.size(),
1325 rub->uploadStaticBuffer(rhi.indexBuffer->buffer(), indexBuffer.data);
1328 if (!targetBuffer.data.isEmpty()) {
1329 const int arraySize = targetBuffer.entries.size() * targetBuffer.numTargets;
1330 const int numTexels = (targetBuffer.data.size() / arraySize) >> 4;
1331 const int texWidth = qCeil(qSqrt(numTexels));
1332 const QSize texSize(texWidth, texWidth);
1333 if (!rhi.targetsTexture) {
1334 rhi.targetsTexture = context->rhi()->newTextureArray(QRhiTexture::RGBA32F, arraySize, texSize);
1335 rhi.targetsTexture->create();
1336 rhiCtxD->registerTexture(rhi.targetsTexture);
1337 }
else if (rhi.targetsTexture->pixelSize() != texSize
1338 || rhi.targetsTexture->arraySize() != arraySize) {
1339 rhi.targetsTexture->setPixelSize(texSize);
1340 rhi.targetsTexture->setArraySize(arraySize);
1341 rhi.targetsTexture->create();
1344 const quint32 layerSize = texWidth * texWidth * 4 * 4;
1345 for (
int arrayId = 0; arrayId < arraySize; ++arrayId) {
1346 QRhiTextureSubresourceUploadDescription targetDesc(targetBuffer.data + arrayId * layerSize, layerSize);
1347 QRhiTextureUploadDescription desc(QRhiTextureUploadEntry(arrayId, 0, targetDesc));
1348 rub->uploadTexture(rhi.targetsTexture, desc);
1351 for (quint32 entryIdx = 0, entryEnd = targetBuffer.entries.size(); entryIdx < entryEnd; ++entryIdx) {
1352 const char *nameStr = targetBuffer.entries[entryIdx].name.constData();
1353 if (!strcmp(nameStr, QSSGMesh::MeshInternal::getPositionAttrName())) {
1354 rhi.ia.targetOffsets[QSSGRhiInputAssemblerState::PositionSemantic] = entryIdx * targetBuffer.numTargets;
1355 }
else if (!strcmp(nameStr, QSSGMesh::MeshInternal::getNormalAttrName())) {
1356 rhi.ia.targetOffsets[QSSGRhiInputAssemblerState::NormalSemantic] = entryIdx * targetBuffer.numTargets;
1357 }
else if (!strcmp(nameStr, QSSGMesh::MeshInternal::getUV0AttrName())) {
1358 rhi.ia.targetOffsets[QSSGRhiInputAssemblerState::TexCoord0Semantic] = entryIdx * targetBuffer.numTargets;
1359 }
else if (!strcmp(nameStr, QSSGMesh::MeshInternal::getUV1AttrName())) {
1360 rhi.ia.targetOffsets[QSSGRhiInputAssemblerState::TexCoord1Semantic] = entryIdx * targetBuffer.numTargets;
1361 }
else if (!strcmp(nameStr, QSSGMesh::MeshInternal::getTexTanAttrName())) {
1362 rhi.ia.targetOffsets[QSSGRhiInputAssemblerState::TangentSemantic] = entryIdx * targetBuffer.numTargets;
1363 }
else if (!strcmp(nameStr, QSSGMesh::MeshInternal::getTexBinormalAttrName())) {
1364 rhi.ia.targetOffsets[QSSGRhiInputAssemblerState::BinormalSemantic] = entryIdx * targetBuffer.numTargets;
1365 }
else if (!strcmp(nameStr, QSSGMesh::MeshInternal::getColorAttrName())) {
1366 rhi.ia.targetOffsets[QSSGRhiInputAssemblerState::ColorSemantic] = entryIdx * targetBuffer.numTargets;
1369 rhi.ia.targetCount = targetBuffer.numTargets;
1370 }
else if (rhi.targetsTexture) {
1371 rhiCtxD->releaseTexture(rhi.targetsTexture);
1372 rhi.targetsTexture =
nullptr;
1373 rhi.ia.targetOffsets = { UINT8_MAX, UINT8_MAX, UINT8_MAX, UINT8_MAX,
1374 UINT8_MAX, UINT8_MAX, UINT8_MAX };
1375 rhi.ia.targetCount = 0;
1378 QVector<QSSGRenderVertexBufferEntry> entryBuffer;
1379 entryBuffer.resize(vertexBuffer.entries.size());
1380 for (quint32 entryIdx = 0, entryEnd = vertexBuffer.entries.size(); entryIdx < entryEnd; ++entryIdx)
1381 entryBuffer[entryIdx] = vertexBuffer.entries[entryIdx].toRenderVertexBufferEntry();
1383 QVarLengthArray<QRhiVertexInputAttribute, 4> inputAttrs;
1384 for (quint32 entryIdx = 0, entryEnd = entryBuffer.size(); entryIdx < entryEnd; ++entryIdx) {
1385 const QSSGRenderVertexBufferEntry &vbe(entryBuffer[entryIdx]);
1386 const int binding = 0;
1387 const int location = 0;
1388 const QRhiVertexInputAttribute::Format format = QSSGRhiHelpers::toVertexInputFormat(vbe.m_componentType, vbe.m_numComponents);
1389 const int offset =
int(vbe.m_firstItemOffset);
1392 const char *nameStr = vbe.m_name.constData();
1393 if (!strcmp(nameStr, QSSGMesh::MeshInternal::getPositionAttrName())) {
1394 rhi.ia.inputs << QSSGRhiInputAssemblerState::PositionSemantic;
1395 }
else if (!strcmp(nameStr, QSSGMesh::MeshInternal::getNormalAttrName())) {
1396 rhi.ia.inputs << QSSGRhiInputAssemblerState::NormalSemantic;
1397 }
else if (!strcmp(nameStr, QSSGMesh::MeshInternal::getUV0AttrName())) {
1398 rhi.ia.inputs << QSSGRhiInputAssemblerState::TexCoord0Semantic;
1399 }
else if (!strcmp(nameStr, QSSGMesh::MeshInternal::getUV1AttrName())) {
1400 rhi.ia.inputs << QSSGRhiInputAssemblerState::TexCoord1Semantic;
1401 }
else if (!strcmp(nameStr, QSSGMesh::MeshInternal::getLightmapUVAttrName())) {
1402 rhi.ia.inputs << QSSGRhiInputAssemblerState::TexCoordLightmapSemantic;
1403 }
else if (!strcmp(nameStr, QSSGMesh::MeshInternal::getTexTanAttrName())) {
1404 rhi.ia.inputs << QSSGRhiInputAssemblerState::TangentSemantic;
1405 }
else if (!strcmp(nameStr, QSSGMesh::MeshInternal::getTexBinormalAttrName())) {
1406 rhi.ia.inputs << QSSGRhiInputAssemblerState::BinormalSemantic;
1407 }
else if (!strcmp(nameStr, QSSGMesh::MeshInternal::getColorAttrName())) {
1408 rhi.ia.inputs << QSSGRhiInputAssemblerState::ColorSemantic;
1409 }
else if (!strcmp(nameStr, QSSGMesh::MeshInternal::getJointAttrName())) {
1410 rhi.ia.inputs << QSSGRhiInputAssemblerState::JointSemantic;
1411 }
else if (!strcmp(nameStr, QSSGMesh::MeshInternal::getWeightAttrName())) {
1412 rhi.ia.inputs << QSSGRhiInputAssemblerState::WeightSemantic;
1414 qWarning(
"Unknown vertex input %s in mesh", nameStr);
1418 QRhiVertexInputAttribute inputAttr(binding, location, format, offset);
1419 inputAttrs.append(inputAttr);
1422 rhi.ia.inputLayout.setAttributes(inputAttrs.cbegin(), inputAttrs.cend());
1423 rhi.ia.inputLayout.setBindings({ vertexBuffer.stride });
1424 rhi.ia.topology = QSSGRhiHelpers::toTopology(QSSGRenderDrawMode(mesh.drawMode()));
1426 if (rhi.ia.topology == QRhiGraphicsPipeline::TriangleFan && !context->rhi()->isFeatureSupported(QRhi::TriangleFanTopology))
1427 qWarning(
"Mesh topology is TriangleFan but this is not supported with the active graphics API. Rendering will be incorrect.");
1429 QVector<QSSGMesh::Mesh::Subset> meshSubsets = mesh.subsets();
1430 for (quint32 subsetIdx = 0, subsetEnd = meshSubsets.size(); subsetIdx < subsetEnd; ++subsetIdx) {
1431 QSSGRenderSubset subset;
1432 const QSSGMesh::Mesh::Subset &source(meshSubsets[subsetIdx]);
1433 subset.bounds = QSSGBounds3(source.bounds.min, source.bounds.max);
1434 subset.count = source.count;
1435 subset.offset = source.offset;
1436 for (
auto &lod : source.lods)
1437 subset.lods.append(QSSGRenderSubset::Lod({lod.count, lod.offset, lod.distance}));
1440 if (rhi.vertexBuffer) {
1441 subset.rhi.vertexBuffer = rhi.vertexBuffer;
1442 subset.rhi.ia = rhi.ia;
1444 if (rhi.indexBuffer)
1445 subset.rhi.indexBuffer = rhi.indexBuffer;
1446 if (rhi.targetsTexture)
1447 subset.rhi.targetsTexture = rhi.targetsTexture;
1449 newMesh->subsets.push_back(subset);
1452 if (!meshSubsets.isEmpty())
1453 newMesh->lightmapSizeHint = meshSubsets.first().lightmapSizeHint;
1458void QSSGBufferManager::releaseGeometry(QSSGRenderGeometry *geometry)
1460 QMutexLocker meshMutexLocker(&meshBufferMutex);
1461 const auto meshItr = customMeshMap.constFind(geometry);
1462 if (meshItr != customMeshMap.cend()) {
1463 if (QSSGBufferManagerStat::enabled(QSSGBufferManagerStat::Level::Debug))
1464 qDebug() <<
"- releaseGeometry: " << geometry << currentLayer;
1465 Q_QUICK3D_PROFILE_START(QQuick3DProfiler::Quick3DCustomMeshLoad);
1466 Q_TRACE_SCOPE(QSSG_customMeshUnload);
1467 decreaseMemoryStat(meshItr.value().mesh);
1468 QSSGRhiContextPrivate *rhiCtxD = QSSGRhiContextPrivate::get(m_contextInterface->rhiContext().get());
1469 rhiCtxD->releaseMesh(meshItr.value().mesh);
1470 customMeshMap.erase(meshItr);
1471 Q_QUICK3D_PROFILE_END_WITH_ID(QQuick3DProfiler::Quick3DCustomMeshLoad,
1472 stats.meshDataSize, geometry->profilingId);
1476void QSSGBufferManager::releaseTextureData(
const QSSGRenderTextureData *data)
1478 QVarLengthArray<CustomImageCacheKey, 4> keys;
1479 for (
auto it = customTextureMap.cbegin(), end = customTextureMap.cend(); it != end; ++it) {
1480 if (it.key().data == data)
1481 keys.append(it.key());
1483 for (
const CustomImageCacheKey &key : keys)
1484 releaseTextureData(key);
1487void QSSGBufferManager::releaseTextureData(
const CustomImageCacheKey &key)
1489 const auto textureDataItr = customTextureMap.constFind(key);
1490 if (textureDataItr != customTextureMap.cend()) {
1491 auto rhiTexture = textureDataItr.value().renderImageTexture.m_texture;
1493 if (QSSGBufferManagerStat::enabled(QSSGBufferManagerStat::Level::Debug))
1494 qDebug() <<
"- releaseTextureData: " << rhiTexture << currentLayer;
1495 Q_QUICK3D_PROFILE_START(QQuick3DProfiler::Quick3DTextureLoad);
1496 Q_TRACE_SCOPE(QSSG_textureUnload);
1497 decreaseMemoryStat(rhiTexture);
1498 QSSGRhiContextPrivate *rhiCtxD = QSSGRhiContextPrivate::get(m_contextInterface->rhiContext().get());
1499 rhiCtxD->releaseTexture(rhiTexture);
1500 Q_QUICK3D_PROFILE_END_WITH_ID(QQuick3DProfiler::Quick3DTextureLoad,
1501 stats.imageDataSize, 0);
1504 customTextureMap.erase(textureDataItr);
1508void QSSGBufferManager::releaseExtensionResult(
const QSSGRenderExtension &rext)
1510 renderExtensionTexture.remove(&rext);
1513void QSSGBufferManager::releaseMesh(
const QSSGRenderPath &inSourcePath)
1515 QMutexLocker meshMutexLocker(&meshBufferMutex);
1516 const auto meshItr = meshMap.constFind(inSourcePath);
1517 if (meshItr != meshMap.cend()) {
1518 if (QSSGBufferManagerStat::enabled(QSSGBufferManagerStat::Level::Debug))
1519 qDebug() <<
"- releaseMesh: " << inSourcePath.path() << currentLayer;
1520 Q_QUICK3D_PROFILE_START(QQuick3DProfiler::Quick3DMeshLoad);
1521 Q_TRACE_SCOPE(QSSG_meshUnload);
1522 decreaseMemoryStat(meshItr.value().mesh);
1523 QSSGRhiContextPrivate *rhiCtxD = QSSGRhiContextPrivate::get(m_contextInterface->rhiContext().get());
1524 rhiCtxD->releaseMesh(meshItr.value().mesh);
1525 meshMap.erase(meshItr);
1526 Q_QUICK3D_PROFILE_END_WITH_STRING(QQuick3DProfiler::Quick3DMeshLoad,
1527 stats.meshDataSize, inSourcePath.path().toUtf8());
1531void QSSGBufferManager::releaseImage(
const ImageCacheKey &key)
1533 const auto imageItr = imageMap.constFind(key);
1534 if (imageItr != imageMap.cend()) {
1535 auto rhiTexture = imageItr.value().renderImageTexture.m_texture;
1537 if (QSSGBufferManagerStat::enabled(QSSGBufferManagerStat::Level::Debug))
1538 qDebug() <<
"- releaseTexture: " << key.path.path() << currentLayer;
1539 Q_QUICK3D_PROFILE_START(QQuick3DProfiler::Quick3DTextureLoad);
1540 Q_TRACE_SCOPE(QSSG_textureUnload);
1541 decreaseMemoryStat(rhiTexture);
1542 QSSGRhiContextPrivate *rhiCtxD = QSSGRhiContextPrivate::get(m_contextInterface->rhiContext().get());
1543 rhiCtxD->releaseTexture(rhiTexture);
1544 Q_QUICK3D_PROFILE_END_WITH_STRING(QQuick3DProfiler::Quick3DTextureLoad,
1545 stats.imageDataSize, key.path.path().toUtf8());
1547 imageMap.erase(imageItr);
1551bool QSSGBufferManager::validateLightmap()
1553 if (lightmapSourceDirty) {
1554 lightmapSourceDirty =
false;
1555 QSharedPointer<QSSGLightmapLoader> loader = QSSGLightmapLoader::open(lightmapSource);
1556 lightmapFileValid = loader !=
nullptr;
1557 if (!lightmapFileValid)
1558 qCWarning(WARNING,
"Lightmaps are disabled.");
1560 return lightmapFileValid;
1563void QSSGBufferManager::cleanupUnreferencedBuffers(quint32 frameId, QSSGRenderLayer *currentLayer)
1565 Q_UNUSED(currentLayer);
1567 QSSGRhiContextPrivate *rhiCtxD = QSSGRhiContextPrivate::get(m_contextInterface->rhiContext().get());
1570 if (frameId == frameCleanupIndex)
1573 auto isUnused = [] (
const QHash<QSSGRenderLayer*, uint32_t> &usages) ->
bool {
1574 for (
const auto &value : std::as_const(usages))
1581 QMutexLocker meshMutexLocker(&meshBufferMutex);
1583 auto meshIterator = meshMap.cbegin();
1584 while (meshIterator != meshMap.cend()) {
1585 if (isUnused(meshIterator.value().usageCounts)) {
1586 if (QSSGBufferManagerStat::enabled(QSSGBufferManagerStat::Level::Debug))
1587 qDebug() <<
"- releaseGeometry: " << meshIterator.key().path() << currentLayer;
1588 decreaseMemoryStat(meshIterator.value().mesh);
1589 rhiCtxD->releaseMesh(meshIterator.value().mesh);
1590 meshIterator = meshMap.erase(meshIterator);
1597 auto customMeshIterator = customMeshMap.cbegin();
1598 while (customMeshIterator != customMeshMap.cend()) {
1599 if (isUnused(customMeshIterator.value().usageCounts)) {
1600 if (QSSGBufferManagerStat::enabled(QSSGBufferManagerStat::Level::Debug))
1601 qDebug() <<
"- releaseGeometry: " << customMeshIterator.key() << currentLayer;
1602 decreaseMemoryStat(customMeshIterator.value().mesh);
1603 rhiCtxD->releaseMesh(customMeshIterator.value().mesh);
1604 customMeshIterator = customMeshMap.erase(customMeshIterator);
1606 ++customMeshIterator;
1612 auto sgIterator = qsgImageMap.cbegin();
1613 while (sgIterator != qsgImageMap.cend()) {
1614 if (isUnused(sgIterator.value().usageCounts)) {
1618 sgIterator = qsgImageMap.erase(sgIterator);
1625 auto imageKeyIterator = imageMap.cbegin();
1626 while (imageKeyIterator != imageMap.cend()) {
1627 if (isUnused(imageKeyIterator.value().usageCounts)) {
1628 auto rhiTexture = imageKeyIterator.value().renderImageTexture.m_texture;
1630 if (QSSGBufferManagerStat::enabled(QSSGBufferManagerStat::Level::Debug))
1631 qDebug() <<
"- releaseTexture: " << imageKeyIterator.key().path.path() << currentLayer;
1632 decreaseMemoryStat(rhiTexture);
1633 rhiCtxD->releaseTexture(rhiTexture);
1635 imageKeyIterator = imageMap.erase(imageKeyIterator);
1642 auto textureDataIterator = customTextureMap.cbegin();
1643 while (textureDataIterator != customTextureMap.cend()) {
1644 if (isUnused(textureDataIterator.value().usageCounts)) {
1645 auto rhiTexture = textureDataIterator.value().renderImageTexture.m_texture;
1647 if (QSSGBufferManagerStat::enabled(QSSGBufferManagerStat::Level::Debug))
1648 qDebug() <<
"- releaseTextureData: " << rhiTexture << currentLayer;
1649 decreaseMemoryStat(rhiTexture);
1650 rhiCtxD->releaseTexture(rhiTexture);
1652 textureDataIterator = customTextureMap.erase(textureDataIterator);
1654 ++textureDataIterator;
1659 auto renderExtensionTextureKeyIterator = renderExtensionTexture.cbegin();
1660 while (renderExtensionTextureKeyIterator != renderExtensionTexture.cend()) {
1664 auto rhiTexture = renderExtensionTextureKeyIterator.value().renderImageTexture.m_texture;
1666 renderExtensionTextureKeyIterator = renderExtensionTexture.erase(renderExtensionTextureKeyIterator);
1668 ++renderExtensionTextureKeyIterator;
1672 frameCleanupIndex = frameId;
1673 if (QSSGBufferManagerStat::enabled(QSSGBufferManagerStat::Level::Usage)) {
1674 qDebug() <<
"QSSGBufferManager::cleanupUnreferencedBuffers()" <<
this <<
"frame:" << frameCleanupIndex << currentLayer;
1675 qDebug() <<
"Textures(by path): " << imageMap.count();
1676 qDebug() <<
"Textures(custom): " << customTextureMap.count();
1677 qDebug() <<
"Textures(Extension)" << renderExtensionTexture.count();
1678 qDebug() <<
"Textures(qsg): " << qsgImageMap.count();
1679 qDebug() <<
"Geometry(by path): " << meshMap.count();
1680 qDebug() <<
"Geometry(custom): " << customMeshMap.count();
1684void QSSGBufferManager::resetUsageCounters(quint32 frameId, QSSGRenderLayer *layer)
1686 currentLayer = layer;
1687 if (frameResetIndex == frameId)
1691 for (
auto &imageData : qsgImageMap)
1692 imageData.usageCounts[layer] = 0;
1695 for (
auto &imageData : imageMap)
1696 imageData.usageCounts[layer] = 0;
1699 for (
auto &imageData : customTextureMap)
1700 imageData.usageCounts[layer] = 0;
1702 for (
auto &meshData : meshMap)
1703 meshData.usageCounts[layer] = 0;
1706 for (
auto &meshData : customMeshMap)
1707 meshData.usageCounts[layer] = 0;
1712 for (
auto &retData : renderExtensionTexture) {
1713 const bool hasTexture = (retData.renderImageTexture.m_texture !=
nullptr);
1714 retData.usageCounts[layer] = uint32_t(hasTexture) * 1;
1717 frameResetIndex = frameId;
1720void QSSGBufferManager::registerMeshData(
const QString &assetId,
const QVector<QSSGMesh::Mesh> &meshData)
1722 auto it = g_assetMeshMap->find(assetId);
1723 if (it != g_assetMeshMap->end())
1726 g_assetMeshMap->insert(assetId, { meshData, 1 });
1729void QSSGBufferManager::unregisterMeshData(
const QString &assetId)
1731 auto it = g_assetMeshMap->find(assetId);
1732 if (it != g_assetMeshMap->end() && (--it->ref == 0))
1733 g_assetMeshMap->erase(AssetMeshMap::const_iterator(it));
1736QSSGRenderMesh *QSSGBufferManager::loadRenderMesh(
const QSSGRenderPath &inMeshPath, QSSGMeshProcessingOptions options)
1738 if (inMeshPath.isNull())
1742 auto meshItr = meshMap.find(inMeshPath);
1743 if (meshItr != meshMap.cend()) {
1744 if (options.isCompatible(meshItr.value().options)) {
1745 meshItr.value().usageCounts[currentLayer]++;
1746 return meshItr.value().mesh;
1750 auto *mesh = meshItr->mesh;
1751 meshMap.erase(meshItr);
1752 meshMap.insert(QSSGRenderPath(inMeshPath.path() + u"@reaped"), { mesh, {{currentLayer, 0}}, 0, {} });
1756 Q_QUICK3D_PROFILE_START(QQuick3DProfiler::Quick3DMeshLoad);
1757 Q_TRACE_SCOPE(QSSG_meshLoadPath, inMeshPath.path());
1759 auto [mesh, debugObjectName] = loadFromLightmapFile(options.lightmapPath, options.lightmapKey);
1761 if (!mesh.isValid()) {
1762 mesh = loadMeshData(inMeshPath);
1763 debugObjectName = QFileInfo(inMeshPath.path()).fileName();
1766 if (!mesh.isValid()) {
1767 qCWarning(WARNING,
"Failed to load mesh: %s", qPrintable(inMeshPath.path()));
1768 Q_QUICK3D_PROFILE_END_WITH_PAYLOAD(QQuick3DProfiler::Quick3DMeshLoad,
1769 stats.meshDataSize);
1772 if (QSSGBufferManagerStat::enabled(QSSGBufferManagerStat::Level::Debug))
1773 qDebug() <<
"+ uploadGeometry: " << inMeshPath.path() << currentLayer;
1775 auto ret = createRenderMesh(mesh, debugObjectName);
1776 meshMap.insert(inMeshPath, { ret, {{currentLayer, 1}}, 0, options });
1777 QSSGRhiContextPrivate *rhiCtxD = QSSGRhiContextPrivate::get(m_contextInterface->rhiContext().get());
1778 rhiCtxD->registerMesh(ret);
1779 increaseMemoryStat(ret);
1780 Q_QUICK3D_PROFILE_END_WITH_STRING(QQuick3DProfiler::Quick3DMeshLoad,
1781 stats.meshDataSize, inMeshPath.path().toUtf8());
1785QSSGRenderMesh *QSSGBufferManager::loadRenderMesh(QSSGRenderGeometry *geometry, QSSGMeshProcessingOptions options)
1787 QSSGRhiContextPrivate *rhiCtxD = QSSGRhiContextPrivate::get(m_contextInterface->rhiContext().get());
1789 auto meshIterator = customMeshMap.find(geometry);
1790 if (meshIterator == customMeshMap.end()) {
1791 meshIterator = customMeshMap.insert(geometry, MeshData());
1792 }
else if (geometry->generationId() != meshIterator->generationId || !options.isCompatible(meshIterator->options)) {
1794 releaseGeometry(geometry);
1795 meshIterator = customMeshMap.insert(geometry, MeshData());
1798 meshIterator.value().usageCounts[currentLayer]++;
1799 return meshIterator.value().mesh;
1802 Q_QUICK3D_PROFILE_START(QQuick3DProfiler::Quick3DCustomMeshLoad);
1803 Q_TRACE_SCOPE(QSSG_customMeshLoad);
1805 auto [mesh, debugObjectName] = loadFromLightmapFile(options.lightmapPath, options.lightmapKey);
1808 if (!geometry->meshData().m_vertexBuffer.isEmpty() || mesh.isValid()) {
1811 if (!mesh.isValid()) {
1812 mesh = QSSGMesh::Mesh::fromRuntimeData(geometry->meshData(), &error);
1813 debugObjectName = geometry->debugObjectName;
1816 if (mesh.isValid()) {
1817 if (QSSGBufferManagerStat::enabled(QSSGBufferManagerStat::Level::Debug))
1818 qDebug() <<
"+ uploadGeometry: " << geometry << currentLayer;
1819 meshIterator->mesh = createRenderMesh(mesh, debugObjectName);
1820 meshIterator->usageCounts[currentLayer] = 1;
1821 meshIterator->generationId = geometry->generationId();
1822 meshIterator->options = options;
1823 rhiCtxD->registerMesh(meshIterator->mesh);
1824 increaseMemoryStat(meshIterator->mesh);
1826 qWarning(
"Mesh building failed: %s", qPrintable(error));
1831 Q_QUICK3D_PROFILE_END_WITH_ID(QQuick3DProfiler::Quick3DCustomMeshLoad,
1832 stats.meshDataSize, geometry->profilingId);
1833 return meshIterator->mesh;
1836std::unique_ptr<QSSGMeshBVH> QSSGBufferManager::loadMeshBVH(
const QSSGRenderPath &inSourcePath)
1838 const QSSGMesh::Mesh mesh = loadMeshData(inSourcePath);
1839 if (!mesh.isValid()) {
1840 qCWarning(WARNING,
"Failed to load mesh: %s", qPrintable(inSourcePath.path()));
1843 QSSGMeshBVHBuilder meshBVHBuilder(mesh);
1844 return meshBVHBuilder.buildTree();
1847std::unique_ptr<QSSGMeshBVH> QSSGBufferManager::loadMeshBVH(QSSGRenderGeometry *geometry)
1853 if (geometry->primitiveType() != QSSGMesh::Mesh::DrawMode::Triangles)
1857 bool hasIndexBuffer =
false;
1858 QSSGRenderComponentType indexBufferFormat = QSSGRenderComponentType::UnsignedInt32;
1863 for (
int i = 0; i < geometry->attributeCount(); ++i) {
1864 auto attribute = geometry->attribute(i);
1865 if (attribute.semantic == QSSGMesh::RuntimeMeshData::Attribute::PositionSemantic) {
1866 posOffset = attribute.offset;
1867 }
else if (attribute.semantic == QSSGMesh::RuntimeMeshData::Attribute::TexCoord0Semantic) {
1869 uvOffset = attribute.offset;
1870 }
else if (!hasUV && attribute.semantic == QSSGMesh::RuntimeMeshData::Attribute::TexCoord1Semantic) {
1872 uvOffset = attribute.offset;
1873 }
else if (attribute.semantic == QSSGMesh::RuntimeMeshData::Attribute::IndexSemantic) {
1874 hasIndexBuffer =
true;
1875 indexBufferFormat = attribute.componentType;
1876 if (indexBufferFormat != QSSGRenderComponentType::Int16
1877 && indexBufferFormat != QSSGRenderComponentType::Int32
1878 && indexBufferFormat != QSSGRenderComponentType::UnsignedInt16
1879 && indexBufferFormat != QSSGRenderComponentType::UnsignedInt32) {
1880 qWarning() <<
"Unsupported index buffer format for geometry";
1886 QSSGMeshBVHBuilder meshBVHBuilder(geometry->vertexBuffer(),
1892 geometry->indexBuffer(),
1894 return meshBVHBuilder.buildTree();
1897std::unique_ptr<QSSGMeshBVH> QSSGBufferManager::loadMeshBVH(
const QSSGMesh::Mesh &mesh)
1899 QSSGMeshBVHBuilder meshBVHBuilder(mesh);
1900 return meshBVHBuilder.buildTree();
1903QSSGMesh::Mesh QSSGBufferManager::loadMeshData(
const QSSGRenderPath &inMeshPath)
1905 QSSGMesh::Mesh result;
1908 if (inMeshPath.path().startsWith(QChar::fromLatin1(
'#')))
1909 result = loadPrimitive(inMeshPath.path());
1912 if (!result.isValid() && inMeshPath.path().startsWith(u'!')) {
1913 const auto &[idx, assetId] = splitRuntimeMeshPath(inMeshPath);
1915 const auto ait = g_assetMeshMap->constFind(assetId);
1916 if (ait != g_assetMeshMap->constEnd()) {
1917 const auto &meshes = ait->meshes;
1918 if (idx < meshes.size())
1919 result = ait->meshes.at(idx);
1922 qWarning(
"Unexpected mesh path!");
1927 if (!result.isValid()) {
1928 QString pathBuilder = inMeshPath.path();
1929 int poundIndex = pathBuilder.lastIndexOf(QChar::fromLatin1(
'#'));
1931 if (poundIndex != -1) {
1932 id = QStringView(pathBuilder).mid(poundIndex + 1).toUInt();
1933 pathBuilder = pathBuilder.left(poundIndex);
1935 if (!pathBuilder.isEmpty()) {
1936 QSharedPointer<QIODevice> device(QSSGInputUtil::getStreamForFile(pathBuilder));
1938 QSSGMesh::Mesh mesh = QSSGMesh::Mesh::loadMesh(device.data(), id);
1948QSSGMesh::Mesh QSSGBufferManager::loadMeshData(
const QSSGRenderGeometry *geometry)
1951 QSSGMesh::Mesh mesh = QSSGMesh::Mesh::fromRuntimeData(geometry->meshData(), &error);
1952 if (!mesh.isValid())
1953 qWarning(
"loadMeshDataForCustomMeshUncached failed: %s", qPrintable(error));
1958void QSSGBufferManager::registerExtensionResult(
const QSSGRenderExtension &extensions,
1959 QRhiTexture *texture)
1962 const bool isMipMapped = texture->flags().testFlag(QRhiTexture::Flag::MipMapped);
1963 const auto mipLevels = isMipMapped ? QRhi::mipLevelsForSize(texture->pixelSize()) : 0;
1964 QSSGRenderImageTextureFlags flags;
1965 const bool isSRGB = texture->flags().testFlag(QRhiTexture::Flag::sRGB);
1966 flags.setLinear(!isSRGB);
1967 const bool isRGBA8 = (texture->format() == QRhiTexture::Format::RGBA8);
1968 flags.setRgbe8(isRGBA8);
1969 renderExtensionTexture.insert(&extensions, ImageData { QSSGRenderImageTexture{ texture, mipLevels, flags }, {}, 0 });
1971 renderExtensionTexture.insert(&extensions, {});
1975void QSSGBufferManager::clear()
1977 QSSGRhiContextPrivate *rhiCtxD = QSSGRhiContextPrivate::get(m_contextInterface->rhiContext().get());
1979 if (meshBufferUpdates) {
1980 meshBufferUpdates->release();
1981 meshBufferUpdates =
nullptr;
1985 QMutexLocker meshMutexLocker(&meshBufferMutex);
1987 auto meshMapCopy = meshMap;
1988 meshMapCopy.detach();
1989 for (
auto iter = meshMapCopy.begin(), end = meshMapCopy.end(); iter != end; ++iter) {
1990 QSSGRenderMesh *theMesh = iter.value().mesh;
1992 if (QSSGBufferManagerStat::enabled(QSSGBufferManagerStat::Level::Debug))
1993 qDebug() <<
"- releaseGeometry: " << iter.key().path() << currentLayer;
1994 decreaseMemoryStat(theMesh);
1995 rhiCtxD->releaseMesh(theMesh);
2001 auto customMeshMapCopy = customMeshMap;
2002 customMeshMapCopy.detach();
2003 for (
auto iter = customMeshMapCopy.begin(), end = customMeshMapCopy.end(); iter != end; ++iter) {
2004 QSSGRenderMesh *theMesh = iter.value().mesh;
2006 if (QSSGBufferManagerStat::enabled(QSSGBufferManagerStat::Level::Debug))
2007 qDebug() <<
"- releaseGeometry: " << iter.key() << currentLayer;
2008 decreaseMemoryStat(theMesh);
2009 rhiCtxD->releaseMesh(theMesh);
2012 customMeshMap.clear();
2016 for (
auto it = imageMap.constBegin(), end = imageMap.constEnd(); it != end; ++it)
2017 releaseImage(it.key());
2022 for (
auto it = customTextureMap.cbegin(), end = customTextureMap.cend(); it != end; ++it)
2023 releaseTextureData(it.key());
2025 customTextureMap.clear();
2029 qsgImageMap.clear();
2032 lightmapSourceDirty =
true;
2035QRhiResourceUpdateBatch *QSSGBufferManager::meshBufferUpdateBatch()
2037 if (!meshBufferUpdates)
2038 meshBufferUpdates = m_contextInterface->rhiContext()->rhi()->nextResourceUpdateBatch();
2039 return meshBufferUpdates;
2042void QSSGBufferManager::commitBufferResourceUpdates()
2044 if (meshBufferUpdates) {
2045 m_contextInterface->rhiContext()->commandBuffer()->resourceUpdate(meshBufferUpdates);
2046 meshBufferUpdates =
nullptr;
2050void QSSGBufferManager::processResourceLoader(
const QSSGRenderResourceLoader *loader)
2052 for (
auto &mesh : std::as_const(loader->meshes))
2053 loadRenderMesh(mesh, {});
2055 for (
auto customMesh : std::as_const(loader->geometries))
2056 loadRenderMesh(
static_cast<QSSGRenderGeometry*>(customMesh), {});
2058 for (
auto texture : std::as_const(loader->textures)) {
2059 const auto image =
static_cast<QSSGRenderImage *>(texture);
2060 loadRenderImage(image);
2064 commitBufferResourceUpdates();
2073 auto format = texture->format();
2074 if (format == QRhiTexture::UnknownFormat)
2077 s = texture->pixelSize().width() * texture->pixelSize().height();
2079
2080
2081
2082
2083
2084
2085
2086
2087
2088
2089
2090
2091
2092
2093
2094
2095
2096
2097
2098
2099
2100
2101
2102
2103
2104
2105 static const quint64 pixelSizes[] = {0, 4, 4, 1, 2, 2, 4, 1, 2, 4, 2, 4, 4, 1, 4, 8, 16, 1, 4, 8, 16, 2, 4, 4, 4, 8};
2107
2108
2109
2110
2111
2112
2113
2114
2115
2116
2117 static const quint64 blockSizes[] = {8, 16, 16, 8, 16, 16, 16, 8, 8, 16};
2118 Q_STATIC_ASSERT_X(QRhiTexture::BC1 == 26 && QRhiTexture::ETC2_RGBA8 == 35,
2119 "QRhiTexture format constant value missmatch.");
2120 if (format < QRhiTexture::BC1)
2121 s *= pixelSizes[format];
2122 else if (format >= QRhiTexture::BC1 && format <= QRhiTexture::ETC2_RGBA8)
2123 s /= blockSizes[format - QRhiTexture::BC1];
2127 if (texture->flags() & QRhiTexture::MipMapped)
2129 if (texture->flags() & QRhiTexture::CubeMap)
2139 s = buffer->buffer()->size();
2143void QSSGBufferManager::increaseMemoryStat(QRhiTexture *texture)
2145 stats.imageDataSize += textureMemorySize(texture);
2146 QSSGRhiContextStats::get(*m_contextInterface->rhiContext()).imageDataSizeChanges(stats.imageDataSize);
2149void QSSGBufferManager::decreaseMemoryStat(QRhiTexture *texture)
2151 stats.imageDataSize = qMax(0u, stats.imageDataSize - textureMemorySize(texture));
2152 QSSGRhiContextStats::get(*m_contextInterface->rhiContext()).imageDataSizeChanges(stats.imageDataSize);
2155void QSSGBufferManager::increaseMemoryStat(QSSGRenderMesh *mesh)
2157 stats.meshDataSize += bufferMemorySize(mesh->subsets.at(0).rhi.vertexBuffer)
2158 + bufferMemorySize(mesh->subsets.at(0).rhi.indexBuffer);
2159 QSSGRhiContextStats::get(*m_contextInterface->rhiContext()).meshDataSizeChanges(stats.meshDataSize);
2162void QSSGBufferManager::decreaseMemoryStat(QSSGRenderMesh *mesh)
2166 s = bufferMemorySize(mesh->subsets.at(0).rhi.vertexBuffer)
2167 + bufferMemorySize(mesh->subsets.at(0).rhi.indexBuffer);
2168 stats.meshDataSize = qMax(0u, stats.meshDataSize - s);
2169 QSSGRhiContextStats::get(*m_contextInterface->rhiContext()).meshDataSizeChanges(stats.meshDataSize);
2172void QSSGBufferManager::setLightmapSource(
const QString &source)
2174 if (lightmapSource != source) {
2175 lightmapSource = source;
2176 lightmapSourceDirty =
true;
2180void QSSGBufferManager::setCurrentlyLightmapBaking(
bool value)
2182 currentlyLightmapBaking = value;
2185size_t qHash(
const QSSGBufferManager::CustomImageCacheKey &k, size_t seed)
noexcept
2189 using MipMap_t = std::underlying_type_t<QSSGBufferManager::MipMode>;
2190 return qHash(*k.data, seed) ^ MipMap_t(k.mipMode);
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 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 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)