Qt 6.x
The Qt SDK
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};
80
81static_assert(std::size(DefineTable) == QSSGShaderFeatures::Count, "Missing feature define?");
82
85
87{
88 if (val)
89 flags |= (static_cast<FlagType>(feature) & ~IndexMask);
90 else
91 flags &= ~(static_cast<FlagType>(feature) & ~IndexMask);
92}
93
94#ifdef QT_QUICK3D_HAS_RUNTIME_SHADERS
95static void initBakerForNonPersistentUse(QShaderBaker *baker, QRhi *rhi)
96{
98 switch (rhi->backend()) {
99 case QRhi::D3D11:
100 case QRhi::D3D12:
101 outputs.append({ QShader::HlslShader, QShaderVersion(50) }); // Shader Model 5.0
102 break;
103 case QRhi::Metal:
104 outputs.append({ QShader::MslShader, QShaderVersion(12) }); // Metal 1.2
105 break;
106 case QRhi::OpenGLES2:
107 {
109#if QT_CONFIG(opengl)
110 auto h = static_cast<const QRhiGles2NativeHandles *>(rhi->nativeHandles());
111 if (h && h->context)
112 format = h->context->format();
113#endif
114 if (format.profile() == QSurfaceFormat::CoreProfile && format.version() >= qMakePair(3, 3)) {
115 outputs.append({ QShader::GlslShader, QShaderVersion(330) }); // OpenGL 3.3+
116 } else {
117 bool isGLESModule = false;
118#if QT_CONFIG(opengl)
120#endif
121 if (format.renderableType() == QSurfaceFormat::OpenGLES || isGLESModule) {
122 if (format.majorVersion() >= 3)
123 outputs.append({ QShader::GlslShader, QShaderVersion(300, QShaderVersion::GlslEs) }); // GLES 3.0+
124 else
125 outputs.append({ QShader::GlslShader, QShaderVersion(100, QShaderVersion::GlslEs) }); // GLES 2.0
126 } else {
127 // Need to default to at least GLSL 130 (OpenGL 3.0), not 120.
128 // The difference is actually relevant when it comes to certain
129 // GLSL features (textureSize, unsigned integers, and with
130 // SPIRV-Cross even bool), and we do not have to care about
131 // pure OpenGL (non-ES) 2.x implementations in practice.
132
133 // For full feature set we need GLSL 140 (OpenGL 3.1), e.g.
134 // because of inverse() used for instancing.
135
136 // GLSL 130 should still be attempted, to support old Mesa
137 // llvmpipe that only gives us OpenGL 3.0. At the time of
138 // writing the opengl32sw.dll shipped with pre-built Qt is one
139 // of these still.
140
141 if (format.version() >= qMakePair(3, 1))
142 outputs.append({ QShader::GlslShader, QShaderVersion(140) }); // OpenGL 3.1+
143 else
144 outputs.append({ QShader::GlslShader, QShaderVersion(130) }); // OpenGL 3.0+
145 }
146 }
147 }
148 break;
149 default: // Vulkan, Null
150 outputs.append({ QShader::SpirvShader, QShaderVersion(100) });
151 break;
152 }
153
154 baker->setGeneratedShaders(outputs);
155 baker->setGeneratedShaderVariants({ QShader::StandardShader });
156}
157
158static void initBakerForPersistentUse(QShaderBaker *baker, QRhi *)
159{
161 outputs.reserve(8);
162 outputs.append({ QShader::SpirvShader, QShaderVersion(100) });
163 outputs.append({ QShader::HlslShader, QShaderVersion(50) }); // Shader Model 5.0
164 outputs.append({ QShader::MslShader, QShaderVersion(12) }); // Metal 1.2
165 outputs.append({ QShader::GlslShader, QShaderVersion(330) }); // OpenGL 3.3+
166 outputs.append({ QShader::GlslShader, QShaderVersion(140) }); // OpenGL 3.1+
167 outputs.append({ QShader::GlslShader, QShaderVersion(130) }); // OpenGL 3.0+
168 outputs.append({ QShader::GlslShader, QShaderVersion(100, QShaderVersion::GlslEs) }); // GLES 2.0
169 outputs.append({ QShader::GlslShader, QShaderVersion(300, QShaderVersion::GlslEs) }); // GLES 3.0+
170
171 // If one of the above cannot be generated due to failing at the
172 // SPIRV-Cross translation stage, it will be skipped, but bake() will not
173 // fail. This is essential, because with the default fail if anything fails
174 // behavior many shaders could not be baked at all due to failing for e.g.
175 // GLSL ES 100. This is a non-issue when choosing the targets dynamically
176 // based on the current API/context, but here we need to ensure what we
177 // generate will work with a different RHI backend, graphics API, and
178 // perhaps even on a different platform (if the cache file is manually
179 // moved). So have to generate what we can, without breaking the
180 // application when the shader is not compatible with a target. (if that
181 // shader is not used at runtime, it's fine anyway, it it is, it won't work
182 // just as with the other, non-caching path)
183 baker->setBreakOnShaderTranslationError(false);
184
185 baker->setGeneratedShaders(outputs);
186 baker->setGeneratedShaderVariants({ QShader::StandardShader });
187}
188
189#else
190static void initBakerForNonPersistentUse(QShaderBaker *, QRhi *)
191{
192}
193
194static void initBakerForPersistentUse(QShaderBaker *, QRhi *)
195{
196}
197#endif // QT_QUICK3D_HAS_RUNTIME_SHADERS
198
199static bool s_autoDiskCacheEnabled = true;
200
202{
203 // these three mirror QOpenGLShaderProgram/QQuickGraphicsConfiguration/QSGRhiSupport
204 static const bool diskCacheDisabled = qEnvironmentVariableIntValue("QT_DISABLE_SHADER_DISK_CACHE")
205 || qEnvironmentVariableIntValue("QSG_RHI_DISABLE_DISK_CACHE");
206 const bool attrDiskCacheDisabled = (qApp ? qApp->testAttribute(Qt::AA_DisableShaderDiskCache) : false);
207 return (!diskCacheDisabled && !attrDiskCacheDisabled && s_autoDiskCacheEnabled);
208
209}
210
211static inline bool ensureWritableDir(const QString &name)
212{
214 return QFileInfo(name).isWritable();
215}
216
218{
219 static bool checked = false;
220 static QString currentCacheDir;
221 static bool cacheWritable = false;
222
223 if (checked)
224 return cacheWritable ? currentCacheDir : QString();
225
226 checked = true;
228 const QString subPath = QLatin1String("/q3dshadercache-") + QSysInfo::buildAbi() + QLatin1Char('/');
229
230 if (!cachePath.isEmpty()) {
231 currentCacheDir = cachePath + subPath;
232 cacheWritable = ensureWritableDir(currentCacheDir);
233 }
234
235 return cacheWritable ? currentCacheDir : QString();
236}
237
239{
240 const QString cacheDir = persistentQsbcDir();
241 if (!cacheDir.isEmpty())
242 return cacheDir + QLatin1String("q3dshadercache.qsbc");
243
244 return QString();
245}
246
248 const InitBakerFunc initBakeFn)
249 : m_rhiContext(ctx),
250 m_initBaker(initBakeFn)
251{
254 m_persistentShaderStorageFileName = persistentQsbcFileName();
255 if (!m_persistentShaderStorageFileName.isEmpty()) {
256 const bool skipCacheFile = qEnvironmentVariableIntValue("QT_QUICK3D_NO_SHADER_CACHE_LOAD");
257 if (!skipCacheFile && QFileInfo::exists(m_persistentShaderStorageFileName)) {
258 if (shaderDebug)
259 qDebug("Attempting to seed material shader cache from %s", qPrintable(m_persistentShaderStorageFileName));
260 if (m_persistentShaderBakingCache.load(m_persistentShaderStorageFileName)) {
261 if (shaderDebug) {
262 const int count = m_persistentShaderBakingCache.availableEntries().count();
263 qDebug("Loaded %d shader pipelines into the material shader cache", count);
264 }
265 }
266 }
267 }
268 }
269
270 if (!m_initBaker) {
271 // It is important to generate all possible shader variants if the qsb
272 // collection is going to be stored on disk. Otherwise switching the
273 // rhi backend could break the application. This is however an overkill
274 // if we know that what we bake will not be reused in future runs of
275 // the application, so do not do it if the disk cache was disabled or
276 // the cache directory was not available (no file system, no
277 // permissions, etc.).
278 m_initBaker = m_persistentShaderStorageFileName.isEmpty() ? initBakerForNonPersistentUse
280 }
281}
282
284{
285 if (!m_persistentShaderStorageFileName.isEmpty())
286 m_persistentShaderBakingCache.save(m_persistentShaderStorageFileName);
287}
288
290{
291 m_rhiShaders.clear();
292
293 // m_persistentShaderBakingCache is not cleared, that is intentional,
294 // otherwise we would permanently lose what got loaded at startup.
295}
296
298 const QSSGShaderFeatures &inFeatures)
299{
300 QSSGShaderCacheKey cacheKey(inKey);
301 cacheKey.m_features = inFeatures;
302 cacheKey.updateHashCode();
303 const auto theIter = m_rhiShaders.constFind(cacheKey);
304 if (theIter != m_rhiShaders.cend())
305 return theIter.value();
306 return nullptr;
307}
308
309
310void QSSGShaderCache::addShaderPreprocessor(QByteArray &str,
311 const QByteArray &inKey,
312 ShaderType shaderType,
313 const QSSGShaderFeatures &inFeatures)
314{
315 m_insertStr.clear();
316
317 m_insertStr += "#version 440\n";
318
319 if (!inKey.isNull()) {
320 m_insertStr += "//Shader name -";
321 m_insertStr += inKey;
322 m_insertStr += "\n";
323 }
324
325 m_insertStr += "#define texture2D texture\n";
326
327 str.insert(0, m_insertStr);
328 QString::size_type insertPos = int(m_insertStr.size());
329
330 m_insertStr.clear();
331 const bool fragOutputEnabled = (!inFeatures.isSet(QSSGShaderFeatures::Feature::DepthPass)) && shaderType == ShaderType::Fragment;
332 for (const auto &def : DefineTable) {
333 m_insertStr.append("#define ");
334 m_insertStr.append(def.name);
335 m_insertStr.append(" ");
336 m_insertStr.append(inFeatures.isSet(def.feature) ? "1" : "0");
337 m_insertStr.append("\n");
338 }
339
340 str.insert(insertPos, m_insertStr);
341 insertPos += int(m_insertStr.size());
342
343 m_insertStr.clear();
344 if (fragOutputEnabled)
345 m_insertStr += "layout(location = 0) out vec4 fragOutput;\n";
346
347 str.insert(insertPos, m_insertStr);
348}
349
351{
352 return QByteArrayLiteral(":/res/rhishaders/");
353}
354
356{
357 return QByteArrayLiteral("qtappshaders.qsbc");
358}
359
361 const QSSGShaderFeatures &inFeatures, QSSGRhiShaderPipeline::StageFlags stageFlags)
362{
363#ifdef QT_QUICK3D_HAS_RUNTIME_SHADERS
364 const QSSGRhiShaderPipelinePtr &rhiShaders = tryGetRhiShaderPipeline(inKey, inFeatures);
365 if (rhiShaders)
366 return rhiShaders;
367
368 QSSGShaderCacheKey tempKey(inKey);
369 tempKey.m_features = inFeatures;
370 tempKey.updateHashCode();
371
372 QByteArray vertexCode = inVert;
373 QByteArray fragmentCode = inFrag;
374
375 if (!vertexCode.isEmpty())
376 addShaderPreprocessor(vertexCode, inKey, ShaderType::Vertex, inFeatures);
377
378 if (!fragmentCode.isEmpty())
379 addShaderPreprocessor(fragmentCode, inKey, ShaderType::Fragment, inFeatures);
380
381 // lo and behold the final shader strings are ready
382
384 QString vertErr, fragErr;
385
386 QShaderBaker baker;
387 m_initBaker(&baker, m_rhiContext.rhi());
388
389 const bool editorMode = QSSGRhiContext::editorMode();
390 // Shader debug is disabled in editor mode
391 const bool shaderDebug = !editorMode && QSSGRhiContext::shaderDebuggingEnabled();
392
393 static auto dumpShader = [](QShader::Stage stage, const QByteArray &code) {
394 switch (stage) {
396 qDebug("VERTEX SHADER:\n*****\n");
397 break;
399 qDebug("FRAGMENT SHADER:\n*****\n");
400 break;
401 default:
402 qDebug("SHADER:\n*****\n");
403 break;
404 }
405 const auto lines = code.split('\n');
406 for (int i = 0; i < lines.size(); i++)
407 qDebug("%3d %s", i + 1, lines.at(i).constData());
408 qDebug("\n*****\n");
409 };
410
411 static auto dumpShaderToFile = [](QShader::Stage stage, const QByteArray &data) {
412 QFile f(dumpFilename(stage));
414 f.write(data);
415 f.close();
416 };
417
418 baker.setSourceString(vertexCode, QShader::VertexStage);
419 QShader vertexShader = baker.bake();
420 const auto vertShaderValid = vertexShader.isValid();
421 if (!vertShaderValid) {
422 vertErr = baker.errorMessage();
423 if (!editorMode) {
424 qWarning("Failed to compile vertex shader:\n");
425 if (!shaderDebug)
426 qWarning() << inKey << '\n' << vertErr;
427 }
428 }
429
430 if (shaderDebug) {
431 dumpShader(QShader::Stage::VertexStage, vertexCode);
432 if (!vertShaderValid)
433 dumpShaderToFile(QShader::Stage::VertexStage, vertexCode);
434 }
435
436 baker.setSourceString(fragmentCode, QShader::FragmentStage);
437 QShader fragmentShader = baker.bake();
438 const bool fragShaderValid = fragmentShader.isValid();
439 if (!fragShaderValid) {
440 fragErr = baker.errorMessage();
441 if (!editorMode) {
442 qWarning("Failed to compile fragment shader \n");
443 if (!shaderDebug)
444 qWarning() << inKey << '\n' << fragErr;
445 }
446 }
447
448 if (shaderDebug) {
449 dumpShader(QShader::Stage::FragmentStage, fragmentCode);
450 if (!fragShaderValid)
451 dumpShaderToFile(QShader::Stage::FragmentStage, fragmentCode);
452 }
453
454 if (vertShaderValid && fragShaderValid) {
455 shaders = std::make_shared<QSSGRhiShaderPipeline>(m_rhiContext);
456 shaders->addStage(QRhiShaderStage(QRhiShaderStage::Vertex, vertexShader), stageFlags);
457 shaders->addStage(QRhiShaderStage(QRhiShaderStage::Fragment, fragmentShader), stageFlags);
458 if (shaderDebug)
459 qDebug("Compilation for vertex and fragment stages succeeded");
460 }
461
462 if (editorMode && s_statusCallback) {
464 const auto vertStatus = vertShaderValid ? Status::Success : Status::Error;
465 const auto fragStatus = fragShaderValid ? Status::Success : Status::Error;
466 QMutexLocker locker(&*s_statusMutex);
467 s_statusCallback(inKey, vertStatus, vertErr, QShader::VertexStage);
468 s_statusCallback(inKey, fragStatus, fragErr, QShader::FragmentStage);
469 }
470
471 auto result = m_rhiShaders.insert(tempKey, shaders).value();
472 if (result && result->vertexStage() && result->fragmentStage()) {
473 QQsbCollection::EntryDesc entryDesc = {
474 inKey,
476 result->vertexStage()->shader(),
477 result->fragmentStage()->shader()
478 };
479 m_persistentShaderBakingCache.addEntry(entryDesc.generateSha(), entryDesc);
480 }
481 return result;
482
483#else
484 Q_UNUSED(inKey);
485 Q_UNUSED(inVert);
486 Q_UNUSED(inFrag);
487 Q_UNUSED(inFeatures);
488 Q_UNUSED(stageFlags);
489 qWarning("Cannot compile and condition shaders at runtime because this build of Qt Quick 3D is not linking to Qt Shader Tools. "
490 "Only pre-processed materials are supported.");
491 return {};
492#endif
493}
494
496 const QSSGShaderFeatures &inFeatures,
499 QSSGRhiShaderPipeline::StageFlags stageFlags)
500{
501 // No lookup in m_rhiShaders. It is up to the caller to do that, if they
502 // want to. We will insert into it at the end, but there is intentionally
503 // no lookup. The result from this function is always a new
504 // QSSGRhiShaderPipeline (it's just much faster to create than the
505 // full-blown generator). That is important for some clients (effect
506 // system) so returning an existing QSSGRhiShaderPipeline is _wrong_.
507
509 if (shaderDebug)
510 qDebug("Loading pregenerated rhi shader(s)");
511
512 Q_TRACE_SCOPE(QSSG_loadShader);
513 Q_QUICK3D_PROFILE_START(QQuick3DProfiler::Quick3DLoadShader);
514
515 // Note that we are required to return a non-null (but empty) shader set even if loading fails.
517
519
520 QQsbIODeviceCollection qsbc(collectionFile);
523 qsbc.extractEntry(entry, entryDesc);
524 else
525 qWarning("Failed to open entry %s", entry.key.constData());
526
527 if (entryDesc.vertShader.isValid() && entryDesc.fragShader.isValid()) {
528 shaders->addStage(QRhiShaderStage(QRhiShaderStage::Vertex, entryDesc.vertShader), stageFlags);
529 shaders->addStage(QRhiShaderStage(QRhiShaderStage::Fragment, entryDesc.fragShader), stageFlags);
530 if (shaderDebug)
531 qDebug("Loading of vertex and fragment stages succeeded");
532 }
533
534 Q_QUICK3D_PROFILE_END_WITH_ID(QQuick3DProfiler::Quick3DLoadShader, 0, obj.profilingId);
535
536 QSSGShaderCacheKey cacheKey(inKey);
537 cacheKey.m_features = inFeatures;
538 cacheKey.updateHashCode();
539
540 const auto inserted = m_rhiShaders.insert(cacheKey, shaders);
541 qsbc.unmap();
542 return inserted.value();
543}
544
546 const QByteArray &inKey,
547 const QSSGShaderFeatures &inFeatures,
548 QSSGRhiShaderPipeline::StageFlags stageFlags)
549{
550 // No lookup in m_rhiShaders. it is up to the caller to do that, if they
551 // want to. We will insert into it at the end, but there is intentionally
552 // no lookup. The result from this function is always a new
553 // QSSGRhiShaderPipeline (it's just much faster to create than the
554 // full-blown generator). That is important for some clients (effect
555 // system) so returning an existing QSSGRhiShaderPipeline is _wrong_.
556
558
559 // Here we are allowed to return null to indicate that there is no such
560 // entry in this particular cache.
561 if (!m_persistentShaderBakingCache.extractEntry(QQsbCollection::Entry(qsbcKey), entryDesc))
562 return {};
563
564 if (entryDesc.vertShader.isValid() && entryDesc.fragShader.isValid()) {
566 if (shaderDebug)
567 qDebug("Loading rhi shaders from disk cache for %s (%s)", qsbcKey.constData(), inKey.constData());
568
570 shaders->addStage(QRhiShaderStage(QRhiShaderStage::Vertex, entryDesc.vertShader), stageFlags);
571 shaders->addStage(QRhiShaderStage(QRhiShaderStage::Fragment, entryDesc.fragShader), stageFlags);
572 QSSGShaderCacheKey cacheKey(inKey);
573 cacheKey.m_features = inFeatures;
574 cacheKey.updateHashCode();
575 return m_rhiShaders.insert(cacheKey, shaders).value();
576 }
577
578 return {};
579}
580
582{
584 if (rhiShaders)
585 return rhiShaders;
586
588 if (shaderDebug)
589 qDebug("Loading builtin rhi shader: %s", inKey.constData());
590
591 Q_TRACE_SCOPE(QSSG_loadShader);
592 Q_QUICK3D_PROFILE_START(QQuick3DProfiler::Quick3DLoadShader);
593
594 // Note that we are required to return a non-null (but empty) shader set even if loading fails.
596
597 // inShaderName is a prefix of a .qsb file, so "abc" means we should
598 // look for abc.vert.qsb and abc.frag.qsb.
599
600 const QString prefix = QString::fromUtf8(resourceFolder() + inKey);
601 const QString vertexFileName = prefix + QLatin1String(".vert.qsb");
602 const QString fragmentFileName = prefix + QLatin1String(".frag.qsb");
603
604 QShader vertexShader;
605 QShader fragmentShader;
606
607 QFile f;
608 f.setFileName(vertexFileName);
609 if (f.open(QIODevice::ReadOnly)) {
610 const QByteArray vsData = f.readAll();
611 vertexShader = QShader::fromSerialized(vsData);
612 f.close();
613 } else {
614 qWarning("Failed to open %s", qPrintable(f.fileName()));
615 }
616 f.setFileName(fragmentFileName);
617 if (f.open(QIODevice::ReadOnly)) {
618 const QByteArray fsData = f.readAll();
619 fragmentShader = QShader::fromSerialized(fsData);
620 f.close();
621 } else {
622 qWarning("Failed to open %s", qPrintable(f.fileName()));
623 }
624
625 if (vertexShader.isValid() && fragmentShader.isValid()) {
627 shaders->addStage(QRhiShaderStage(QRhiShaderStage::Fragment, fragmentShader));
628 if (shaderDebug)
629 qDebug("Loading of vertex and fragment stages succeeded");
630 }
631
632 Q_QUICK3D_PROFILE_END_WITH_STRING(QQuick3DProfiler::Quick3DLoadShader, 0, inKey);
633
634 QSSGShaderCacheKey cacheKey(inKey);
635 cacheKey.m_features = QSSGShaderFeatures();
636 cacheKey.updateHashCode();
637
638 const auto inserted = m_rhiShaders.insert(cacheKey, shaders);
639 return inserted.value();
640}
641
642namespace QtQuick3DEditorHelpers {
644{
645 QMutexLocker locker(&*s_statusMutex);
647}
648
650{
652}
653
655{
656 return ::isAutoDiskCacheEnabled();
657}
658
659}
660
\inmodule QtCore
Definition qbytearray.h:57
qsizetype size() const noexcept
Returns the number of bytes in this byte array.
Definition qbytearray.h:474
const char * constData() const noexcept
Returns a pointer to the const data stored in the byte array.
Definition qbytearray.h:122
bool isEmpty() const noexcept
Returns true if the byte array has size 0; otherwise returns false.
Definition qbytearray.h:106
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...
bool isNull() const noexcept
Returns true if this byte array is null; otherwise returns false.
static QDir root()
Returns the root directory.
Definition qdir.h:221
bool mkpath(const QString &dirPath) const
Creates the directory path dirPath.
Definition qdir.cpp:1579
\inmodule QtCore \reentrant
Definition qfileinfo.h:22
bool isWritable() const
Returns true if the user can write to the file; otherwise returns false.
bool exists() const
Returns true if the file exists; otherwise returns false.
\inmodule QtCore
Definition qfile.h:93
const_iterator constFind(const Key &key) const noexcept
Definition qhash.h:1279
const_iterator cend() const noexcept
Definition qhash.h:1208
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:949
iterator insert(const Key &key, const T &value)
Inserts a new item with the key and a value of value.
Definition qhash.h:1283
Definition qlist.h:74
void reserve(qsizetype size)
Definition qlist.h:746
void append(parameter_type t)
Definition qlist.h:441
\inmodule QtCore
Definition qmutex.h:317
\inmodule QtCore
Definition qmutex.h:285
static OpenGLModuleType openGLModuleType()
Returns the underlying OpenGL implementation type.
static FeatureSet toFeatureSet(const T &ssgFeatureSet)
bool map(MapMode mode)
bool extractEntry(Entry entry, EntryDesc &entryDesc) override
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:371
\inmodule QtGui
Definition qrhi.h:1767
Implementation backend() const
Definition qrhi.cpp:8289
@ Metal
Definition qrhi.h:1774
@ D3D11
Definition qrhi.h:1773
@ D3D12
Definition qrhi.h:1775
@ OpenGLES2
Definition qrhi.h:1772
const QRhiNativeHandles * nativeHandles()
Definition qrhi.cpp:9708
static bool shaderDebuggingEnabled()
static bool editorMode()
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)
QSSGRhiShaderPipelinePtr loadBuiltinForRhi(const QByteArray &inKey)
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:510
@ 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:313
static QString writableLocation(StandardLocation type)
\macro QT_RESTRICTED_CAST_FROM_ASCII
Definition qstring.h:127
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
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:7956
qsizetype size_type
Definition qstring.h:861
static QString fromUtf8(QByteArrayView utf8)
This is an overloaded member function, provided for convenience. It differs from the above function o...
Definition qstring.cpp:5857
bool isEmpty() const
Returns true if the string has no characters; otherwise returns false.
Definition qstring.h:1083
QString & insert(qsizetype i, QChar c)
Definition qstring.cpp:3110
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:656
EGLContext ctx
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:461
#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,...)
#define qDebug
[1]
Definition qlogging.h:160
#define qWarning
Definition qlogging.h:162
GLenum GLenum GLsizei count
GLfloat GLfloat f
GLbitfield flags
GLboolean enable
GLint GLsizei GLsizei GLenum GLenum GLsizei void * data
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]
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:1391
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:45
QSSGShaderFeatures::Feature feature
\inmodule QtCore \reentrant
Definition qchar.h:17
QByteArray generateSha() const
QSSGShaderFeatures m_features
constexpr bool isSet(Feature feature) const
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)