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
24QT_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
33struct 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
52protected:
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
82struct 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
125template<quint32 TBitWidth>
126struct 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
187private:
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
195struct 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
247struct 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
309struct 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
358struct 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
417struct 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
497struct 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
833struct 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
932Q_STATIC_ASSERT(std::is_trivially_destructible<QSSGShaderDefaultMaterialKey>::value);
933
934
935inline size_t qHash(const QSSGShaderDefaultMaterialKey &key)
936{
937 return key.hash();
938}
939
940QT_END_NAMESPACE
941
942#endif
943

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