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)); }
47 case QShader::VertexStage:
48 return QStringLiteral(
"failedvert.txt");
50 case QShader::FragmentStage:
51 return QStringLiteral(
"failedfrag.txt");
54 return QStringLiteral(
"failedshader.txt");
60 const char *
name =
nullptr;
65 {
"QSSG_ENABLE_LIGHT_PROBE", QSSGShaderFeatures::Feature::LightProbe },
66 {
"QSSG_ENABLE_IBL_ORIENTATION", QSSGShaderFeatures::Feature::IblOrientation },
67 {
"QSSG_ENABLE_SSM", QSSGShaderFeatures::Feature::Ssm },
68 {
"QSSG_ENABLE_SSAO", QSSGShaderFeatures::Feature::Ssao },
69 {
"QSSG_ENABLE_DEPTH_PASS", QSSGShaderFeatures::Feature::DepthPass },
70 {
"QSSG_ENABLE_ORTHO_SHADOW_PASS", QSSGShaderFeatures::Feature::OrthoShadowPass },
71 {
"QSSG_ENABLE_PERSPECTIVE_SHADOW_PASS", QSSGShaderFeatures::Feature::PerspectiveShadowPass },
72 {
"QSSG_ENABLE_LINEAR_TONEMAPPING", QSSGShaderFeatures::Feature::LinearTonemapping },
73 {
"QSSG_ENABLE_ACES_TONEMAPPING", QSSGShaderFeatures::Feature::AcesTonemapping },
74 {
"QSSG_ENABLE_HEJLDAWSON_TONEMAPPING", QSSGShaderFeatures::Feature::HejlDawsonTonemapping },
75 {
"QSSG_ENABLE_FILMIC_TONEMAPPING", QSSGShaderFeatures::Feature::FilmicTonemapping },
76 {
"QSSG_ENABLE_RGBE_LIGHT_PROBE", QSSGShaderFeatures::Feature::RGBELightProbe },
77 {
"QSSG_ENABLE_OPAQUE_DEPTH_PRE_PASS", QSSGShaderFeatures::Feature::OpaqueDepthPrePass },
78 {
"QSSG_ENABLE_REFLECTION_PROBE", QSSGShaderFeatures::Feature::ReflectionProbe },
79 {
"QSSG_REDUCE_MAX_NUM_LIGHTS", QSSGShaderFeatures::Feature::ReduceMaxNumLights },
80 {
"QSSG_ENABLE_LIGHTMAP", QSSGShaderFeatures::Feature::Lightmap },
81 {
"QSSG_DISABLE_MULTIVIEW", QSSGShaderFeatures::Feature::DisableMultiView },
82 {
"QSSG_FORCE_IBL_EXPOSURE", QSSGShaderFeatures::Feature::ForceIblExposure },
83 {
"QSSG_ENABLE_NORMAL_PASS", QSSGShaderFeatures::Feature::NormalPass },
84 {
"QSSG_ENABLE_USER_RENDER_PASS", QSSGShaderFeatures::Feature::UserRenderPass},
85 {
"QSSG_ENABLE_MOTION_VECTOR", QSSGShaderFeatures::Feature::MotionVector }
88static_assert(std::size(DefineTable) == QSSGShaderFeatures::Count,
"Missing feature define?");
90const char *QSSGShaderFeatures::asDefineString(QSSGShaderFeatures::Feature feature) {
return DefineTable[
static_cast<FlagType>(feature) & QSSGShaderFeatures::IndexMask].name; }
91QSSGShaderFeatures::Feature QSSGShaderFeatures::fromIndex(quint32 idx) {
return DefineTable[idx].feature; }
93void QSSGShaderFeatures::set(QSSGShaderFeatures::Feature feature,
bool val)
96 flags |= (
static_cast<FlagType>(feature) & ~IndexMask);
98 flags &= ~(
static_cast<FlagType>(feature) & ~IndexMask);
101QList<QShaderVersion> QSSGShaderCache::glslTargetsForContext(
const QSurfaceFormat &format,
bool isGLESModule)
103 QList<QShaderVersion> versions;
108 const auto fromVersion = [](
const std::pair<
int,
int> &version,
const QShaderVersion::Flags flags = {}) -> QShaderVersion {
109 return QShaderVersion(version.first * 100 + version.second * 10, flags);
112 const auto version = format.version();
114 if (format.profile() == QSurfaceFormat::CoreProfile && version >= qMakePair(3, 3)) {
117 versions.append(QShaderVersion(330));
119 if (version > qMakePair(3, 3))
120 versions.append(fromVersion(version));
121 }
else if (format.renderableType() == QSurfaceFormat::OpenGLES || isGLESModule) {
122 if (format.majorVersion() >= 3) {
125 versions.append(QShaderVersion(300, QShaderVersion::GlslEs));
127 if (version > qMakePair(3, 0))
128 versions.append(fromVersion(version, QShaderVersion::GlslEs));
130 versions.append(QShaderVersion(100, QShaderVersion::GlslEs));
147 if (version >= qMakePair(3, 1))
148 versions.append(QShaderVersion(140));
150 versions.append(QShaderVersion(130));
156#ifdef QT_QUICK3D_HAS_RUNTIME_SHADERS
157static void initBakerForNonPersistentUse(QShaderBaker *baker, QRhi *rhi)
159 QVector<QShaderBaker::GeneratedShader> outputs;
160 switch (rhi->backend()) {
162 outputs.append({ QShader::HlslShader, QShaderVersion(50) });
165 outputs.append({ QShader::HlslShader, QShaderVersion(61) });
168#if defined(Q_OS_VISIONOS)
169 outputs.append({ QShader::MslShader, QShaderVersion(21) });
171 outputs.append({ QShader::MslShader, QShaderVersion(12) });
174 case QRhi::OpenGLES2:
176 QSurfaceFormat format = QSurfaceFormat::defaultFormat();
177 bool isGLESModule =
false;
179 auto h =
static_cast<
const QRhiGles2NativeHandles *>(rhi->nativeHandles());
181 format = h->context->format();
182 isGLESModule = QOpenGLContext::openGLModuleType() == QOpenGLContext::LibGLES;
184 const QList<QShaderVersion> versions = QSSGShaderCache::glslTargetsForContext(format, isGLESModule);
185 outputs.reserve(outputs.size() + versions.size());
186 for (
const QShaderVersion &version : versions)
187 outputs.append({ QShader::GlslShader, version });
191 outputs.append({ QShader::SpirvShader, QShaderVersion(100) });
197 baker->setBreakOnShaderTranslationError(
false);
198 baker->setGeneratedShaders(outputs);
199 baker->setGeneratedShaderVariants({ QShader::StandardShader });
202void QSSGShaderCache::initBakerForPersistentUse(QShaderBaker *baker, QRhi *)
204 QVector<QShaderBaker::GeneratedShader> outputs;
208 outputs.append({ QShader::SpirvShader, QShaderVersion(100) });
209 outputs.append({ QShader::HlslShader, QShaderVersion(50) });
210 outputs.append({ QShader::HlslShader, QShaderVersion(61) });
211#if defined(Q_OS_VISIONOS)
212 outputs.append({ QShader::MslShader, QShaderVersion(21) });
214 outputs.append({ QShader::MslShader, QShaderVersion(12) });
217 outputs.append({ QShader::GlslShader, QShaderVersion(430) });
218 outputs.append({ QShader::GlslShader, QShaderVersion(420) });
219 outputs.append({ QShader::GlslShader, QShaderVersion(330) });
220 outputs.append({ QShader::GlslShader, QShaderVersion(140) });
221 outputs.append({ QShader::GlslShader, QShaderVersion(130) });
222 outputs.append({ QShader::GlslShader, QShaderVersion(100, QShaderVersion::GlslEs) });
224 outputs.append({ QShader::GlslShader, QShaderVersion(300, QShaderVersion::GlslEs) });
225 outputs.append({ QShader::GlslShader, QShaderVersion(310, QShaderVersion::GlslEs) });
226 outputs.append({ QShader::GlslShader, QShaderVersion(320, QShaderVersion::GlslEs) });
240 baker->setBreakOnShaderTranslationError(
false);
242 baker->setGeneratedShaders(outputs);
243 baker->setGeneratedShaderVariants({ QShader::StandardShader });
251void QSSGShaderCache::initBakerForPersistentUse(QShaderBaker *, QRhi *)
261 static const bool diskCacheDisabled = qEnvironmentVariableIntValue(
"QT_DISABLE_SHADER_DISK_CACHE")
262 || qEnvironmentVariableIntValue(
"QSG_RHI_DISABLE_DISK_CACHE");
263 const bool attrDiskCacheDisabled = (qApp ? qApp->testAttribute(Qt::AA_DisableShaderDiskCache) :
false);
270 QDir::root().mkpath(name);
271 return QFileInfo(name).isWritable();
276 static bool checked =
false;
277 static QString currentCacheDir;
278 static bool cacheWritable =
false;
281 return cacheWritable ? currentCacheDir : QString();
284 const QString cachePath = QStandardPaths::writableLocation(QStandardPaths::CacheLocation);
285 const QString subPath = QLatin1String(
"/q3dshadercache-") + QSysInfo::buildAbi() + QLatin1Char(
'/');
287 if (!cachePath.isEmpty()) {
288 currentCacheDir = cachePath + subPath;
289 cacheWritable = ensureWritableDir(currentCacheDir);
292 return cacheWritable ? currentCacheDir : QString();
297 const QString cacheDir = persistentQsbcDir();
298 if (!cacheDir.isEmpty())
299 return cacheDir + QLatin1String(
"q3dshadercache.qsbc");
304QSSGShaderCache::QSSGShaderCache(QSSGRhiContext &ctx,
305 const InitBakerFunc initBakeFn)
307 m_initBaker(initBakeFn),
308 m_builtInShaders(*
this)
310 if (isAutoDiskCacheEnabled()) {
311 const bool shaderDebug = !QSSGRhiContextPrivate::editorMode() && QSSGRhiContextPrivate::shaderDebuggingEnabled();
312 m_persistentShaderStorageFileName = persistentQsbcFileName();
313 if (!m_persistentShaderStorageFileName.isEmpty()) {
314 const bool skipCacheFile = qEnvironmentVariableIntValue(
"QT_QUICK3D_NO_SHADER_CACHE_LOAD");
315 if (!skipCacheFile && QFileInfo::exists(m_persistentShaderStorageFileName)) {
317 qDebug(
"Attempting to seed material shader cache from %s", qPrintable(m_persistentShaderStorageFileName));
318 if (m_persistentShaderBakingCache.load(m_persistentShaderStorageFileName)) {
320 const int count = m_persistentShaderBakingCache.availableEntries().count();
321 qDebug(
"Loaded %d shader pipelines into the material shader cache", count);
336 m_initBaker = m_persistentShaderStorageFileName.isEmpty() ? initBakerForNonPersistentUse
337 : QSSGShaderCache::initBakerForPersistentUse;
341QSSGShaderCache::~QSSGShaderCache()
343 if (!m_persistentShaderStorageFileName.isEmpty())
344 m_persistentShaderBakingCache.save(m_persistentShaderStorageFileName);
347void QSSGShaderCache::releaseCachedResources()
349 m_builtInShaders.releaseCachedResources();
351 m_rhiShaders.clear();
357QSSGRhiShaderPipelinePtr QSSGShaderCache::tryGetRhiShaderPipeline(
const QByteArray &inKey,
358 const QSSGShaderFeatures &inFeatures)
360 QSSGShaderCacheKey cacheKey(inKey);
361 cacheKey.m_features = inFeatures;
362 cacheKey.updateHashCode();
363 const auto theIter = m_rhiShaders.constFind(cacheKey);
364 if (theIter != m_rhiShaders.cend())
365 return theIter.value();
370void QSSGShaderCache::addShaderPreprocessor(QByteArray &str,
371 const QByteArray &inKey,
372 ShaderType shaderType,
373 const QSSGShaderFeatures &inFeatures,
374 const QSSGUserShaderAugmentation &shaderAugmentation,
379 m_insertStr +=
"#version 440\n";
381 if (!inKey.isNull()) {
382 m_insertStr +=
"//Shader name -";
383 m_insertStr += inKey;
387 m_insertStr +=
"#define texture2D texture\n";
390 m_insertStr +=
"#define QSHADER_VIEW_COUNT ";
391 m_insertStr += QByteArray::number(viewCount);
394 str.insert(0, m_insertStr);
395 QString::size_type insertPos =
int(m_insertStr.size());
398 const bool fragOutputEnabled = (!inFeatures.isSet(QSSGShaderFeatures::Feature::DepthPass)) && shaderType == ShaderType::Fragment;
399 for (
const auto &def : DefineTable) {
400 m_insertStr.append(
"#define ");
401 m_insertStr.append(def.name);
402 m_insertStr.append(
" ");
403 m_insertStr.append(inFeatures.isSet(def.feature) ?
"1" :
"0");
404 m_insertStr.append(
"\n");
407 str.insert(insertPos, m_insertStr);
408 insertPos +=
int(m_insertStr.size());
411 if (fragOutputEnabled) {
412 if (shaderAugmentation.outputs.size() != 0) {
413 const auto &outputs = shaderAugmentation.outputs;
414 for (
int i = 0; i < outputs.size(); ++i) {
415 m_insertStr +=
"layout(location = ";
416 m_insertStr += QByteArray::number(i);
417 m_insertStr +=
") out vec4 ";
418 m_insertStr += outputs[i];
419 m_insertStr +=
";\n";
422 m_insertStr +=
"layout(location = 0) out vec4 fragOutput;\n";
426 str.insert(insertPos, m_insertStr);
429QByteArray QSSGShaderCache::resourceFolder()
431 return QByteArrayLiteral(
":/res/rhishaders/");
434QByteArray QSSGShaderCache::shaderCollectionFile()
436 return QByteArrayLiteral(
"qtappshaders.qsbc");
439QByteArray QSSGShaderCache::particleShaderCollectionFile()
441 return QByteArrayLiteral(
"qtparticleshaders.qsbc");
444QSSGRhiShaderPipelinePtr QSSGShaderCache::compileForRhi(
const QByteArray &inKey,
const QByteArray &inVert,
const QByteArray &inFrag,
445 const QSSGShaderFeatures &inFeatures, QSSGRhiShaderPipeline::StageFlags stageFlags,
446 const QSSGUserShaderAugmentation &shaderAugmentation,
448 bool perTargetCompilation,
449 const QByteArray &inCacheKey)
451#ifdef QT_QUICK3D_HAS_RUNTIME_SHADERS
455 const QByteArray &cacheKeyString = inCacheKey.isEmpty() ? inKey : inCacheKey;
457 const QSSGRhiShaderPipelinePtr &rhiShaders = tryGetRhiShaderPipeline(cacheKeyString, inFeatures);
461 QSSGShaderCacheKey tempKey(cacheKeyString);
462 tempKey.m_features = inFeatures;
463 tempKey.updateHashCode();
465 QByteArray vertexCode = inVert;
466 QByteArray fragmentCode = inFrag;
468 if (!vertexCode.isEmpty())
469 addShaderPreprocessor(vertexCode, inKey, ShaderType::Vertex, inFeatures, shaderAugmentation, viewCount);
471 if (!fragmentCode.isEmpty())
472 addShaderPreprocessor(fragmentCode, inKey, ShaderType::Fragment, inFeatures, shaderAugmentation, viewCount);
476 QSSGRhiShaderPipelinePtr shaders;
477 QString vertErr, fragErr;
480 m_initBaker(&baker, m_rhiContext.rhi());
485 baker.setPerTargetCompilation(perTargetCompilation);
489 baker.setMultiViewCount(viewCount);
493 const bool mediumPrecision = qEnvironmentVariableIntValue(
"QT_QUICK3D_MEDIUM_PRECISION");
495 baker.setGlslOptions(QShaderBaker::GlslOption::GlslEsFragDefaultFloatPrecisionMedium);
497 const bool editorMode = QSSGRhiContextPrivate::editorMode();
499 const bool shaderDebug = !editorMode && QSSGRhiContextPrivate::shaderDebuggingEnabled();
501 static auto dumpShader = [](QShader::Stage stage,
const QByteArray &code) {
503 case QShader::Stage::VertexStage:
504 qDebug(
"VERTEX SHADER:\n*****\n");
506 case QShader::Stage::FragmentStage:
507 qDebug(
"FRAGMENT SHADER:\n*****\n");
510 qDebug(
"SHADER:\n*****\n");
513 const auto lines = code.split(
'\n');
514 for (
int i = 0; i < lines.size(); i++)
515 qDebug(
"%3d %s", i + 1, lines.at(i).constData());
519 static auto dumpShaderToFile = [](QShader::Stage stage,
const QByteArray &data) {
520 QFile f(dumpFilename(stage));
521 if (f.open(QIODevice::WriteOnly | QIODevice::Text)) {
524 qWarning(
"Failed to write to file: %s (%s)\n",
525 qPrintable(f.fileName()), qPrintable(f.errorString()));
529 baker.setSourceString(vertexCode, QShader::VertexStage);
530 QShader vertexShader = baker.bake();
531 const auto vertShaderValid = vertexShader.isValid();
532 if (!vertShaderValid) {
533 vertErr = baker.errorMessage();
535 qWarning(
"Failed to compile vertex shader: %s\n", qPrintable(vertErr));
537 qWarning() << inKey <<
'\n';
542 dumpShader(QShader::Stage::VertexStage, vertexCode);
543 if (!vertShaderValid)
544 dumpShaderToFile(QShader::Stage::VertexStage, vertexCode);
547 baker.setSourceString(fragmentCode, QShader::FragmentStage);
548 QShader fragmentShader = baker.bake();
549 const bool fragShaderValid = fragmentShader.isValid();
550 if (!fragShaderValid) {
551 fragErr = baker.errorMessage();
553 qWarning(
"Failed to compile fragment shader: %s\n", qPrintable(fragErr));
555 qWarning() << inKey <<
'\n';
560 dumpShader(QShader::Stage::FragmentStage, fragmentCode);
561 if (!fragShaderValid)
562 dumpShaderToFile(QShader::Stage::FragmentStage, fragmentCode);
565 if (vertShaderValid && fragShaderValid) {
566 shaders = std::make_shared<QSSGRhiShaderPipeline>(m_rhiContext);
567 shaders->addStage(QRhiShaderStage(QRhiShaderStage::Vertex, vertexShader), stageFlags);
568 shaders->addStage(QRhiShaderStage(QRhiShaderStage::Fragment, fragmentShader), stageFlags);
570 qDebug(
"Compilation for vertex and fragment stages succeeded");
573 if (editorMode && s_statusCallback) {
574 using namespace QtQuick3DEditorHelpers::ShaderBaker;
575 const auto vertStatus = vertShaderValid ? Status::Success : Status::Error;
576 const auto fragStatus = fragShaderValid ? Status::Success : Status::Error;
577 QMutexLocker locker(&*s_statusMutex);
578 s_statusCallback(inKey, vertStatus, vertErr, QShader::VertexStage);
579 s_statusCallback(inKey, fragStatus, fragErr, QShader::FragmentStage);
582 auto result = m_rhiShaders.insert(tempKey, shaders).value();
583 if (result && result->vertexStage() && result->fragmentStage()) {
584 QQsbCollection::EntryDesc entryDesc = {
586 QQsbCollection::toFeatureSet(inFeatures),
587 result->vertexStage()->shader(),
588 result->fragmentStage()->shader()
590 m_persistentShaderBakingCache.addEntry(entryDesc.generateSha(), entryDesc);
598 Q_UNUSED(inFeatures);
599 Q_UNUSED(stageFlags);
600 Q_UNUSED(inCacheKey);
601 qWarning(
"Cannot compile and condition shaders at runtime because this build of Qt Quick 3D is not linking to Qt Shader Tools. "
602 "Only pre-processed materials are supported.");
607QSSGRhiShaderPipelinePtr QSSGShaderCache::newPipelineFromPregenerated(
const QByteArray &inKey,
608 const QSSGShaderFeatures &inFeatures,
609 QQsbCollection::Entry entry,
610 const QSSGRenderGraphObject &obj,
611 QSSGRhiShaderPipeline::StageFlags stageFlags)
620 const bool shaderDebug = !QSSGRhiContextPrivate::editorMode() && QSSGRhiContextPrivate::shaderDebuggingEnabled();
622 qDebug(
"Loading pregenerated rhi shader(s)");
624 Q_TRACE_SCOPE(QSSG_loadShader);
625 Q_QUICK3D_PROFILE_START(QQuick3DProfiler::Quick3DLoadShader);
628 QSSGRhiShaderPipelinePtr shaders(
new QSSGRhiShaderPipeline(m_rhiContext));
630 const QStringList collectionFiles
631 = {QString::fromLatin1(resourceFolder() + shaderCollectionFile()),
632 QString::fromLatin1(resourceFolder() + particleShaderCollectionFile())};
634 QQsbCollection::EntryDesc entryDesc;
635 QScopedPointer<QQsbIODeviceCollection> qsbc;
636 for (
const auto &collectionFile : collectionFiles) {
637 QFileInfo info(collectionFile);
641 qsbc.reset(
new QQsbIODeviceCollection(collectionFile));
642 if (qsbc->map(QQsbIODeviceCollection::Read)) {
643 if (qsbc->extractEntry(entry, entryDesc))
651 qWarning(
"Failed to open entry %s", entry.key.constData());
653 if (entryDesc.vertShader.isValid() && entryDesc.fragShader.isValid()) {
654 shaders->addStage(QRhiShaderStage(QRhiShaderStage::Vertex, entryDesc.vertShader), stageFlags);
655 shaders->addStage(QRhiShaderStage(QRhiShaderStage::Fragment, entryDesc.fragShader), stageFlags);
657 qDebug(
"Loading of vertex and fragment stages succeeded");
660#if !QT_CONFIG(qml_debug)
663 Q_QUICK3D_PROFILE_END_WITH_ID(QQuick3DProfiler::Quick3DLoadShader, 0, obj.profilingId);
666 QSSGShaderCacheKey cacheKey(inKey);
667 cacheKey.m_features = inFeatures;
668 cacheKey.updateHashCode();
670 const auto inserted = m_rhiShaders.insert(cacheKey, shaders);
673 return inserted.value();
676QSSGRhiShaderPipelinePtr QSSGShaderCache::tryNewPipelineFromPersistentCache(
const QByteArray &qsbcKey,
677 const QByteArray &inKey,
678 const QSSGShaderFeatures &inFeatures,
679 QSSGRhiShaderPipeline::StageFlags stageFlags)
688 QQsbCollection::EntryDesc entryDesc;
692 if (!m_persistentShaderBakingCache.extractEntry(QQsbCollection::Entry(qsbcKey), entryDesc))
695 if (entryDesc.vertShader.isValid() && entryDesc.fragShader.isValid()) {
696 const bool shaderDebug = !QSSGRhiContextPrivate::editorMode() && QSSGRhiContextPrivate::shaderDebuggingEnabled();
698 qDebug(
"Loading rhi shaders from disk cache for %s (%s)", qsbcKey.constData(), inKey.constData());
700 QSSGRhiShaderPipelinePtr shaders(
new QSSGRhiShaderPipeline(m_rhiContext));
701 shaders->addStage(QRhiShaderStage(QRhiShaderStage::Vertex, entryDesc.vertShader), stageFlags);
702 shaders->addStage(QRhiShaderStage(QRhiShaderStage::Fragment, entryDesc.fragShader), stageFlags);
703 QSSGShaderCacheKey cacheKey(inKey);
704 cacheKey.m_features = inFeatures;
705 cacheKey.updateHashCode();
706 return m_rhiShaders.insert(cacheKey, shaders).value();
712QSSGRhiShaderPipelinePtr QSSGShaderCache::loadBuiltinUncached(
const QByteArray &inKey,
int viewCount,
713 QSSGRhiShaderPipeline::StageFlags vertexStageFlags)
715 const bool shaderDebug = !QSSGRhiContextPrivate::editorMode() && QSSGRhiContextPrivate::shaderDebuggingEnabled();
717 qDebug(
"Loading builtin rhi shader: %s (view count: %d)", inKey.constData(), viewCount);
719 Q_TRACE_SCOPE(QSSG_loadShader);
720 Q_QUICK3D_PROFILE_START(QQuick3DProfiler::Quick3DLoadShader);
723 QSSGRhiShaderPipelinePtr shaders(
new QSSGRhiShaderPipeline(m_rhiContext));
728 const QString prefix = QString::fromUtf8(resourceFolder() + inKey);
729 QString vertexFileName = prefix + QLatin1String(
".vert.qsb");
730 QString fragmentFileName = prefix + QLatin1String(
".frag.qsb");
734 if (viewCount == 2) {
735 vertexFileName += QLatin1String(
".mv2qsb");
736 fragmentFileName += QLatin1String(
".mv2qsb");
739 QShader vertexShader;
740 QShader fragmentShader;
743 f.setFileName(vertexFileName);
744 if (f.open(QIODevice::ReadOnly)) {
745 const QByteArray vsData = f.readAll();
746 vertexShader = QShader::fromSerialized(vsData);
749 qWarning(
"Failed to open %s", qPrintable(f.fileName()));
751 f.setFileName(fragmentFileName);
752 if (f.open(QIODevice::ReadOnly)) {
753 const QByteArray fsData = f.readAll();
754 fragmentShader = QShader::fromSerialized(fsData);
757 qWarning(
"Failed to open %s", qPrintable(f.fileName()));
760 if (vertexShader.isValid() && fragmentShader.isValid()) {
761 shaders->addStage(QRhiShaderStage(QRhiShaderStage::Vertex, vertexShader), vertexStageFlags);
762 shaders->addStage(QRhiShaderStage(QRhiShaderStage::Fragment, fragmentShader));
764 qDebug(
"Loading of vertex and fragment stages succeeded");
767 Q_QUICK3D_PROFILE_END_WITH_STRING(QQuick3DProfiler::Quick3DLoadShader, 0, inKey);
775 QMutexLocker locker(&*s_statusMutex);
776 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 dumpFilename(QShader::Stage stage)
static QString persistentQsbcDir()
static void initBakerForNonPersistentUse(QShaderBaker *, QRhi *)
static constexpr DefineEntry DefineTable[]