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
5#include "qssgrendershaderlibrarymanager_p.h"
6
7#include <QtQuick3DRuntimeRender/private/qssgrendercontextcore_p.h>
8#include <QtQuick3DRuntimeRender/private/qssgrenderloadedtexture_p.h>
9
10#include <QXmlStreamReader>
11#include <QFileInfo>
12#include <QCryptographicHash>
13
14#include <QtQuick3DRuntimeRender/private/qssgruntimerenderlogging_p.h>
15
16QT_BEGIN_NAMESPACE
17
18QString QSSGShaderLibraryManager::getShaderCodeLibraryDirectory()
19{
20 return QStringLiteral("res/effectlib");
21}
22static QByteArray includeSearch() { return QByteArrayLiteral("#include \""); };
23static QByteArray copyrightHeaderStart() { return QByteArrayLiteral("/****************************************************************************"); }
24static QByteArray copyrightHeaderEnd() { return QByteArrayLiteral("****************************************************************************/"); }
25
26QSSGShaderLibraryManager::QSSGShaderLibraryManager() {}
27
28QSSGShaderLibraryManager::~QSSGShaderLibraryManager() {}
29
30static inline char stageKey(QSSGShaderCache::ShaderType type)
31{
32 switch (type) {
33 case QSSGShaderCache::ShaderType::Vertex:
34 return 'V';
35 case QSSGShaderCache::ShaderType::Fragment:
36 return 'F';
37 default:
38 break;
39 }
40 return '?';
41}
42
43void QSSGShaderLibraryManager::setShaderSource(const QByteArray &inShaderPathKey, QSSGShaderCache::ShaderType type,
44 const QByteArray &inSource, const QSSGCustomShaderMetaData &meta)
45{
46 QWriteLocker locker(&m_lock);
47
48 const QByteArray perStageKey = stageKey(type) + inShaderPathKey;
49 {
50 auto it = m_expandedFiles.find(key: perStageKey);
51 if (it != m_expandedFiles.end())
52 it.value() = inSource;
53 else
54 m_expandedFiles.insert(key: perStageKey, value: inSource);
55 }
56
57 {
58 auto it = m_metadata.find(key: perStageKey);
59 if (it != m_metadata.end())
60 it.value() = meta;
61 else
62 m_metadata.insert(key: perStageKey, value: meta);
63 }
64}
65
66void QSSGShaderLibraryManager::resolveIncludeFiles(QByteArray &theReadBuffer, const QByteArray &inMaterialInfoString)
67{
68 // Now do search and replace for the headers
69 for (int thePos = theReadBuffer.indexOf(bv: includeSearch()); thePos != -1;
70 thePos = theReadBuffer.indexOf(bv: includeSearch(), from: thePos + 1)) {
71 int theEndQuote = theReadBuffer.indexOf(c: '\"', from: thePos + includeSearch().size() + 1);
72 // Indicates an unterminated include file.
73 if (theEndQuote == -1) {
74 qCCritical(INVALID_OPERATION, "Unterminated include in file: %s", inMaterialInfoString.constData());
75 theReadBuffer.clear();
76 break;
77 }
78 const int theActualBegin = thePos + includeSearch().size();
79 const auto &theInclude = theReadBuffer.mid(index: theActualBegin, len: theEndQuote - theActualBegin);
80 // If we haven't included the file yet this round
81 auto contents = getIncludeContents(inShaderPathKey: theInclude);
82 // Strip copywrite headers from include if present
83 if (contents.startsWith(bv: copyrightHeaderStart())) {
84 int clipPos = contents.indexOf(bv: copyrightHeaderEnd()) ;
85 if (clipPos >= 0)
86 contents.remove(index: 0, len: clipPos + copyrightHeaderEnd().size());
87 }
88 // Write insert comment for begin source
89 contents.prepend(QByteArrayLiteral("\n// begin \"") + theInclude + QByteArrayLiteral("\"\n"));
90 // Write insert comment for end source
91 contents.append(QByteArrayLiteral("\n// end \"" ) + theInclude + QByteArrayLiteral("\"\n"));
92
93 theReadBuffer = theReadBuffer.replace(index: thePos, len: (theEndQuote + 1) - thePos, s: contents);
94 }
95}
96
97QByteArray QSSGShaderLibraryManager::getIncludeContents(const QByteArray &inShaderPathKey)
98{
99 QWriteLocker locker(&m_lock);
100
101 auto theInsert = m_expandedFiles.constFind(key: inShaderPathKey);
102 const bool found = (theInsert != m_expandedFiles.cend());
103
104 QByteArray theReadBuffer;
105 if (!found) {
106 const QString defaultDir = getShaderCodeLibraryDirectory();
107 const auto ver = QByteArrayLiteral("rhi");
108
109 QString fullPath;
110 QSharedPointer<QIODevice> theStream;
111 QTextStream stream(&fullPath);
112 stream << defaultDir << QLatin1Char('/') << ver << QLatin1Char('/') << QString::fromLocal8Bit(ba: inShaderPathKey);
113 theStream = QSSGInputUtil::getStreamForFile(inPath: fullPath, inQuiet: true);
114 if (theStream.isNull()) {
115 fullPath.clear();
116 QTextStream stream(&fullPath);
117 stream << defaultDir << QLatin1Char('/') << QString::fromLocal8Bit(ba: inShaderPathKey);
118 theStream = QSSGInputUtil::getStreamForFile(inPath: fullPath, inQuiet: false);
119 }
120 if (!theStream.isNull()) {
121 char readBuf[1024];
122 qint64 amountRead = 0;
123 do {
124 amountRead = theStream->read(data: readBuf, maxlen: 1024);
125 if (amountRead)
126 theReadBuffer.append(s: readBuf, len: int(amountRead));
127 } while (amountRead);
128 } else {
129 qCCritical(INVALID_OPERATION, "Failed to find include file %s", qPrintable(QString::fromLocal8Bit(inShaderPathKey)));
130 Q_ASSERT(false);
131 }
132 theInsert = m_expandedFiles.insert(key: inShaderPathKey, value: theReadBuffer);
133 } else {
134 theReadBuffer = theInsert.value();
135 }
136
137 locker.unlock();
138 resolveIncludeFiles(theReadBuffer, inMaterialInfoString: inShaderPathKey);
139
140 return theReadBuffer;
141}
142
143QByteArray QSSGShaderLibraryManager::getShaderSource(const QByteArray &inShaderPathKey, QSSGShaderCache::ShaderType type)
144{
145 QReadLocker locker(&m_lock);
146
147 const QByteArray perStageKey = stageKey(type) + inShaderPathKey;
148 auto it = m_expandedFiles.constFind(key: perStageKey);
149 if (it != m_expandedFiles.cend())
150 return it.value();
151
152 qWarning(msg: "No shader source stored for key %s", perStageKey.constData());
153 return QByteArray();
154}
155
156QSSGCustomShaderMetaData QSSGShaderLibraryManager::getShaderMetaData(const QByteArray &inShaderPathKey, QSSGShaderCache::ShaderType type)
157{
158 QReadLocker locker(&m_lock);
159
160 const QByteArray perStageKey = stageKey(type) + inShaderPathKey;
161 auto it = m_metadata.constFind(key: perStageKey);
162 if (it != m_metadata.cend())
163 return it.value();
164
165 qWarning(msg: "No shader metadata stored for key %s", perStageKey.constData());
166 return {};
167}
168
169void QSSGShaderLibraryManager::loadPregeneratedShaderInfo()
170{
171 const auto collectionFilePath = QString::fromLatin1(ba: QSSGShaderCache::resourceFolder() + QSSGShaderCache::shaderCollectionFile());
172 QFile file(collectionFilePath);
173 if (file.exists()) {
174 QQsbIODeviceCollection qsbc(file);
175 if (qsbc.map(mode: QQsbIODeviceCollection::Read))
176 m_preGeneratedShaderEntries = qsbc.availableEntries();
177 qsbc.unmap();
178 }
179}
180
181static int calcLightPoint(const QSSGShaderDefaultMaterialKey &key, int i) {
182 QSSGShaderDefaultMaterialKeyProperties prop;
183 return prop.m_lightFlags[i].getValue(inDataStore: key) + prop.m_lightSpotFlags[i].getValue(inDataStore: key) * 2
184 + prop.m_lightAreaFlags[i].getValue(inDataStore: key) * 4 + prop.m_lightShadowFlags[i].getValue(inDataStore: key) * 8;
185};
186
187bool QSSGShaderLibraryManager::compare(const QSSGShaderDefaultMaterialKey &key1, const QSSGShaderDefaultMaterialKey &key2)
188{
189 QSSGShaderDefaultMaterialKeyProperties props;
190#define COMPARE_PROP(x) \
191 if (props.x.getValue(key1) < props.x.getValue(key2)) return true;
192
193 COMPARE_PROP(m_hasLighting)
194 COMPARE_PROP(m_hasIbl)
195 COMPARE_PROP(m_specularEnabled)
196 COMPARE_PROP(m_fresnelEnabled)
197 COMPARE_PROP(m_vertexColorsEnabled)
198 COMPARE_PROP(m_specularModel)
199 COMPARE_PROP(m_vertexAttributes)
200 COMPARE_PROP(m_alphaMode)
201
202 for (int i = 0; i < QSSGShaderDefaultMaterialKeyProperties::ImageMapCount; i++) {
203 COMPARE_PROP(m_imageMaps[i])
204 }
205 for (int i = 0; i < QSSGShaderDefaultMaterialKeyProperties::SingleChannelImageCount; i++) {
206 COMPARE_PROP(m_textureChannels[i])
207 }
208 COMPARE_PROP(m_lightCount)
209 for (int i = 0; i < QSSGShaderDefaultMaterialKeyProperties::LightCount; i++) {
210 int lp1 = calcLightPoint(key: key1, i);
211 int lp2 = calcLightPoint(key: key2, i);
212 if (lp1 < lp2)
213 return true;
214 }
215#undef COMPARE_PROP
216 return false;
217}
218
219QT_END_NAMESPACE
220

source code of qtquick3d/src/runtimerender/resourcemanager/qssgrendershaderlibrarymanager.cpp