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#ifndef QSSG_RENDER_SHADER_CACHE_H
6#define QSSG_RENDER_SHADER_CACHE_H
7
8//
9// W A R N I N G
10// -------------
11//
12// This file is not part of the Qt API. It exists purely as an
13// implementation detail. This header file may change from version to
14// version without notice, or even be removed.
15//
16// We mean it.
17//
18
19#include <QtQuick3DRuntimeRender/private/qtquick3druntimerenderglobal_p.h>
20#include <QtQuick3DUtils/private/qssgdataref_p.h>
21#include <QtQuick3DUtils/private/qqsbcollection_p.h>
22
23#include <QtQuick3DRuntimeRender/private/qssgrhicontext_p.h>
24
25#include <QtCore/QString>
26#include <QtCore/qcryptographichash.h>
27#include <QtCore/QSharedPointer>
28#include <QtCore/QVector>
29
30QT_BEGIN_NAMESPACE
31
32class QSSGRenderContextInterface;
33class QSSGRhiShaderPipeline;
34class QShaderBaker;
35class QRhi;
36
37struct Q_QUICK3DRUNTIMERENDER_EXPORT QSSGShaderFeatures
38{
39
40// There are a number of macros used to turn on or off various features.
41// This allows those features to be propagated into the shader cache's caching mechanism.
42// They will be translated into #define name value where value is 1 or zero depending on
43// if the feature is enabled or not.
44//
45// In snippets that use this feature the code would look something like this:
46
47/*
48#ifndef QSSG_ENABLE_<FEATURE>
49#define QSSG_ENABLE_<FEATURE> 0
50#endif
51
52void func()
53{
54 ...
55#if QSSG_ENABLE_<FEATURE>
56 ...
57#endif
58 ...
59}
60*/
61
62// NOTE: The order of these will affect generated keys, so re-ordering these
63// will break already baked shaders (e.g. shadergen).
64using FlagType = quint32;
65enum class Feature : FlagType
66{
67 LightProbe = (1 << 8),
68 IblOrientation = (1 << 9) + 1,
69 Ssm = (1 << 10) + 2,
70 Ssao = (1 << 11) + 3,
71 DepthPass = (1 << 12) + 4,
72 OrthoShadowPass = (1 << 13) + 5,
73 CubeShadowPass = (1 << 14) + 6,
74 LinearTonemapping = (1 << 15) + 7,
75 AcesTonemapping = (1 << 16) + 8,
76 HejlDawsonTonemapping = (1 << 17) + 9,
77 FilmicTonemapping = (1 << 18) + 10,
78 RGBELightProbe = (1 << 19) + 11,
79 OpaqueDepthPrePass = (1 << 20) + 12,
80 ReflectionProbe = (1 << 21) + 13,
81 ReduceMaxNumLights = (1 << 22) + 14,
82 Lightmap = (1 << 23) + 15,
83
84 LastFeature
85};
86
87static constexpr FlagType IndexMask = 0xff;
88static constexpr quint32 Count = (static_cast<FlagType>(Feature::LastFeature) & IndexMask);
89
90static const char *asDefineString(QSSGShaderFeatures::Feature feature);
91static Feature fromIndex(quint32 idx);
92
93constexpr bool isNull() const { return flags == 0; }
94constexpr bool isSet(Feature feature) const { return (static_cast<FlagType>(feature) & flags); }
95void set(Feature feature, bool val);
96
97FlagType flags = 0;
98
99inline friend bool operator==(QSSGShaderFeatures a, QSSGShaderFeatures b) { return a.flags == b.flags; }
100
101void disableTonemapping()
102{
103 set(feature: Feature::LinearTonemapping, val: false);
104 set(feature: Feature::AcesTonemapping, val: false);
105 set(feature: Feature::FilmicTonemapping, val: false);
106 set(feature: Feature::HejlDawsonTonemapping, val: false);
107}
108
109inline friend QDebug operator<<(QDebug stream, const QSSGShaderFeatures &features)
110{
111 QVarLengthArray<const char *, Count> enabledFeatureStrings;
112 for (quint32 idx = 0; idx < Count; ++idx) {
113 const Feature feature = fromIndex(idx);
114 if (features.isSet(feature))
115 enabledFeatureStrings.append(t: asDefineString(feature));
116 }
117 stream.nospace() << "QSSGShaderFeatures(";
118 for (int i = 0; i < enabledFeatureStrings.size(); ++i)
119 stream.nospace() << (i > 0 ? ", " : "") << enabledFeatureStrings[i];
120 stream.nospace() << ")";
121 return stream;
122}
123
124};
125
126Q_QUICK3DRUNTIMERENDER_EXPORT size_t qHash(QSSGShaderFeatures features) noexcept;
127
128struct QSSGShaderCacheKey
129{
130 QByteArray m_key;
131 QSSGShaderFeatures m_features;
132 size_t m_hashCode = 0;
133
134 explicit QSSGShaderCacheKey(const QByteArray &key = QByteArray()) : m_key(key), m_hashCode(0) {}
135
136 QSSGShaderCacheKey(const QSSGShaderCacheKey &other) = default;
137 QSSGShaderCacheKey &operator=(const QSSGShaderCacheKey &other) = default;
138
139 static inline size_t generateHashCode(const QByteArray &key, QSSGShaderFeatures features)
140 {
141 return qHash(key) ^ qHash(features);
142 }
143
144 void updateHashCode()
145 {
146 m_hashCode = generateHashCode(key: m_key, features: m_features);
147 }
148
149 bool operator==(const QSSGShaderCacheKey &inOther) const
150 {
151 return m_key == inOther.m_key && m_features == inOther.m_features;
152 }
153};
154
155inline size_t qHash(const QSSGShaderCacheKey &key)
156{
157 return key.m_hashCode;
158}
159
160class Q_QUICK3DRUNTIMERENDER_EXPORT QSSGShaderCache
161{
162 Q_DISABLE_COPY(QSSGShaderCache)
163public:
164 enum class ShaderType
165 {
166 Vertex = 0,
167 Fragment = 1
168 };
169
170 using InitBakerFunc = void (*)(QShaderBaker *baker, QRhi *rhi);
171private:
172 typedef QHash<QSSGShaderCacheKey, QSSGRhiShaderPipelinePtr> TRhiShaderMap;
173 QSSGRhiContext &m_rhiContext; // Not own, the RCI owns us and the QSSGRhiContext.
174 TRhiShaderMap m_rhiShaders;
175 QByteArray m_insertStr; // member to potentially reuse the allocation after clear
176 InitBakerFunc m_initBaker;
177 QQsbInMemoryCollection m_persistentShaderBakingCache;
178 QString m_persistentShaderStorageFileName;
179
180 void addShaderPreprocessor(QByteArray &str,
181 const QByteArray &inKey,
182 ShaderType shaderType,
183 const QSSGShaderFeatures &inFeatures);
184
185public:
186 QSSGShaderCache(QSSGRhiContext &ctx,
187 const InitBakerFunc initBakeFn = nullptr);
188 ~QSSGShaderCache();
189
190 void releaseCachedResources();
191
192 QQsbInMemoryCollection &persistentShaderBakingCache() { return m_persistentShaderBakingCache; }
193
194 QSSGRhiShaderPipelinePtr tryGetRhiShaderPipeline(const QByteArray &inKey,
195 const QSSGShaderFeatures &inFeatures);
196
197 QSSGRhiShaderPipelinePtr tryNewPipelineFromPersistentCache(const QByteArray &qsbcKey,
198 const QByteArray &inKey,
199 const QSSGShaderFeatures &inFeatures,
200 QSSGRhiShaderPipeline::StageFlags stageFlags = {});
201
202 QSSGRhiShaderPipelinePtr newPipelineFromPregenerated(const QByteArray &inKey,
203 const QSSGShaderFeatures &inFeatures,
204 QQsbCollection::Entry entry,
205 const QSSGRenderGraphObject &obj,
206 QSSGRhiShaderPipeline::StageFlags stageFlags = {});
207
208 QSSGRhiShaderPipelinePtr compileForRhi(const QByteArray &inKey,
209 const QByteArray &inVert,
210 const QByteArray &inFrag,
211 const QSSGShaderFeatures &inFeatures,
212 QSSGRhiShaderPipeline::StageFlags stageFlags);
213
214 QSSGRhiShaderPipelinePtr loadBuiltinForRhi(const QByteArray &inKey);
215
216 static QByteArray resourceFolder();
217 static QByteArray shaderCollectionFile();
218};
219
220namespace QtQuick3DEditorHelpers {
221namespace ShaderBaker
222{
223 enum class Status : quint8
224 {
225 Success,
226 Error
227 };
228 using StatusCallback = void(*)(const QByteArray &descKey, Status status, const QString &err, QShader::Stage stage);
229 Q_QUICK3DRUNTIMERENDER_EXPORT void setStatusCallback(StatusCallback cb);
230}
231
232namespace ShaderCache
233{
234 // Used by DS and the QML puppet!
235 // Note: Needs to be called before any QSSGShaderCache instance is created.
236 Q_QUICK3DRUNTIMERENDER_EXPORT void setAutomaticDiskCache(bool enable);
237 Q_QUICK3DRUNTIMERENDER_EXPORT bool isAutomaticDiskCacheEnabled();
238}
239
240}
241
242QT_END_NAMESPACE
243
244#endif
245

source code of qtquick3d/src/runtimerender/qssgrendershadercache_p.h