7#include "graphobjects/qssgrendergraphobject_p.h"
11#include <QtQuick3DUtils/private/qssgutils_p.h>
12#include <QtQuick3DUtils/private/qquick3dprofiler_p.h>
14#include <QtQuick3DRuntimeRender/private/qssgruntimerenderlogging_p.h>
15#include <qtquick3d_tracepoints_p.h>
17#include <QCoreApplication>
18#include <QStandardPaths>
23#include <QtGui/qsurfaceformat.h>
25# include <QtGui/qopenglcontext.h>
28#ifdef QT_QUICK3D_HAS_RUNTIME_SHADERS
29#include <rhi/qshaderbaker.h>
32#include <QtCore/qmutex.h>
36Q_TRACE_POINT(qtquick3d, QSSG_loadShader_entry)
37Q_TRACE_POINT(qtquick3d, QSSG_loadShader_exit)
39static QtQuick3DEditorHelpers::ShaderBaker::StatusCallback s_statusCallback =
nullptr;
42size_t qHash(QSSGShaderFeatures features)
noexcept {
return (features.flags & (~QSSGShaderFeatures::IndexMask)); }
44#ifdef QT_QUICK3D_HAS_RUNTIME_SHADERS
46static QString dumpFilename(QShader::Stage stage)
49 case QShader::VertexStage:
50 return QStringLiteral(
"failedvert.txt");
52 case QShader::FragmentStage:
53 return QStringLiteral(
"failedfrag.txt");
55 case QShader::ComputeStage:
56 return QStringLiteral(
"failedcomp.txt");
59 return QStringLiteral(
"failedshader.txt");
63static void dumpShader(QShader::Stage stage,
const QByteArray &code)
66 case QShader::Stage::VertexStage:
67 qDebug(
"VERTEX SHADER:\n*****\n");
69 case QShader::Stage::FragmentStage:
70 qDebug(
"FRAGMENT SHADER:\n*****\n");
72 case QShader::Stage::ComputeStage:
73 qDebug(
"COMPUTE SHADER:\n*****\n");
76 qDebug(
"SHADER:\n*****\n");
79 const auto lines = code.split(
'\n');
80 for (
int i = 0; i < lines.size(); i++)
81 qDebug(
"%3d %s", i + 1, lines.at(i).constData());
85static void dumpShaderToFile(QShader::Stage stage,
const QByteArray &data)
87 QFile f(dumpFilename(stage));
88 if (f.open(QIODevice::WriteOnly | QIODevice::Text)) {
91 qWarning(
"Failed to write to file: %s (%s)\n",
92 qPrintable(f.fileName()), qPrintable(f.errorString()));
99 const char *
name =
nullptr;
104 {
"QSSG_ENABLE_LIGHT_PROBE", QSSGShaderFeatures::Feature::LightProbe },
105 {
"QSSG_ENABLE_IBL_ORIENTATION", QSSGShaderFeatures::Feature::IblOrientation },
106 {
"QSSG_ENABLE_SSM", QSSGShaderFeatures::Feature::Ssm },
107 {
"QSSG_ENABLE_SSAO", QSSGShaderFeatures::Feature::Ssao },
108 {
"QSSG_ENABLE_DEPTH_PASS", QSSGShaderFeatures::Feature::DepthPass },
109 {
"QSSG_ENABLE_ORTHO_SHADOW_PASS", QSSGShaderFeatures::Feature::OrthoShadowPass },
110 {
"QSSG_ENABLE_PERSPECTIVE_SHADOW_PASS", QSSGShaderFeatures::Feature::PerspectiveShadowPass },
111 {
"QSSG_ENABLE_LINEAR_TONEMAPPING", QSSGShaderFeatures::Feature::LinearTonemapping },
112 {
"QSSG_ENABLE_ACES_TONEMAPPING", QSSGShaderFeatures::Feature::AcesTonemapping },
113 {
"QSSG_ENABLE_HEJLDAWSON_TONEMAPPING", QSSGShaderFeatures::Feature::HejlDawsonTonemapping },
114 {
"QSSG_ENABLE_FILMIC_TONEMAPPING", QSSGShaderFeatures::Feature::FilmicTonemapping },
115 {
"QSSG_ENABLE_RGBE_LIGHT_PROBE", QSSGShaderFeatures::Feature::RGBELightProbe },
116 {
"QSSG_ENABLE_OPAQUE_DEPTH_PRE_PASS", QSSGShaderFeatures::Feature::OpaqueDepthPrePass },
117 {
"QSSG_ENABLE_REFLECTION_PROBE", QSSGShaderFeatures::Feature::ReflectionProbe },
118 {
"QSSG_REDUCE_MAX_NUM_LIGHTS", QSSGShaderFeatures::Feature::ReduceMaxNumLights },
119 {
"QSSG_ENABLE_LIGHTMAP", QSSGShaderFeatures::Feature::Lightmap },
120 {
"QSSG_DISABLE_MULTIVIEW", QSSGShaderFeatures::Feature::DisableMultiView },
121 {
"QSSG_FORCE_IBL_EXPOSURE", QSSGShaderFeatures::Feature::ForceIblExposure },
122 {
"QSSG_ENABLE_NORMAL_PASS", QSSGShaderFeatures::Feature::NormalPass },
123 {
"QSSG_ENABLE_USER_RENDER_PASS", QSSGShaderFeatures::Feature::UserRenderPass},
124 {
"QSSG_ENABLE_MOTION_VECTOR", QSSGShaderFeatures::Feature::MotionVector }
127static_assert(std::size(DefineTable) == QSSGShaderFeatures::Count,
"Missing feature define?");
129const char *QSSGShaderFeatures::asDefineString(QSSGShaderFeatures::Feature feature) {
return DefineTable[
static_cast<FlagType>(feature) & QSSGShaderFeatures::IndexMask].name; }
130QSSGShaderFeatures::Feature QSSGShaderFeatures::fromIndex(quint32 idx) {
return DefineTable[idx].feature; }
132void QSSGShaderFeatures::set(QSSGShaderFeatures::Feature feature,
bool val)
135 flags |= (
static_cast<FlagType>(feature) & ~IndexMask);
137 flags &= ~(
static_cast<FlagType>(feature) & ~IndexMask);
140QList<QShaderVersion> QSSGShaderCache::glslTargetsForContext(
const QSurfaceFormat &format,
bool isGLESModule)
142 QList<QShaderVersion> versions;
147 const auto fromVersion = [](
const std::pair<
int,
int> &version,
const QShaderVersion::Flags flags = {}) -> QShaderVersion {
148 return QShaderVersion(version.first * 100 + version.second * 10, flags);
151 const auto version = format.version();
153 if (format.profile() == QSurfaceFormat::CoreProfile && version >= qMakePair(3, 3)) {
156 versions.append(QShaderVersion(330));
158 if (version > qMakePair(3, 3))
159 versions.append(fromVersion(version));
160 }
else if (format.renderableType() == QSurfaceFormat::OpenGLES || isGLESModule) {
161 if (format.majorVersion() >= 3) {
164 versions.append(QShaderVersion(300, QShaderVersion::GlslEs));
166 if (version > qMakePair(3, 0))
167 versions.append(fromVersion(version, QShaderVersion::GlslEs));
169 versions.append(QShaderVersion(100, QShaderVersion::GlslEs));
186 versions.append(QShaderVersion(version >= qMakePair(3, 1) ? 140 : 130));
187 if (version >= qMakePair(3, 3))
188 versions.append(fromVersion(version));
194#ifdef QT_QUICK3D_HAS_RUNTIME_SHADERS
195static void initBakerForNonPersistentUse(QShaderBaker *baker, QRhi *rhi)
197 QVector<QShaderBaker::GeneratedShader> outputs;
198 switch (rhi->backend()) {
200 outputs.append({ QShader::HlslShader, QShaderVersion(50) });
203 outputs.append({ QShader::HlslShader, QShaderVersion(61) });
206#if defined(Q_OS_VISIONOS)
207 outputs.append({ QShader::MslShader, QShaderVersion(21) });
209 outputs.append({ QShader::MslShader, QShaderVersion(12) });
212 case QRhi::OpenGLES2:
214 QSurfaceFormat format = QSurfaceFormat::defaultFormat();
215 bool isGLESModule =
false;
217 auto h =
static_cast<
const QRhiGles2NativeHandles *>(rhi->nativeHandles());
219 format = h->context->format();
220 isGLESModule = QOpenGLContext::openGLModuleType() == QOpenGLContext::LibGLES;
222 const QList<QShaderVersion> versions = QSSGShaderCache::glslTargetsForContext(format, isGLESModule);
223 outputs.reserve(outputs.size() + versions.size());
224 for (
const QShaderVersion &version : versions)
225 outputs.append({ QShader::GlslShader, version });
229 outputs.append({ QShader::SpirvShader, QShaderVersion(100) });
235 baker->setBreakOnShaderTranslationError(
false);
236 baker->setGeneratedShaders(outputs);
237 baker->setGeneratedShaderVariants({ QShader::StandardShader });
240void QSSGShaderCache::initBakerForPersistentUse(QShaderBaker *baker, QRhi *)
242 QVector<QShaderBaker::GeneratedShader> outputs;
246 outputs.append({ QShader::SpirvShader, QShaderVersion(100) });
247 outputs.append({ QShader::HlslShader, QShaderVersion(50) });
248 outputs.append({ QShader::HlslShader, QShaderVersion(61) });
249#if defined(Q_OS_VISIONOS)
250 outputs.append({ QShader::MslShader, QShaderVersion(21) });
252 outputs.append({ QShader::MslShader, QShaderVersion(12) });
255 outputs.append({ QShader::GlslShader, QShaderVersion(430) });
256 outputs.append({ QShader::GlslShader, QShaderVersion(420) });
257 outputs.append({ QShader::GlslShader, QShaderVersion(330) });
258 outputs.append({ QShader::GlslShader, QShaderVersion(140) });
259 outputs.append({ QShader::GlslShader, QShaderVersion(130) });
260 outputs.append({ QShader::GlslShader, QShaderVersion(100, QShaderVersion::GlslEs) });
262 outputs.append({ QShader::GlslShader, QShaderVersion(300, QShaderVersion::GlslEs) });
263 outputs.append({ QShader::GlslShader, QShaderVersion(310, QShaderVersion::GlslEs) });
264 outputs.append({ QShader::GlslShader, QShaderVersion(320, QShaderVersion::GlslEs) });
278 baker->setBreakOnShaderTranslationError(
false);
280 baker->setGeneratedShaders(outputs);
281 baker->setGeneratedShaderVariants({ QShader::StandardShader });
289void QSSGShaderCache::initBakerForPersistentUse(QShaderBaker *, QRhi *)
299 static const bool diskCacheDisabled = qEnvironmentVariableIntValue(
"QT_DISABLE_SHADER_DISK_CACHE")
300 || qEnvironmentVariableIntValue(
"QSG_RHI_DISABLE_DISK_CACHE");
301 const bool attrDiskCacheDisabled = (qApp ? qApp->testAttribute(Qt::AA_DisableShaderDiskCache) :
false);
308 QDir::root().mkpath(name);
309 return QFileInfo(name).isWritable();
314 static bool checked =
false;
315 static QString currentCacheDir;
316 static bool cacheWritable =
false;
319 return cacheWritable ? currentCacheDir : QString();
322 const QString cachePath = QStandardPaths::writableLocation(QStandardPaths::CacheLocation);
323 const QString subPath = QLatin1String(
"/q3dshadercache-") + QSysInfo::buildAbi() + QLatin1Char(
'/');
325 if (!cachePath.isEmpty()) {
326 currentCacheDir = cachePath + subPath;
327 cacheWritable = ensureWritableDir(currentCacheDir);
330 return cacheWritable ? currentCacheDir : QString();
335 const QString cacheDir = persistentQsbcDir();
336 if (!cacheDir.isEmpty())
337 return cacheDir + QLatin1String(
"q3dshadercache.qsbc");
342QSSGShaderCache::QSSGShaderCache(QSSGRhiContext &ctx,
343 const InitBakerFunc initBakeFn)
345 m_initBaker(initBakeFn),
346 m_builtInShaders(*
this)
348 if (isAutoDiskCacheEnabled()) {
349 const bool shaderDebug = !QSSGRhiContextPrivate::editorMode() && QSSGRhiContextPrivate::shaderDebuggingEnabled();
350 m_persistentShaderStorageFileName = persistentQsbcFileName();
351 if (!m_persistentShaderStorageFileName.isEmpty()) {
352 const bool skipCacheFile = qEnvironmentVariableIntValue(
"QT_QUICK3D_NO_SHADER_CACHE_LOAD");
353 if (!skipCacheFile && QFileInfo::exists(m_persistentShaderStorageFileName)) {
355 qDebug(
"Attempting to seed material shader cache from %s", qPrintable(m_persistentShaderStorageFileName));
356 if (m_persistentShaderBakingCache.load(m_persistentShaderStorageFileName)) {
358 const int count = m_persistentShaderBakingCache.availableEntries().count();
359 qDebug(
"Loaded %d shader pipelines into the material shader cache", count);
374 m_initBaker = m_persistentShaderStorageFileName.isEmpty() ? initBakerForNonPersistentUse
375 : QSSGShaderCache::initBakerForPersistentUse;
379QSSGShaderCache::~QSSGShaderCache()
381 if (!m_persistentShaderStorageFileName.isEmpty())
382 m_persistentShaderBakingCache.save(m_persistentShaderStorageFileName);
385void QSSGShaderCache::releaseCachedResources()
387 m_builtInShaders.releaseCachedResources();
389 m_rhiShaders.clear();
395QSSGRhiShaderPipelinePtr QSSGShaderCache::tryGetRhiShaderPipeline(
const QByteArray &inKey,
396 const QSSGShaderFeatures &inFeatures)
398 QSSGShaderCacheKey cacheKey(inKey);
399 cacheKey.m_features = inFeatures;
400 cacheKey.updateHashCode();
401 const auto theIter = m_rhiShaders.constFind(cacheKey);
402 if (theIter != m_rhiShaders.cend())
403 return theIter.value();
408void QSSGShaderCache::addShaderPreprocessor(QByteArray &str,
409 const QByteArray &inKey,
410 ShaderType shaderType,
411 const QSSGShaderFeatures &inFeatures,
412 const QSSGUserShaderAugmentation &shaderAugmentation,
417 m_insertStr +=
"#version 440\n";
419 if (!inKey.isNull()) {
420 m_insertStr +=
"//Shader name -";
421 m_insertStr += inKey;
425 m_insertStr +=
"#define texture2D texture\n";
428 m_insertStr +=
"#define QSHADER_VIEW_COUNT ";
429 m_insertStr += QByteArray::number(viewCount);
432 str.insert(0, m_insertStr);
433 QString::size_type insertPos =
int(m_insertStr.size());
436 const bool fragOutputEnabled = (!inFeatures.isSet(QSSGShaderFeatures::Feature::DepthPass)) && shaderType == ShaderType::Fragment;
437 for (
const auto &def : DefineTable) {
438 m_insertStr.append(
"#define ");
439 m_insertStr.append(def.name);
440 m_insertStr.append(
" ");
441 m_insertStr.append(inFeatures.isSet(def.feature) ?
"1" :
"0");
442 m_insertStr.append(
"\n");
445 str.insert(insertPos, m_insertStr);
446 insertPos +=
int(m_insertStr.size());
449 if (fragOutputEnabled) {
450 if (shaderAugmentation.outputs.size() != 0) {
451 const auto &outputs = shaderAugmentation.outputs;
452 for (
int i = 0; i < outputs.size(); ++i) {
453 m_insertStr +=
"layout(location = ";
454 m_insertStr += QByteArray::number(i);
455 m_insertStr +=
") out vec4 ";
456 m_insertStr += outputs[i];
457 m_insertStr +=
";\n";
460 m_insertStr +=
"layout(location = 0) out vec4 fragOutput;\n";
464 str.insert(insertPos, m_insertStr);
467QByteArray QSSGShaderCache::resourceFolder()
469 return QByteArrayLiteral(
":/res/rhishaders/");
472QByteArray QSSGShaderCache::shaderCollectionFile()
474 return QByteArrayLiteral(
"qtappshaders.qsbc");
477QByteArray QSSGShaderCache::particleShaderCollectionFile()
479 return QByteArrayLiteral(
"qtparticleshaders.qsbc");
482QSSGRhiShaderPipelinePtr QSSGShaderCache::compileGraphicsPipeline(
483 const QByteArray &inKey,
const QByteArray &inVert,
const QByteArray &inFrag,
484 const QSSGShaderFeatures &inFeatures, QSSGRhiShaderPipeline::StageFlags stageFlags,
485 const QSSGUserShaderAugmentation &shaderAugmentation,
486 int viewCount,
bool perTargetCompilation,
const QByteArray &inCacheKey)
488#ifdef QT_QUICK3D_HAS_RUNTIME_SHADERS
492 const QByteArray &cacheKeyString = inCacheKey.isEmpty() ? inKey : inCacheKey;
494 const QSSGRhiShaderPipelinePtr &rhiShaders = tryGetRhiShaderPipeline(cacheKeyString, inFeatures);
496 QSSG_ASSERT(rhiShaders->vertexStage() && rhiShaders->fragmentStage(),
return {});
500 QSSGShaderCacheKey tempKey(cacheKeyString);
501 tempKey.m_features = inFeatures;
502 tempKey.updateHashCode();
504 QByteArray vertexCode = inVert;
505 QByteArray fragmentCode = inFrag;
507 if (!vertexCode.isEmpty())
508 addShaderPreprocessor(vertexCode, inKey, ShaderType::Vertex, inFeatures, shaderAugmentation, viewCount);
510 if (!fragmentCode.isEmpty())
511 addShaderPreprocessor(fragmentCode, inKey, ShaderType::Fragment, inFeatures, shaderAugmentation, viewCount);
515 QSSGRhiShaderPipelinePtr shaders;
516 QString vertErr, fragErr;
519 m_initBaker(&baker, m_rhiContext.rhi());
524 baker.setPerTargetCompilation(perTargetCompilation);
528 baker.setMultiViewCount(viewCount);
532 const bool mediumPrecision = qEnvironmentVariableIntValue(
"QT_QUICK3D_MEDIUM_PRECISION");
534 baker.setGlslOptions(QShaderBaker::GlslOption::GlslEsFragDefaultFloatPrecisionMedium);
536 const bool editorMode = QSSGRhiContextPrivate::editorMode();
538 const bool shaderDebug = !editorMode && QSSGRhiContextPrivate::shaderDebuggingEnabled();
540 baker.setSourceString(vertexCode, QShader::VertexStage);
541 QShader vertexShader = baker.bake();
542 const auto vertShaderValid = vertexShader.isValid();
543 if (!vertShaderValid) {
544 vertErr = baker.errorMessage();
546 qWarning(
"Failed to compile vertex shader: %s\n", qPrintable(vertErr));
548 qWarning() << inKey <<
'\n';
553 dumpShader(QShader::Stage::VertexStage, vertexCode);
554 if (!vertShaderValid)
555 dumpShaderToFile(QShader::Stage::VertexStage, vertexCode);
558 baker.setSourceString(fragmentCode, QShader::FragmentStage);
559 QShader fragmentShader = baker.bake();
560 const bool fragShaderValid = fragmentShader.isValid();
561 if (!fragShaderValid) {
562 fragErr = baker.errorMessage();
564 qWarning(
"Failed to compile fragment shader: %s\n", qPrintable(fragErr));
566 qWarning() << inKey <<
'\n';
571 dumpShader(QShader::Stage::FragmentStage, fragmentCode);
572 if (!fragShaderValid)
573 dumpShaderToFile(QShader::Stage::FragmentStage, fragmentCode);
576 if (vertShaderValid && fragShaderValid) {
577 shaders = std::make_shared<QSSGRhiShaderPipeline>(m_rhiContext);
578 shaders->addStage(QRhiShaderStage(QRhiShaderStage::Vertex, vertexShader), stageFlags);
579 shaders->addStage(QRhiShaderStage(QRhiShaderStage::Fragment, fragmentShader), stageFlags);
581 qDebug(
"Compilation for vertex and fragment stages succeeded");
584 if (editorMode && s_statusCallback) {
585 using namespace QtQuick3DEditorHelpers::ShaderBaker;
586 const auto vertStatus = vertShaderValid ? Status::Success : Status::Error;
587 const auto fragStatus = fragShaderValid ? Status::Success : Status::Error;
588 QMutexLocker locker(&*s_statusMutex);
589 s_statusCallback(inKey, vertStatus, vertErr, QShader::VertexStage);
590 s_statusCallback(inKey, fragStatus, fragErr, QShader::FragmentStage);
593 auto result = m_rhiShaders.insert(tempKey, shaders).value();
594 if (result && result->vertexStage() && result->fragmentStage()) {
595 QQsbCollection::EntryDesc entryDesc = {
597 QQsbCollection::toFeatureSet(inFeatures),
598 result->vertexStage()->shader(),
599 result->fragmentStage()->shader()
601 m_persistentShaderBakingCache.addEntry(entryDesc.generateSha(), entryDesc);
609 Q_UNUSED(inFeatures);
610 Q_UNUSED(stageFlags);
611 Q_UNUSED(inCacheKey);
612 qWarning(
"Cannot compile and condition shaders at runtime because this build of Qt Quick 3D is not linking to Qt Shader Tools. "
613 "Only pre-processed materials are supported.");
618QSSGRhiShaderPipelinePtr QSSGShaderCache::compileComputePipeline(
619 const QByteArray &inKey,
const QByteArray &inComp,
620 int viewCount,
bool perTargetCompilation,
const QByteArray &inCacheKey)
622#ifdef QT_QUICK3D_HAS_RUNTIME_SHADERS
623 const QByteArray &cacheKeyString = inCacheKey.isEmpty() ? inKey : inCacheKey;
624 const QSSGRhiShaderPipelinePtr &rhiShaders = tryGetRhiShaderPipeline(cacheKeyString, {});
626 QSSG_ASSERT(rhiShaders->computeStage(),
return {});
629 QSSGShaderCacheKey tempKey(cacheKeyString);
630 tempKey.updateHashCode();
633 m_initBaker(&baker, m_rhiContext.rhi());
635 QByteArray computeCode =
"#version 440\n";
637 if (!inKey.isNull()) {
638 computeCode +=
"//Shader name -";
639 computeCode += inKey;
644 computeCode +=
"#define QSHADER_VIEW_COUNT ";
645 computeCode += QByteArray::number(viewCount);
648 computeCode += inComp;
653 baker.setPerTargetCompilation(perTargetCompilation);
654 baker.setSourceString(computeCode, QShader::ComputeStage);
655 baker.setMultiViewCount(viewCount);
657 const bool editorMode = QSSGRhiContextPrivate::editorMode();
659 const bool shaderDebug = !editorMode && QSSGRhiContextPrivate::shaderDebuggingEnabled();
661 QShader computeShader = baker.bake();
662 QSSGRhiShaderPipelinePtr shaders;
663 const auto valid = computeShader.isValid();
665 dumpShader(QShader::Stage::ComputeStage, computeCode);
667 dumpShaderToFile(QShader::Stage::ComputeStage, computeCode);
670 shaders = std::make_shared<QSSGRhiShaderPipeline>(m_rhiContext);
671 shaders->addStage(QRhiShaderStage(QRhiShaderStage::Compute, computeShader), {});
673 qDebug(
"Compilation for compute stage succeeded");
674 }
else if (!editorMode) {
675 qWarning(
"Failed to compile compute shader: %s\n", qPrintable(baker.errorMessage()));
677 qWarning() << inKey <<
'\n';
679 auto result = m_rhiShaders.insert(tempKey, shaders).value();
680 if (result && result->computeStage()) {
681 QQsbCollection::EntryDesc entryDesc = {
684 result->computeStage()->shader(),
687 m_persistentShaderBakingCache.addEntry(entryDesc.generateSha(), entryDesc);
694 Q_UNUSED(perTargetCompilation);
695 Q_UNUSED(inCacheKey);
696 qWarning(
"Cannot compile and condition shaders at runtime because this build of Qt Quick 3D is not linking to Qt Shader Tools. "
697 "Only pre-processed materials are supported.");
702QSSGRhiShaderPipelinePtr QSSGShaderCache::newPipelineFromPregenerated(
const QByteArray &inKey,
703 const QSSGShaderFeatures &inFeatures,
704 QQsbCollection::Entry entry,
705 const QSSGRenderGraphObject &obj,
706 QSSGRhiShaderPipeline::StageFlags stageFlags)
715 const bool shaderDebug = !QSSGRhiContextPrivate::editorMode() && QSSGRhiContextPrivate::shaderDebuggingEnabled();
717 qDebug(
"Loading pregenerated rhi shader(s)");
719 Q_TRACE_SCOPE(QSSG_loadShader);
720 Q_QUICK3D_PROFILE_START(QQuick3DProfiler::Quick3DLoadShader);
723 QSSGRhiShaderPipelinePtr shaders(
new QSSGRhiShaderPipeline(m_rhiContext));
725 const QStringList collectionFiles
726 = {QString::fromLatin1(resourceFolder() + shaderCollectionFile()),
727 QString::fromLatin1(resourceFolder() + particleShaderCollectionFile())};
729 QQsbCollection::EntryDesc entryDesc;
730 QScopedPointer<QQsbIODeviceCollection> qsbc;
731 for (
const auto &collectionFile : collectionFiles) {
732 QFileInfo info(collectionFile);
736 qsbc.reset(
new QQsbIODeviceCollection(collectionFile));
737 if (qsbc->map(QQsbIODeviceCollection::Read)) {
738 if (qsbc->extractEntry(entry, entryDesc))
746 qWarning(
"Failed to open entry %s", entry.key.constData());
748 if (entryDesc.vertShader.isValid() && entryDesc.fragShader.isValid()) {
749 shaders->addStage(QRhiShaderStage(QRhiShaderStage::Vertex, entryDesc.vertShader), stageFlags);
750 shaders->addStage(QRhiShaderStage(QRhiShaderStage::Fragment, entryDesc.fragShader), stageFlags);
752 qDebug(
"Loading of vertex and fragment stages succeeded");
755#if !QT_CONFIG(qml_debug)
758 Q_QUICK3D_PROFILE_END_WITH_ID(QQuick3DProfiler::Quick3DLoadShader, 0, obj.profilingId);
761 QSSGShaderCacheKey cacheKey(inKey);
762 cacheKey.m_features = inFeatures;
763 cacheKey.updateHashCode();
765 const auto inserted = m_rhiShaders.insert(cacheKey, shaders);
768 return inserted.value();
771QSSGRhiShaderPipelinePtr QSSGShaderCache::tryNewPipelineFromPersistentCache(
const QByteArray &qsbcKey,
772 const QByteArray &inKey,
773 const QSSGShaderFeatures &inFeatures,
774 QSSGRhiShaderPipeline::StageFlags stageFlags)
783 QQsbCollection::EntryDesc entryDesc;
787 if (!m_persistentShaderBakingCache.extractEntry(QQsbCollection::Entry(qsbcKey), entryDesc))
790 if (entryDesc.vertShader.isValid() && entryDesc.fragShader.isValid()) {
791 const bool shaderDebug = !QSSGRhiContextPrivate::editorMode() && QSSGRhiContextPrivate::shaderDebuggingEnabled();
793 qDebug(
"Loading rhi shaders from disk cache for %s (%s)", qsbcKey.constData(), inKey.constData());
795 QSSGRhiShaderPipelinePtr shaders(
new QSSGRhiShaderPipeline(m_rhiContext));
796 shaders->addStage(QRhiShaderStage(QRhiShaderStage::Vertex, entryDesc.vertShader), stageFlags);
797 shaders->addStage(QRhiShaderStage(QRhiShaderStage::Fragment, entryDesc.fragShader), stageFlags);
798 QSSGShaderCacheKey cacheKey(inKey);
799 cacheKey.m_features = inFeatures;
800 cacheKey.updateHashCode();
801 return m_rhiShaders.insert(cacheKey, shaders).value();
802 }
else if (entryDesc.vertShader.isValid()) {
803 const bool shaderDebug = !QSSGRhiContextPrivate::editorMode() && QSSGRhiContextPrivate::shaderDebuggingEnabled();
805 qDebug(
"Loading rhi shaders from disk cache for %s (%s)", qsbcKey.constData(), inKey.constData());
807 if (entryDesc.vertShader.stage() != QShader::ComputeStage) {
808 qWarning(
"Failed to find compute stage while loading rhi shaders from disk cache for %s.", qsbcKey.constData());
811 QSSGRhiShaderPipelinePtr shaders(
new QSSGRhiShaderPipeline(m_rhiContext));
812 shaders->addStage(QRhiShaderStage(QRhiShaderStage::Compute, entryDesc.vertShader), stageFlags);
813 QSSGShaderCacheKey cacheKey(inKey);
814 cacheKey.m_features = inFeatures;
815 cacheKey.updateHashCode();
816 return m_rhiShaders.insert(cacheKey, shaders).value();
822QSSGRhiShaderPipelinePtr QSSGShaderCache::loadBuiltinUncached(
const QByteArray &inKey,
int viewCount,
823 QSSGRhiShaderPipeline::StageFlags vertexStageFlags)
825 const bool shaderDebug = !QSSGRhiContextPrivate::editorMode() && QSSGRhiContextPrivate::shaderDebuggingEnabled();
827 qDebug(
"Loading builtin rhi shader: %s (view count: %d)", inKey.constData(), viewCount);
829 Q_TRACE_SCOPE(QSSG_loadShader);
830 Q_QUICK3D_PROFILE_START(QQuick3DProfiler::Quick3DLoadShader);
833 QSSGRhiShaderPipelinePtr shaders(
new QSSGRhiShaderPipeline(m_rhiContext));
838 const QString prefix = QString::fromUtf8(resourceFolder() + inKey);
839 QString vertexFileName = prefix + QLatin1String(
".vert.qsb");
840 QString fragmentFileName = prefix + QLatin1String(
".frag.qsb");
844 if (viewCount == 2) {
845 vertexFileName += QLatin1String(
".mv2qsb");
846 fragmentFileName += QLatin1String(
".mv2qsb");
849 QShader vertexShader;
850 QShader fragmentShader;
853 f.setFileName(vertexFileName);
854 if (f.open(QIODevice::ReadOnly)) {
855 const QByteArray vsData = f.readAll();
856 vertexShader = QShader::fromSerialized(vsData);
859 qWarning(
"Failed to open %s", qPrintable(f.fileName()));
861 f.setFileName(fragmentFileName);
862 if (f.open(QIODevice::ReadOnly)) {
863 const QByteArray fsData = f.readAll();
864 fragmentShader = QShader::fromSerialized(fsData);
867 qWarning(
"Failed to open %s", qPrintable(f.fileName()));
870 if (vertexShader.isValid() && fragmentShader.isValid()) {
871 shaders->addStage(QRhiShaderStage(QRhiShaderStage::Vertex, vertexShader), vertexStageFlags);
872 shaders->addStage(QRhiShaderStage(QRhiShaderStage::Fragment, fragmentShader));
874 qDebug(
"Loading of vertex and fragment stages succeeded");
877 Q_QUICK3D_PROFILE_END_WITH_STRING(QQuick3DProfiler::Quick3DLoadShader, 0, inKey);
885 QMutexLocker locker(&*s_statusMutex);
886 s_statusCallback = cb;
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()
Q_GLOBAL_STATIC(QReadWriteLock, g_updateMutex)
static QString persistentQsbcFileName()
static bool isAutoDiskCacheEnabled()
size_t qHash(QSSGShaderFeatures features) noexcept
static bool ensureWritableDir(const QString &name)
static bool s_autoDiskCacheEnabled
static QString persistentQsbcDir()
static void initBakerForNonPersistentUse(QShaderBaker *, QRhi *)
static constexpr DefineEntry DefineTable[]