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_KEY_H |
6 | #define QSSG_RENDER_SHADER_KEY_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 <QtQuick3DUtils/private/qssgdataref_p.h> |
20 | #include <QtQuick3DUtils/private/qssgrenderbasetypes_p.h> |
21 | #include <QtQuick3DRuntimeRender/private/qssgrenderdefaultmaterial_p.h> |
22 | #include <QtQuick3DRuntimeRender/private/qssgrhicontext_p.h> |
23 | |
24 | QT_BEGIN_NAMESPACE |
25 | // We have an ever expanding set of properties we like to hash into one or more 32 bit |
26 | // quantities. |
27 | // Furthermore we would like this set of properties to be convertable to string |
28 | // So the shader cache file itself is somewhat human readable/diagnosable. |
29 | // To do this we create a set of objects that act as properties to the master shader key. |
30 | // These objects are tallied in order to figure out their actual offset into the shader key's |
31 | // data store. They are also run through in order to create the string shader cache key. |
32 | |
33 | struct QSSGShaderKeyPropertyBase |
34 | { |
35 | QByteArrayView name; |
36 | quint32 offset; |
37 | explicit constexpr QSSGShaderKeyPropertyBase(const char *inName = "" ) : name(inName), offset(0) {} |
38 | quint32 getOffset() const { return offset; } |
39 | void setOffset(quint32 of) { offset = of; } |
40 | |
41 | template<quint32 TBitWidth> |
42 | quint32 getMaskTemplate() const |
43 | { |
44 | quint32 bit = offset % 32; |
45 | quint32 startValue = (1 << TBitWidth) - 1; |
46 | quint32 mask = startValue << bit; |
47 | return mask; |
48 | } |
49 | |
50 | quint32 getIdx() const { return offset / 32; } |
51 | |
52 | protected: |
53 | void internalToString(QByteArray &ioStr, const QByteArrayView &inBuffer) const |
54 | { |
55 | ioStr.append(a: name); |
56 | ioStr.append(c: '='); |
57 | ioStr.append(a: inBuffer); |
58 | } |
59 | |
60 | static void internalToString(QByteArray &ioStr, const QByteArrayView &name, bool inValue) |
61 | { |
62 | if (inValue) { |
63 | ioStr.append(a: name); |
64 | ioStr.append(c: '='); |
65 | ioStr.append(a: inValue ? QByteArrayView("true" ) : QByteArrayView("false" )); |
66 | } |
67 | } |
68 | static bool getBoolValue(const QByteArray& str, const QByteArrayView &name) |
69 | { |
70 | const int index = str.indexOf(bv: name); |
71 | if (index < 0) |
72 | return false; |
73 | const qsizetype nameLen = name.size(); |
74 | if (str[index + nameLen] != '=') |
75 | return false; |
76 | if (str.mid(index: index + nameLen + 1, len: 4) == QByteArrayView("true" )) |
77 | return true; |
78 | return false; |
79 | } |
80 | }; |
81 | |
82 | struct QSSGShaderKeyBoolean : public QSSGShaderKeyPropertyBase |
83 | { |
84 | enum { |
85 | BitWidth = 1, |
86 | }; |
87 | |
88 | explicit constexpr QSSGShaderKeyBoolean(const char *inName = "" ) : QSSGShaderKeyPropertyBase(inName) {} |
89 | |
90 | quint32 getMask() const { return getMaskTemplate<BitWidth>(); } |
91 | void setValue(QSSGDataRef<quint32> inDataStore, bool inValue) const |
92 | { |
93 | const qint32 idx = qint32(getIdx()); |
94 | Q_ASSERT(idx >= 0 && idx <= INT32_MAX); |
95 | Q_ASSERT(inDataStore.size() > idx); |
96 | quint32 mask = getMask(); |
97 | quint32 &target = inDataStore[idx]; |
98 | if (inValue) { |
99 | target = target | mask; |
100 | } else { |
101 | mask = ~mask; |
102 | target = target & mask; |
103 | } |
104 | } |
105 | |
106 | bool getValue(QSSGDataView<quint32> inDataStore) const |
107 | { |
108 | quint32 idx = getIdx(); |
109 | quint32 mask = getMask(); |
110 | const quint32 &target = inDataStore[idx]; |
111 | return (target & mask) ? true : false; |
112 | } |
113 | |
114 | void toString(QByteArray &ioStr, QSSGDataView<quint32> inKeySet) const |
115 | { |
116 | bool isHigh = getValue(inDataStore: inKeySet); |
117 | internalToString(ioStr, name, inValue: isHigh); |
118 | } |
119 | void fromString(const QByteArray &ioStr, QSSGDataRef<quint32> inKeySet) |
120 | { |
121 | setValue(inDataStore: inKeySet, inValue: getBoolValue(str: ioStr, name)); |
122 | } |
123 | }; |
124 | |
125 | template<quint32 TBitWidth> |
126 | struct QSSGShaderKeyUnsigned : public QSSGShaderKeyPropertyBase |
127 | { |
128 | enum { |
129 | BitWidth = TBitWidth, |
130 | }; |
131 | explicit constexpr QSSGShaderKeyUnsigned(const char *inName = "" ) : QSSGShaderKeyPropertyBase(inName) {} |
132 | quint32 getMask() const { return getMaskTemplate<BitWidth>(); } |
133 | void setValue(QSSGDataRef<quint32> inDataStore, quint32 inValue) const |
134 | { |
135 | quint32 startValue = (1 << TBitWidth) - 1; |
136 | // Ensure inValue is within range of bit width. |
137 | inValue = inValue & startValue; |
138 | quint32 bit = offset % 32; |
139 | quint32 mask = getMask(); |
140 | quint32 idx = getIdx(); |
141 | inValue = inValue << bit; |
142 | quint32 &target = inDataStore[idx]; |
143 | // Get rid of existing value |
144 | quint32 inverseMask = ~mask; |
145 | target = target & inverseMask; |
146 | target = target | inValue; |
147 | } |
148 | |
149 | quint32 getValue(QSSGDataView<quint32> inDataStore) const |
150 | { |
151 | quint32 idx = getIdx(); |
152 | quint32 bit = offset % 32; |
153 | quint32 mask = getMask(); |
154 | const quint32 &target = inDataStore[idx]; |
155 | |
156 | quint32 retval = target & mask; |
157 | retval = retval >> bit; |
158 | return retval; |
159 | } |
160 | |
161 | void toString(QByteArray &ioStr, QSSGDataView<quint32> inKeySet) const |
162 | { |
163 | quint32 value = getValue(inDataStore: inKeySet); |
164 | char buf[64]; |
165 | memset(s: buf, c: 0, n: sizeof (buf)); |
166 | toStr(item: value, buffer: toDataRef(type: buf, count: 64)); |
167 | internalToString(ioStr, buf); |
168 | } |
169 | |
170 | void fromString(const QByteArray &ioStr, QSSGDataRef<quint32> inKeySet) |
171 | { |
172 | const qsizetype nameLen = name.size(); |
173 | const qsizetype strOffset = ioStr.indexOf(name); |
174 | if (strOffset >= 0) { |
175 | /* The key is stored as name=val */ |
176 | if (ioStr[strOffset + nameLen] != '=') |
177 | return; |
178 | const QByteArray s = ioStr.right(len: ioStr.size() - strOffset - nameLen - 1); |
179 | int i = 0; |
180 | while (QChar(QLatin1Char(s[i])).isDigit()) |
181 | i++; |
182 | const quint32 value = s.left(len: i).toInt(); |
183 | setValue(inDataStore: inKeySet, inValue: value); |
184 | } |
185 | } |
186 | |
187 | private: |
188 | static quint32 toStr(quint32 item, QSSGDataRef<char> buffer) |
189 | { |
190 | // hope the buffer is big enough... |
191 | return static_cast<quint32>(::snprintf(s: buffer.begin(), maxlen: buffer.size(), format: "%u" , item)); |
192 | } |
193 | }; |
194 | |
195 | struct QSSGShaderKeyTextureChannel : public QSSGShaderKeyUnsigned<2> |
196 | { |
197 | enum TexturChannelBits { |
198 | R = 0, |
199 | G = 1, |
200 | B = 2, |
201 | A = 3, |
202 | }; |
203 | explicit QSSGShaderKeyTextureChannel(const char *inName = "" ) : QSSGShaderKeyUnsigned<2>(inName) {} |
204 | |
205 | TexturChannelBits getTextureChannel(QSSGDataView<quint32> inKeySet) const |
206 | { |
207 | return TexturChannelBits(getValue(inDataStore: inKeySet)); |
208 | } |
209 | |
210 | void setTextureChannel(TexturChannelBits channel, QSSGDataRef<quint32> inKeySet) |
211 | { |
212 | setValue(inDataStore: inKeySet, inValue: quint32(channel)); |
213 | } |
214 | static constexpr char textureChannelToChar[4] = { |
215 | 'R', |
216 | 'G', |
217 | 'B', |
218 | 'A' |
219 | }; |
220 | void toString(QByteArray &ioStr, QSSGDataView<quint32> inKeySet) const |
221 | { |
222 | ioStr.append(a: name); |
223 | ioStr.append(c: '='); |
224 | ioStr.append(c: textureChannelToChar[getTextureChannel(inKeySet)]); |
225 | } |
226 | void fromString(const QByteArray &ioStr, QSSGDataRef<quint32> inKeySet) |
227 | { |
228 | const qsizetype nameLen = name.size(); |
229 | const qsizetype strOffset = ioStr.indexOf(bv: name); |
230 | if (strOffset >= 0) { |
231 | /* The key is stored as name=ch */ |
232 | if (ioStr[strOffset + nameLen] != '=') |
233 | return; |
234 | const char ch = ioStr[strOffset + nameLen + 1]; |
235 | if (ch == 'R') |
236 | setValue(inDataStore: inKeySet, inValue: TexturChannelBits::R); |
237 | else if (ch == 'G') |
238 | setValue(inDataStore: inKeySet, inValue: TexturChannelBits::G); |
239 | else if (ch == 'B') |
240 | setValue(inDataStore: inKeySet, inValue: TexturChannelBits::B); |
241 | else if (ch == 'A') |
242 | setValue(inDataStore: inKeySet, inValue: TexturChannelBits::A); |
243 | } |
244 | } |
245 | }; |
246 | |
247 | struct QSSGShaderKeyImageMap : public QSSGShaderKeyUnsigned<5> |
248 | { |
249 | enum ImageMapBits { |
250 | Enabled = 1 << 0, |
251 | EnvMap = 1 << 1, |
252 | LightProbe = 1 << 2, |
253 | Identity = 1 << 3, |
254 | UsesUV1 = 1 << 4 |
255 | }; |
256 | |
257 | explicit QSSGShaderKeyImageMap(const char *inName = "" ) : QSSGShaderKeyUnsigned<5>(inName) {} |
258 | |
259 | bool getBitValue(ImageMapBits imageBit, QSSGDataView<quint32> inKeySet) const |
260 | { |
261 | return (getValue(inDataStore: inKeySet) & imageBit) ? true : false; |
262 | } |
263 | |
264 | void setBitValue(ImageMapBits imageBit, bool inValue, QSSGDataRef<quint32> inKeySet) |
265 | { |
266 | quint32 theValue = getValue(inDataStore: inKeySet); |
267 | quint32 mask = imageBit; |
268 | if (inValue) { |
269 | theValue = theValue | mask; |
270 | } else { |
271 | mask = ~mask; |
272 | theValue = theValue & mask; |
273 | } |
274 | setValue(inDataStore: inKeySet, inValue: theValue); |
275 | } |
276 | |
277 | bool isEnabled(QSSGDataView<quint32> inKeySet) const { return getBitValue(imageBit: Enabled, inKeySet); } |
278 | void setEnabled(QSSGDataRef<quint32> inKeySet, bool val) { setBitValue(imageBit: Enabled, inValue: val, inKeySet); } |
279 | |
280 | bool isEnvMap(QSSGDataView<quint32> inKeySet) const { return getBitValue(imageBit: EnvMap, inKeySet); } |
281 | void setEnvMap(QSSGDataRef<quint32> inKeySet, bool val) { setBitValue(imageBit: EnvMap, inValue: val, inKeySet); } |
282 | |
283 | bool isLightProbe(QSSGDataView<quint32> inKeySet) const { return getBitValue(imageBit: LightProbe, inKeySet); } |
284 | void setLightProbe(QSSGDataRef<quint32> inKeySet, bool val) { setBitValue(imageBit: LightProbe, inValue: val, inKeySet); } |
285 | |
286 | bool isIdentityTransform(QSSGDataView<quint32> inKeySet) const { return getBitValue(imageBit: Identity, inKeySet); } |
287 | void setIdentityTransform(QSSGDataRef<quint32> inKeySet, bool val) { setBitValue(imageBit: Identity, inValue: val, inKeySet); } |
288 | |
289 | bool isUsingUV1(QSSGDataView<quint32> inKeySet) const { return getBitValue(imageBit: UsesUV1, inKeySet); } |
290 | void setUsesUV1(QSSGDataRef<quint32> inKeySet, bool val) { setBitValue(imageBit: UsesUV1, inValue: val, inKeySet); } |
291 | |
292 | void toString(QByteArray &ioStr, QSSGDataView<quint32> inKeySet) const |
293 | { |
294 | ioStr.append(a: name); |
295 | ioStr.append(a: QByteArrayView("={" )); |
296 | internalToString(ioStr, name: QByteArrayView("enabled" ), inValue: isEnabled(inKeySet)); |
297 | ioStr.append(c: ';'); |
298 | internalToString(ioStr, name: QByteArrayView("envMap" ), inValue: isEnvMap(inKeySet)); |
299 | ioStr.append(c: ';'); |
300 | internalToString(ioStr, name: QByteArrayView("lightProbe" ), inValue: isLightProbe(inKeySet)); |
301 | ioStr.append(c: ';'); |
302 | internalToString(ioStr, name: QByteArrayView("identity" ), inValue: isIdentityTransform(inKeySet)); |
303 | ioStr.append(c: ';'); |
304 | internalToString(ioStr, name: QByteArrayView("usesUV1" ), inValue: isUsingUV1(inKeySet)); |
305 | ioStr.append(c: '}'); |
306 | } |
307 | }; |
308 | |
309 | struct QSSGShaderKeySpecularModel : QSSGShaderKeyUnsigned<2> |
310 | { |
311 | explicit QSSGShaderKeySpecularModel(const char *inName = "" ) : QSSGShaderKeyUnsigned<2>(inName) {} |
312 | |
313 | void setSpecularModel(QSSGDataRef<quint32> inKeySet, QSSGRenderDefaultMaterial::MaterialSpecularModel inModel) |
314 | { |
315 | setValue(inDataStore: inKeySet, inValue: quint32(inModel)); |
316 | } |
317 | |
318 | QSSGRenderDefaultMaterial::MaterialSpecularModel getSpecularModel(QSSGDataView<quint32> inKeySet) const |
319 | { |
320 | return static_cast<QSSGRenderDefaultMaterial::MaterialSpecularModel>(getValue(inDataStore: inKeySet)); |
321 | } |
322 | |
323 | void toString(QByteArray &ioStr, QSSGDataView<quint32> inKeySet) const |
324 | { |
325 | ioStr.append(a: name); |
326 | ioStr.append(c: '='); |
327 | switch (getSpecularModel(inKeySet)) { |
328 | case QSSGRenderDefaultMaterial::MaterialSpecularModel::KGGX: |
329 | ioStr.append(a: QByteArrayView("KGGX" )); |
330 | break; |
331 | case QSSGRenderDefaultMaterial::MaterialSpecularModel::Default: |
332 | ioStr.append(a: QByteArrayView("Default" )); |
333 | break; |
334 | } |
335 | ioStr.append(c: ';'); |
336 | } |
337 | void fromString(const QByteArray &ioStr, QSSGDataRef<quint32> inKeySet) |
338 | { |
339 | const qsizetype nameLen = name.size(); |
340 | const int strOffset = ioStr.indexOf(bv: name); |
341 | if (strOffset >= 0) { |
342 | /* The key is stored as name=specularMode; */ |
343 | if (ioStr[strOffset + nameLen] != '=') |
344 | return; |
345 | const int codeOffsetBegin = strOffset + nameLen + 1; |
346 | int codeOffset = 0; |
347 | while (ioStr[codeOffsetBegin + codeOffset] != ';') |
348 | codeOffset++; |
349 | const QByteArray val = ioStr.mid(index: codeOffsetBegin, len: codeOffset); |
350 | if (val == QByteArrayView("KGGX" )) |
351 | setSpecularModel(inKeySet, inModel: QSSGRenderDefaultMaterial::MaterialSpecularModel::KGGX); |
352 | if (val == QByteArrayView("Default" )) |
353 | setSpecularModel(inKeySet, inModel: QSSGRenderDefaultMaterial::MaterialSpecularModel::Default); |
354 | } |
355 | } |
356 | }; |
357 | |
358 | struct QSSGShaderKeyAlphaMode : QSSGShaderKeyUnsigned<2> |
359 | { |
360 | explicit QSSGShaderKeyAlphaMode(const char *inName = "" ) : QSSGShaderKeyUnsigned<2>(inName) {} |
361 | |
362 | void setAlphaMode(QSSGDataRef<quint32> inKeySet, QSSGRenderDefaultMaterial::MaterialAlphaMode inMode) |
363 | { |
364 | setValue(inDataStore: inKeySet, inValue: quint32(inMode)); |
365 | } |
366 | |
367 | QSSGRenderDefaultMaterial::MaterialAlphaMode getAlphaMode(QSSGDataView<quint32> inKeySet) const |
368 | { |
369 | return static_cast<QSSGRenderDefaultMaterial::MaterialAlphaMode>(getValue(inDataStore: inKeySet)); |
370 | } |
371 | |
372 | void toString(QByteArray &ioStr, QSSGDataView<quint32> inKeySet) const |
373 | { |
374 | ioStr.append(a: name); |
375 | ioStr.append(c: '='); |
376 | switch (getAlphaMode(inKeySet)) { |
377 | case QSSGRenderDefaultMaterial::MaterialAlphaMode::Default: |
378 | ioStr.append(a: QByteArrayView("Default" )); |
379 | break; |
380 | case QSSGRenderDefaultMaterial::MaterialAlphaMode::Mask: |
381 | ioStr.append(a: QByteArrayView("Mask" )); |
382 | break; |
383 | case QSSGRenderDefaultMaterial::MaterialAlphaMode::Blend: |
384 | ioStr.append(a: QByteArrayView("Blend" )); |
385 | break; |
386 | case QSSGRenderDefaultMaterial::MaterialAlphaMode::Opaque: |
387 | ioStr.append(a: QByteArrayView("Opaque" )); |
388 | break; |
389 | } |
390 | ioStr.append(c: ';'); |
391 | } |
392 | void fromString(const QByteArray &ioStr, QSSGDataRef<quint32> inKeySet) |
393 | { |
394 | const qsizetype nameLen = name.size(); |
395 | const qsizetype strOffset = ioStr.indexOf(bv: name); |
396 | if (strOffset >= 0) { |
397 | /* The key is stored as name=alphaMode; */ |
398 | if (ioStr[strOffset + nameLen] != '=') |
399 | return; |
400 | const int codeOffsetBegin = strOffset + nameLen + 1; |
401 | int codeOffset = 0; |
402 | while (ioStr[codeOffsetBegin + codeOffset] != ';') |
403 | codeOffset++; |
404 | const QByteArray val = ioStr.mid(index: codeOffsetBegin, len: codeOffset); |
405 | if (val == QByteArrayView("Default" )) |
406 | setAlphaMode(inKeySet, inMode: QSSGRenderDefaultMaterial::MaterialAlphaMode::Default); |
407 | if (val == QByteArrayView("Mask" )) |
408 | setAlphaMode(inKeySet, inMode: QSSGRenderDefaultMaterial::MaterialAlphaMode::Mask); |
409 | if (val == QByteArrayView("Blend" )) |
410 | setAlphaMode(inKeySet, inMode: QSSGRenderDefaultMaterial::MaterialAlphaMode::Blend); |
411 | if (val == QByteArrayView("Opaque" )) |
412 | setAlphaMode(inKeySet, inMode: QSSGRenderDefaultMaterial::MaterialAlphaMode::Opaque); |
413 | } |
414 | } |
415 | }; |
416 | |
417 | struct QSSGShaderKeyVertexAttribute : public QSSGShaderKeyUnsigned<9> |
418 | { |
419 | enum VertexAttributeBits { |
420 | Position = 1 << 0, |
421 | Normal = 1 << 1, |
422 | TexCoord0 = 1 << 2, |
423 | TexCoord1 = 1 << 3, |
424 | Tangent = 1 << 4, |
425 | Binormal = 1 << 5, |
426 | Color = 1 << 6, |
427 | JointAndWeight = 1 << 7, |
428 | TexCoordLightmap = 1 << 8 |
429 | }; |
430 | |
431 | explicit QSSGShaderKeyVertexAttribute(const char *inName = "" ) : QSSGShaderKeyUnsigned<9>(inName) {} |
432 | |
433 | bool getBitValue(VertexAttributeBits bit, QSSGDataView<quint32> inKeySet) const |
434 | { |
435 | return (getValue(inDataStore: inKeySet) & bit) ? true : false; |
436 | } |
437 | void setBitValue(VertexAttributeBits bit, QSSGDataRef<quint32> inKeySet, bool value) const |
438 | { |
439 | quint32 v = getValue(inDataStore: inKeySet); |
440 | v = value ? (v | bit) : (v & ~bit); |
441 | setValue(inDataStore: inKeySet, inValue: v); |
442 | } |
443 | |
444 | void toString(QByteArray &ioStr, QSSGDataView<quint32> inKeySet) const |
445 | { |
446 | ioStr.append(a: name); |
447 | ioStr.append(a: QByteArrayView("={" )); |
448 | internalToString(ioStr, name: QByteArrayView("position" ), inValue: getBitValue(bit: Position, inKeySet)); |
449 | ioStr.append(c: ';'); |
450 | internalToString(ioStr, name: QByteArrayView("normal" ), inValue: getBitValue(bit: Normal, inKeySet)); |
451 | ioStr.append(c: ';'); |
452 | internalToString(ioStr, name: QByteArrayView("texcoord0" ), inValue: getBitValue(bit: TexCoord0, inKeySet)); |
453 | ioStr.append(c: ';'); |
454 | internalToString(ioStr, name: QByteArrayView("texcoord1" ), inValue: getBitValue(bit: TexCoord1, inKeySet)); |
455 | ioStr.append(c: ';'); |
456 | internalToString(ioStr, name: QByteArrayView("tangent" ), inValue: getBitValue(bit: Tangent, inKeySet)); |
457 | ioStr.append(c: ';'); |
458 | internalToString(ioStr, name: QByteArrayView("binormal" ), inValue: getBitValue(bit: Binormal, inKeySet)); |
459 | ioStr.append(c: ';'); |
460 | internalToString(ioStr, name: QByteArrayView("color" ), inValue: getBitValue(bit: Color, inKeySet)); |
461 | ioStr.append(c: ';'); |
462 | internalToString(ioStr, name: QByteArrayView("texcoordlightmap" ), inValue: getBitValue(bit: TexCoordLightmap, inKeySet)); |
463 | ioStr.append(c: '}'); |
464 | internalToString(ioStr, name: QByteArrayView("joint&weight" ), inValue: getBitValue(bit: JointAndWeight, inKeySet)); |
465 | ioStr.append(c: '}'); |
466 | } |
467 | void fromString(const QByteArray &ioStr, QSSGDataRef<quint32> inKeySet) |
468 | { |
469 | const qsizetype nameLen = name.size(); |
470 | const qsizetype strOffset = ioStr.indexOf(bv: name); |
471 | if (strOffset >= 0) { |
472 | /* The key is stored as name={;;;;;;} */ |
473 | if (ioStr[strOffset + nameLen] != '=') |
474 | return; |
475 | if (ioStr[strOffset + nameLen + 1] != '{') |
476 | return; |
477 | const int codeOffsetBegin = strOffset + nameLen + 2; |
478 | int codeOffset = 0; |
479 | while (ioStr[codeOffsetBegin + codeOffset] != '}') |
480 | codeOffset++; |
481 | const QByteArray val = ioStr.mid(index: codeOffsetBegin, len: codeOffset); |
482 | const QVector<QByteArray> list = val.split(sep: ';'); |
483 | if (list.size() != 8) |
484 | return; |
485 | setBitValue(bit: Position, inKeySet, value: getBoolValue(str: list[0], name: QByteArrayView("position" ))); |
486 | setBitValue(bit: Normal, inKeySet, value: getBoolValue(str: list[1], name: QByteArrayView("normal" ))); |
487 | setBitValue(bit: TexCoord0, inKeySet, value: getBoolValue(str: list[2], name: QByteArrayView("texcoord0" ))); |
488 | setBitValue(bit: TexCoord1, inKeySet, value: getBoolValue(str: list[3], name: QByteArrayView("texcoord1" ))); |
489 | setBitValue(bit: Tangent, inKeySet, value: getBoolValue(str: list[4], name: QByteArrayView("tangent" ))); |
490 | setBitValue(bit: Binormal, inKeySet, value: getBoolValue(str: list[5], name: QByteArrayView("binormal" ))); |
491 | setBitValue(bit: Color, inKeySet, value: getBoolValue(str: list[6], name: QByteArrayView("color" ))); |
492 | setBitValue(bit: TexCoordLightmap, inKeySet, value: getBoolValue(str: list[7], name: QByteArrayView("texcoordlightmap" ))); |
493 | } |
494 | } |
495 | }; |
496 | |
497 | struct QSSGShaderDefaultMaterialKeyProperties |
498 | { |
499 | enum { |
500 | LightCount = QSSG_MAX_NUM_LIGHTS, |
501 | }; |
502 | enum { |
503 | SingleChannelImageCount = 10, |
504 | }; |
505 | enum ImageMapNames { |
506 | DiffuseMap = 0, |
507 | EmissiveMap, |
508 | SpecularMap, |
509 | BaseColorMap, |
510 | BumpMap, |
511 | SpecularAmountMap, |
512 | NormalMap, |
513 | ClearcoatNormalMap, |
514 | // single channel images |
515 | OpacityMap, |
516 | RoughnessMap, |
517 | MetalnessMap, |
518 | OcclusionMap, |
519 | TranslucencyMap, |
520 | HeightMap, |
521 | ClearcoatMap, |
522 | ClearcoatRoughnessMap, |
523 | TransmissionMap, |
524 | ThicknessMap, |
525 | |
526 | ImageMapCount, |
527 | SingleChannelImagesFirst = OpacityMap |
528 | }; |
529 | enum ImageChannelNames { |
530 | OpacityChannel = 0, |
531 | RoughnessChannel, |
532 | MetalnessChannel, |
533 | OcclusionChannel, |
534 | TranslucencyChannel, |
535 | HeightChannel, |
536 | ClearcoatChannel, |
537 | ClearcoatRoughnessChannel, |
538 | TransmissionChannel, |
539 | ThicknessChannel |
540 | }; |
541 | |
542 | QSSGShaderKeyBoolean m_hasLighting; |
543 | QSSGShaderKeyBoolean m_hasIbl; |
544 | QSSGShaderKeyUnsigned<4> m_lightCount; |
545 | QSSGShaderKeyBoolean m_lightFlags[LightCount]; |
546 | QSSGShaderKeyBoolean m_lightSpotFlags[LightCount]; |
547 | QSSGShaderKeyBoolean m_lightAreaFlags[LightCount]; |
548 | QSSGShaderKeyBoolean m_lightShadowFlags[LightCount]; |
549 | QSSGShaderKeyBoolean m_specularEnabled; |
550 | QSSGShaderKeyBoolean m_fresnelEnabled; |
551 | QSSGShaderKeyBoolean m_vertexColorsEnabled; |
552 | QSSGShaderKeySpecularModel m_specularModel; |
553 | QSSGShaderKeyImageMap m_imageMaps[ImageMapCount]; |
554 | QSSGShaderKeyTextureChannel m_textureChannels[SingleChannelImageCount]; |
555 | QSSGShaderKeyUnsigned<16> m_boneCount; |
556 | QSSGShaderKeyBoolean m_isDoubleSided; |
557 | QSSGShaderKeyBoolean m_overridesPosition; |
558 | QSSGShaderKeyBoolean m_usesProjectionMatrix; |
559 | QSSGShaderKeyBoolean m_usesInverseProjectionMatrix; |
560 | QSSGShaderKeyBoolean m_usesPointsTopology; |
561 | QSSGShaderKeyBoolean m_usesVarColor; |
562 | QSSGShaderKeyAlphaMode m_alphaMode; |
563 | QSSGShaderKeyVertexAttribute m_vertexAttributes; |
564 | QSSGShaderKeyBoolean m_usesFloatJointIndices; |
565 | qsizetype m_stringBufferSizeHint = 0; |
566 | QSSGShaderKeyBoolean m_usesInstancing; |
567 | QSSGShaderKeyUnsigned<8> m_targetCount; |
568 | QSSGShaderKeyUnsigned<8> m_targetPositionOffset; |
569 | QSSGShaderKeyUnsigned<8> m_targetNormalOffset; |
570 | QSSGShaderKeyUnsigned<8> m_targetTangentOffset; |
571 | QSSGShaderKeyUnsigned<8> m_targetBinormalOffset; |
572 | QSSGShaderKeyUnsigned<8> m_targetTexCoord0Offset; |
573 | QSSGShaderKeyUnsigned<8> m_targetTexCoord1Offset; |
574 | QSSGShaderKeyUnsigned<8> m_targetColorOffset; |
575 | QSSGShaderKeyBoolean m_blendParticles; |
576 | QSSGShaderKeyBoolean m_clearcoatEnabled; |
577 | QSSGShaderKeyBoolean m_transmissionEnabled; |
578 | QSSGShaderKeyBoolean m_specularAAEnabled; |
579 | QSSGShaderKeyBoolean m_lightmapEnabled; |
580 | QSSGShaderKeyBoolean m_specularGlossyEnabled; |
581 | QSSGShaderKeyUnsigned<4> m_debugMode; |
582 | QSSGShaderKeyBoolean m_fogEnabled; |
583 | |
584 | QSSGShaderDefaultMaterialKeyProperties() |
585 | : m_hasLighting("hasLighting" ) |
586 | , m_hasIbl("hasIbl" ) |
587 | , m_lightCount("lightCount" ) |
588 | , m_specularEnabled("specularEnabled" ) |
589 | , m_fresnelEnabled("fresnelEnabled" ) |
590 | , m_vertexColorsEnabled("vertexColorsEnabled" ) |
591 | , m_specularModel("specularModel" ) |
592 | , m_boneCount("boneCount" ) |
593 | , m_isDoubleSided("isDoubleSided" ) |
594 | , m_overridesPosition("overridesPosition" ) |
595 | , m_usesProjectionMatrix("usesProjectionMatrix" ) |
596 | , m_usesInverseProjectionMatrix("usesInverseProjectionMatrix" ) |
597 | , m_usesPointsTopology("usesPointsTopology" ) |
598 | , m_usesVarColor("usesVarColor" ) |
599 | , m_alphaMode("alphaMode" ) |
600 | , m_vertexAttributes("vertexAttributes" ) |
601 | , m_usesFloatJointIndices("usesFloatJointIndices" ) |
602 | , m_usesInstancing("usesInstancing" ) |
603 | , m_targetCount("targetCount" ) |
604 | , m_targetPositionOffset("targetPositionOffset" ) |
605 | , m_targetNormalOffset("targetNormalOffset" ) |
606 | , m_targetTangentOffset("targetTangentOffset" ) |
607 | , m_targetBinormalOffset("targetBinormalOffset" ) |
608 | , m_targetTexCoord0Offset("targetTexCoord0Offset" ) |
609 | , m_targetTexCoord1Offset("targetTexCoord1Offset" ) |
610 | , m_targetColorOffset("targetColorOffset" ) |
611 | , m_blendParticles("blendParticles" ) |
612 | , m_clearcoatEnabled("clearcoatEnabled" ) |
613 | , m_transmissionEnabled("transmissionEnabled" ) |
614 | , m_specularAAEnabled("specularAAEnabled" ) |
615 | , m_lightmapEnabled("lightmapEnabled" ) |
616 | , m_specularGlossyEnabled("specularGlossyEnabled" ) |
617 | , m_debugMode("debugMode" ) |
618 | , m_fogEnabled("fogEnabled" ) |
619 | { |
620 | m_lightFlags[0].name = "light0HasPosition" ; |
621 | m_lightFlags[1].name = "light1HasPosition" ; |
622 | m_lightFlags[2].name = "light2HasPosition" ; |
623 | m_lightFlags[3].name = "light3HasPosition" ; |
624 | m_lightFlags[4].name = "light4HasPosition" ; |
625 | m_lightFlags[5].name = "light5HasPosition" ; |
626 | m_lightFlags[6].name = "light6HasPosition" ; |
627 | m_lightFlags[7].name = "light7HasPosition" ; |
628 | m_lightFlags[8].name = "light8HasPosition" ; |
629 | m_lightFlags[9].name = "light9HasPosition" ; |
630 | m_lightFlags[10].name = "light10HasPosition" ; |
631 | m_lightFlags[11].name = "light11HasPosition" ; |
632 | m_lightFlags[12].name = "light12HasPosition" ; |
633 | m_lightFlags[13].name = "light13HasPosition" ; |
634 | m_lightFlags[14].name = "light14HasPosition" ; |
635 | |
636 | m_lightSpotFlags[0].name = "light0HasSpot" ; |
637 | m_lightSpotFlags[1].name = "light1HasSpot" ; |
638 | m_lightSpotFlags[2].name = "light2HasSpot" ; |
639 | m_lightSpotFlags[3].name = "light3HasSpot" ; |
640 | m_lightSpotFlags[4].name = "light4HasSpot" ; |
641 | m_lightSpotFlags[5].name = "light5HasSpot" ; |
642 | m_lightSpotFlags[6].name = "light6HasSpot" ; |
643 | m_lightSpotFlags[7].name = "light7HasSpot" ; |
644 | m_lightSpotFlags[8].name = "light8HasSpot" ; |
645 | m_lightSpotFlags[9].name = "light9HasSpot" ; |
646 | m_lightSpotFlags[10].name = "light10HasSpot" ; |
647 | m_lightSpotFlags[11].name = "light11HasSpot" ; |
648 | m_lightSpotFlags[12].name = "light12HasSpot" ; |
649 | m_lightSpotFlags[13].name = "light13HasSpot" ; |
650 | m_lightSpotFlags[14].name = "light14HasSpot" ; |
651 | |
652 | m_lightAreaFlags[0].name = "light0HasArea" ; |
653 | m_lightAreaFlags[1].name = "light1HasArea" ; |
654 | m_lightAreaFlags[2].name = "light2HasArea" ; |
655 | m_lightAreaFlags[3].name = "light3HasArea" ; |
656 | m_lightAreaFlags[4].name = "light4HasArea" ; |
657 | m_lightAreaFlags[5].name = "light5HasArea" ; |
658 | m_lightAreaFlags[6].name = "light6HasArea" ; |
659 | m_lightAreaFlags[7].name = "light7HasArea" ; |
660 | m_lightAreaFlags[8].name = "light8HasArea" ; |
661 | m_lightAreaFlags[9].name = "light9HasArea" ; |
662 | m_lightAreaFlags[10].name = "light10HasArea" ; |
663 | m_lightAreaFlags[11].name = "light11HasArea" ; |
664 | m_lightAreaFlags[12].name = "light12HasArea" ; |
665 | m_lightAreaFlags[13].name = "light13HasArea" ; |
666 | m_lightAreaFlags[14].name = "light14HasArea" ; |
667 | |
668 | m_lightShadowFlags[0].name = "light0HasShadow" ; |
669 | m_lightShadowFlags[1].name = "light1HasShadow" ; |
670 | m_lightShadowFlags[2].name = "light2HasShadow" ; |
671 | m_lightShadowFlags[3].name = "light3HasShadow" ; |
672 | m_lightShadowFlags[4].name = "light4HasShadow" ; |
673 | m_lightShadowFlags[5].name = "light5HasShadow" ; |
674 | m_lightShadowFlags[6].name = "light6HasShadow" ; |
675 | m_lightShadowFlags[7].name = "light7HasShadow" ; |
676 | m_lightShadowFlags[8].name = "light8HasShadow" ; |
677 | m_lightShadowFlags[9].name = "light9HasShadow" ; |
678 | m_lightShadowFlags[10].name = "light10HasShadow" ; |
679 | m_lightShadowFlags[11].name = "light11HasShadow" ; |
680 | m_lightShadowFlags[12].name = "light12HasShadow" ; |
681 | m_lightShadowFlags[13].name = "light13HasShadow" ; |
682 | m_lightShadowFlags[14].name = "light14HasShadow" ; |
683 | |
684 | m_imageMaps[0].name = "diffuseMap" ; |
685 | m_imageMaps[1].name = "emissiveMap" ; |
686 | m_imageMaps[2].name = "specularMap" ; |
687 | m_imageMaps[3].name = "baseColorMap" ; |
688 | m_imageMaps[4].name = "bumpMap" ; |
689 | m_imageMaps[5].name = "specularAmountMap" ; |
690 | m_imageMaps[6].name = "normalMap" ; |
691 | m_imageMaps[7].name = "clearcoatNormalMap" ; |
692 | m_imageMaps[8].name = "opacityMap" ; |
693 | m_imageMaps[9].name = "roughnessMap" ; |
694 | m_imageMaps[10].name = "metalnessMap" ; |
695 | m_imageMaps[11].name = "occlusionMap" ; |
696 | m_imageMaps[12].name = "translucencyMap" ; |
697 | m_imageMaps[13].name = "heightMap" ; |
698 | m_imageMaps[14].name = "clearcoatMap" ; |
699 | m_imageMaps[15].name = "clearcoatRoughnessMap" ; |
700 | m_imageMaps[16].name = "transmissionMap" ; |
701 | m_imageMaps[17].name = "thicknessMap" ; |
702 | |
703 | m_textureChannels[0].name = "opacityMap_channel" ; |
704 | m_textureChannels[1].name = "roughnessMap_channel" ; |
705 | m_textureChannels[2].name = "metalnessMap_channel" ; |
706 | m_textureChannels[3].name = "occlusionMap_channel" ; |
707 | m_textureChannels[4].name = "translucencyMap_channel" ; |
708 | m_textureChannels[5].name = "heightMap_channel" ; |
709 | m_textureChannels[6].name = "clearcoatMap_channel" ; |
710 | m_textureChannels[7].name = "clearcoatRoughnessMap_channel" ; |
711 | m_textureChannels[8].name = "transmissionMap_channel" ; |
712 | m_textureChannels[9].name = "thicknessMap_channel" ; |
713 | |
714 | init(); |
715 | } |
716 | |
717 | template<typename TVisitor> |
718 | void visitProperties(TVisitor &inVisitor) |
719 | { |
720 | inVisitor.visit(m_hasLighting); |
721 | inVisitor.visit(m_hasIbl); |
722 | inVisitor.visit(m_lightCount); |
723 | |
724 | for (auto &lightFlag : m_lightFlags) |
725 | inVisitor.visit(lightFlag); |
726 | |
727 | for (auto &lightSpotFlag : m_lightSpotFlags) |
728 | inVisitor.visit(lightSpotFlag); |
729 | |
730 | for (auto &lightAreaFlag : m_lightAreaFlags) |
731 | inVisitor.visit(lightAreaFlag); |
732 | |
733 | for (auto &lightShadowFlag : m_lightShadowFlags) |
734 | inVisitor.visit(lightShadowFlag); |
735 | |
736 | inVisitor.visit(m_specularEnabled); |
737 | inVisitor.visit(m_fresnelEnabled); |
738 | inVisitor.visit(m_vertexColorsEnabled); |
739 | inVisitor.visit(m_specularModel); |
740 | |
741 | for (quint32 idx = 0, end = ImageMapCount; idx < end; ++idx) |
742 | inVisitor.visit(m_imageMaps[idx]); |
743 | |
744 | for (auto &textureChannel : m_textureChannels) |
745 | inVisitor.visit(textureChannel); |
746 | |
747 | inVisitor.visit(m_boneCount); |
748 | inVisitor.visit(m_isDoubleSided); |
749 | inVisitor.visit(m_overridesPosition); |
750 | inVisitor.visit(m_usesProjectionMatrix); |
751 | inVisitor.visit(m_usesInverseProjectionMatrix); |
752 | inVisitor.visit(m_usesPointsTopology); |
753 | inVisitor.visit(m_usesVarColor); |
754 | inVisitor.visit(m_alphaMode); |
755 | inVisitor.visit(m_vertexAttributes); |
756 | inVisitor.visit(m_usesFloatJointIndices); |
757 | inVisitor.visit(m_usesInstancing); |
758 | inVisitor.visit(m_targetCount); |
759 | inVisitor.visit(m_targetPositionOffset); |
760 | inVisitor.visit(m_targetNormalOffset); |
761 | inVisitor.visit(m_targetTangentOffset); |
762 | inVisitor.visit(m_targetBinormalOffset); |
763 | inVisitor.visit(m_targetTexCoord0Offset); |
764 | inVisitor.visit(m_targetTexCoord1Offset); |
765 | inVisitor.visit(m_targetColorOffset); |
766 | inVisitor.visit(m_blendParticles); |
767 | inVisitor.visit(m_clearcoatEnabled); |
768 | inVisitor.visit(m_transmissionEnabled); |
769 | inVisitor.visit(m_specularAAEnabled); |
770 | inVisitor.visit(m_lightmapEnabled); |
771 | inVisitor.visit(m_specularGlossyEnabled); |
772 | inVisitor.visit(m_debugMode); |
773 | inVisitor.visit(m_fogEnabled); |
774 | } |
775 | |
776 | struct OffsetVisitor |
777 | { |
778 | quint32 m_offset; |
779 | OffsetVisitor() : m_offset(0) {} |
780 | template<typename TPropType> |
781 | void visit(TPropType &inProp) |
782 | { |
783 | // if we cross the 32 bit border we just move |
784 | // to the next dword. |
785 | // This cost a few extra bits but prevents tedious errors like |
786 | // loosing shader key bits because they got moved beyond the 32 border |
787 | quint32 bit = m_offset % 32; |
788 | if (bit + TPropType::BitWidth > 32) { |
789 | m_offset += 32 - bit; |
790 | } |
791 | |
792 | inProp.setOffset(m_offset); |
793 | m_offset += TPropType::BitWidth; |
794 | } |
795 | }; |
796 | |
797 | struct StringSizeVisitor |
798 | { |
799 | qsizetype size = 0; |
800 | template<typename P> |
801 | constexpr void visit(const P &prop) |
802 | { |
803 | size += prop.name.size(); |
804 | } |
805 | }; |
806 | |
807 | struct InitVisitor |
808 | { |
809 | OffsetVisitor offsetVisitor; |
810 | StringSizeVisitor stringSizeVisitor; |
811 | |
812 | template<typename P> |
813 | void visit(P &prop) |
814 | { |
815 | offsetVisitor.visit(prop); |
816 | stringSizeVisitor.visit(prop); |
817 | } |
818 | }; |
819 | |
820 | void init() |
821 | { |
822 | InitVisitor visitor; |
823 | visitProperties(inVisitor&: visitor); |
824 | |
825 | // If this assert fires, then the default material key needs more bits. |
826 | Q_ASSERT(visitor.offsetVisitor.m_offset < 416); |
827 | // This is so we can do some guestimate of how big the string buffer needs |
828 | // to be to avoid doing a lot of allocations when concatenating the strings. |
829 | m_stringBufferSizeHint = visitor.stringSizeVisitor.size; |
830 | } |
831 | }; |
832 | |
833 | struct QSSGShaderDefaultMaterialKey |
834 | { |
835 | enum { |
836 | DataBufferSize = 13, |
837 | }; |
838 | quint32 m_dataBuffer[DataBufferSize]; // 13 * 4 * 8 = 416 bits |
839 | size_t m_featureSetHash; |
840 | |
841 | explicit QSSGShaderDefaultMaterialKey(size_t inFeatureSetHash) : m_featureSetHash(inFeatureSetHash) |
842 | { |
843 | for (size_t idx = 0; idx < DataBufferSize; ++idx) |
844 | m_dataBuffer[idx] = 0; |
845 | } |
846 | |
847 | QSSGShaderDefaultMaterialKey() : m_featureSetHash(0) |
848 | { |
849 | for (size_t idx = 0; idx < DataBufferSize; ++idx) |
850 | m_dataBuffer[idx] = 0; |
851 | } |
852 | |
853 | size_t hash() const |
854 | { |
855 | size_t retval = 0; |
856 | for (size_t idx = 0; idx < DataBufferSize; ++idx) |
857 | retval = retval ^ qHash(key: m_dataBuffer[idx]); |
858 | return retval ^ m_featureSetHash; |
859 | } |
860 | |
861 | bool operator==(const QSSGShaderDefaultMaterialKey &other) const |
862 | { |
863 | bool retval = true; |
864 | for (size_t idx = 0; idx < DataBufferSize && retval; ++idx) |
865 | retval = m_dataBuffer[idx] == other.m_dataBuffer[idx]; |
866 | return retval && m_featureSetHash == other.m_featureSetHash; |
867 | } |
868 | |
869 | // Cast operators to make getting properties easier. |
870 | operator QSSGDataRef<quint32>() { return toDataRef(type: m_dataBuffer, count: DataBufferSize); } |
871 | operator QSSGDataView<quint32>() const { return toDataView(type: m_dataBuffer, count: DataBufferSize); } |
872 | |
873 | struct StringVisitor |
874 | { |
875 | QByteArray &m_str; |
876 | QSSGDataView<quint32> m_keyStore; |
877 | StringVisitor(QByteArray &s, QSSGDataView<quint32> ks) : m_str(s), m_keyStore(ks) {} |
878 | template<typename TPropType> |
879 | void visit(const TPropType &prop) |
880 | { |
881 | const qsizetype originalSize = m_str.size(); |
882 | if (m_str.size()) |
883 | m_str.append(c: ';'); |
884 | prop.toString(m_str, m_keyStore); |
885 | // if the only thing we added was the semicolon |
886 | // then nuke the semicolon |
887 | if (originalSize && m_str.size() == (originalSize + 1)) |
888 | m_str.resize(size: originalSize); |
889 | } |
890 | }; |
891 | |
892 | struct StringInVisitor |
893 | { |
894 | const QByteArray &m_str; |
895 | QSSGDataRef<quint32> m_keyStore; |
896 | StringInVisitor(const QByteArray &s, QSSGDataRef<quint32> ks) : m_str(s), m_keyStore(ks) {} |
897 | |
898 | template<typename TPropType> |
899 | void visit(TPropType &prop) |
900 | { |
901 | prop.fromString(m_str, m_keyStore); |
902 | } |
903 | }; |
904 | |
905 | void toString(QByteArray &ioString, const QSSGShaderDefaultMaterialKeyProperties &inProperties) const |
906 | { |
907 | ioString.reserve(asize: inProperties.m_stringBufferSizeHint); |
908 | StringVisitor theVisitor(ioString, *this); |
909 | const_cast<QSSGShaderDefaultMaterialKeyProperties &>(inProperties).visitProperties(inVisitor&: theVisitor); |
910 | } |
911 | void fromString(QByteArray &ioString, QSSGShaderDefaultMaterialKeyProperties &inProperties) |
912 | { |
913 | StringInVisitor theVisitor(ioString, *this); |
914 | inProperties.visitProperties(inVisitor&: theVisitor); |
915 | } |
916 | QByteArray toByteArray() const |
917 | { |
918 | QByteArray ret; |
919 | ret.resize(size: sizeof(m_dataBuffer)); |
920 | memcpy(dest: ret.data(), src: m_dataBuffer, n: sizeof(m_dataBuffer)); |
921 | return ret; |
922 | } |
923 | bool fromByteArray(const QByteArray &data) const |
924 | { |
925 | if (data.size() != sizeof(m_dataBuffer)) |
926 | return false; |
927 | memcpy(dest: (void *)m_dataBuffer, src: data.data(), n: sizeof(m_dataBuffer)); |
928 | return true; |
929 | } |
930 | }; |
931 | |
932 | Q_STATIC_ASSERT(std::is_trivially_destructible<QSSGShaderDefaultMaterialKey>::value); |
933 | |
934 | |
935 | inline size_t qHash(const QSSGShaderDefaultMaterialKey &key) |
936 | { |
937 | return key.hash(); |
938 | } |
939 | |
940 | QT_END_NAMESPACE |
941 | |
942 | #endif |
943 | |