Qt
Internal/Contributor docs for the Qt SDK. <b>Note:</b> These are NOT official API docs; those are found <a href='https://doc.qt.io/'>here</a>.
Loading...
Searching...
No Matches
qssgrenderbuffermanager.cpp
Go to the documentation of this file.
1// Copyright (C) 2008-2012 NVIDIA Corporation.
2// Copyright (C) 2019 The Qt Company Ltd.
3// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
4
6
7#include <QtQuick3DRuntimeRender/private/qssgrenderloadedtexture_p.h>
8
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>
13
14#include <QtQuick/QSGTexture>
16#include <QtCore/QDir>
17#include <QtGui/private/qimage_p.h>
18#include <QtQuick/private/qsgtexture_p.h>
19#include <QtQuick/private/qsgcompressedtexture_p.h>
20
21#include "../utils/qssgrenderbasetypes_p.h"
22#include <QtQuick3DRuntimeRender/private/qssgrendergeometry_p.h>
23#include <QtQuick3DRuntimeRender/private/qssgrendermodel_p.h>
24#include <QtQuick3DRuntimeRender/private/qssgrenderimage_p.h>
25#include <QtQuick3DRuntimeRender/private/qssgrendertexturedata_p.h>
27#include <QtQuick3DRuntimeRender/private/qssglightmapper_p.h>
28#include <QtQuick3DRuntimeRender/private/qssgrenderresourceloader_p.h>
29#include <qtquick3d_tracepoints_p.h>
31
33
35{
36enum class Level
37{
38 Debug = 0x1,
39 Usage = 0x2,
40 // 0x3 DebugAndUsage
41};
42
43#ifdef QT_DEBUG
44static bool enabled(Level level)
45{
46 constexpr Level DebugAndUsage { quint8(Level::Debug) | quint8(Level::Usage) };
47 static auto v = qEnvironmentVariableIntValue("QSSG_BUFFERMANAGER_DEBUG");
48 return v && Level{v} <= DebugAndUsage && (v & quint8(level));
49}
50#else
51static constexpr bool enabled(Level) { return false; }
52#endif
53
54}
55
56Q_TRACE_POINT(qtquick3d, QSSG_textureLoad_entry);
57Q_TRACE_POINT(qtquick3d, QSSG_textureLoad_exit);
58Q_TRACE_POINT(qtquick3d, QSSG_meshLoad_entry);
59Q_TRACE_POINT(qtquick3d, QSSG_meshLoad_exit);
60Q_TRACE_POINT(qtquick3d, QSSG_meshLoadPath_entry, const QString &path);
61Q_TRACE_POINT(qtquick3d, QSSG_meshLoadPath_exit);
62Q_TRACE_POINT(qtquick3d, QSSG_textureUnload_entry);
63Q_TRACE_POINT(qtquick3d, QSSG_textureUnload_exit);
64Q_TRACE_POINT(qtquick3d, QSSG_meshUnload_entry);
65Q_TRACE_POINT(qtquick3d, QSSG_meshUnload_exit);
66Q_TRACE_POINT(qtquick3d, QSSG_customMeshLoad_entry);
67Q_TRACE_POINT(qtquick3d, QSSG_customMeshLoad_exit);
68Q_TRACE_POINT(qtquick3d, QSSG_customMeshUnload_entry);
69Q_TRACE_POINT(qtquick3d, QSSG_customMeshUnload_exit);
70Q_TRACE_POINT(qtquick3d, QSSG_textureLoadPath_entry, const QString &path);
71Q_TRACE_POINT(qtquick3d, QSSG_textureLoadPath_exit);
72
74{
75 QVector<QSSGMesh::Mesh> meshes;
77};
78using AssetMeshMap = QHash<QString, MeshStorageRef>;
79
80Q_GLOBAL_STATIC(AssetMeshMap, g_assetMeshMap)
81
82// Returns !idx@asset_id
83QString QSSGBufferManager::runtimeMeshSourceName(const QString &assetId, qsizetype meshId)
84{
85 return QString::fromUtf16(u"!%1@%2").arg(QString::number(meshId), assetId);
86}
87
88using MeshIdxNamePair = QPair<qsizetype, QString>;
90{
91 const auto &path = rpath.path();
92 Q_ASSERT(path.startsWith(u'!'));
93 const auto strings = path.mid(1).split(u'@');
94 const bool hasData = (strings.size() == 2) && !strings[0].isEmpty() && !strings[1].isEmpty();
95 qsizetype idx = -1;
96 bool ok = false;
97 if (hasData)
98 idx = strings.at(0).toLongLong(&ok);
99
100 return (ok) ? qMakePair(idx, strings.at(1)) : qMakePair(qsizetype(-1), QString());
101}
102
103namespace {
104struct PrimitiveEntry
105{
106 // Name of the primitive as it will be in e.g., the QML file
107 const char *primitive;
108 // Name of the primitive file on the filesystem
109 const char *file;
110};
111}
112
113static const int nPrimitives = 5;
114static const PrimitiveEntry primitives[nPrimitives] = {
115 {"#Rectangle", "/Rectangle.mesh"},
116 {"#Sphere","/Sphere.mesh"},
117 {"#Cube","/Cube.mesh"},
118 {"#Cone","/Cone.mesh"},
119 {"#Cylinder","/Cylinder.mesh"},
120};
121
122static const char *primitivesDirectory = "res//primitives";
123
124static constexpr QSize sizeForMipLevel(int mipLevel, const QSize &baseLevelSize)
125{
126 return QSize(qMax(1, baseLevelSize.width() >> mipLevel), qMax(1, baseLevelSize.height() >> mipLevel));
127}
128
132
134{
135 clear();
136 m_contextInterface = nullptr;
137}
138
143
145{
146 clear();
147}
148
150{
151 // frameResetIndex must be +1 since it's depending on being the
152 // next frame and this is the cleanup after the final frame as
153 // the layer is destroyed
154 resetUsageCounters(frameResetIndex + 1, layer);
155 cleanupUnreferencedBuffers(frameResetIndex + 1, layer);
156}
157
159 MipMode inMipMode,
160 LoadRenderImageFlags flags)
161{
162 if (inMipMode == MipModeFollowRenderImage)
163 inMipMode = image->m_generateMipmaps ? MipModeEnable : MipModeDisable;
164
165 const auto &context = m_contextInterface->rhiContext();
167 if (image->m_qsgTexture) {
168 QRhi *rhi = context->rhi();
169 QSGTexture *qsgTexture = image->m_qsgTexture;
170 QRhiTexture *rhiTex = qsgTexture->rhiTexture(); // this may not be valid until commit and that's ok
171 if (!rhiTex || rhiTex->rhi() == rhi) {
172 // A QSGTexture from a textureprovider that is not a QSGDynamicTexture
173 // needs to be pushed to get its content updated (or even to create a
174 // QRhiTexture in the first place).
176 if (qsgTexture->isAtlasTexture()) {
177 // This returns a non-atlased QSGTexture (or does nothing if the
178 // extraction has already been done), the ownership of which stays with
179 // the atlas. As we do not store (and do not own) qsgTexture below,
180 // apart from using it as a cache key and querying its QRhiTexture
181 // (which we again do not own), we can just pretend we got the
182 // non-atlased QSGTexture in the first place.
183 qsgTexture = qsgTexture->removedFromAtlas(rub);
184 }
185 qsgTexture->commitTextureOperations(rhi, rub);
186 context->commandBuffer()->resourceUpdate(rub);
187 auto theImage = qsgImageMap.find(qsgTexture);
188 if (theImage == qsgImageMap.end())
189 theImage = qsgImageMap.insert(qsgTexture, ImageData());
190 theImage.value().renderImageTexture.m_texture = qsgTexture->rhiTexture();
191 theImage.value().renderImageTexture.m_flags.setHasTransparency(qsgTexture->hasAlphaChannel());
192 theImage.value().usageCounts[currentLayer]++;
193 result = theImage.value().renderImageTexture;
194 // inMipMode is ignored completely when sourcing the texture from a
195 // QSGTexture. Mipmap generation is not supported, whereas
196 // attempting to use such a texture as a light probe will fail. (no
197 // mip levels, no pre-filtering) In the latter case, print a warning
198 // because that will definitely lead to visual problems in the result.
199 if (inMipMode == MipModeBsdf)
200 qWarning("Cannot use QSGTexture from Texture.sourceItem as light probe.");
201 } else {
202 qWarning("Cannot use QSGTexture (presumably from Texture.sourceItem) created in another "
203 "window that was using a different graphics device/context. "
204 "Avoid using View3D.importScene between multiple windows.");
205 }
206
207 } else if (image->m_rawTextureData) {
208 Q_TRACE_SCOPE(QSSG_textureLoad);
209 Q_QUICK3D_PROFILE_START(QQuick3DProfiler::Quick3DTextureLoad);
210 result = loadTextureData(image->m_rawTextureData, inMipMode);
211 Q_QUICK3D_PROFILE_END_WITH_ID(QQuick3DProfiler::Quick3DTextureLoad, stats.imageDataSize, image->profilingId);
212 } else if (!image->m_imagePath.isEmpty()) {
213
214 const ImageCacheKey imageKey = { image->m_imagePath, inMipMode, int(image->type) };
215 auto foundIt = imageMap.find(imageKey);
216 if (foundIt != imageMap.cend()) {
217 result = foundIt.value().renderImageTexture;
218 } else {
219 Q_QUICK3D_PROFILE_START(QQuick3DProfiler::Quick3DTextureLoad);
220 QScopedPointer<QSSGLoadedTexture> theLoadedTexture;
221 const auto &path = image->m_imagePath.path();
222 const bool flipY = flags.testFlag(LoadWithFlippedY);
223 Q_TRACE_SCOPE(QSSG_textureLoadPath, path);
224 theLoadedTexture.reset(QSSGLoadedTexture::load(path, image->m_format, flipY));
225 if (theLoadedTexture) {
226 foundIt = imageMap.insert(imageKey, ImageData());
227 CreateRhiTextureFlags rhiTexFlags = ScanForTransparency;
228 if (image->type == QSSGRenderGraphObject::Type::ImageCube)
229 rhiTexFlags |= CubeMap;
230 if (!createRhiTexture(foundIt.value().renderImageTexture, theLoadedTexture.data(), inMipMode, rhiTexFlags, QFileInfo(path).fileName())) {
231 foundIt.value() = ImageData();
233 qDebug() << "+ uploadTexture: " << image->m_imagePath.path() << currentLayer;
234 }
235 result = foundIt.value().renderImageTexture;
236 increaseMemoryStat(result.m_texture);
237 } else {
238 // We want to make sure that bad path fails once and doesn't fail over and over
239 // again
240 // which could slow down the system quite a bit.
241 foundIt = imageMap.insert(imageKey, ImageData());
242 qCWarning(WARNING, "Failed to load image: %s", qPrintable(path));
243 }
244 Q_QUICK3D_PROFILE_END_WITH_STRING(QQuick3DProfiler::Quick3DTextureLoad, stats.imageDataSize, path.toUtf8());
245 }
246 foundIt.value().usageCounts[currentLayer]++;
247 } else if (image->m_extensionsSource) {
248 auto it = renderExtensionTexture.find(image->m_extensionsSource);
249 if (it != renderExtensionTexture.end()) {
250 it->usageCounts[currentLayer]++;
251 result = it->renderImageTexture;
252 increaseMemoryStat(result.m_texture);
253 }
254 }
255 return result;
256}
257
258QSSGRenderImageTexture QSSGBufferManager::loadTextureData(QSSGRenderTextureData *data, MipMode inMipMode)
259{
260 const CustomImageCacheKey imageKey = { data, inMipMode };
261 auto theImageData = customTextureMap.find(imageKey);
262 if (theImageData == customTextureMap.end()) {
263 theImageData = customTextureMap.insert(imageKey, ImageData());
264 } else if (data->generationId() != theImageData->generationId) {
265 // release first
266 releaseTextureData(imageKey);
267 // reinsert the placeholder since releaseTextureData removed from map
268 theImageData = customTextureMap.insert(imageKey, ImageData());
269 } else {
270 // Return the currently loaded texture
271 theImageData.value().usageCounts[currentLayer]++;
272 return theImageData.value().renderImageTexture;
273 }
274
275 // Load the texture
276 QScopedPointer<QSSGLoadedTexture> theLoadedTexture;
277 if (!data->textureData().isNull()) {
278 theLoadedTexture.reset(QSSGLoadedTexture::loadTextureData(data));
279 theLoadedTexture->ownsData = false;
280 CreateRhiTextureFlags rhiTexFlags = {};
281 if (theLoadedTexture->depth > 0)
282 rhiTexFlags |= Texture3D;
283 if (createRhiTexture(theImageData.value().renderImageTexture, theLoadedTexture.data(), inMipMode, rhiTexFlags, data->debugObjectName)) {
285 qDebug() << "+ uploadTexture: " << data << currentLayer;
286 theImageData.value().generationId = data->generationId();
287 increaseMemoryStat(theImageData.value().renderImageTexture.m_texture);
288 } else {
289 theImageData.value() = ImageData();
290 }
291 }
292
293 theImageData.value().usageCounts[currentLayer]++;
294 return theImageData.value().renderImageTexture;
295}
296
298{
301
303 const ImageCacheKey imageKey = { QSSGRenderPath(imagePath), MipModeDisable, int(QSSGRenderGraphObject::Type::Image2D) };
304 auto foundIt = imageMap.find(imageKey);
305 if (foundIt != imageMap.end()) {
306 result = foundIt.value().renderImageTexture;
307 } else {
308 Q_QUICK3D_PROFILE_START(QQuick3DProfiler::Quick3DTextureLoad);
309 Q_TRACE_SCOPE(QSSG_textureLoadPath, imagePath);
310 QScopedPointer<QSSGLoadedTexture> theLoadedTexture;
311 theLoadedTexture.reset(QSSGLoadedTexture::load(imagePath, format));
312 if (!theLoadedTexture)
313 qCWarning(WARNING, "Failed to load lightmap image: %s", qPrintable(imagePath));
314 foundIt = imageMap.insert(imageKey, ImageData());
315 if (theLoadedTexture) {
316 if (!createRhiTexture(foundIt.value().renderImageTexture, theLoadedTexture.data(), MipModeDisable, {}, imagePath))
317 foundIt.value() = ImageData();
318 result = foundIt.value().renderImageTexture;
319 }
320 increaseMemoryStat(result.m_texture);
321 Q_QUICK3D_PROFILE_END_WITH_STRING(QQuick3DProfiler::Quick3DTextureLoad, stats.imageDataSize, imagePath.toUtf8());
322 }
323 foundIt.value().usageCounts[currentLayer]++;
324 return result;
325}
326
331
333{
334 if (!model.meshPath.isNull()) {
335 const auto foundIt = meshMap.constFind(model.meshPath);
336 if (foundIt != meshMap.constEnd())
337 return foundIt->mesh;
338 }
339
340 if (model.geometry) {
341 const auto foundIt = customMeshMap.constFind(model.geometry);
342 if (foundIt != customMeshMap.constEnd())
343 return foundIt->mesh;
344 }
345
346 return nullptr;
347}
348
350{
351 switch (format.format) {
352
354 return QRhiTexture::RGBA8;
356 return QRhiTexture::R8;
359 return QRhiTexture::R16;
369 return QRhiTexture::R16F;
371 return QRhiTexture::R32F;
373 return QRhiTexture::RGBA8;
375 return QRhiTexture::BC1;
377 return QRhiTexture::BC2;
379 return QRhiTexture::BC3;
410
411
413 return QRhiTexture::RGBA8; // Note: user must keep track of color space manually
414
415 default:
416 qWarning() << "Unsupported texture format" << format.format;
418 }
419
420}
421
422// Vertex data for rendering environment cube map
423static const float cube[] = {
424 -1.0f,-1.0f,-1.0f, // -X side
425 -1.0f,-1.0f, 1.0f,
426 -1.0f, 1.0f, 1.0f,
427 -1.0f, 1.0f, 1.0f,
428 -1.0f, 1.0f,-1.0f,
429 -1.0f,-1.0f,-1.0f,
430
431 -1.0f,-1.0f,-1.0f, // -Z side
432 1.0f, 1.0f,-1.0f,
433 1.0f,-1.0f,-1.0f,
434 -1.0f,-1.0f,-1.0f,
435 -1.0f, 1.0f,-1.0f,
436 1.0f, 1.0f,-1.0f,
437
438 -1.0f,-1.0f,-1.0f, // -Y side
439 1.0f,-1.0f,-1.0f,
440 1.0f,-1.0f, 1.0f,
441 -1.0f,-1.0f,-1.0f,
442 1.0f,-1.0f, 1.0f,
443 -1.0f,-1.0f, 1.0f,
444
445 -1.0f, 1.0f,-1.0f, // +Y side
446 -1.0f, 1.0f, 1.0f,
447 1.0f, 1.0f, 1.0f,
448 -1.0f, 1.0f,-1.0f,
449 1.0f, 1.0f, 1.0f,
450 1.0f, 1.0f,-1.0f,
451
452 1.0f, 1.0f,-1.0f, // +X side
453 1.0f, 1.0f, 1.0f,
454 1.0f,-1.0f, 1.0f,
455 1.0f,-1.0f, 1.0f,
456 1.0f,-1.0f,-1.0f,
457 1.0f, 1.0f,-1.0f,
458
459 -1.0f, 1.0f, 1.0f, // +Z side
460 -1.0f,-1.0f, 1.0f,
461 1.0f, 1.0f, 1.0f,
462 -1.0f,-1.0f, 1.0f,
463 1.0f,-1.0f, 1.0f,
464 1.0f, 1.0f, 1.0f,
465
466 0.0f, 1.0f, // -X side
467 1.0f, 1.0f,
468 1.0f, 0.0f,
469 1.0f, 0.0f,
470 0.0f, 0.0f,
471 0.0f, 1.0f,
472
473 1.0f, 1.0f, // -Z side
474 0.0f, 0.0f,
475 0.0f, 1.0f,
476 1.0f, 1.0f,
477 1.0f, 0.0f,
478 0.0f, 0.0f,
479
480 1.0f, 0.0f, // -Y side
481 1.0f, 1.0f,
482 0.0f, 1.0f,
483 1.0f, 0.0f,
484 0.0f, 1.0f,
485 0.0f, 0.0f,
486
487 1.0f, 0.0f, // +Y side
488 0.0f, 0.0f,
489 0.0f, 1.0f,
490 1.0f, 0.0f,
491 0.0f, 1.0f,
492 1.0f, 1.0f,
493
494 1.0f, 0.0f, // +X side
495 0.0f, 0.0f,
496 0.0f, 1.0f,
497 0.0f, 1.0f,
498 1.0f, 1.0f,
499 1.0f, 0.0f,
500
501 0.0f, 0.0f, // +Z side
502 0.0f, 1.0f,
503 1.0f, 0.0f,
504 0.0f, 1.0f,
505 1.0f, 1.0f,
506 1.0f, 0.0f,
507};
508
509bool QSSGBufferManager::createEnvironmentMap(const QSSGLoadedTexture *inImage, QSSGRenderImageTexture *outTexture, const QString &debugObjectName)
510{
511 // The objective of this method is to take the equirectangular texture
512 // provided by inImage and create a cubeMap that contains both pre-filtered
513 // specular environment maps, as well as a irradiance map for diffuse
514 // operations.
515 // To achieve this though we first convert convert the Equirectangular texture
516 // to a cubeMap with genereated mip map levels (no filtering) to make the
517 // process of creating the prefiltered and irradiance maps eaiser. This
518 // intermediate texture as well as the original equirectangular texture are
519 // destroyed after this frame completes, and all further associations with
520 // the source lightProbe texture are instead associated with the final
521 // generated environment map.
522 // The intermediate environment cubemap is used to generate the final
523 // cubemap. This cubemap will generate 6 mip levels for each face
524 // (the remaining faces are unused). This is what the contents of each
525 // face mip level looks like:
526 // 0: Pre-filtered with roughness 0 (basically unfiltered)
527 // 1: Pre-filtered with roughness 0.25
528 // 2: Pre-filtered with roughness 0.5
529 // 3: Pre-filtered with roughness 0.75
530 // 4: Pre-filtered with rougnness 1.0
531 // 5: Irradiance map (ideally at least 16x16)
532 // It would be better if we could use a separate cubemap for irradiance, but
533 // right now there is a 1:1 association between texture sources on the front-
534 // end and backend.
535 const auto &context = m_contextInterface->rhiContext();
536 auto *rhi = context->rhi();
537 // Right now minimum face size needs to be 512x512 to be able to have 6 reasonably sized mips
538 int suggestedSize = inImage->height * 0.5f;
539 suggestedSize = qMax(512, suggestedSize);
540 const QSize environmentMapSize(suggestedSize, suggestedSize);
541 const bool isRGBE = inImage->format.format == QSSGRenderTextureFormat::Format::RGBE8;
542 const QRhiTexture::Format sourceTextureFormat = toRhiFormat(inImage->format.format);
543 // Check if we can use the source texture at all
544 if (!rhi->isTextureFormatSupported(sourceTextureFormat))
545 return false;
546
547 QRhiTexture::Format cubeTextureFormat = inImage->format.isCompressedTextureFormat()
548 ? QRhiTexture::RGBA16F // let's just assume that if compressed textures are available, then it's at least a GLES 3.0 level API
549 : sourceTextureFormat;
550#ifdef Q_OS_IOS
551 // iOS doesn't support mip map filtering on RGBA32F textures
552 if (cubeTextureFormat == QRhiTexture::RGBA32F)
553 cubeTextureFormat = QRhiTexture::RGBA16F;
554#endif
555
556 const int colorSpace = inImage->isSRGB ? 1 : 0; // 0 Linear | 1 sRGB
557
558 // Phase 1: Convert the Equirectangular texture to a Cubemap
559 QRhiTexture *envCubeMap = rhi->newTexture(cubeTextureFormat, environmentMapSize, 1,
561 if (!envCubeMap->create()) {
562 qWarning("Failed to create Environment Cube Map");
563 return false;
564 }
565 envCubeMap->deleteLater();
566
567 // Create a renderbuffer the size of a the cubeMap face
568 QRhiRenderBuffer *envMapRenderBuffer = rhi->newRenderBuffer(QRhiRenderBuffer::Color, environmentMapSize);
569 if (!envMapRenderBuffer->create()) {
570 qWarning("Failed to create Environment Map Render Buffer");
571 return false;
572 }
573 envMapRenderBuffer->deleteLater();
574
575 const QByteArray rtName = debugObjectName.toLatin1();
576
577 // Setup the 6 render targets for each cube face
578 QVarLengthArray<QRhiTextureRenderTarget *, 6> renderTargets;
579 QRhiRenderPassDescriptor *renderPassDesc = nullptr;
580 for (const auto face : QSSGRenderTextureCubeFaces) {
581 QRhiColorAttachment att(envCubeMap);
582 att.setLayer(quint8(face));
584 rtDesc.setColorAttachments({att});
585 auto renderTarget = rhi->newTextureRenderTarget(rtDesc);
586 renderTarget->setName(rtName + QByteArrayLiteral(" env cube face: ") + QSSGBaseTypeHelpers::displayName(face));
587 renderTarget->setDescription(rtDesc);
588 if (!renderPassDesc)
589 renderPassDesc = renderTarget->newCompatibleRenderPassDescriptor();
590 renderTarget->setRenderPassDescriptor(renderPassDesc);
591 if (!renderTarget->create()) {
592 qWarning("Failed to build env map render target");
593 return false;
594 }
595 renderTarget->deleteLater();
596 renderTargets << renderTarget;
597 }
598 renderPassDesc->deleteLater();
599
600 // Setup the sampler for reading the equirectangular loaded texture
601 QSize size(inImage->width, inImage->height);
602 auto *sourceTexture = rhi->newTexture(sourceTextureFormat, size, 1);
603 if (!sourceTexture->create()) {
604 qWarning("failed to create source env map texture");
605 return false;
606 }
607 sourceTexture->deleteLater();
608
609 // Upload the equirectangular texture
610 const auto desc = inImage->textureFileData.isValid()
612 { 0, 0, QRhiTextureSubresourceUploadDescription(inImage->textureFileData.getDataView().toByteArray()) })
613 : QRhiTextureUploadDescription({ 0, 0, { inImage->data, inImage->dataSizeInBytes } });
614
615 auto *rub = rhi->nextResourceUpdateBatch();
616 rub->uploadTexture(sourceTexture, desc);
617
618 const QSSGRhiSamplerDescription samplerDesc {
625 };
626 QRhiSampler *sampler = context->sampler(samplerDesc);
627
628 // Load shader and setup render pipeline
629 const auto &shaderCache = m_contextInterface->shaderCache();
630 const auto &envMapShaderStages = shaderCache->getBuiltInRhiShaders().getRhiEnvironmentmapShader();
631
632 // Vertex Buffer - Just a single cube that will be viewed from inside
633 QRhiBuffer *vertexBuffer = rhi->newBuffer(QRhiBuffer::Immutable, QRhiBuffer::VertexBuffer, sizeof(cube));
634 vertexBuffer->create();
635 vertexBuffer->deleteLater();
636 rub->uploadStaticBuffer(vertexBuffer, cube);
637
638 // Uniform Buffer - 2x mat4
639 int ubufElementSize = rhi->ubufAligned(128);
640 QRhiBuffer *uBuf = rhi->newBuffer(QRhiBuffer::Dynamic, QRhiBuffer::UniformBuffer, ubufElementSize * 6);
641 uBuf->create();
642 uBuf->deleteLater();
643
644 int ubufEnvMapElementSize = rhi->ubufAligned(4);
645 QRhiBuffer *uBufEnvMap = rhi->newBuffer(QRhiBuffer::Dynamic, QRhiBuffer::UniformBuffer, ubufEnvMapElementSize * 6);
646 uBufEnvMap->create();
647 uBufEnvMap->deleteLater();
648
649 // Shader Resource Bindings
650 QRhiShaderResourceBindings *envMapSrb = rhi->newShaderResourceBindings();
651 envMapSrb->setBindings({
655 });
656 envMapSrb->create();
657 envMapSrb->deleteLater();
658
659 // Pipeline
660 QRhiGraphicsPipeline *envMapPipeline = rhi->newGraphicsPipeline();
662 envMapPipeline->setFrontFace(QRhiGraphicsPipeline::CCW);
663 envMapPipeline->setShaderStages({
664 *envMapShaderStages->vertexStage(),
665 *envMapShaderStages->fragmentStage()
666 });
667
668 QRhiVertexInputLayout inputLayout;
669 inputLayout.setBindings({
670 { 3 * sizeof(float) }
671 });
672 inputLayout.setAttributes({
674 });
675
676 envMapPipeline->setVertexInputLayout(inputLayout);
677 envMapPipeline->setShaderResourceBindings(envMapSrb);
678 envMapPipeline->setRenderPassDescriptor(renderPassDesc);
679 if (!envMapPipeline->create()) {
680 qWarning("failed to create source env map pipeline state");
681 return false;
682 }
683 envMapPipeline->deleteLater();
684
685 // Do the actual render passes
686 auto *cb = context->commandBuffer();
687 cb->debugMarkBegin("Environment Cubemap Generation");
688 Q_QUICK3D_PROFILE_START(QQuick3DProfiler::Quick3DRenderPass);
689 Q_TRACE(QSSG_renderPass_entry, QStringLiteral("Environment Cubemap Generation"));
690 const QRhiCommandBuffer::VertexInput vbufBinding(vertexBuffer, 0);
691
692 // Set the Uniform Data
693 QMatrix4x4 mvp = rhi->clipSpaceCorrMatrix();
694 mvp.perspective(90.0f, 1.0f, 0.1f, 10.0f);
695
696 auto lookAt = [](const QVector3D &eye, const QVector3D &center, const QVector3D &up) {
697 QMatrix4x4 viewMatrix;
698 viewMatrix.lookAt(eye, center, up);
699 return viewMatrix;
700 };
701 QVarLengthArray<QMatrix4x4, 6> views;
702 views.append(lookAt(QVector3D(0.0f, 0.0f, 0.0f), QVector3D(1.0, 0.0, 0.0), QVector3D(0.0f, -1.0f, 0.0f)));
703 views.append(lookAt(QVector3D(0.0f, 0.0f, 0.0f), QVector3D(-1.0, 0.0, 0.0), QVector3D(0.0f, -1.0f, 0.0f)));
704 if (rhi->isYUpInFramebuffer()) {
705 views.append(lookAt(QVector3D(0.0f, 0.0f, 0.0f), QVector3D(0.0, 1.0, 0.0), QVector3D(0.0f, 0.0f, 1.0f)));
706 views.append(lookAt(QVector3D(0.0f, 0.0f, 0.0f), QVector3D(0.0, -1.0, 0.0), QVector3D(0.0f, 0.0f, -1.0f)));
707 } else {
708 views.append(lookAt(QVector3D(0.0f, 0.0f, 0.0f), QVector3D(0.0, -1.0, 0.0), QVector3D(0.0f, 0.0f, -1.0f)));
709 views.append(lookAt(QVector3D(0.0f, 0.0f, 0.0f), QVector3D(0.0, 1.0, 0.0), QVector3D(0.0f, 0.0f, 1.0f)));
710 }
711 views.append(lookAt(QVector3D(0.0f, 0.0f, 0.0f), QVector3D(0.0, 0.0, 1.0), QVector3D(0.0f, -1.0f, 0.0f)));
712 views.append(lookAt(QVector3D(0.0f, 0.0f, 0.0f), QVector3D(0.0, 0.0, -1.0), QVector3D(0.0f, -1.0f, 0.0f)));
713 for (const auto face : QSSGRenderTextureCubeFaces) {
714 rub->updateDynamicBuffer(uBuf, quint8(face) * ubufElementSize, 64, mvp.constData());
715 rub->updateDynamicBuffer(uBuf, quint8(face) * ubufElementSize + 64, 64, views[quint8(face)].constData());
716 rub->updateDynamicBuffer(uBufEnvMap, quint8(face) * ubufEnvMapElementSize, 4, &colorSpace);
717 }
718 cb->resourceUpdate(rub);
719
720 for (const auto face : QSSGRenderTextureCubeFaces) {
721 cb->beginPass(renderTargets[quint8(face)], QColor(0, 0, 0, 1), { 1.0f, 0 }, nullptr, context->commonPassFlags());
722 Q_QUICK3D_PROFILE_START(QQuick3DProfiler::Quick3DRenderPass);
723 QSSGRHICTX_STAT(context, beginRenderPass(renderTargets[quint8(face)]));
724
725 // Execute render pass
726 cb->setGraphicsPipeline(envMapPipeline);
727 cb->setVertexInput(0, 1, &vbufBinding);
728 cb->setViewport(QRhiViewport(0, 0, environmentMapSize.width(), environmentMapSize.height()));
729 QVector<QPair<int, quint32>> dynamicOffset = {
730 { 0, quint32(ubufElementSize * quint8(face)) },
731 { 2, quint32(ubufEnvMapElementSize * quint8(face) )}
732 };
733 cb->setShaderResources(envMapSrb, 2, dynamicOffset.constData());
734 Q_QUICK3D_PROFILE_START(QQuick3DProfiler::Quick3DRenderCall);
735 cb->draw(36);
737 Q_QUICK3D_PROFILE_END_WITH_PAYLOAD(QQuick3DProfiler::Quick3DRenderCall, 36llu | (1llu << 32));
738
739 cb->endPass();
740 QSSGRHICTX_STAT(context, endRenderPass());
741 Q_QUICK3D_PROFILE_END_WITH_STRING(QQuick3DProfiler::Quick3DRenderPass, 0, QSSG_RENDERPASS_NAME("environment_map", 0, face));
742 }
743 cb->debugMarkEnd();
744 Q_QUICK3D_PROFILE_END_WITH_STRING(QQuick3DProfiler::Quick3DRenderPass, 0, QByteArrayLiteral("environment_cube_generation"));
745 Q_TRACE(QSSG_renderPass_exit);
746
747 if (!isRGBE) {
748 // Generate mipmaps for envMap
749 rub = rhi->nextResourceUpdateBatch();
750 rub->generateMips(envCubeMap);
751 cb->resourceUpdate(rub);
752 }
753
754 // Phase 2: Generate the pre-filtered environment cubemap
755 cb->debugMarkBegin("Pre-filtered Environment Cubemap Generation");
756 Q_QUICK3D_PROFILE_START(QQuick3DProfiler::Quick3DRenderPass);
757 Q_TRACE(QSSG_renderPass_entry, QStringLiteral("Pre-filtered Environment Cubemap Generation"));
758 QRhiTexture *preFilteredEnvCubeMap = rhi->newTexture(cubeTextureFormat, environmentMapSize, 1, QRhiTexture::RenderTarget | QRhiTexture::CubeMap| QRhiTexture::MipMapped);
759 if (!preFilteredEnvCubeMap->create())
760 qWarning("Failed to create Pre-filtered Environment Cube Map");
761 preFilteredEnvCubeMap->setName(rtName);
762 int mipmapCount = rhi->mipLevelsForSize(environmentMapSize);
763 mipmapCount = qMin(mipmapCount, 6); // don't create more than 6 mip levels
764 QMap<int, QSize> mipLevelSizes;
765 QMap<int, QVarLengthArray<QRhiTextureRenderTarget *, 6>> renderTargetsMap;
766 QRhiRenderPassDescriptor *renderPassDescriptorPhase2 = nullptr;
767
768 // Create a renderbuffer for each mip level
769 for (int mipLevel = 0; mipLevel < mipmapCount; ++mipLevel) {
770 const QSize levelSize = QSize(environmentMapSize.width() * std::pow(0.5, mipLevel),
771 environmentMapSize.height() * std::pow(0.5, mipLevel));
772 mipLevelSizes.insert(mipLevel, levelSize);
773 // Setup Render targets (6 * mipmapCount)
774 QVarLengthArray<QRhiTextureRenderTarget *, 6> renderTargets;
775 for (const auto face : QSSGRenderTextureCubeFaces) {
776 QRhiColorAttachment att(preFilteredEnvCubeMap);
777 att.setLayer(quint8(face));
778 att.setLevel(mipLevel);
780 rtDesc.setColorAttachments({att});
781 auto renderTarget = rhi->newTextureRenderTarget(rtDesc);
782 renderTarget->setName(rtName + QByteArrayLiteral(" env prefilter mip/face: ")
784 renderTarget->setDescription(rtDesc);
785 if (!renderPassDescriptorPhase2)
786 renderPassDescriptorPhase2 = renderTarget->newCompatibleRenderPassDescriptor();
787 renderTarget->setRenderPassDescriptor(renderPassDescriptorPhase2);
788 if (!renderTarget->create())
789 qWarning("Failed to build prefilter env map render target");
790 renderTarget->deleteLater();
791 renderTargets << renderTarget;
792 }
793 renderTargetsMap.insert(mipLevel, renderTargets);
794 renderPassDescriptorPhase2->deleteLater();
795 }
796
797 // Load the prefilter shader stages
798 const auto &prefilterShaderStages = shaderCache->getBuiltInRhiShaders().getRhienvironmentmapPreFilterShader(isRGBE);
799
800 // Create a new Sampler
801 const QSSGRhiSamplerDescription samplerMipMapDesc {
808 };
809
810 QRhiSampler *envMapCubeSampler = nullptr;
811 // Only use mipmap interpoliation if not using RGBE
812 if (!isRGBE)
813 envMapCubeSampler = context->sampler(samplerMipMapDesc);
814 else
815 envMapCubeSampler = sampler;
816
817 // Reuse Vertex Buffer from phase 1
818 // Reuse UniformBuffer from phase 1 (for vertex shader)
819
820 // UniformBuffer
821 // float roughness;
822 // float resolution;
823 // float lodBias;
824 // int sampleCount;
825 // int distribution;
826
827 int ubufPrefilterElementSize = rhi->ubufAligned(20);
828 QRhiBuffer *uBufPrefilter = rhi->newBuffer(QRhiBuffer::Dynamic, QRhiBuffer::UniformBuffer, ubufPrefilterElementSize * mipmapCount);
829 uBufPrefilter->create();
830 uBufPrefilter->deleteLater();
831
832 // Shader Resource Bindings
833 QRhiShaderResourceBindings *preFilterSrb = rhi->newShaderResourceBindings();
834 preFilterSrb->setBindings({
838 });
839 preFilterSrb->create();
840 preFilterSrb->deleteLater();
841
842 // Pipeline
843 QRhiGraphicsPipeline *prefilterPipeline = rhi->newGraphicsPipeline();
844 prefilterPipeline->setCullMode(QRhiGraphicsPipeline::Front);
845 prefilterPipeline->setFrontFace(QRhiGraphicsPipeline::CCW);
846 prefilterPipeline->setDepthOp(QRhiGraphicsPipeline::LessOrEqual);
847 prefilterPipeline->setShaderStages({
848 *prefilterShaderStages->vertexStage(),
849 *prefilterShaderStages->fragmentStage()
850 });
851 // same as phase 1
852 prefilterPipeline->setVertexInputLayout(inputLayout);
853 prefilterPipeline->setShaderResourceBindings(preFilterSrb);
854 prefilterPipeline->setRenderPassDescriptor(renderPassDescriptorPhase2);
855 if (!prefilterPipeline->create()) {
856 qWarning("failed to create pre-filter env map pipeline state");
857 return false;
858 }
859 prefilterPipeline->deleteLater();
860
861 // Uniform Data
862 // set the roughness uniform buffer data
863 rub = rhi->nextResourceUpdateBatch();
864 const float resolution = environmentMapSize.width();
865 const float lodBias = 0.0f;
866 const int sampleCount = 1024;
867 for (int mipLevel = 0; mipLevel < mipmapCount; ++mipLevel) {
868 Q_ASSERT(mipmapCount - 2);
869 const float roughness = float(mipLevel) / float(mipmapCount - 2);
870 const int distribution = mipLevel == (mipmapCount - 1) ? 0 : 1; // last mip level is for irradiance
871 rub->updateDynamicBuffer(uBufPrefilter, mipLevel * ubufPrefilterElementSize, 4, &roughness);
872 rub->updateDynamicBuffer(uBufPrefilter, mipLevel * ubufPrefilterElementSize + 4, 4, &resolution);
873 rub->updateDynamicBuffer(uBufPrefilter, mipLevel * ubufPrefilterElementSize + 4 + 4, 4, &lodBias);
874 rub->updateDynamicBuffer(uBufPrefilter, mipLevel * ubufPrefilterElementSize + 4 + 4 + 4, 4, &sampleCount);
875 rub->updateDynamicBuffer(uBufPrefilter, mipLevel * ubufPrefilterElementSize + 4 + 4 + 4 + 4, 4, &distribution);
876 }
877
878 cb->resourceUpdate(rub);
879
880 // Render
881 for (int mipLevel = 0; mipLevel < mipmapCount; ++mipLevel) {
882 for (const auto face : QSSGRenderTextureCubeFaces) {
883 cb->beginPass(renderTargetsMap[mipLevel][quint8(face)], QColor(0, 0, 0, 1), { 1.0f, 0 }, nullptr, context->commonPassFlags());
884 QSSGRHICTX_STAT(context, beginRenderPass(renderTargetsMap[mipLevel][quint8(face)]));
885 Q_QUICK3D_PROFILE_START(QQuick3DProfiler::Quick3DRenderPass);
886 cb->setGraphicsPipeline(prefilterPipeline);
887 cb->setVertexInput(0, 1, &vbufBinding);
888 cb->setViewport(QRhiViewport(0, 0, mipLevelSizes[mipLevel].width(), mipLevelSizes[mipLevel].height()));
889 QVector<QPair<int, quint32>> dynamicOffsets = {
890 { 0, quint32(ubufElementSize * quint8(face)) },
891 { 2, quint32(ubufPrefilterElementSize * mipLevel) }
892 };
893 Q_QUICK3D_PROFILE_START(QQuick3DProfiler::Quick3DRenderCall);
894
895 cb->setShaderResources(preFilterSrb, 2, dynamicOffsets.constData());
896 cb->draw(36);
898 Q_QUICK3D_PROFILE_END_WITH_PAYLOAD(QQuick3DProfiler::Quick3DRenderCall, 36llu | (1llu << 32));
899 cb->endPass();
900 QSSGRHICTX_STAT(context, endRenderPass());
901 Q_QUICK3D_PROFILE_END_WITH_STRING(QQuick3DProfiler::Quick3DRenderPass, 0, QSSG_RENDERPASS_NAME("environment_map", mipLevel, face));
902 }
903 }
904 cb->debugMarkEnd();
905 Q_QUICK3D_PROFILE_END_WITH_STRING(QQuick3DProfiler::Quick3DRenderPass, 0, QByteArrayLiteral("environment_cube_prefilter"));
906 Q_TRACE(QSSG_renderPass_exit);
907
908 outTexture->m_texture = preFilteredEnvCubeMap;
909 outTexture->m_mipmapCount = mipmapCount;
910 return true;
911}
912
913bool QSSGBufferManager::createRhiTexture(QSSGRenderImageTexture &texture,
914 const QSSGLoadedTexture *inTexture,
915 MipMode inMipMode,
916 CreateRhiTextureFlags inFlags,
917 const QString &debugObjectName)
918{
920 QVarLengthArray<QRhiTextureUploadEntry, 16> textureUploads;
921 int textureSampleCount = 1;
922 QRhiTexture::Flags textureFlags;
923 int mipmapCount = 1;
924 const bool checkTransp = inFlags.testFlag(ScanForTransparency);
925 bool hasTransp = false;
926
927 const auto &context = m_contextInterface->rhiContext();
929 auto *rhi = context->rhi();
931 QSize size;
932 int depth = 0;
933 if (inTexture->format.format == QSSGRenderTextureFormat::Format::RGBE8)
934 texture.m_flags.setRgbe8(true);
935 if (!inTexture->isSRGB)
936 texture.m_flags.setLinear(true);
937 if (inMipMode == MipModeBsdf && (inTexture->data || inTexture->textureFileData.isValid())) {
938 // Before creating an environment map, check if the provided texture is a
939 // pre-baked environment map
940 if (inTexture->textureFileData.isValid() && inTexture->textureFileData.keyValueMetadata().contains("QT_IBL_BAKER_VERSION")) {
941 Q_ASSERT(inTexture->textureFileData.numFaces() == 6);
942 Q_ASSERT(inTexture->textureFileData.numLevels() >= 5);
943
944 const QTextureFileData &tex = inTexture->textureFileData;
945 rhiFormat = toRhiFormat(inTexture->format.format);
946 size = tex.size();
947 mipmapCount = tex.numLevels();
948 const int faceCount = tex.numFaces();
949 QRhiTexture *environmentCubeMap = rhi->newTexture(rhiFormat, size, 1, QRhiTexture::CubeMap | QRhiTexture::MipMapped);
950 environmentCubeMap->setName(debugObjectName.toLatin1());
951 environmentCubeMap->create();
952 for (int layer = 0; layer < faceCount; ++layer) {
953 for (int level = 0; level < mipmapCount; ++level) {
956 subDesc.setData(tex.getDataView(level, layer).toByteArray());
957 textureUploads << QRhiTextureUploadEntry { layer, level, subDesc };
958 }
959 }
960 texture.m_texture = environmentCubeMap;
961
962 QRhiTextureUploadDescription uploadDescription;
963 uploadDescription.setEntries(textureUploads.cbegin(), textureUploads.cend());
964 auto *rub = rhi->nextResourceUpdateBatch();
965 rub->uploadTexture(environmentCubeMap, uploadDescription);
966 context->commandBuffer()->resourceUpdate(rub);
967 texture.m_mipmapCount = mipmapCount;
968 rhiCtxD->registerTexture(texture.m_texture);
969 return true;
970 }
971
972 // If we get this far then we need to create an environment map at runtime.
973 if (createEnvironmentMap(inTexture, &texture, debugObjectName)) {
974 rhiCtxD->registerTexture(texture.m_texture);
975 return true;
976 } else {
977 qWarning() << "Failed to create environment map";
978 return false;
979 }
980 } else if (inTexture->textureFileData.isValid()) {
981 const QTextureFileData &tex = inTexture->textureFileData;
982 size = tex.size();
983 mipmapCount = tex.numLevels();
984
985 int numFaces = 1;
986 // Just having a container with 6 faces is not enough, we only treat it
987 // as a cubemap if it was requested to be treated as such. Otherwise
988 // only face 0 is used.
989 if (tex.numFaces() == 6 && inFlags.testFlag(CubeMap))
990 numFaces = 6;
991
992 for (int level = 0; level < tex.numLevels(); ++level) {
995 for (int face = 0; face < numFaces; ++face) {
996 subDesc.setData(tex.getDataView(level, face).toByteArray());
997 textureUploads << QRhiTextureUploadEntry{ face, level, subDesc };
998 }
999 }
1000
1001 rhiFormat = toRhiFormat(inTexture->format.format);
1002 if (checkTransp) {
1003 auto glFormat = tex.glInternalFormat() ? tex.glInternalFormat() : tex.glFormat();
1004 hasTransp = !QSGCompressedTexture::formatIsOpaque(glFormat);
1005 }
1006 } else if (inFlags.testFlag(Texture3D)) {
1007 // 3D textures are currently only setup via QQuick3DTextureData
1008 quint32 formatSize = (quint32)inTexture->format.getSizeofFormat();
1009 quint32 size2D = inTexture->width * inTexture->height * formatSize;
1010 if (inTexture->dataSizeInBytes >= (quint32)(size2D * inTexture->depth)) {
1011 size = QSize(inTexture->width, inTexture->height);
1012 depth = inTexture->depth;
1013 rhiFormat = toRhiFormat(inTexture->format.format);
1014 for (int slice = 0; slice < inTexture->depth; ++slice) {
1015 QRhiTextureSubresourceUploadDescription sliceUpload((char *)inTexture->data + slice * size2D, size2D);
1016 textureUploads << QRhiTextureUploadEntry(slice, 0, sliceUpload);
1017 }
1018 } else {
1019 qWarning() << "Texture size set larger than the data";
1020 }
1021 } else {
1023 if (!inTexture->image.isNull()) {
1024 rhiFormat = toRhiFormat(inTexture->format.format);
1025 size = inTexture->image.size();
1026 subDesc.setImage(inTexture->image);
1027 if (checkTransp)
1028 hasTransp = QImageData::get(inTexture->image)->checkForAlphaPixels();
1029 } else if (inTexture->data) {
1030 rhiFormat = toRhiFormat(inTexture->format.format);
1031 size = QSize(inTexture->width, inTexture->height);
1032 QByteArray buf(static_cast<const char *>(inTexture->data), qMax(0, int(inTexture->dataSizeInBytes)));
1033 subDesc.setData(buf);
1034 if (checkTransp)
1035 hasTransp = inTexture->scanForTransparency();
1036
1037 }
1038 subDesc.setSourceSize(size);
1039 if (!subDesc.data().isEmpty() || !subDesc.image().isNull())
1040 textureUploads << QRhiTextureUploadEntry{0, 0, subDesc};
1041 }
1042
1043 static const auto textureSizeWarning = [](QSize requestedSize, qsizetype maxSize) {
1044 return QStringLiteral("Requested texture width and height (%1x%2) exceeds the maximum allowed size (%3)!")
1045 .arg(requestedSize.width()).arg(requestedSize.height()).arg(maxSize);
1046 };
1047 static auto maxTextureSize = rhi->resourceLimit(QRhi::ResourceLimit::TextureSizeMax);
1048 const auto validTexSize = size.width() <= maxTextureSize && size.height() <= maxTextureSize;
1049 QSSG_ASSERT_X(validTexSize, qPrintable(textureSizeWarning(size, maxTextureSize)), return false);
1050
1051 bool generateMipmaps = false;
1052 if (inMipMode == MipModeEnable && mipmapCount == 1) {
1054 generateMipmaps = true;
1055 mipmapCount = rhi->mipLevelsForSize(size);
1056 }
1057
1058 if (mipmapCount > 1)
1059 textureFlags |= QRhiTexture::Flag::MipMapped;
1060
1061 if (inFlags.testFlag(CubeMap))
1062 textureFlags |= QRhiTexture::CubeMap;
1063
1064 if (textureUploads.isEmpty() || size.isEmpty() || rhiFormat == QRhiTexture::UnknownFormat) {
1065 qWarning() << "Could not load texture";
1066 return false;
1067 } else if (!rhi->isTextureFormatSupported(rhiFormat)) {
1068 qWarning() << "Unsupported texture format";
1069 return false;
1070 }
1071
1072 QRhiTexture *tex = nullptr;
1073 if (inFlags.testFlag(Texture3D) && depth > 0)
1074 tex = rhi->newTexture(rhiFormat, size.width(), size.height(), depth, textureSampleCount, textureFlags);
1075 else
1076 tex = rhi->newTexture(rhiFormat, size, textureSampleCount, textureFlags);
1077
1078 QSSG_ASSERT(tex != nullptr, return false);
1079
1080 tex->setName(debugObjectName.toLatin1());
1081 tex->create();
1082
1083 if (checkTransp)
1084 texture.m_flags.setHasTransparency(hasTransp);
1085 texture.m_texture = tex;
1086
1087 QRhiTextureUploadDescription uploadDescription;
1088 uploadDescription.setEntries(textureUploads.cbegin(), textureUploads.cend());
1089 auto *rub = rhi->nextResourceUpdateBatch(); // TODO: optimize
1090 rub->uploadTexture(tex, uploadDescription);
1091 if (generateMipmaps)
1092 rub->generateMips(tex);
1093 context->commandBuffer()->resourceUpdate(rub);
1094
1095 texture.m_mipmapCount = mipmapCount;
1096
1097 rhiCtxD->registerTexture(texture.m_texture); // owned by the QSSGRhiContext from here on
1098 return true;
1099}
1100
1102{
1103 QByteArray theName = primitive.toUtf8();
1104 for (size_t idx = 0; idx < nPrimitives; ++idx) {
1105 if (primitives[idx].primitive == theName) {
1107 pathBuilder += QLatin1String(primitives[idx].file);
1108 return pathBuilder;
1109 }
1110 }
1111 return {};
1112}
1113
1115{
1116 return &meshBufferMutex;
1117}
1118
1119QSSGMesh::Mesh QSSGBufferManager::loadPrimitive(const QString &inRelativePath)
1120{
1121 QString path = primitivePath(inRelativePath);
1122 const quint32 id = 1;
1123 QSharedPointer<QIODevice> device(QSSGInputUtil::getStreamForFile(path));
1124 if (device) {
1126 if (mesh.isValid())
1127 return mesh;
1128 }
1129
1130 qCCritical(INTERNAL_ERROR, "Unable to find mesh primitive %s", qPrintable(path));
1131 return QSSGMesh::Mesh();
1132}
1133
1135{
1137 if (model->hasLightmap()) {
1138 options.wantsLightmapUVs = true;
1139 options.lightmapBaseResolution = model->lightmapBaseResolution;
1140 }
1141
1142 QSSGRenderMesh *theMesh = nullptr;
1143 if (model->meshPath.isNull() && model->geometry) {
1144 theMesh = loadRenderMesh(model->geometry, options);
1145 } else {
1146 if (model->hasLightmap()) {
1149 }
1150 theMesh = loadRenderMesh(model->meshPath, options);
1151 }
1152
1153 return theMesh;
1154}
1155
1157{
1158 QSSGBounds3 retval;
1159 // Custom Geometry
1160 if (model->geometry) {
1161 retval = QSSGBounds3(model->geometry->boundsMin(), model->geometry->boundsMax());
1162 } else if (!model->meshPath.isNull()){
1163 // Check if the Mesh is already loaded
1164 QSSGRenderMesh *theMesh = nullptr;
1165 auto meshItr = meshMap.constFind(model->meshPath);
1166 if (meshItr != meshMap.cend())
1167 theMesh = meshItr.value().mesh;
1168 if (theMesh) {
1169 // The mesh was already loaded, so calculate the
1170 // bounds from subsets of the QSSGRenderMesh
1171 const auto &subSets = theMesh->subsets;
1172 for (const auto &subSet : subSets)
1173 retval.include(subSet.bounds);
1174 } else {
1175 // The model has not been loaded yet, load it without uploading the geometry
1176 // TODO: Try to do this without loading the whole mesh struct
1177 QSSGMesh::Mesh mesh = loadMeshData(model->meshPath);
1178 if (mesh.isValid()) {
1179 auto const &subsets = mesh.subsets();
1180 for (const auto &subset : subsets)
1181 retval.include(QSSGBounds3(subset.bounds.min, subset.bounds.max));
1182 }
1183 }
1184 }
1185 return retval;
1186}
1187
1188QSSGRenderMesh *QSSGBufferManager::createRenderMesh(const QSSGMesh::Mesh &mesh, const QString &debugObjectName)
1189{
1191 QSSGRenderWinding(mesh.winding()));
1192 const QSSGMesh::Mesh::VertexBuffer vertexBuffer = mesh.vertexBuffer();
1193 const QSSGMesh::Mesh::IndexBuffer indexBuffer = mesh.indexBuffer();
1194 const QSSGMesh::Mesh::TargetBuffer targetBuffer = mesh.targetBuffer();
1195
1198 if (!indexBuffer.data.isEmpty()) {
1199 indexBufComponentType = QSSGRenderComponentType(indexBuffer.componentType);
1200 const quint32 sizeofType = quint32(QSSGBaseTypeHelpers::getSizeOfType(indexBufComponentType));
1201 if (sizeofType == 2 || sizeofType == 4) {
1202 // Ensure type is unsigned; else things will fail in rendering pipeline.
1203 if (indexBufComponentType == QSSGRenderComponentType::Int16)
1204 indexBufComponentType = QSSGRenderComponentType::UnsignedInt16;
1205 if (indexBufComponentType == QSSGRenderComponentType::Int32)
1206 indexBufComponentType = QSSGRenderComponentType::UnsignedInt32;
1207 rhiIndexFormat = indexBufComponentType == QSSGRenderComponentType::UnsignedInt32
1209 } else {
1210 Q_ASSERT(false);
1211 }
1212 }
1213
1214 struct {
1215 QSSGRhiBufferPtr vertexBuffer;
1216 QSSGRhiBufferPtr indexBuffer;
1218 QRhiTexture *targetsTexture = nullptr;
1219 } rhi;
1220
1221 QRhiResourceUpdateBatch *rub = meshBufferUpdateBatch();
1222 const auto &context = m_contextInterface->rhiContext();
1224 rhi.vertexBuffer = std::make_shared<QSSGRhiBuffer>(*context.get(),
1227 vertexBuffer.stride,
1228 vertexBuffer.data.size());
1229 rhi.vertexBuffer->buffer()->setName(debugObjectName.toLatin1()); // this is what shows up in DebugView
1230 rub->uploadStaticBuffer(rhi.vertexBuffer->buffer(), vertexBuffer.data);
1231
1232 if (!indexBuffer.data.isEmpty()) {
1233 rhi.indexBuffer = std::make_shared<QSSGRhiBuffer>(*context.get(),
1236 0,
1237 indexBuffer.data.size(),
1238 rhiIndexFormat);
1239 rub->uploadStaticBuffer(rhi.indexBuffer->buffer(), indexBuffer.data);
1240 }
1241
1242 if (!targetBuffer.data.isEmpty()) {
1243 const int arraySize = targetBuffer.entries.size() * targetBuffer.numTargets;
1244 const int numTexels = (targetBuffer.data.size() / arraySize) >> 4; // byte size to vec4
1245 const int texWidth = qCeil(qSqrt(numTexels));
1246 const QSize texSize(texWidth, texWidth);
1247 if (!rhi.targetsTexture) {
1248 rhi.targetsTexture = context->rhi()->newTextureArray(QRhiTexture::RGBA32F, arraySize, texSize);
1249 rhi.targetsTexture->create();
1250 rhiCtxD->registerTexture(rhi.targetsTexture);
1251 } else if (rhi.targetsTexture->pixelSize() != texSize
1252 || rhi.targetsTexture->arraySize() != arraySize) {
1253 rhi.targetsTexture->setPixelSize(texSize);
1254 rhi.targetsTexture->setArraySize(arraySize);
1255 rhi.targetsTexture->create();
1256 }
1257
1258 const quint32 layerSize = texWidth * texWidth * 4 * 4;
1259 for (int arrayId = 0; arrayId < arraySize; ++arrayId) {
1260 QRhiTextureSubresourceUploadDescription targetDesc(targetBuffer.data + arrayId * layerSize, layerSize);
1262 rub->uploadTexture(rhi.targetsTexture, desc);
1263 }
1264
1265 for (quint32 entryIdx = 0, entryEnd = targetBuffer.entries.size(); entryIdx < entryEnd; ++entryIdx) {
1266 const char *nameStr = targetBuffer.entries[entryIdx].name.constData();
1267 if (!strcmp(nameStr, QSSGMesh::MeshInternal::getPositionAttrName())) {
1268 rhi.ia.targetOffsets[QSSGRhiInputAssemblerState::PositionSemantic] = entryIdx * targetBuffer.numTargets;
1269 } else if (!strcmp(nameStr, QSSGMesh::MeshInternal::getNormalAttrName())) {
1270 rhi.ia.targetOffsets[QSSGRhiInputAssemblerState::NormalSemantic] = entryIdx * targetBuffer.numTargets;
1271 } else if (!strcmp(nameStr, QSSGMesh::MeshInternal::getUV0AttrName())) {
1272 rhi.ia.targetOffsets[QSSGRhiInputAssemblerState::TexCoord0Semantic] = entryIdx * targetBuffer.numTargets;
1273 } else if (!strcmp(nameStr, QSSGMesh::MeshInternal::getUV1AttrName())) {
1274 rhi.ia.targetOffsets[QSSGRhiInputAssemblerState::TexCoord1Semantic] = entryIdx * targetBuffer.numTargets;
1275 } else if (!strcmp(nameStr, QSSGMesh::MeshInternal::getTexTanAttrName())) {
1276 rhi.ia.targetOffsets[QSSGRhiInputAssemblerState::TangentSemantic] = entryIdx * targetBuffer.numTargets;
1277 } else if (!strcmp(nameStr, QSSGMesh::MeshInternal::getTexBinormalAttrName())) {
1278 rhi.ia.targetOffsets[QSSGRhiInputAssemblerState::BinormalSemantic] = entryIdx * targetBuffer.numTargets;
1279 } else if (!strcmp(nameStr, QSSGMesh::MeshInternal::getColorAttrName())) {
1280 rhi.ia.targetOffsets[QSSGRhiInputAssemblerState::ColorSemantic] = entryIdx * targetBuffer.numTargets;
1281 }
1282 }
1283 rhi.ia.targetCount = targetBuffer.numTargets;
1284 } else if (rhi.targetsTexture) {
1285 rhiCtxD->releaseTexture(rhi.targetsTexture);
1286 rhi.targetsTexture = nullptr;
1287 rhi.ia.targetOffsets = { UINT8_MAX, UINT8_MAX, UINT8_MAX, UINT8_MAX,
1288 UINT8_MAX, UINT8_MAX, UINT8_MAX };
1289 rhi.ia.targetCount = 0;
1290 }
1291
1292 QVector<QSSGRenderVertexBufferEntry> entryBuffer;
1293 entryBuffer.resize(vertexBuffer.entries.size());
1294 for (quint32 entryIdx = 0, entryEnd = vertexBuffer.entries.size(); entryIdx < entryEnd; ++entryIdx)
1295 entryBuffer[entryIdx] = vertexBuffer.entries[entryIdx].toRenderVertexBufferEntry();
1296
1297 QVarLengthArray<QRhiVertexInputAttribute, 4> inputAttrs;
1298 for (quint32 entryIdx = 0, entryEnd = entryBuffer.size(); entryIdx < entryEnd; ++entryIdx) {
1299 const QSSGRenderVertexBufferEntry &vbe(entryBuffer[entryIdx]);
1300 const int binding = 0;
1301 const int location = 0; // for now, will be resolved later, hence the separate inputLayoutInputNames list
1302 const QRhiVertexInputAttribute::Format format = QSSGRhiHelpers::toVertexInputFormat(vbe.m_componentType, vbe.m_numComponents);
1303 const int offset = int(vbe.m_firstItemOffset);
1304
1305 bool ok = true;
1306 const char *nameStr = vbe.m_name.constData();
1307 if (!strcmp(nameStr, QSSGMesh::MeshInternal::getPositionAttrName())) {
1308 rhi.ia.inputs << QSSGRhiInputAssemblerState::PositionSemantic;
1309 } else if (!strcmp(nameStr, QSSGMesh::MeshInternal::getNormalAttrName())) {
1310 rhi.ia.inputs << QSSGRhiInputAssemblerState::NormalSemantic;
1311 } else if (!strcmp(nameStr, QSSGMesh::MeshInternal::getUV0AttrName())) {
1312 rhi.ia.inputs << QSSGRhiInputAssemblerState::TexCoord0Semantic;
1313 } else if (!strcmp(nameStr, QSSGMesh::MeshInternal::getUV1AttrName())) {
1314 rhi.ia.inputs << QSSGRhiInputAssemblerState::TexCoord1Semantic;
1315 } else if (!strcmp(nameStr, QSSGMesh::MeshInternal::getLightmapUVAttrName())) {
1316 rhi.ia.inputs << QSSGRhiInputAssemblerState::TexCoordLightmapSemantic;
1317 } else if (!strcmp(nameStr, QSSGMesh::MeshInternal::getTexTanAttrName())) {
1318 rhi.ia.inputs << QSSGRhiInputAssemblerState::TangentSemantic;
1319 } else if (!strcmp(nameStr, QSSGMesh::MeshInternal::getTexBinormalAttrName())) {
1320 rhi.ia.inputs << QSSGRhiInputAssemblerState::BinormalSemantic;
1321 } else if (!strcmp(nameStr, QSSGMesh::MeshInternal::getColorAttrName())) {
1322 rhi.ia.inputs << QSSGRhiInputAssemblerState::ColorSemantic;
1323 } else if (!strcmp(nameStr, QSSGMesh::MeshInternal::getJointAttrName())) {
1324 rhi.ia.inputs << QSSGRhiInputAssemblerState::JointSemantic;
1325 } else if (!strcmp(nameStr, QSSGMesh::MeshInternal::getWeightAttrName())) {
1326 rhi.ia.inputs << QSSGRhiInputAssemblerState::WeightSemantic;
1327 } else {
1328 qWarning("Unknown vertex input %s in mesh", nameStr);
1329 ok = false;
1330 }
1331 if (ok) {
1332 QRhiVertexInputAttribute inputAttr(binding, location, format, offset);
1333 inputAttrs.append(inputAttr);
1334 }
1335 }
1336 rhi.ia.inputLayout.setAttributes(inputAttrs.cbegin(), inputAttrs.cend());
1337 rhi.ia.inputLayout.setBindings({ vertexBuffer.stride });
1338 rhi.ia.topology = QSSGRhiHelpers::toTopology(QSSGRenderDrawMode(mesh.drawMode()));
1339
1340 if (rhi.ia.topology == QRhiGraphicsPipeline::TriangleFan && !context->rhi()->isFeatureSupported(QRhi::TriangleFanTopology))
1341 qWarning("Mesh topology is TriangleFan but this is not supported with the active graphics API. Rendering will be incorrect.");
1342
1343 QVector<QSSGMesh::Mesh::Subset> meshSubsets = mesh.subsets();
1344 for (quint32 subsetIdx = 0, subsetEnd = meshSubsets.size(); subsetIdx < subsetEnd; ++subsetIdx) {
1345 QSSGRenderSubset subset;
1346 const QSSGMesh::Mesh::Subset &source(meshSubsets[subsetIdx]);
1347 subset.bounds = QSSGBounds3(source.bounds.min, source.bounds.max);
1348 subset.count = source.count;
1349 subset.offset = source.offset;
1350 for (auto &lod : source.lods)
1351 subset.lods.append(QSSGRenderSubset::Lod({lod.count, lod.offset, lod.distance}));
1352
1353
1354 if (rhi.vertexBuffer) {
1355 subset.rhi.vertexBuffer = rhi.vertexBuffer;
1356 subset.rhi.ia = rhi.ia;
1357 }
1358 if (rhi.indexBuffer)
1359 subset.rhi.indexBuffer = rhi.indexBuffer;
1360 if (rhi.targetsTexture)
1361 subset.rhi.targetsTexture = rhi.targetsTexture;
1362
1363 newMesh->subsets.push_back(subset);
1364 }
1365
1366 if (!meshSubsets.isEmpty())
1367 newMesh->lightmapSizeHint = meshSubsets.first().lightmapSizeHint;
1368
1369 return newMesh;
1370}
1371
1373{
1374 QMutexLocker meshMutexLocker(&meshBufferMutex);
1375 const auto meshItr = customMeshMap.constFind(geometry);
1376 if (meshItr != customMeshMap.cend()) {
1378 qDebug() << "- releaseGeometry: " << geometry << currentLayer;
1379 Q_QUICK3D_PROFILE_START(QQuick3DProfiler::Quick3DCustomMeshLoad);
1380 Q_TRACE_SCOPE(QSSG_customMeshUnload);
1381 decreaseMemoryStat(meshItr.value().mesh);
1382 QSSGRhiContextPrivate *rhiCtxD = QSSGRhiContextPrivate::get(m_contextInterface->rhiContext().get());
1383 rhiCtxD->releaseMesh(meshItr.value().mesh);
1384 customMeshMap.erase(meshItr);
1385 Q_QUICK3D_PROFILE_END_WITH_ID(QQuick3DProfiler::Quick3DCustomMeshLoad,
1386 stats.meshDataSize, geometry->profilingId);
1387 }
1388}
1389
1391{
1392 QVarLengthArray<CustomImageCacheKey, 4> keys;
1393 for (auto it = customTextureMap.cbegin(), end = customTextureMap.cend(); it != end; ++it) {
1394 if (it.key().data == data)
1395 keys.append(it.key());
1396 }
1397 for (const CustomImageCacheKey &key : keys)
1399}
1400
1402{
1403 const auto textureDataItr = customTextureMap.constFind(key);
1404 if (textureDataItr != customTextureMap.cend()) {
1405 auto rhiTexture = textureDataItr.value().renderImageTexture.m_texture;
1406 if (rhiTexture) {
1408 qDebug() << "- releaseTextureData: " << rhiTexture << currentLayer;
1409 Q_QUICK3D_PROFILE_START(QQuick3DProfiler::Quick3DTextureLoad);
1410 Q_TRACE_SCOPE(QSSG_textureUnload);
1411 decreaseMemoryStat(rhiTexture);
1412 QSSGRhiContextPrivate *rhiCtxD = QSSGRhiContextPrivate::get(m_contextInterface->rhiContext().get());
1413 rhiCtxD->releaseTexture(rhiTexture);
1414 Q_QUICK3D_PROFILE_END_WITH_ID(QQuick3DProfiler::Quick3DTextureLoad,
1415 stats.imageDataSize, 0);
1416
1417 }
1418 customTextureMap.erase(textureDataItr);
1419 }
1420}
1421
1423{
1424 renderExtensionTexture.remove(&rext);
1425}
1426
1427void QSSGBufferManager::releaseMesh(const QSSGRenderPath &inSourcePath)
1428{
1429 QMutexLocker meshMutexLocker(&meshBufferMutex);
1430 const auto meshItr = meshMap.constFind(inSourcePath);
1431 if (meshItr != meshMap.cend()) {
1433 qDebug() << "- releaseMesh: " << inSourcePath.path() << currentLayer;
1434 Q_QUICK3D_PROFILE_START(QQuick3DProfiler::Quick3DMeshLoad);
1435 Q_TRACE_SCOPE(QSSG_meshUnload);
1436 decreaseMemoryStat(meshItr.value().mesh);
1437 QSSGRhiContextPrivate *rhiCtxD = QSSGRhiContextPrivate::get(m_contextInterface->rhiContext().get());
1438 rhiCtxD->releaseMesh(meshItr.value().mesh);
1439 meshMap.erase(meshItr);
1440 Q_QUICK3D_PROFILE_END_WITH_STRING(QQuick3DProfiler::Quick3DMeshLoad,
1441 stats.meshDataSize, inSourcePath.path().toUtf8());
1442 }
1443}
1444
1445void QSSGBufferManager::releaseImage(const ImageCacheKey &key)
1446{
1447 const auto imageItr = imageMap.constFind(key);
1448 if (imageItr != imageMap.cend()) {
1449 auto rhiTexture = imageItr.value().renderImageTexture.m_texture;
1450 if (rhiTexture) {
1452 qDebug() << "- releaseTexture: " << key.path.path() << currentLayer;
1453 Q_QUICK3D_PROFILE_START(QQuick3DProfiler::Quick3DTextureLoad);
1454 Q_TRACE_SCOPE(QSSG_textureUnload);
1455 decreaseMemoryStat(rhiTexture);
1456 QSSGRhiContextPrivate *rhiCtxD = QSSGRhiContextPrivate::get(m_contextInterface->rhiContext().get());
1457 rhiCtxD->releaseTexture(rhiTexture);
1458 Q_QUICK3D_PROFILE_END_WITH_STRING(QQuick3DProfiler::Quick3DTextureLoad,
1459 stats.imageDataSize, key.path.path().toUtf8());
1460 }
1461 imageMap.erase(imageItr);
1462 }
1463}
1464
1466{
1467 Q_UNUSED(currentLayer);
1468
1469 QSSGRhiContextPrivate *rhiCtxD = QSSGRhiContextPrivate::get(m_contextInterface->rhiContext().get());
1470
1471 // Don't cleanup if
1472 if (frameId == frameCleanupIndex)
1473 return;
1474
1475 auto isUnused = [] (const QHash<QSSGRenderLayer*, uint32_t> &usages) -> bool {
1476 for (const auto &value : std::as_const(usages))
1477 if (value != 0)
1478 return false;
1479 return true;
1480 };
1481
1482 {
1483 QMutexLocker meshMutexLocker(&meshBufferMutex);
1484 // Meshes (by path)
1485 auto meshIterator = meshMap.cbegin();
1486 while (meshIterator != meshMap.cend()) {
1487 if (isUnused(meshIterator.value().usageCounts)) {
1489 qDebug() << "- releaseGeometry: " << meshIterator.key().path() << currentLayer;
1490 decreaseMemoryStat(meshIterator.value().mesh);
1491 rhiCtxD->releaseMesh(meshIterator.value().mesh);
1492 meshIterator = meshMap.erase(meshIterator);
1493 } else {
1494 ++meshIterator;
1495 }
1496 }
1497
1498 // Meshes (custom)
1499 auto customMeshIterator = customMeshMap.cbegin();
1500 while (customMeshIterator != customMeshMap.cend()) {
1501 if (isUnused(customMeshIterator.value().usageCounts)) {
1503 qDebug() << "- releaseGeometry: " << customMeshIterator.key() << currentLayer;
1504 decreaseMemoryStat(customMeshIterator.value().mesh);
1505 rhiCtxD->releaseMesh(customMeshIterator.value().mesh);
1506 customMeshIterator = customMeshMap.erase(customMeshIterator);
1507 } else {
1508 ++customMeshIterator;
1509 }
1510 }
1511 }
1512
1513 // SG Textures
1514 auto sgIterator = qsgImageMap.cbegin();
1515 while (sgIterator != qsgImageMap.cend()) {
1516 if (isUnused(sgIterator.value().usageCounts)) {
1517 // Texture is no longer uses, so stop tracking
1518 // We do not need to delete/release the texture
1519 // because we don't own it.
1520 sgIterator = qsgImageMap.erase(sgIterator);
1521 } else {
1522 ++sgIterator;
1523 }
1524 }
1525
1526 // Images
1527 auto imageKeyIterator = imageMap.cbegin();
1528 while (imageKeyIterator != imageMap.cend()) {
1529 if (isUnused(imageKeyIterator.value().usageCounts)) {
1530 auto rhiTexture = imageKeyIterator.value().renderImageTexture.m_texture;
1531 if (rhiTexture) {
1533 qDebug() << "- releaseTexture: " << imageKeyIterator.key().path.path() << currentLayer;
1534 decreaseMemoryStat(rhiTexture);
1535 rhiCtxD->releaseTexture(rhiTexture);
1536 }
1537 imageKeyIterator = imageMap.erase(imageKeyIterator);
1538 } else {
1539 ++imageKeyIterator;
1540 }
1541 }
1542
1543 // Custom Texture Data
1544 auto textureDataIterator = customTextureMap.cbegin();
1545 while (textureDataIterator != customTextureMap.cend()) {
1546 if (isUnused(textureDataIterator.value().usageCounts)) {
1547 auto rhiTexture = textureDataIterator.value().renderImageTexture.m_texture;
1548 if (rhiTexture) {
1550 qDebug() << "- releaseTextureData: " << rhiTexture << currentLayer;
1551 decreaseMemoryStat(rhiTexture);
1552 rhiCtxD->releaseTexture(rhiTexture);
1553 }
1554 textureDataIterator = customTextureMap.erase(textureDataIterator);
1555 } else {
1556 ++textureDataIterator;
1557 }
1558 }
1559
1560 // Textures from render extensions
1561 auto renderExtensionTextureKeyIterator = renderExtensionTexture.cbegin();
1562 while (renderExtensionTextureKeyIterator != renderExtensionTexture.cend()) {
1563 if (isUnused(renderExtensionTextureKeyIterator.value().usageCounts)) {
1564 auto rhiTexture = renderExtensionTextureKeyIterator.value().renderImageTexture.m_texture;
1565 if (rhiTexture) {
1567 qDebug() << "- releaseTexture: " << (*renderExtensionTextureKeyIterator).renderImageTexture.m_texture << currentLayer;
1568 decreaseMemoryStat(rhiTexture);
1569 // NOTE: We don't own the texture, it's own by the user, so don't release.
1570 }
1571 renderExtensionTextureKeyIterator = renderExtensionTexture.erase(renderExtensionTextureKeyIterator);
1572 } else {
1573 ++renderExtensionTextureKeyIterator;
1574 }
1575 }
1576
1577 // Resource Tracking Debug Code
1578 frameCleanupIndex = frameId;
1580 qDebug() << "QSSGBufferManager::cleanupUnreferencedBuffers()" << this << "frame:" << frameCleanupIndex << currentLayer;
1581 qDebug() << "Textures(by path): " << imageMap.count();
1582 qDebug() << "Textures(custom): " << customTextureMap.count();
1583 qDebug() << "Textures(Extension)" << renderExtensionTexture.count();
1584 qDebug() << "Textures(qsg): " << qsgImageMap.count();
1585 qDebug() << "Geometry(by path): " << meshMap.count();
1586 qDebug() << "Geometry(custom): " << customMeshMap.count();
1587 }
1588}
1589
1591{
1592 currentLayer = layer;
1593 if (frameResetIndex == frameId)
1594 return;
1595
1596 // SG Textures
1597 for (auto &imageData : qsgImageMap)
1598 imageData.usageCounts[layer] = 0;
1599
1600 // Images
1601 for (auto &imageData : imageMap)
1602 imageData.usageCounts[layer] = 0;
1603
1604 // TextureDatas
1605 for (auto &imageData : customTextureMap)
1606 imageData.usageCounts[layer] = 0;
1607 // Meshes
1608 for (auto &meshData : meshMap)
1609 meshData.usageCounts[layer] = 0;
1610
1611 // Meshes (custom)
1612 for (auto &meshData : customMeshMap)
1613 meshData.usageCounts[layer] = 0;
1614
1615 // Textures from render extensions
1616 // NOTE: We retain the usage count as 1 as long as there is a texture
1617 // registered by a extension.
1618 for (auto &retData : renderExtensionTexture) {
1619 const bool hasTexture = (retData.renderImageTexture.m_texture != nullptr);
1620 retData.usageCounts[layer] = uint32_t(hasTexture) * 1;
1621 }
1622
1623 frameResetIndex = frameId;
1624}
1625
1626void QSSGBufferManager::registerMeshData(const QString &assetId, const QVector<QSSGMesh::Mesh> &meshData)
1627{
1628 auto it = g_assetMeshMap->find(assetId);
1629 if (it != g_assetMeshMap->end())
1630 ++it->ref;
1631 else
1632 g_assetMeshMap->insert(assetId, { meshData, 1 });
1633}
1634
1636{
1637 auto it = g_assetMeshMap->find(assetId);
1638 if (it != g_assetMeshMap->end() && (--it->ref == 0))
1639 g_assetMeshMap->erase(AssetMeshMap::const_iterator(it));
1640}
1641
1642QSSGRenderMesh *QSSGBufferManager::loadRenderMesh(const QSSGRenderPath &inMeshPath, QSSGMeshProcessingOptions options)
1643{
1644 if (inMeshPath.isNull())
1645 return nullptr;
1646
1647 // check if it is already loaded
1648 auto meshItr = meshMap.find(inMeshPath);
1649 if (meshItr != meshMap.cend()) {
1650 if (options.isCompatible(meshItr.value().options)) {
1651 meshItr.value().usageCounts[currentLayer]++;
1652 return meshItr.value().mesh;
1653 } else {
1654 // Re-Insert the mesh with a new name and a "zero" usage count, this will cause the
1655 // mesh to be released before the next frame starts.
1656 auto *mesh = meshItr->mesh;
1657 meshMap.erase(meshItr);
1658 meshMap.insert(QSSGRenderPath(inMeshPath.path() + u"@reaped"), { mesh, {{currentLayer, 0}}, 0, {} });
1659 }
1660 }
1661
1662 Q_QUICK3D_PROFILE_START(QQuick3DProfiler::Quick3DMeshLoad);
1663 Q_TRACE_SCOPE(QSSG_meshLoadPath, inMeshPath.path());
1664
1666 QString resultSourcePath;
1667
1668 if (options.wantsLightmapUVs && !options.meshFileOverride.isEmpty()) {
1669 // So now we have a hint, e.g "qlm_xxxx.mesh" that says that if that
1670 // file exists, then we should prefer that because it has the lightmap
1671 // UV unwrapping and associated rebuilding already done.
1672 if (QFile::exists(options.meshFileOverride)) {
1673 resultSourcePath = options.meshFileOverride;
1674 result = loadMeshData(QSSGRenderPath(options.meshFileOverride));
1675 }
1676 }
1677
1678 if (!result.isValid()) {
1679 resultSourcePath = inMeshPath.path();
1680 result = loadMeshData(inMeshPath);
1681 }
1682
1683 if (!result.isValid()) {
1684 qCWarning(WARNING, "Failed to load mesh: %s", qPrintable(inMeshPath.path()));
1685 Q_QUICK3D_PROFILE_END_WITH_PAYLOAD(QQuick3DProfiler::Quick3DMeshLoad,
1686 stats.meshDataSize);
1687 return nullptr;
1688 }
1690 qDebug() << "+ uploadGeometry: " << inMeshPath.path() << currentLayer;
1691
1692 if (options.wantsLightmapUVs) {
1693 // Does nothing if the lightmap uv attribute is already present,
1694 // otherwise this is a potentially expensive step that will do UV
1695 // unwrapping and rebuild much of the mesh's data.
1696 result.createLightmapUVChannel(options.lightmapBaseResolution);
1697 }
1698
1699 auto ret = createRenderMesh(result, QFileInfo(resultSourcePath).fileName());
1700 meshMap.insert(inMeshPath, { ret, {{currentLayer, 1}}, 0, options });
1701 QSSGRhiContextPrivate *rhiCtxD = QSSGRhiContextPrivate::get(m_contextInterface->rhiContext().get());
1702 rhiCtxD->registerMesh(ret);
1703 increaseMemoryStat(ret);
1704 Q_QUICK3D_PROFILE_END_WITH_STRING(QQuick3DProfiler::Quick3DMeshLoad,
1705 stats.meshDataSize, inMeshPath.path().toUtf8());
1706 return ret;
1707}
1708
1709QSSGRenderMesh *QSSGBufferManager::loadRenderMesh(QSSGRenderGeometry *geometry, QSSGMeshProcessingOptions options)
1710{
1711 QSSGRhiContextPrivate *rhiCtxD = QSSGRhiContextPrivate::get(m_contextInterface->rhiContext().get());
1712
1713 auto meshIterator = customMeshMap.find(geometry);
1714 if (meshIterator == customMeshMap.end()) {
1715 meshIterator = customMeshMap.insert(geometry, MeshData());
1716 } else if (geometry->generationId() != meshIterator->generationId || !options.isCompatible(meshIterator->options)) {
1717 // Release old data
1718 releaseGeometry(geometry);
1719 meshIterator = customMeshMap.insert(geometry, MeshData());
1720 } else {
1721 // An up-to-date mesh was found
1722 meshIterator.value().usageCounts[currentLayer]++;
1723 return meshIterator.value().mesh;
1724 }
1725
1726 Q_QUICK3D_PROFILE_START(QQuick3DProfiler::Quick3DCustomMeshLoad);
1727 Q_TRACE_SCOPE(QSSG_customMeshLoad);
1728
1729 if (!geometry->meshData().m_vertexBuffer.isEmpty()) {
1730 // Mesh data needs to be loaded
1731 QString error;
1733 if (mesh.isValid()) {
1735 qDebug() << "+ uploadGeometry: " << geometry << currentLayer;
1736 if (options.wantsLightmapUVs) {
1737 // Custom geometry will get a dynamically generated lightmap UV
1738 // channel, unless attr_lightmapuv already exists.
1740 }
1741
1742 meshIterator->mesh = createRenderMesh(mesh, geometry->debugObjectName);
1743 meshIterator->usageCounts[currentLayer] = 1;
1744 meshIterator->generationId = geometry->generationId();
1745 meshIterator->options = options;
1746 rhiCtxD->registerMesh(meshIterator->mesh);
1747 increaseMemoryStat(meshIterator->mesh);
1748 } else {
1749 qWarning("Mesh building failed: %s", qPrintable(error));
1750 }
1751 }
1752 // else an empty mesh is not an error, leave the QSSGRenderMesh null, it will not be rendered then
1753
1754 Q_QUICK3D_PROFILE_END_WITH_ID(QQuick3DProfiler::Quick3DCustomMeshLoad,
1755 stats.meshDataSize, geometry->profilingId);
1756 return meshIterator->mesh;
1757}
1758
1759std::unique_ptr<QSSGMeshBVH> QSSGBufferManager::loadMeshBVH(const QSSGRenderPath &inSourcePath)
1760{
1761 const QSSGMesh::Mesh mesh = loadMeshData(inSourcePath);
1762 if (!mesh.isValid()) {
1763 qCWarning(WARNING, "Failed to load mesh: %s", qPrintable(inSourcePath.path()));
1764 return nullptr;
1765 }
1766 QSSGMeshBVHBuilder meshBVHBuilder(mesh);
1767 return meshBVHBuilder.buildTree();
1768}
1769
1770std::unique_ptr<QSSGMeshBVH> QSSGBufferManager::loadMeshBVH(QSSGRenderGeometry *geometry)
1771{
1772 if (!geometry)
1773 return nullptr;
1774
1775 // We only support generating a BVH with Triangle primitives
1776 if (geometry->primitiveType() != QSSGMesh::Mesh::DrawMode::Triangles)
1777 return nullptr;
1778
1779 // Build BVH
1780 bool hasIndexBuffer = false;
1782 bool hasUV = false;
1783 int uvOffset = -1;
1784 int posOffset = -1;
1785
1786 for (int i = 0; i < geometry->attributeCount(); ++i) {
1787 auto attribute = geometry->attribute(i);
1789 posOffset = attribute.offset;
1791 hasUV = true;
1792 uvOffset = attribute.offset;
1793 } else if (!hasUV && attribute.semantic == QSSGMesh::RuntimeMeshData::Attribute::TexCoord1Semantic) {
1794 hasUV = true;
1795 uvOffset = attribute.offset;
1797 hasIndexBuffer = true;
1798 if (attribute.componentType == QSSGMesh::Mesh::ComponentType::Int16)
1799 indexBufferFormat = QSSGRenderComponentType::Int16;
1800 else if (attribute.componentType == QSSGMesh::Mesh::ComponentType::Int32)
1801 indexBufferFormat = QSSGRenderComponentType::Int32;
1802 }
1803 }
1804
1805 QSSGMeshBVHBuilder meshBVHBuilder(geometry->vertexBuffer(),
1806 geometry->stride(),
1807 posOffset,
1808 hasUV,
1809 uvOffset,
1810 hasIndexBuffer,
1811 geometry->indexBuffer(),
1812 indexBufferFormat);
1813 return meshBVHBuilder.buildTree();
1814}
1815
1817{
1819
1820 // check to see if this is a primitive mesh
1821 if (inMeshPath.path().startsWith(QChar::fromLatin1('#')))
1822 result = loadPrimitive(inMeshPath.path());
1823
1824 // check if this is an imported mesh. Expected path format: !name@path_to_asset
1825 if (!result.isValid() && inMeshPath.path().startsWith(u'!')) {
1826 const auto &[idx, assetId] = splitRuntimeMeshPath(inMeshPath);
1827 if (idx >= 0) {
1828 const auto ait = g_assetMeshMap->constFind(assetId);
1829 if (ait != g_assetMeshMap->constEnd()) {
1830 const auto &meshes = ait->meshes;
1831 if (idx < meshes.size())
1832 result = ait->meshes.at(idx);
1833 }
1834 } else {
1835 qWarning("Unexpected mesh path!");
1836 }
1837 }
1838
1839 // Attempt a load from the filesystem otherwise.
1840 if (!result.isValid()) {
1841 QString pathBuilder = inMeshPath.path();
1842 int poundIndex = pathBuilder.lastIndexOf(QChar::fromLatin1('#'));
1843 quint32 id = 0;
1844 if (poundIndex != -1) {
1845 id = QStringView(pathBuilder).mid(poundIndex + 1).toUInt();
1846 pathBuilder = pathBuilder.left(poundIndex);
1847 }
1848 if (!pathBuilder.isEmpty()) {
1849 QSharedPointer<QIODevice> device(QSSGInputUtil::getStreamForFile(pathBuilder));
1850 if (device) {
1852 if (mesh.isValid())
1853 result = mesh;
1854 }
1855 }
1856 }
1857
1858 return result;
1859}
1860
1862{
1863 QString error;
1865 if (!mesh.isValid())
1866 qWarning("loadMeshDataForCustomMeshUncached failed: %s", qPrintable(error));
1867
1868 return mesh;
1869}
1870
1873{
1874 if (texture) {
1875 texture->flags().testFlag(QRhiTexture::Flag::MipMapped);
1876 const auto mipLevels = QRhi::mipLevelsForSize(texture->pixelSize());
1878 const bool isSRGB = texture->flags().testFlag(QRhiTexture::Flag::sRGB);
1879 flags.setLinear(!isSRGB);
1880 const bool isRGBA8 = (texture->format() == QRhiTexture::Format::RGBA8);
1881 flags.setRgbe8(isRGBA8);
1882 const quint32 usageCount = 1 /* At least '1' as long as a texture is registered */;
1883 renderExtensionTexture.insert(&extensions, ImageData { QSSGRenderImageTexture{ texture, mipLevels, flags }, {}, usageCount });
1884 } else {
1885 renderExtensionTexture.insert(&extensions, {});
1886 }
1887}
1888
1889void QSSGBufferManager::clear()
1890{
1891 QSSGRhiContextPrivate *rhiCtxD = QSSGRhiContextPrivate::get(m_contextInterface->rhiContext().get());
1892
1893 if (meshBufferUpdates) {
1894 meshBufferUpdates->release();
1895 meshBufferUpdates = nullptr;
1896 }
1897
1898 {
1899 QMutexLocker meshMutexLocker(&meshBufferMutex);
1900 // Meshes (by path)
1901 auto meshMapCopy = meshMap;
1902 meshMapCopy.detach();
1903 for (auto iter = meshMapCopy.begin(), end = meshMapCopy.end(); iter != end; ++iter) {
1904 QSSGRenderMesh *theMesh = iter.value().mesh;
1905 if (theMesh) {
1907 qDebug() << "- releaseGeometry: " << iter.key().path() << currentLayer;
1908 decreaseMemoryStat(theMesh);
1909 rhiCtxD->releaseMesh(theMesh);
1910 }
1911 }
1912 meshMap.clear();
1913
1914 // Meshes (custom)
1915 auto customMeshMapCopy = customMeshMap;
1916 customMeshMapCopy.detach();
1917 for (auto iter = customMeshMapCopy.begin(), end = customMeshMapCopy.end(); iter != end; ++iter) {
1918 QSSGRenderMesh *theMesh = iter.value().mesh;
1919 if (theMesh) {
1921 qDebug() << "- releaseGeometry: " << iter.key() << currentLayer;
1922 decreaseMemoryStat(theMesh);
1923 rhiCtxD->releaseMesh(theMesh);
1924 }
1925 }
1926 customMeshMap.clear();
1927 }
1928
1929 // Textures (by path)
1930 for (auto it = imageMap.constBegin(), end = imageMap.constEnd(); it != end; ++it)
1931 releaseImage(it.key());
1932
1933 imageMap.clear();
1934
1935 // Textures (custom)
1936 for (auto it = customTextureMap.cbegin(), end = customTextureMap.cend(); it != end; ++it)
1937 releaseTextureData(it.key());
1938
1939 customTextureMap.clear();
1940
1941 // Textures (QSG)
1942 // these don't have any owned objects to release so just clearing is fine.
1943 qsgImageMap.clear();
1944}
1945
1946QRhiResourceUpdateBatch *QSSGBufferManager::meshBufferUpdateBatch()
1947{
1948 if (!meshBufferUpdates)
1949 meshBufferUpdates = m_contextInterface->rhiContext()->rhi()->nextResourceUpdateBatch();
1950 return meshBufferUpdates;
1951}
1952
1954{
1955 if (meshBufferUpdates) {
1956 m_contextInterface->rhiContext()->commandBuffer()->resourceUpdate(meshBufferUpdates);
1957 meshBufferUpdates = nullptr;
1958 }
1959}
1960
1962{
1963 for (auto &mesh : std::as_const(loader->meshes))
1964 loadRenderMesh(mesh, {});
1965
1966 for (auto customMesh : std::as_const(loader->geometries))
1967 loadRenderMesh(static_cast<QSSGRenderGeometry*>(customMesh), {});
1968
1969 for (auto texture : std::as_const(loader->textures)) {
1970 const auto image = static_cast<QSSGRenderImage *>(texture);
1972 }
1973
1974 // Make sure the uploads occur
1976}
1977
1979{
1980 quint64 s = 0;
1981 if (!texture)
1982 return s;
1983
1984 auto format = texture->format();
1986 return 0;
1987
1988 s = texture->pixelSize().width() * texture->pixelSize().height();
1989 /*
1990 UnknownFormat,
1991 RGBA8,
1992 BGRA8,
1993 R8,
1994 RG8,
1995 R16,
1996 RG16,
1997 RED_OR_ALPHA8,
1998 RGBA16F,
1999 RGBA32F,
2000 R16F,
2001 R32F,
2002 RGB10A2,
2003 D16,
2004 D24,
2005 D24S8,
2006 D32F,*/
2007 static const quint64 pixelSizes[] = {0, 4, 4, 1, 2, 2, 4, 1, 2, 4, 2, 4, 4, 2, 4, 4, 4};
2008 /*
2009 BC1,
2010 BC2,
2011 BC3,
2012 BC4,
2013 BC5,
2014 BC6H,
2015 BC7,
2016 ETC2_RGB8,
2017 ETC2_RGB8A1,
2018 ETC2_RGBA8,*/
2019 static const quint64 blockSizes[] = {8, 16, 16, 8, 16, 16, 16, 8, 8, 16};
2021 "QRhiTexture format constant value missmatch.");
2023 s *= pixelSizes[format];
2025 s /= blockSizes[format - QRhiTexture::BC1];
2026 else
2027 s /= 16;
2028
2029 if (texture->flags() & QRhiTexture::MipMapped)
2030 s += s / 4;
2031 if (texture->flags() & QRhiTexture::CubeMap)
2032 s *= 6;
2033 return s;
2034}
2035
2037{
2038 quint64 s = 0;
2039 if (!buffer)
2040 return s;
2041 s = buffer->buffer()->size();
2042 return s;
2043}
2044
2046{
2048 QSSGRhiContextStats::get(*m_contextInterface->rhiContext()).imageDataSizeChanges(stats.imageDataSize);
2049}
2050
2052{
2054 QSSGRhiContextStats::get(*m_contextInterface->rhiContext()).imageDataSizeChanges(stats.imageDataSize);
2055}
2056
2058{
2059 stats.meshDataSize += bufferMemorySize(mesh->subsets.at(0).rhi.vertexBuffer)
2060 + bufferMemorySize(mesh->subsets.at(0).rhi.indexBuffer);
2061 QSSGRhiContextStats::get(*m_contextInterface->rhiContext()).meshDataSizeChanges(stats.meshDataSize);
2062}
2063
2065{
2066 quint64 s = 0;
2067 if (mesh)
2068 s = bufferMemorySize(mesh->subsets.at(0).rhi.vertexBuffer)
2069 + bufferMemorySize(mesh->subsets.at(0).rhi.indexBuffer);
2070 stats.meshDataSize = qMax(0u, stats.meshDataSize - s);
2071 QSSGRhiContextStats::get(*m_contextInterface->rhiContext()).meshDataSizeChanges(stats.meshDataSize);
2072}
2073
IOBluetoothDevice * device
QByteArray toByteArray() const
Definition qbytearray.h:796
\inmodule QtCore
Definition qbytearray.h:57
qsizetype size() const noexcept
Returns the number of bytes in this byte array.
Definition qbytearray.h:494
bool isEmpty() const noexcept
Returns true if the byte array has size 0; otherwise returns false.
Definition qbytearray.h:107
static QByteArray number(int, int base=10)
Returns a byte-array representing the whole number n as text.
The QColor class provides colors based on RGB, HSV or CMYK values.
Definition qcolor.h:31
bool exists() const
This is an overloaded member function, provided for convenience. It differs from the above function o...
Definition qfile.cpp:351
constexpr bool testFlag(Enum flag) const noexcept
Definition qflags.h:126
The QMatrix4x4 class represents a 4x4 transformation matrix in 3D space.
Definition qmatrix4x4.h:25
void lookAt(const QVector3D &eye, const QVector3D &center, const QVector3D &up)
Multiplies this matrix by a viewing matrix derived from an eye point.
void perspective(float verticalAngle, float aspectRatio, float nearPlane, float farPlane)
Multiplies this matrix by another that applies a perspective projection.
\inmodule QtCore
Definition qmutex.h:313
\inmodule QtCore
Definition qmutex.h:281
\inmodule QtGui
Definition qrhi.h:846
@ Immutable
Definition qrhi.h:849
@ Dynamic
Definition qrhi.h:851
@ Static
Definition qrhi.h:850
@ IndexBuffer
Definition qrhi.h:856
@ VertexBuffer
Definition qrhi.h:855
@ UniformBuffer
Definition qrhi.h:857
virtual bool create()=0
Creates the corresponding native graphics resources.
\inmodule QtGui
Definition qrhi.h:576
QPair< QRhiBuffer *, quint32 > VertexInput
Synonym for QPair<QRhiBuffer *, quint32>.
Definition qrhi.h:1680
IndexFormat
Specifies the index data type.
Definition qrhi.h:1653
\inmodule QtGui
Definition qrhi.h:1270
void setCullMode(CullMode mode)
Sets the specified face culling mode.
Definition qrhi.h:1393
\inmodule QtGui
Definition qrhi.h:1094
\inmodule QtGui
Definition qrhi.h:1142
\inmodule QtGui
Definition qrhi.h:1731
void setName(const QByteArray &name)
Sets a name for the object.
Definition qrhi.cpp:3581
void deleteLater()
When called without a frame being recorded, this function is equivalent to deleting the object.
Definition qrhi.cpp:3545
\inmodule QtGui
Definition qrhi.h:1030
@ ClampToEdge
Definition qrhi.h:1040
static QRhiShaderResourceBinding sampledTexture(int binding, StageFlags stage, QRhiTexture *tex, QRhiSampler *sampler)
Definition qrhi.cpp:5640
static QRhiShaderResourceBinding uniformBufferWithDynamicOffset(int binding, StageFlags stage, QRhiBuffer *buf, quint32 size)
Definition qrhi.cpp:5600
\inmodule QtGui
Definition qrhi.h:1214
void setBindings(std::initializer_list< QRhiShaderResourceBinding > list)
Sets the list of bindings.
Definition qrhi.h:1218
void setColorAttachments(std::initializer_list< QRhiColorAttachment > list)
Sets the list of color attachments.
Definition qrhi.h:627
void setSourceSize(const QSize &size)
Sets the source size in pixels.
Definition qrhi.h:676
\inmodule QtGui
Definition qrhi.h:716
void setEntries(std::initializer_list< QRhiTextureUploadEntry > list)
Sets the list of entries.
Definition qrhi.h:722
\inmodule QtGui
Definition qrhi.h:693
\inmodule QtGui
Definition qrhi.h:895
@ UsedWithGenerateMips
Definition qrhi.h:903
@ MipMapped
Definition qrhi.h:900
@ RenderTarget
Definition qrhi.h:898
@ CubeMap
Definition qrhi.h:899
virtual bool create()=0
Creates the corresponding native graphics resources.
Format
Specifies the texture format.
Definition qrhi.h:914
@ ASTC_10x8
Definition qrhi.h:959
@ ASTC_12x12
Definition qrhi.h:962
@ ASTC_8x5
Definition qrhi.h:954
@ ASTC_10x5
Definition qrhi.h:957
@ RGBA32F
Definition qrhi.h:926
@ ETC2_RGBA8
Definition qrhi.h:947
@ ASTC_5x5
Definition qrhi.h:951
@ ASTC_4x4
Definition qrhi.h:949
@ ASTC_6x6
Definition qrhi.h:953
@ ASTC_12x10
Definition qrhi.h:961
@ ASTC_5x4
Definition qrhi.h:950
@ RED_OR_ALPHA8
Definition qrhi.h:923
@ ASTC_6x5
Definition qrhi.h:952
@ ASTC_8x8
Definition qrhi.h:956
@ RGBA16F
Definition qrhi.h:925
@ ASTC_10x6
Definition qrhi.h:958
@ ASTC_10x10
Definition qrhi.h:960
@ UnknownFormat
Definition qrhi.h:915
@ ASTC_8x6
Definition qrhi.h:955
\inmodule QtGui
Definition qrhi.h:232
Format
Specifies the type of the element data.
Definition qrhi.h:234
\inmodule QtGui
Definition qrhi.h:321
void setBindings(std::initializer_list< QRhiVertexInputBinding > list)
Sets the bindings from the specified list.
Definition qrhi.h:325
void setAttributes(std::initializer_list< QRhiVertexInputAttribute > list)
Sets the attributes from the specified list.
Definition qrhi.h:337
\inmodule QtGui
Definition qrhi.h:85
\inmodule QtGuiPrivate \inheaderfile rhi/qrhi.h
Definition qrhi.h:1804
static int mipLevelsForSize(const QSize &size)
Definition qrhi.cpp:10008
@ TextureSizeMax
Definition qrhi.h:1888
@ TriangleFanTopology
Definition qrhi.h:1849
QRhiResourceUpdateBatch * nextResourceUpdateBatch()
Definition qrhi.cpp:9252
static bool formatIsOpaque(quint32 glTextureFormat)
\inmodule QtQuick
Definition qsgtexture.h:20
static const char * displayName(QSSGRenderTextureCubeFace face)
static size_t getSizeOfType(QSSGRenderComponentType type)
Class representing 3D range or axis aligned bounding box.
void setRenderContextInterface(QSSGRenderContextInterface *ctx)
QSSGRenderImageTexture loadSkinmap(QSSGRenderTextureData *skin)
static std::unique_ptr< QSSGMeshBVH > loadMeshBVH(const QSSGRenderPath &inSourcePath)
void releaseGeometry(QSSGRenderGeometry *geometry)
QSSGBounds3 getModelBounds(const QSSGRenderModel *model) const
static QRhiTexture::Format toRhiFormat(const QSSGRenderTextureFormat format)
void increaseMemoryStat(QRhiTexture *texture)
void releaseResourcesForLayer(QSSGRenderLayer *layer)
void cleanupUnreferencedBuffers(quint32 frameId, QSSGRenderLayer *layer)
QSSGRenderMesh * getMeshForPicking(const QSSGRenderModel &model) const
static QSSGMesh::Mesh loadMeshData(const QSSGRenderPath &inSourcePath)
QSSGRenderImageTexture loadRenderImage(const QSSGRenderImage *image, MipMode inMipMode=MipModeFollowRenderImage, LoadRenderImageFlags flags=LoadWithFlippedY)
void registerExtensionResult(const QSSGRenderExtension &extensions, QRhiTexture *texture)
static void unregisterMeshData(const QString &assetId)
static QString primitivePath(const QString &primitive)
void releaseTextureData(const QSSGRenderTextureData *data)
void releaseExtensionResult(const QSSGRenderExtension &rext)
QSSGRenderMesh * loadMesh(const QSSGRenderModel *model)
QSSGRenderImageTexture loadLightmap(const QSSGRenderModel &model)
static void registerMeshData(const QString &assetId, const QVector< QSSGMesh::Mesh > &meshData)
void decreaseMemoryStat(QRhiTexture *texture)
void resetUsageCounters(quint32 frameId, QSSGRenderLayer *layer)
void processResourceLoader(const QSSGRenderResourceLoader *loader)
static QString lightmapAssetPathForLoad(const QSSGRenderModel &model, LightmapAsset asset)
bool isValid() const
Definition qssgmesh_p.h:159
bool createLightmapUVChannel(uint lightmapBaseResolution)
static Mesh fromRuntimeData(const RuntimeMeshData &data, QString *error)
Definition qssgmesh.cpp:764
static Mesh loadMesh(QIODevice *device, quint32 id=0)
Definition qssgmesh.cpp:581
VertexBuffer vertexBuffer() const
Definition qssgmesh_p.h:140
Winding winding() const
Definition qssgmesh_p.h:162
IndexBuffer indexBuffer() const
Definition qssgmesh_p.h:141
QVector< Subset > subsets() const
Definition qssgmesh_p.h:143
TargetBuffer targetBuffer() const
Definition qssgmesh_p.h:142
DrawMode drawMode() const
Definition qssgmesh_p.h:161
const std::unique_ptr< QSSGRhiContext > & rhiContext() const
const std::unique_ptr< QSSGShaderCache > & shaderCache() const
\inmodule QtQuick3D
Attribute attribute(int idx) const
const QSSGMesh::RuntimeMeshData & meshData() const
uint32_t generationId() const
const QByteArray & indexBuffer() const
const QByteArray & vertexBuffer() const
QSSGMesh::Mesh::DrawMode primitiveType() const
static QSSGRhiContextPrivate * get(QSSGRhiContext *q)
static QSSGRhiContextStats & get(QSSGRhiContext &rhiCtx)
iterator end()
Definition qset.h:140
iterator find(const T &value)
Definition qset.h:159
iterator insert(const T &value)
Definition qset.h:155
\inmodule QtCore
Definition qsize.h:25
\inmodule QtCore
Definition qstringview.h:78
constexpr QStringView mid(qsizetype pos, qsizetype n=-1) const noexcept
Returns the substring of length length starting at position start in this object.
\macro QT_RESTRICTED_CAST_FROM_ASCII
Definition qstring.h:129
QByteArray toLatin1() const &
Definition qstring.h:630
qsizetype lastIndexOf(QChar c, Qt::CaseSensitivity cs=Qt::CaseSensitive) const noexcept
Definition qstring.h:296
static QString fromLatin1(QByteArrayView ba)
This is an overloaded member function, provided for convenience. It differs from the above function o...
Definition qstring.cpp:5871
static QString fromUtf16(const char16_t *, qsizetype size=-1)
Definition qstring.cpp:6045
bool isEmpty() const noexcept
Returns true if the string has no characters; otherwise returns false.
Definition qstring.h:192
static QString number(int, int base=10)
This is an overloaded member function, provided for convenience. It differs from the above function o...
Definition qstring.cpp:8084
QByteArray toUtf8() const &
Definition qstring.h:634
quint32 glInternalFormat() const
quint32 glFormat() const
QByteArrayView getDataView(int level=0, int face=0) const
The QVector3D class represents a vector or vertex in 3D space.
Definition qvectornd.h:171
EGLContext ctx
list append(new Employee("Blackpool", "Stephen"))
QSet< QString >::iterator it
QRhiVertexInputAttribute::Format toVertexInputFormat(QSSGRenderComponentType compType, quint32 numComps)
QRhiGraphicsPipeline::Topology toTopology(QSSGRenderDrawMode drawMode)
Combined button and popup list for selecting options.
QTextStream & center(QTextStream &stream)
Calls QTextStream::setFieldAlignment(QTextStream::AlignCenter) on stream and returns stream.
Definition image.cpp:4
static void * context
#define Q_STATIC_ASSERT_X(Condition, Message)
Definition qassert.h:111
#define QByteArrayLiteral(str)
Definition qbytearray.h:52
DBusConnection const char DBusError DBusBusType DBusError return DBusConnection DBusHandleMessageFunction void DBusFreeFunction return DBusConnection return DBusConnection return const char DBusError return DBusConnection DBusMessage dbus_uint32_t return DBusConnection dbus_bool_t DBusConnection DBusAddWatchFunction DBusRemoveWatchFunction DBusWatchToggledFunction void DBusFreeFunction return DBusConnection DBusDispatchStatusFunction void DBusFreeFunction DBusTimeout return DBusTimeout return DBusWatch return DBusWatch unsigned int return DBusError const DBusError return const DBusMessage return DBusMessage return DBusMessage return DBusMessage return DBusMessage return DBusMessage return DBusMessageIter * iter
DBusConnection const char DBusError * error
EGLOutputLayerEXT EGLint EGLAttrib value
[5]
EGLOutputLayerEXT layer
EGLOutputLayerEXT EGLint attribute
qfloat16 qSqrt(qfloat16 f)
Definition qfloat16.h:289
#define Q_GLOBAL_STATIC(TYPE, NAME,...)
#define qDebug
[1]
Definition qlogging.h:164
#define qWarning
Definition qlogging.h:166
#define qCCritical(category,...)
#define qCWarning(category,...)
return ret
int qCeil(T v)
Definition qmath.h:36
constexpr const T & qMin(const T &a, const T &b)
Definition qminmax.h:40
constexpr const T & qMax(const T &a, const T &b)
Definition qminmax.h:42
GLint location
GLsizei const GLfloat * v
[13]
GLint GLenum GLsizei GLsizei GLsizei depth
GLenum GLuint GLint level
GLuint64 key
GLint GLsizei GLsizei height
GLenum GLuint GLintptr GLsizeiptr size
[1]
GLuint GLuint end
GLsizei const GLchar ** strings
[1]
GLuint sampler
GLint GLsizei GLsizei GLenum GLenum GLsizei void * data
GLenum face
GLenum GLuint buffer
GLint GLsizei width
GLenum GLuint GLenum GLsizei const GLchar * buf
GLbitfield flags
GLenum GLuint texture
GLenum GLuint GLintptr offset
GLint ref
GLuint sourceTexture
GLint GLsizei GLsizei GLenum format
GLsizei GLsizei GLchar * source
GLdouble s
[6]
Definition qopenglext.h:235
GLint lod
GLsizei const GLchar *const * path
GLuint64EXT * result
[6]
QT_BEGIN_NAMESPACE constexpr decltype(auto) qMakePair(T1 &&value1, T2 &&value2) noexcept(noexcept(std::make_pair(std::forward< T1 >(value1), std::forward< T2 >(value2))))
Definition qpair.h:19
#define Q_QUICK3D_PROFILE_END_WITH_PAYLOAD(Type, Payload)
#define Q_QUICK3D_PROFILE_START(Type)
#define Q_QUICK3D_PROFILE_END_WITH_ID(Type, Payload, POID)
#define QSSG_RENDERPASS_NAME(passName, level, face)
#define Q_QUICK3D_PROFILE_END_WITH_STRING(Type, Payload, Str)
#define Q_ASSERT(cond)
Definition qrandom.cpp:47
#define QSSG_ASSERT_X(cond, msg, action)
#define QSSG_ASSERT(cond, action)
static constexpr QSSGRenderTextureCubeFace QSSGRenderTextureCubeFaces[]
QSSGRenderComponentType
static constexpr QSize sizeForMipLevel(int mipLevel, const QSize &baseLevelSize)
static quint64 textureMemorySize(QRhiTexture *texture)
static MeshIdxNamePair splitRuntimeMeshPath(const QSSGRenderPath &rpath)
static const float cube[]
static const PrimitiveEntry primitives[nPrimitives]
static quint64 bufferMemorySize(const QSSGRhiBufferPtr &buffer)
static const int nPrimitives
QHash< QString, MeshStorageRef > AssetMeshMap
struct MeshStorageRef Q_TRACE_POINT
static const char * primitivesDirectory
QPair< qsizetype, QString > MeshIdxNamePair
#define QSSGRHICTX_STAT(ctx, f)
std::shared_ptr< QSSGRhiBuffer > QSSGRhiBufferPtr
QSSGRhiInputAssemblerStatePrivate::InputAssemblerState QSSGRhiInputAssemblerState
SSL_CTX int(* cb)(SSL *ssl, unsigned char **out, unsigned char *outlen, const unsigned char *in, unsigned int inlen, void *arg)
#define qPrintable(string)
Definition qstring.h:1531
QLatin1StringView QLatin1String
Definition qstringfwd.h:31
#define QStringLiteral(str)
Q_CORE_EXPORT int qEnvironmentVariableIntValue(const char *varName, bool *ok=nullptr) noexcept
#define Q_UNUSED(x)
#define Q_TRACE_SCOPE(x,...)
Definition qtrace_p.h:146
#define Q_TRACE(x,...)
Definition qtrace_p.h:144
unsigned int quint32
Definition qtypes.h:50
unsigned long long quint64
Definition qtypes.h:61
ptrdiff_t qsizetype
Definition qtypes.h:165
unsigned char quint8
Definition qtypes.h:46
#define enabled
QSqlQueryModel * model
[16]
QFile file
[0]
QStringList keys
QByteArray imageData
[15]
myFilter draw(painter, QPoint(0, 0), originalPixmap)
QJSValue cube
QVector< QSSGMesh::Mesh > meshes
static QImageData * get(QImage &img) noexcept
Definition qimage_p.h:37
static constexpr bool enabled(Level)
static QSharedPointer< QIODevice > getStreamForFile(const QString &inPath, bool inQuiet=false, QString *outPath=nullptr)
static QSSGLoadedTexture * load(const QString &inPath, const QSSGRenderTextureFormat &inFormat, bool inFlipY=true)
static QSSGLoadedTexture * loadTextureData(QSSGRenderTextureData *textureData)
bool isCompatible(const QSSGMeshProcessingOptions &other) const
static const char * getLightmapUVAttrName()
Definition qssgmesh_p.h:396
static const char * getNormalAttrName()
Definition qssgmesh_p.h:393
static const char * getUV1AttrName()
Definition qssgmesh_p.h:395
static const char * getTexBinormalAttrName()
Definition qssgmesh_p.h:398
static const char * getPositionAttrName()
Definition qssgmesh_p.h:392
static const char * getTexTanAttrName()
Definition qssgmesh_p.h:397
static const char * getColorAttrName()
Definition qssgmesh_p.h:399
static const char * getJointAttrName()
Definition qssgmesh_p.h:400
static const char * getUV0AttrName()
Definition qssgmesh_p.h:394
static const char * getWeightAttrName()
Definition qssgmesh_p.h:401
QVector< VertexBufferEntry > entries
Definition qssgmesh_p.h:115
QVector< VertexBufferEntry > entries
Definition qssgmesh_p.h:104
QVector< QSSGRenderSubset > subsets
QVector< QSSGRenderGraphObject * > geometries
QVector< QSSGRenderGraphObject * > textures
QVector< QSSGRenderPath > meshes
struct QSSGRenderSubset::@757 rhi
\variable QSSGRhiGraphicsPipelineState::depthFunc