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
qssgrendershadercache.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
8
9#include <QtQuick3DUtils/private/qssgutils_p.h>
10#include <QtQuick3DUtils/private/qquick3dprofiler_p.h>
11
12#include <QtQuick3DRuntimeRender/private/qssgruntimerenderlogging_p.h>
13#include <qtquick3d_tracepoints_p.h>
14
15#include <QCoreApplication>
16#include <QStandardPaths>
17#include <QString>
18#include <QFile>
19#include <QDir>
20
21#include <QtGui/qsurfaceformat.h>
22#if QT_CONFIG(opengl)
23# include <QtGui/qopenglcontext.h>
24#endif
25
26#ifdef QT_QUICK3D_HAS_RUNTIME_SHADERS
27#include <rhi/qshaderbaker.h>
28#endif
29
30#include <QtCore/qmutex.h>
31
33
34Q_TRACE_POINT(qtquick3d, QSSG_loadShader_entry)
35Q_TRACE_POINT(qtquick3d, QSSG_loadShader_exit)
36
37static QtQuick3DEditorHelpers::ShaderBaker::StatusCallback s_statusCallback = nullptr;
38Q_GLOBAL_STATIC(QMutex, s_statusMutex);
39
40size_t qHash(QSSGShaderFeatures features) noexcept { return (features.flags & (~QSSGShaderFeatures::IndexMask)); }
41
43{
44 switch (stage) {
46 return QStringLiteral("failedvert.txt");
47 break;
49 return QStringLiteral("failedfrag.txt");
50 break;
51 default:
52 return QStringLiteral("failedshader.txt");
53 }
54}
55
57{
58 const char *name = nullptr;
60};
61
62static constexpr DefineEntry DefineTable[] {
63 { "QSSG_ENABLE_LIGHT_PROBE", QSSGShaderFeatures::Feature::LightProbe },
64 { "QSSG_ENABLE_IBL_ORIENTATION", QSSGShaderFeatures::Feature::IblOrientation },
65 { "QSSG_ENABLE_SSM", QSSGShaderFeatures::Feature::Ssm },
66 { "QSSG_ENABLE_SSAO", QSSGShaderFeatures::Feature::Ssao },
67 { "QSSG_ENABLE_DEPTH_PASS", QSSGShaderFeatures::Feature::DepthPass },
68 { "QSSG_ENABLE_ORTHO_SHADOW_PASS", QSSGShaderFeatures::Feature::OrthoShadowPass },
69 { "QSSG_ENABLE_CUBE_SHADOW_PASS", QSSGShaderFeatures::Feature::CubeShadowPass },
70 { "QSSG_ENABLE_LINEAR_TONEMAPPING", QSSGShaderFeatures::Feature::LinearTonemapping },
71 { "QSSG_ENABLE_ACES_TONEMAPPING", QSSGShaderFeatures::Feature::AcesTonemapping },
72 { "QSSG_ENABLE_HEJLDAWSON_TONEMAPPING", QSSGShaderFeatures::Feature::HejlDawsonTonemapping },
73 { "QSSG_ENABLE_FILMIC_TONEMAPPING", QSSGShaderFeatures::Feature::FilmicTonemapping },
74 { "QSSG_ENABLE_RGBE_LIGHT_PROBE", QSSGShaderFeatures::Feature::RGBELightProbe },
75 { "QSSG_ENABLE_OPAQUE_DEPTH_PRE_PASS", QSSGShaderFeatures::Feature::OpaqueDepthPrePass },
76 { "QSSG_ENABLE_REFLECTION_PROBE", QSSGShaderFeatures::Feature::ReflectionProbe },
77 { "QSSG_REDUCE_MAX_NUM_LIGHTS", QSSGShaderFeatures::Feature::ReduceMaxNumLights },
78 { "QSSG_ENABLE_LIGHTMAP", QSSGShaderFeatures::Feature::Lightmap },
79 { "QSSG_DISABLE_MULTIVIEW", QSSGShaderFeatures::Feature::DisableMultiView },
80 { "QSSG_FORCE_IBL_EXPOSURE", QSSGShaderFeatures::Feature::ForceIblExposure },
81};
82
83static_assert(std::size(DefineTable) == QSSGShaderFeatures::Count, "Missing feature define?");
84
87
89{
90 if (val)
91 flags |= (static_cast<FlagType>(feature) & ~IndexMask);
92 else
93 flags &= ~(static_cast<FlagType>(feature) & ~IndexMask);
94}
95
96#ifdef QT_QUICK3D_HAS_RUNTIME_SHADERS
97static void initBakerForNonPersistentUse(QShaderBaker *baker, QRhi *rhi)
98{
99 QVector<QShaderBaker::GeneratedShader> outputs;
100 switch (rhi->backend()) {
101 case QRhi::D3D11:
102 outputs.append({ QShader::HlslShader, QShaderVersion(50) }); // Shader Model 5.0
103 break;
104 case QRhi::D3D12:
105 outputs.append({ QShader::HlslShader, QShaderVersion(61) }); // Shader Model 6.1 (includes multiview support)
106 break;
107 case QRhi::Metal:
108 outputs.append({ QShader::MslShader, QShaderVersion(12) }); // Metal 1.2
109 break;
110 case QRhi::OpenGLES2:
111 {
113#if QT_CONFIG(opengl)
114 auto h = static_cast<const QRhiGles2NativeHandles *>(rhi->nativeHandles());
115 if (h && h->context)
116 format = h->context->format();
117#endif
118 if (format.profile() == QSurfaceFormat::CoreProfile && format.version() >= qMakePair(3, 3)) {
119 outputs.append({ QShader::GlslShader, QShaderVersion(330) }); // OpenGL 3.3+
120 } else {
121 bool isGLESModule = false;
122#if QT_CONFIG(opengl)
124#endif
125 if (format.renderableType() == QSurfaceFormat::OpenGLES || isGLESModule) {
126 if (format.majorVersion() >= 3)
127 outputs.append({ QShader::GlslShader, QShaderVersion(300, QShaderVersion::GlslEs) }); // GLES 3.0+
128 else
129 outputs.append({ QShader::GlslShader, QShaderVersion(100, QShaderVersion::GlslEs) }); // GLES 2.0
130 } else {
131 // Need to default to at least GLSL 130 (OpenGL 3.0), not 120.
132 // The difference is actually relevant when it comes to certain
133 // GLSL features (textureSize, unsigned integers, and with
134 // SPIRV-Cross even bool), and we do not have to care about
135 // pure OpenGL (non-ES) 2.x implementations in practice.
136
137 // For full feature set we need GLSL 140 (OpenGL 3.1), e.g.
138 // because of inverse() used for instancing.
139
140 // GLSL 130 should still be attempted, to support old Mesa
141 // llvmpipe that only gives us OpenGL 3.0. At the time of
142 // writing the opengl32sw.dll shipped with pre-built Qt is one
143 // of these still.
144
145 if (format.version() >= qMakePair(3, 1))
146 outputs.append({ QShader::GlslShader, QShaderVersion(140) }); // OpenGL 3.1+
147 else
148 outputs.append({ QShader::GlslShader, QShaderVersion(130) }); // OpenGL 3.0+
149 }
150 }
151 }
152 break;
153 default: // Vulkan, Null
154 outputs.append({ QShader::SpirvShader, QShaderVersion(100) });
155 break;
156 }
157
158 baker->setGeneratedShaders(outputs);
159 baker->setGeneratedShaderVariants({ QShader::StandardShader });
160}
161
162static void initBakerForPersistentUse(QShaderBaker *baker, QRhi *)
163{
164 QVector<QShaderBaker::GeneratedShader> outputs;
165 outputs.reserve(8);
166 outputs.append({ QShader::SpirvShader, QShaderVersion(100) });
167 outputs.append({ QShader::HlslShader, QShaderVersion(50) }); // Shader Model 5.0
168 outputs.append({ QShader::HlslShader, QShaderVersion(61) }); // Shader Model 6.1 (for multiview on d3d12)
169 outputs.append({ QShader::MslShader, QShaderVersion(12) }); // Metal 1.2
170 outputs.append({ QShader::GlslShader, QShaderVersion(330) }); // OpenGL 3.3+
171 outputs.append({ QShader::GlslShader, QShaderVersion(140) }); // OpenGL 3.1+
172 outputs.append({ QShader::GlslShader, QShaderVersion(130) }); // OpenGL 3.0+
173 outputs.append({ QShader::GlslShader, QShaderVersion(100, QShaderVersion::GlslEs) }); // GLES 2.0
174 outputs.append({ QShader::GlslShader, QShaderVersion(300, QShaderVersion::GlslEs) }); // GLES 3.0+
175
176 // If one of the above cannot be generated due to failing at the
177 // SPIRV-Cross translation stage, it will be skipped, but bake() will not
178 // fail. This is essential, because with the default fail if anything fails
179 // behavior many shaders could not be baked at all due to failing for e.g.
180 // GLSL ES 100. This is a non-issue when choosing the targets dynamically
181 // based on the current API/context, but here we need to ensure what we
182 // generate will work with a different RHI backend, graphics API, and
183 // perhaps even on a different platform (if the cache file is manually
184 // moved). So have to generate what we can, without breaking the
185 // application when the shader is not compatible with a target. (if that
186 // shader is not used at runtime, it's fine anyway, it it is, it won't work
187 // just as with the other, non-caching path)
188 baker->setBreakOnShaderTranslationError(false);
189
190 baker->setGeneratedShaders(outputs);
191 baker->setGeneratedShaderVariants({ QShader::StandardShader });
192}
193
194#else
195static void initBakerForNonPersistentUse(QShaderBaker *, QRhi *)
196{
197}
198
199static void initBakerForPersistentUse(QShaderBaker *, QRhi *)
200{
201}
202#endif // QT_QUICK3D_HAS_RUNTIME_SHADERS
203
204static bool s_autoDiskCacheEnabled = true;
205
207{
208 // these three mirror QOpenGLShaderProgram/QQuickGraphicsConfiguration/QSGRhiSupport
209 static const bool diskCacheDisabled = qEnvironmentVariableIntValue("QT_DISABLE_SHADER_DISK_CACHE")
210 || qEnvironmentVariableIntValue("QSG_RHI_DISABLE_DISK_CACHE");
211 const bool attrDiskCacheDisabled = (qApp ? qApp->testAttribute(Qt::AA_DisableShaderDiskCache) : false);
212 return (!diskCacheDisabled && !attrDiskCacheDisabled && s_autoDiskCacheEnabled);
213
214}
215
216static inline bool ensureWritableDir(const QString &name)
217{
218 QDir::root().mkpath(name);
219 return QFileInfo(name).isWritable();
220}
221
223{
224 static bool checked = false;
225 static QString currentCacheDir;
226 static bool cacheWritable = false;
227
228 if (checked)
229 return cacheWritable ? currentCacheDir : QString();
230
231 checked = true;
233 const QString subPath = QLatin1String("/q3dshadercache-") + QSysInfo::buildAbi() + QLatin1Char('/');
234
235 if (!cachePath.isEmpty()) {
236 currentCacheDir = cachePath + subPath;
237 cacheWritable = ensureWritableDir(currentCacheDir);
238 }
239
240 return cacheWritable ? currentCacheDir : QString();
241}
242
244{
245 const QString cacheDir = persistentQsbcDir();
246 if (!cacheDir.isEmpty())
247 return cacheDir + QLatin1String("q3dshadercache.qsbc");
248
249 return QString();
250}
251
253 const InitBakerFunc initBakeFn)
254 : m_rhiContext(ctx),
255 m_initBaker(initBakeFn),
256 m_builtInShaders(*this)
257{
260 m_persistentShaderStorageFileName = persistentQsbcFileName();
261 if (!m_persistentShaderStorageFileName.isEmpty()) {
262 const bool skipCacheFile = qEnvironmentVariableIntValue("QT_QUICK3D_NO_SHADER_CACHE_LOAD");
263 if (!skipCacheFile && QFileInfo::exists(m_persistentShaderStorageFileName)) {
264 if (shaderDebug)
265 qDebug("Attempting to seed material shader cache from %s", qPrintable(m_persistentShaderStorageFileName));
266 if (m_persistentShaderBakingCache.load(m_persistentShaderStorageFileName)) {
267 if (shaderDebug) {
268 const int count = m_persistentShaderBakingCache.availableEntries().count();
269 qDebug("Loaded %d shader pipelines into the material shader cache", count);
270 }
271 }
272 }
273 }
274 }
275
276 if (!m_initBaker) {
277 // It is important to generate all possible shader variants if the qsb
278 // collection is going to be stored on disk. Otherwise switching the
279 // rhi backend could break the application. This is however an overkill
280 // if we know that what we bake will not be reused in future runs of
281 // the application, so do not do it if the disk cache was disabled or
282 // the cache directory was not available (no file system, no
283 // permissions, etc.).
284 m_initBaker = m_persistentShaderStorageFileName.isEmpty() ? initBakerForNonPersistentUse
286 }
287}
288
290{
291 if (!m_persistentShaderStorageFileName.isEmpty())
292 m_persistentShaderBakingCache.save(m_persistentShaderStorageFileName);
293}
294
296{
297 m_builtInShaders.releaseCachedResources();
298
299 m_rhiShaders.clear();
300
301 // m_persistentShaderBakingCache is not cleared, that is intentional,
302 // otherwise we would permanently lose what got loaded at startup.
303}
304
306 const QSSGShaderFeatures &inFeatures)
307{
309 cacheKey.m_features = inFeatures;
310 cacheKey.updateHashCode();
311 const auto theIter = m_rhiShaders.constFind(cacheKey);
312 if (theIter != m_rhiShaders.cend())
313 return theIter.value();
314 return nullptr;
315}
316
317
318void QSSGShaderCache::addShaderPreprocessor(QByteArray &str,
319 const QByteArray &inKey,
320 ShaderType shaderType,
321 const QSSGShaderFeatures &inFeatures,
322 int viewCount)
323{
324 m_insertStr.clear();
325
326 m_insertStr += "#version 440\n";
327
328 if (!inKey.isNull()) {
329 m_insertStr += "//Shader name -";
330 m_insertStr += inKey;
331 m_insertStr += "\n";
332 }
333
334 m_insertStr += "#define texture2D texture\n";
335
336 // match Qt Quick and QSGMaterial(Shader)
337 m_insertStr += "#define QSHADER_VIEW_COUNT ";
338 m_insertStr += QByteArray::number(viewCount);
339 m_insertStr += "\n";
340
341 str.insert(0, m_insertStr);
342 QString::size_type insertPos = int(m_insertStr.size());
343
344 m_insertStr.clear();
345 const bool fragOutputEnabled = (!inFeatures.isSet(QSSGShaderFeatures::Feature::DepthPass)) && shaderType == ShaderType::Fragment;
346 for (const auto &def : DefineTable) {
347 m_insertStr.append("#define ");
348 m_insertStr.append(def.name);
349 m_insertStr.append(" ");
350 m_insertStr.append(inFeatures.isSet(def.feature) ? "1" : "0");
351 m_insertStr.append("\n");
352 }
353
354 str.insert(insertPos, m_insertStr);
355 insertPos += int(m_insertStr.size());
356
357 m_insertStr.clear();
358 if (fragOutputEnabled)
359 m_insertStr += "layout(location = 0) out vec4 fragOutput;\n";
360
361 str.insert(insertPos, m_insertStr);
362}
363
365{
366 return QByteArrayLiteral(":/res/rhishaders/");
367}
368
370{
371 return QByteArrayLiteral("qtappshaders.qsbc");
372}
373
375 const QSSGShaderFeatures &inFeatures, QSSGRhiShaderPipeline::StageFlags stageFlags,
376 int viewCount,
377 bool perTargetCompilation)
378{
379#ifdef QT_QUICK3D_HAS_RUNTIME_SHADERS
380 const QSSGRhiShaderPipelinePtr &rhiShaders = tryGetRhiShaderPipeline(inKey, inFeatures);
381 if (rhiShaders)
382 return rhiShaders;
383
384 QSSGShaderCacheKey tempKey(inKey);
385 tempKey.m_features = inFeatures;
386 tempKey.updateHashCode();
387
388 QByteArray vertexCode = inVert;
389 QByteArray fragmentCode = inFrag;
390
391 if (!vertexCode.isEmpty())
392 addShaderPreprocessor(vertexCode, inKey, ShaderType::Vertex, inFeatures, viewCount);
393
394 if (!fragmentCode.isEmpty())
395 addShaderPreprocessor(fragmentCode, inKey, ShaderType::Fragment, inFeatures, viewCount);
396
397 // lo and behold the final shader strings are ready
398
400 QString vertErr, fragErr;
401
402 QShaderBaker baker;
403 m_initBaker(&baker, m_rhiContext.rhi());
404
405 // If requested, per-target compilation allows doing things like #if
406 // QSHADER_HLSL in the shader code, at the expense of spending more time in
407 // bake())
408 baker.setPerTargetCompilation(perTargetCompilation);
409
410 // This is in the shader key, but cannot query that here anymore now that it's serialized.
411 // So we get it as a dedicated argument.
412 baker.setMultiViewCount(viewCount);
413
414 const bool editorMode = QSSGRhiContextPrivate::editorMode();
415 // Shader debug is disabled in editor mode
416 const bool shaderDebug = !editorMode && QSSGRhiContextPrivate::shaderDebuggingEnabled();
417
418 static auto dumpShader = [](QShader::Stage stage, const QByteArray &code) {
419 switch (stage) {
421 qDebug("VERTEX SHADER:\n*****\n");
422 break;
424 qDebug("FRAGMENT SHADER:\n*****\n");
425 break;
426 default:
427 qDebug("SHADER:\n*****\n");
428 break;
429 }
430 const auto lines = code.split('\n');
431 for (int i = 0; i < lines.size(); i++)
432 qDebug("%3d %s", i + 1, lines.at(i).constData());
433 qDebug("\n*****\n");
434 };
435
436 static auto dumpShaderToFile = [](QShader::Stage stage, const QByteArray &data) {
437 QFile f(dumpFilename(stage));
439 f.write(data);
440 f.close();
441 };
442
443 baker.setSourceString(vertexCode, QShader::VertexStage);
444 QShader vertexShader = baker.bake();
445 const auto vertShaderValid = vertexShader.isValid();
446 if (!vertShaderValid) {
447 vertErr = baker.errorMessage();
448 if (!editorMode) {
449 qWarning("Failed to compile vertex shader: %s\n", qPrintable(vertErr));
450 if (!shaderDebug)
451 qWarning() << inKey << '\n';
452 }
453 }
454
455 if (shaderDebug) {
456 dumpShader(QShader::Stage::VertexStage, vertexCode);
457 if (!vertShaderValid)
458 dumpShaderToFile(QShader::Stage::VertexStage, vertexCode);
459 }
460
461 baker.setSourceString(fragmentCode, QShader::FragmentStage);
462 QShader fragmentShader = baker.bake();
463 const bool fragShaderValid = fragmentShader.isValid();
464 if (!fragShaderValid) {
465 fragErr = baker.errorMessage();
466 if (!editorMode) {
467 qWarning("Failed to compile fragment shader: %s\n", qPrintable(fragErr));
468 if (!shaderDebug)
469 qWarning() << inKey << '\n';
470 }
471 }
472
473 if (shaderDebug) {
474 dumpShader(QShader::Stage::FragmentStage, fragmentCode);
475 if (!fragShaderValid)
476 dumpShaderToFile(QShader::Stage::FragmentStage, fragmentCode);
477 }
478
479 if (vertShaderValid && fragShaderValid) {
480 shaders = std::make_shared<QSSGRhiShaderPipeline>(m_rhiContext);
481 shaders->addStage(QRhiShaderStage(QRhiShaderStage::Vertex, vertexShader), stageFlags);
482 shaders->addStage(QRhiShaderStage(QRhiShaderStage::Fragment, fragmentShader), stageFlags);
483 if (shaderDebug)
484 qDebug("Compilation for vertex and fragment stages succeeded");
485 }
486
487 if (editorMode && s_statusCallback) {
489 const auto vertStatus = vertShaderValid ? Status::Success : Status::Error;
490 const auto fragStatus = fragShaderValid ? Status::Success : Status::Error;
491 QMutexLocker locker(&*s_statusMutex);
492 s_statusCallback(inKey, vertStatus, vertErr, QShader::VertexStage);
493 s_statusCallback(inKey, fragStatus, fragErr, QShader::FragmentStage);
494 }
495
496 auto result = m_rhiShaders.insert(tempKey, shaders).value();
497 if (result && result->vertexStage() && result->fragmentStage()) {
498 QQsbCollection::EntryDesc entryDesc = {
499 inKey,
501 result->vertexStage()->shader(),
502 result->fragmentStage()->shader()
503 };
504 m_persistentShaderBakingCache.addEntry(entryDesc.generateSha(), entryDesc);
505 }
506 return result;
507
508#else
509 Q_UNUSED(inKey);
510 Q_UNUSED(inVert);
511 Q_UNUSED(inFrag);
512 Q_UNUSED(inFeatures);
513 Q_UNUSED(stageFlags);
514 qWarning("Cannot compile and condition shaders at runtime because this build of Qt Quick 3D is not linking to Qt Shader Tools. "
515 "Only pre-processed materials are supported.");
516 return {};
517#endif
518}
519
521 const QSSGShaderFeatures &inFeatures,
524 QSSGRhiShaderPipeline::StageFlags stageFlags)
525{
526 // No lookup in m_rhiShaders. It is up to the caller to do that, if they
527 // want to. We will insert into it at the end, but there is intentionally
528 // no lookup. The result from this function is always a new
529 // QSSGRhiShaderPipeline (it's just much faster to create than the
530 // full-blown generator). That is important for some clients (effect
531 // system) so returning an existing QSSGRhiShaderPipeline is _wrong_.
532
534 if (shaderDebug)
535 qDebug("Loading pregenerated rhi shader(s)");
536
537 Q_TRACE_SCOPE(QSSG_loadShader);
538 Q_QUICK3D_PROFILE_START(QQuick3DProfiler::Quick3DLoadShader);
539
540 // Note that we are required to return a non-null (but empty) shader set even if loading fails.
542
544
545 QQsbIODeviceCollection qsbc(collectionFile);
547 if (qsbc.map(QQsbIODeviceCollection::Read))
548 qsbc.extractEntry(entry, entryDesc);
549 else
550 qWarning("Failed to open entry %s", entry.key.constData());
551
552 if (entryDesc.vertShader.isValid() && entryDesc.fragShader.isValid()) {
553 shaders->addStage(QRhiShaderStage(QRhiShaderStage::Vertex, entryDesc.vertShader), stageFlags);
554 shaders->addStage(QRhiShaderStage(QRhiShaderStage::Fragment, entryDesc.fragShader), stageFlags);
555 if (shaderDebug)
556 qDebug("Loading of vertex and fragment stages succeeded");
557 }
558
559#if !QT_CONFIG(qml_debug)
560 Q_UNUSED(obj);
561#else
562 Q_QUICK3D_PROFILE_END_WITH_ID(QQuick3DProfiler::Quick3DLoadShader, 0, obj.profilingId);
563#endif
564
566 cacheKey.m_features = inFeatures;
567 cacheKey.updateHashCode();
568
569 const auto inserted = m_rhiShaders.insert(cacheKey, shaders);
570 qsbc.unmap();
571 return inserted.value();
572}
573
575 const QByteArray &inKey,
576 const QSSGShaderFeatures &inFeatures,
577 QSSGRhiShaderPipeline::StageFlags stageFlags)
578{
579 // No lookup in m_rhiShaders. it is up to the caller to do that, if they
580 // want to. We will insert into it at the end, but there is intentionally
581 // no lookup. The result from this function is always a new
582 // QSSGRhiShaderPipeline (it's just much faster to create than the
583 // full-blown generator). That is important for some clients (effect
584 // system) so returning an existing QSSGRhiShaderPipeline is _wrong_.
585
587
588 // Here we are allowed to return null to indicate that there is no such
589 // entry in this particular cache.
590 if (!m_persistentShaderBakingCache.extractEntry(QQsbCollection::Entry(qsbcKey), entryDesc))
591 return {};
592
593 if (entryDesc.vertShader.isValid() && entryDesc.fragShader.isValid()) {
595 if (shaderDebug)
596 qDebug("Loading rhi shaders from disk cache for %s (%s)", qsbcKey.constData(), inKey.constData());
597
599 shaders->addStage(QRhiShaderStage(QRhiShaderStage::Vertex, entryDesc.vertShader), stageFlags);
600 shaders->addStage(QRhiShaderStage(QRhiShaderStage::Fragment, entryDesc.fragShader), stageFlags);
602 cacheKey.m_features = inFeatures;
603 cacheKey.updateHashCode();
604 return m_rhiShaders.insert(cacheKey, shaders).value();
605 }
606
607 return {};
608}
609
610QSSGRhiShaderPipelinePtr QSSGShaderCache::loadBuiltinUncached(const QByteArray &inKey, int viewCount)
611{
613 if (shaderDebug)
614 qDebug("Loading builtin rhi shader: %s (view count: %d)", inKey.constData(), viewCount);
615
616 Q_TRACE_SCOPE(QSSG_loadShader);
617 Q_QUICK3D_PROFILE_START(QQuick3DProfiler::Quick3DLoadShader);
618
619 // Note that we are required to return a non-null (but empty) shader set even if loading fails.
621
622 // inShaderName is a prefix of a .qsb file, so "abc" means we should
623 // look for abc.vert.qsb and abc.frag.qsb.
624
625 const QString prefix = QString::fromUtf8(resourceFolder() + inKey);
626 QString vertexFileName = prefix + QLatin1String(".vert.qsb");
627 QString fragmentFileName = prefix + QLatin1String(".frag.qsb");
628
629 // This must match QSGMaterial(Shader) in Qt Quick, in particular the
630 // QSGMaterialShader::setShaderFileName() overload taking a viewCount.
631 if (viewCount == 2) {
632 vertexFileName += QLatin1String(".mv2qsb");
633 fragmentFileName += QLatin1String(".mv2qsb");
634 }
635
636 QShader vertexShader;
637 QShader fragmentShader;
638
639 QFile f;
640 f.setFileName(vertexFileName);
641 if (f.open(QIODevice::ReadOnly)) {
642 const QByteArray vsData = f.readAll();
643 vertexShader = QShader::fromSerialized(vsData);
644 f.close();
645 } else {
646 qWarning("Failed to open %s", qPrintable(f.fileName()));
647 }
648 f.setFileName(fragmentFileName);
649 if (f.open(QIODevice::ReadOnly)) {
650 const QByteArray fsData = f.readAll();
651 fragmentShader = QShader::fromSerialized(fsData);
652 f.close();
653 } else {
654 qWarning("Failed to open %s", qPrintable(f.fileName()));
655 }
656
657 if (vertexShader.isValid() && fragmentShader.isValid()) {
659 shaders->addStage(QRhiShaderStage(QRhiShaderStage::Fragment, fragmentShader));
660 if (shaderDebug)
661 qDebug("Loading of vertex and fragment stages succeeded");
662 }
663
664 Q_QUICK3D_PROFILE_END_WITH_STRING(QQuick3DProfiler::Quick3DLoadShader, 0, inKey);
665
666 return shaders;
667}
668
669namespace QtQuick3DEditorHelpers {
671{
672 QMutexLocker locker(&*s_statusMutex);
674}
675
680
682{
683 return ::isAutoDiskCacheEnabled();
684}
685
686}
687
\inmodule QtCore
Definition qbytearray.h:57
qsizetype size() const noexcept
Returns the number of bytes in this byte array.
Definition qbytearray.h:494
static QByteArray number(int, int base=10)
Returns a byte-array representing the whole number n as text.
void clear()
Clears the contents of the byte array and makes it null.
QByteArray & append(char c)
This is an overloaded member function, provided for convenience. It differs from the above function o...
static QDir root()
Returns the root directory.
Definition qdir.h:224
bool isWritable() const
Returns true if the user can write to the file system entry this QFileInfo refers to; otherwise retur...
bool exists() const
Returns true if the file system entry this QFileInfo refers to exists; otherwise returns false.
\inmodule QtCore
Definition qfile.h:93
void setFileName(const QString &name)
Sets the name of the file.
Definition qfile.cpp:302
const_iterator constFind(const Key &key) const noexcept
Definition qhash.h:1299
const_iterator cend() const noexcept
Definition qhash.h:1218
void clear() noexcept(std::is_nothrow_destructible< Node >::value)
Removes all items from the hash and frees up all memory used by it.
Definition qhash.h:951
iterator insert(const Key &key, const T &value)
Inserts a new item with the key and a value of value.
Definition qhash.h:1303
\inmodule QtCore
Definition qmutex.h:313
\inmodule QtCore
Definition qmutex.h:281
static OpenGLModuleType openGLModuleType()
Returns the underlying OpenGL implementation type.
static FeatureSet toFeatureSet(const T &ssgFeatureSet)
Entry addEntry(const QByteArray &key, const EntryDesc &entryDesc) override
bool load(const QString &filename)
bool save(const QString &filename)
EntryMap availableEntries() const override
bool extractEntry(Entry entry, EntryDesc &entryDesc) override
\variable QRhiGles2InitParams::format
\inmodule QtGui
Definition qrhi.h:379
\inmodule QtGuiPrivate \inheaderfile rhi/qrhi.h
Definition qrhi.h:1804
Implementation backend() const
Definition qrhi.cpp:8651
@ Metal
Definition qrhi.h:1811
@ D3D11
Definition qrhi.h:1810
@ D3D12
Definition qrhi.h:1812
@ OpenGLES2
Definition qrhi.h:1809
const QRhiNativeHandles * nativeHandles()
Definition qrhi.cpp:10137
static bool shaderDebuggingEnabled()
\inmodule QtQuick3D
QRhi * rhi() const
QSSGRhiShaderPipelinePtr tryGetRhiShaderPipeline(const QByteArray &inKey, const QSSGShaderFeatures &inFeatures)
QSSGShaderCache(QSSGRhiContext &ctx, const InitBakerFunc initBakeFn=nullptr)
static QByteArray shaderCollectionFile()
static QByteArray resourceFolder()
QSSGRhiShaderPipelinePtr newPipelineFromPregenerated(const QByteArray &inKey, const QSSGShaderFeatures &inFeatures, QQsbCollection::Entry entry, const QSSGRenderGraphObject &obj, QSSGRhiShaderPipeline::StageFlags stageFlags={})
QSSGRhiShaderPipelinePtr compileForRhi(const QByteArray &inKey, const QByteArray &inVert, const QByteArray &inFrag, const QSSGShaderFeatures &inFeatures, QSSGRhiShaderPipeline::StageFlags stageFlags, int viewCount, bool perTargetCompilation)
QSSGRhiShaderPipelinePtr tryNewPipelineFromPersistentCache(const QByteArray &qsbcKey, const QByteArray &inKey, const QSSGShaderFeatures &inFeatures, QSSGRhiShaderPipeline::StageFlags stageFlags={})
void(*)(QShaderBaker *baker, QRhi *rhi) InitBakerFunc
qsizetype count() const
Definition qset.h:154
\inmodule QtGui
Definition qshader.h:32
\inmodule QtGui
Definition qshader.h:81
static QShader fromSerialized(const QByteArray &data)
Creates a new QShader instance from the given data.
Definition qshader.cpp:540
@ StandardShader
Definition qshader.h:104
@ SpirvShader
Definition qshader.h:93
@ GlslShader
Definition qshader.h:94
@ HlslShader
Definition qshader.h:95
@ MslShader
Definition qshader.h:97
Stage
Describes the stage of the graphics pipeline the shader is suitable for.
Definition qshader.h:83
@ VertexStage
Definition qshader.h:84
@ FragmentStage
Definition qshader.h:88
bool isValid() const
Definition qshader.cpp:343
static QString writableLocation(StandardLocation type)
\macro QT_RESTRICTED_CAST_FROM_ASCII
Definition qstring.h:129
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
QStringList split(const QString &sep, Qt::SplitBehavior behavior=Qt::KeepEmptyParts, Qt::CaseSensitivity cs=Qt::CaseSensitive) const
Splits the string into substrings wherever sep occurs, and returns the list of those strings.
Definition qstring.cpp:8218
bool isEmpty() const noexcept
Returns true if the string has no characters; otherwise returns false.
Definition qstring.h:192
qsizetype size_type
Definition qstring.h:950
static QString fromUtf8(QByteArrayView utf8)
This is an overloaded member function, provided for convenience. It differs from the above function o...
Definition qstring.cpp:6018
QString & insert(qsizetype i, QChar c)
Definition qstring.cpp:3132
The QSurfaceFormat class represents the format of a QSurface. \inmodule QtGui.
static QSurfaceFormat defaultFormat()
Returns the global default surface format.
static QString buildAbi()
Definition qsysinfo.cpp:657
EGLContext ctx
#define this
Definition dialogs.cpp:9
QString str
[2]
Combined button and popup list for selecting options.
Q_QUICK3DRUNTIMERENDER_EXPORT void setStatusCallback(StatusCallback cb)
void(*)(const QByteArray &descKey, Status status, const QString &err, QShader::Stage stage) StatusCallback
Q_QUICK3DRUNTIMERENDER_EXPORT void setAutomaticDiskCache(bool enable)
Q_QUICK3DRUNTIMERENDER_EXPORT bool isAutomaticDiskCacheEnabled()
@ AA_DisableShaderDiskCache
Definition qnamespace.h:462
#define QByteArrayLiteral(str)
Definition qbytearray.h:52
#define qApp
static const QPainterPath::ElementType * subPath(const QPainterPath::ElementType *t, const QPainterPath::ElementType *end, const qreal *points, bool *closed)
#define Q_GLOBAL_STATIC(TYPE, NAME,...)
static QByteArray cacheKey(Args &&...args)
#define qDebug
[1]
Definition qlogging.h:164
#define qWarning
Definition qlogging.h:166
GLenum GLenum GLsizei count
GLint GLsizei GLsizei GLenum GLenum GLsizei void * data
GLfloat GLfloat f
GLbitfield flags
GLboolean enable
GLuint name
GLint GLsizei GLsizei GLenum format
GLfloat GLfloat GLfloat GLfloat h
GLhandleARB obj
[2]
GLuint GLfloat * val
GLuint entry
GLsizei GLsizei GLuint * shaders
Definition qopenglext.h:677
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_START(Type)
#define Q_QUICK3D_PROFILE_END_WITH_ID(Type, Payload, POID)
#define Q_QUICK3D_PROFILE_END_WITH_STRING(Type, Payload, Str)
static QString persistentQsbcFileName()
static bool isAutoDiskCacheEnabled()
size_t qHash(QSSGShaderFeatures features) noexcept
static bool ensureWritableDir(const QString &name)
static constexpr DefineEntry DefineTable[]
static bool s_autoDiskCacheEnabled
static QString dumpFilename(QShader::Stage stage)
static void initBakerForPersistentUse(QShaderBaker *, QRhi *)
static QT_BEGIN_NAMESPACE QtQuick3DEditorHelpers::ShaderBaker::StatusCallback s_statusCallback
static QString persistentQsbcDir()
static void initBakerForNonPersistentUse(QShaderBaker *, QRhi *)
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: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_POINT(provider, tracepoint,...)
Definition qtrace_p.h:232
unsigned int quint32
Definition qtypes.h:50
QSSGShaderFeatures::Feature feature
\inmodule QtCore \reentrant
Definition qchar.h:18
static constexpr FlagType IndexMask
void set(Feature feature, bool val)
static constexpr quint32 Count
static const char * asDefineString(QSSGShaderFeatures::Feature feature)
static Feature fromIndex(quint32 idx)