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