1/****************************************************************************
2**
3** Copyright (C) 2019 The Qt Company Ltd.
4** Contact: http://www.qt.io/licensing/
5**
6** This file is part of the Qt Gui module
7**
8** $QT_BEGIN_LICENSE:LGPL3$
9** Commercial License Usage
10** Licensees holding valid commercial Qt licenses may use this file in
11** accordance with the commercial license agreement provided with the
12** Software or, alternatively, in accordance with the terms contained in
13** a written agreement between you and The Qt Company. For licensing terms
14** and conditions see http://www.qt.io/terms-conditions. For further
15** information use the contact form at http://www.qt.io/contact-us.
16**
17** GNU Lesser General Public License Usage
18** Alternatively, this file may be used under the terms of the GNU Lesser
19** General Public License version 3 as published by the Free Software
20** Foundation and appearing in the file LICENSE.LGPLv3 included in the
21** packaging of this file. Please review the following information to
22** ensure the GNU Lesser General Public License version 3 requirements
23** will be met: https://www.gnu.org/licenses/lgpl.html.
24**
25** GNU General Public License Usage
26** Alternatively, this file may be used under the terms of the GNU
27** General Public License version 2.0 or later as published by the Free
28** Software Foundation and appearing in the file LICENSE.GPL included in
29** the packaging of this file. Please review the following information to
30** ensure the GNU General Public License version 2.0 requirements will be
31** met: http://www.gnu.org/licenses/gpl-2.0.html.
32**
33** $QT_END_LICENSE$
34**
35****************************************************************************/
36
37#include "qshader_p_p.h"
38#include <QDataStream>
39#include <QBuffer>
40
41QT_BEGIN_NAMESPACE
42
43/*!
44 \class QShader
45 \internal
46 \inmodule QtGui
47
48 \brief Contains multiple versions of a shader translated to multiple shading languages,
49 together with reflection metadata.
50
51 QShader is the entry point to shader code in the graphics API agnostic
52 Qt world. Instead of using GLSL shader sources, as was the custom with Qt
53 5.x, new graphics systems with backends for multiple graphics APIs, such
54 as, Vulkan, Metal, Direct3D, and OpenGL, take QShader as their input
55 whenever a shader needs to be specified.
56
57 A QShader instance is empty and thus invalid by default. To get a useful
58 instance, the two typical methods are:
59
60 \list
61
62 \li Generate the contents offline, during build time or earlier, using the
63 \c qsb command line tool. The result is a binary file that is shipped with
64 the application, read via QIODevice::readAll(), and then deserialized via
65 fromSerialized(). For more information, see QShaderBaker.
66
67 \li Generate at run time via QShaderBaker. This is an expensive operation,
68 but allows applications to use user-provided or dynamically generated
69 shader source strings.
70
71 \endlist
72
73 When used together with the Qt Rendering Hardware Interface and its
74 classes, like QRhiGraphicsPipeline, no further action is needed from the
75 application's side as these classes are prepared to consume a QShader
76 whenever a shader needs to be specified for a given stage of the graphics
77 pipeline.
78
79 Alternatively, applications can access
80
81 \list
82
83 \li the source or byte code for any of the shading language versions that
84 are included in the QShader,
85
86 \li the name of the entry point for the shader,
87
88 \li the reflection metadata containing a description of the shader's
89 inputs, outputs and resources like uniform blocks. This is essential when
90 an application or framework needs to discover the inputs of a shader at
91 runtime due to not having advance knowledge of the vertex attributes or the
92 layout of the uniform buffers used by the shader.
93
94 \endlist
95
96 QShader makes no assumption about the shading language that was used
97 as the source for generating the various versions and variants that are
98 included in it.
99
100 QShader uses implicit sharing similarly to many core Qt types, and so
101 can be returned or passed by value. Detach happens implicitly when calling
102 a setter.
103
104 For reference, QRhi expects that a QShader suitable for all its
105 backends contains at least the following:
106
107 \list
108
109 \li SPIR-V 1.0 bytecode suitable for Vulkan 1.0 or newer
110
111 \li GLSL/ES 100 source code suitable for OpenGL ES 2.0 or newer
112
113 \li GLSL 120 source code suitable for OpenGL 2.1
114
115 \li HLSL Shader Model 5.0 source code or the corresponding DXBC bytecode suitable for Direct3D 11
116
117 \li Metal Shading Language 1.2 source code or the corresponding bytecode suitable for Metal
118
119 \endlist
120
121 \sa QShaderBaker
122 */
123
124/*!
125 \enum QShader::Stage
126 Describes the stage of the graphics pipeline the shader is suitable for.
127
128 \value VertexStage Vertex shader
129 \value TessellationControlStage Tessellation control (hull) shader
130 \value TessellationEvaluationStage Tessellation evaluation (domain) shader
131 \value GeometryStage Geometry shader
132 \value FragmentStage Fragment (pixel) shader
133 \value ComputeStage Compute shader
134 */
135
136/*!
137 \class QShaderVersion
138 \internal
139 \inmodule QtGui
140
141 \brief Specifies the shading language version.
142
143 While languages like SPIR-V or the Metal Shading Language use traditional
144 version numbers, shaders for other APIs can use slightly different
145 versioning schemes. All those are mapped to a single version number in
146 here, however. For HLSL, the version refers to the Shader Model version,
147 like 5.0, 5.1, or 6.0. For GLSL an additional flag is needed to choose
148 between GLSL and GLSL/ES.
149
150 Below is a list with the most common examples of shader versions for
151 different graphics APIs:
152
153 \list
154
155 \li Vulkan (SPIR-V): 100
156 \li OpenGL: 120, 330, 440, etc.
157 \li OpenGL ES: 100 with GlslEs, 300 with GlslEs, etc.
158 \li Direct3D: 50, 51, 60
159 \li Metal: 12, 20
160 \endlist
161
162 A default constructed QShaderVersion contains a version of 100 and no
163 flags set.
164 */
165
166/*!
167 \enum QShaderVersion::Flag
168
169 Describes the flags that can be set.
170
171 \value GlslEs Indicates that GLSL/ES is meant in combination with GlslShader
172 */
173
174/*!
175 \class QShaderKey
176 \internal
177 \inmodule QtGui
178
179 \brief Specifies the shading language, the version with flags, and the variant.
180
181 A default constructed QShaderKey has source set to SpirvShader and
182 sourceVersion set to 100. sourceVariant defaults to StandardShader.
183 */
184
185/*!
186 \enum QShader::Source
187 Describes what kind of shader code an entry contains.
188
189 \value SpirvShader SPIR-V
190 \value GlslShader GLSL
191 \value HlslShader HLSL
192 \value DxbcShader Direct3D bytecode (HLSL compiled by \c fxc)
193 \value MslShader Metal Shading Language
194 \value DxilShader Direct3D bytecode (HLSL compiled by \c dxc)
195 \value MetalLibShader Pre-compiled Metal bytecode
196 */
197
198/*!
199 \enum QShader::Variant
200 Describes what kind of shader code an entry contains.
201
202 \value StandardShader The normal, unmodified version of the shader code.
203 \value BatchableVertexShader Vertex shader rewritten to be suitable for Qt Quick scenegraph batching.
204 */
205
206/*!
207 \class QShaderCode
208 \internal
209 \inmodule QtGui
210
211 \brief Contains source or binary code for a shader and additional metadata.
212
213 When shader() is empty after retrieving a QShaderCode instance from
214 QShader, it indicates no shader code was found for the requested key.
215 */
216
217/*!
218 Constructs a new, empty (and thus invalid) QShader instance.
219 */
220QShader::QShader()
221 : d(new QShaderPrivate)
222{
223}
224
225/*!
226 \internal
227 */
228void QShader::detach()
229{
230 qAtomicDetach(d);
231}
232
233/*!
234 \internal
235 */
236QShader::QShader(const QShader &other)
237 : d(other.d)
238{
239 d->ref.ref();
240}
241
242/*!
243 \internal
244 */
245QShader &QShader::operator=(const QShader &other)
246{
247 qAtomicAssign(d, x: other.d);
248 return *this;
249}
250
251/*!
252 Destructor.
253 */
254QShader::~QShader()
255{
256 if (!d->ref.deref())
257 delete d;
258}
259
260/*!
261 \return true if the QShader contains at least one shader version.
262 */
263bool QShader::isValid() const
264{
265 return !d->shaders.isEmpty();
266}
267
268/*!
269 \return the pipeline stage the shader is meant for.
270 */
271QShader::Stage QShader::stage() const
272{
273 return d->stage;
274}
275
276/*!
277 Sets the pipeline \a stage.
278 */
279void QShader::setStage(Stage stage)
280{
281 if (stage != d->stage) {
282 detach();
283 d->stage = stage;
284 }
285}
286
287/*!
288 \return the reflection metadata for the shader.
289 */
290QShaderDescription QShader::description() const
291{
292 return d->desc;
293}
294
295/*!
296 Sets the reflection metadata to \a desc.
297 */
298void QShader::setDescription(const QShaderDescription &desc)
299{
300 detach();
301 d->desc = desc;
302}
303
304/*!
305 \return the list of available shader versions
306 */
307QVector<QShaderKey> QShader::availableShaders() const
308{
309 return d->shaders.keys().toVector();
310}
311
312/*!
313 \return the source or binary code for a given shader version specified by \a key.
314 */
315QShaderCode QShader::shader(const QShaderKey &key) const
316{
317 return d->shaders.value(akey: key);
318}
319
320/*!
321 Stores the source or binary \a shader code for a given shader version specified by \a key.
322 */
323void QShader::setShader(const QShaderKey &key, const QShaderCode &shader)
324{
325 if (d->shaders.value(akey: key) == shader)
326 return;
327
328 detach();
329 d->shaders[key] = shader;
330}
331
332/*!
333 Removes the source or binary shader code for a given \a key.
334 Does nothing when not found.
335 */
336void QShader::removeShader(const QShaderKey &key)
337{
338 auto it = d->shaders.find(akey: key);
339 if (it == d->shaders.end())
340 return;
341
342 detach();
343 d->shaders.erase(it);
344}
345
346static void writeShaderKey(QDataStream *ds, const QShaderKey &k)
347{
348 *ds << int(k.source());
349 *ds << k.sourceVersion().version();
350 *ds << k.sourceVersion().flags();
351 *ds << int(k.sourceVariant());
352}
353
354/*!
355 \return a serialized binary version of all the data held by the
356 QShader, suitable for writing to files or other I/O devices.
357
358 \sa fromSerialized()
359 */
360QByteArray QShader::serialized() const
361{
362 QBuffer buf;
363 QDataStream ds(&buf);
364 ds.setVersion(QDataStream::Qt_5_10);
365 if (!buf.open(openMode: QIODevice::WriteOnly))
366 return QByteArray();
367
368 ds << QShaderPrivate::QSB_VERSION;
369 ds << int(d->stage);
370 d->desc.serialize(stream: &ds);
371 ds << d->shaders.count();
372 for (auto it = d->shaders.cbegin(), itEnd = d->shaders.cend(); it != itEnd; ++it) {
373 const QShaderKey &k(it.key());
374 writeShaderKey(ds: &ds, k);
375 const QShaderCode &shader(d->shaders.value(akey: k));
376 ds << shader.shader();
377 ds << shader.entryPoint();
378 }
379 ds << d->bindings.count();
380 for (auto it = d->bindings.cbegin(), itEnd = d->bindings.cend(); it != itEnd; ++it) {
381 const QShaderKey &k(it.key());
382 writeShaderKey(ds: &ds, k);
383 const NativeResourceBindingMap &map(it.value());
384 ds << map.count();
385 for (auto mapIt = map.cbegin(), mapItEnd = map.cend(); mapIt != mapItEnd; ++mapIt) {
386 ds << mapIt.key();
387 ds << mapIt.value().first;
388 ds << mapIt.value().second;
389 }
390 }
391
392 return qCompress(data: buf.buffer());
393}
394
395static void readShaderKey(QDataStream *ds, QShaderKey *k)
396{
397 int intVal;
398 *ds >> intVal;
399 k->setSource(QShader::Source(intVal));
400 QShaderVersion ver;
401 *ds >> intVal;
402 ver.setVersion(intVal);
403 *ds >> intVal;
404 ver.setFlags(QShaderVersion::Flags(intVal));
405 k->setSourceVersion(ver);
406 *ds >> intVal;
407 k->setSourceVariant(QShader::Variant(intVal));
408}
409
410/*!
411 Creates a new QShader instance from the given \a data.
412
413 \sa serialized()
414 */
415QShader QShader::fromSerialized(const QByteArray &data)
416{
417 QByteArray udata = qUncompress(data);
418 QBuffer buf(&udata);
419 QDataStream ds(&buf);
420 ds.setVersion(QDataStream::Qt_5_10);
421 if (!buf.open(openMode: QIODevice::ReadOnly))
422 return QShader();
423
424 QShader bs;
425 QShaderPrivate *d = QShaderPrivate::get(s: &bs);
426 Q_ASSERT(d->ref.loadRelaxed() == 1); // must be detached
427 int intVal;
428 ds >> intVal;
429 d->qsbVersion = intVal;
430 if (d->qsbVersion != QShaderPrivate::QSB_VERSION
431 && d->qsbVersion != QShaderPrivate::QSB_VERSION_WITHOUT_VAR_ARRAYDIMS
432 && d->qsbVersion != QShaderPrivate::QSB_VERSION_WITH_CBOR
433 && d->qsbVersion != QShaderPrivate::QSB_VERSION_WITH_BINARY_JSON
434 && d->qsbVersion != QShaderPrivate::QSB_VERSION_WITHOUT_BINDINGS)
435 {
436 qWarning(msg: "Attempted to deserialize QShader with unknown version %d.", d->qsbVersion);
437 return QShader();
438 }
439
440 ds >> intVal;
441 d->stage = Stage(intVal);
442 if (d->qsbVersion > QShaderPrivate::QSB_VERSION_WITH_CBOR) {
443 d->desc = QShaderDescription::deserialize(stream: &ds, version: d->qsbVersion);
444 } else if (d->qsbVersion > QShaderPrivate::QSB_VERSION_WITH_BINARY_JSON) {
445 QByteArray descBin;
446 ds >> descBin;
447 d->desc = QShaderDescription::fromCbor(data: descBin);
448 } else {
449#if QT_CONFIG(binaryjson) && QT_DEPRECATED_SINCE(5, 15)
450 QT_WARNING_PUSH
451 QT_WARNING_DISABLE_DEPRECATED
452 QByteArray descBin;
453 ds >> descBin;
454 d->desc = QShaderDescription::fromBinaryJson(data: descBin);
455 QT_WARNING_POP
456#else
457 qWarning("Cannot load QShaderDescription from binary JSON due to disabled binaryjson feature");
458 d->desc = QShaderDescription();
459#endif
460 }
461 int count;
462 ds >> count;
463 for (int i = 0; i < count; ++i) {
464 QShaderKey k;
465 readShaderKey(ds: &ds, k: &k);
466 QShaderCode shader;
467 QByteArray s;
468 ds >> s;
469 shader.setShader(s);
470 ds >> s;
471 shader.setEntryPoint(s);
472 d->shaders[k] = shader;
473 }
474
475 if (d->qsbVersion > QShaderPrivate::QSB_VERSION_WITHOUT_BINDINGS) {
476 ds >> count;
477 for (int i = 0; i < count; ++i) {
478 QShaderKey k;
479 readShaderKey(ds: &ds, k: &k);
480 NativeResourceBindingMap map;
481 int mapSize;
482 ds >> mapSize;
483 for (int b = 0; b < mapSize; ++b) {
484 int binding;
485 ds >> binding;
486 int firstNativeBinding;
487 ds >> firstNativeBinding;
488 int secondNativeBinding;
489 ds >> secondNativeBinding;
490 map.insert(akey: binding, avalue: { firstNativeBinding, secondNativeBinding });
491 }
492 d->bindings.insert(akey: k, avalue: map);
493 }
494 }
495
496 return bs;
497}
498
499QShaderVersion::QShaderVersion(int v, Flags f)
500 : m_version(v), m_flags(f)
501{
502}
503
504QShaderCode::QShaderCode(const QByteArray &code, const QByteArray &entry)
505 : m_shader(code), m_entryPoint(entry)
506{
507}
508
509QShaderKey::QShaderKey(QShader::Source s,
510 const QShaderVersion &sver,
511 QShader::Variant svar)
512 : m_source(s),
513 m_sourceVersion(sver),
514 m_sourceVariant(svar)
515{
516}
517
518/*!
519 Returns \c true if the two QShader objects \a lhs and \a rhs are equal,
520 meaning they are for the same stage with matching sets of shader source or
521 binary code.
522
523 \relates QShader
524 */
525bool operator==(const QShader &lhs, const QShader &rhs) Q_DECL_NOTHROW
526{
527 return lhs.d->stage == rhs.d->stage
528 && lhs.d->shaders == rhs.d->shaders;
529 // do not bother with desc and bindings, if the shader code is the same, the description must match too
530}
531
532/*!
533 \fn bool operator!=(const QShader &lhs, const QShader &rhs)
534
535 Returns \c false if the values in the two QShader objects \a a and \a b
536 are equal; otherwise returns \c true.
537
538 \relates QShader
539 */
540
541/*!
542 Returns the hash value for \a s, using \a seed to seed the calculation.
543
544 \relates QShader
545 */
546uint qHash(const QShader &s, uint seed) Q_DECL_NOTHROW
547{
548 uint h = s.stage();
549 for (auto it = s.d->shaders.constBegin(), itEnd = s.d->shaders.constEnd(); it != itEnd; ++it)
550 h += qHash(k: it.key(), seed) + qHash(key: it.value().shader(), seed);
551 return h;
552}
553
554/*!
555 Returns \c true if the two QShaderVersion objects \a lhs and \a rhs are
556 equal.
557
558 \relates QShaderVersion
559 */
560bool operator==(const QShaderVersion &lhs, const QShaderVersion &rhs) Q_DECL_NOTHROW
561{
562 return lhs.version() == rhs.version() && lhs.flags() == rhs.flags();
563}
564
565/*!
566 \fn bool operator!=(const QShaderVersion &lhs, const QShaderVersion &rhs)
567
568 Returns \c false if the values in the two QShaderVersion objects \a a
569 and \a b are equal; otherwise returns \c true.
570
571 \relates QShaderVersion
572 */
573
574/*!
575 Returns \c true if the two QShaderKey objects \a lhs and \a rhs are equal.
576
577 \relates QShaderKey
578 */
579bool operator==(const QShaderKey &lhs, const QShaderKey &rhs) Q_DECL_NOTHROW
580{
581 return lhs.source() == rhs.source() && lhs.sourceVersion() == rhs.sourceVersion()
582 && lhs.sourceVariant() == rhs.sourceVariant();
583}
584
585/*!
586 \fn bool operator!=(const QShaderKey &lhs, const QShaderKey &rhs)
587
588 Returns \c false if the values in the two QShaderKey objects \a a
589 and \a b are equal; otherwise returns \c true.
590
591 \relates QShaderKey
592 */
593
594/*!
595 Returns the hash value for \a k, using \a seed to seed the calculation.
596
597 \relates QShaderKey
598 */
599uint qHash(const QShaderKey &k, uint seed) Q_DECL_NOTHROW
600{
601 return seed + 10 * k.source() + k.sourceVersion().version() + k.sourceVersion().flags() + k.sourceVariant();
602}
603
604/*!
605 Returns \c true if the two QShaderCode objects \a lhs and \a rhs are equal.
606
607 \relates QShaderCode
608 */
609bool operator==(const QShaderCode &lhs, const QShaderCode &rhs) Q_DECL_NOTHROW
610{
611 return lhs.shader() == rhs.shader() && lhs.entryPoint() == rhs.entryPoint();
612}
613
614/*!
615 \fn bool operator!=(const QShaderCode &lhs, const QShaderCode &rhs)
616
617 Returns \c false if the values in the two QShaderCode objects \a a
618 and \a b are equal; otherwise returns \c true.
619
620 \relates QShaderCode
621 */
622
623#ifndef QT_NO_DEBUG_STREAM
624QDebug operator<<(QDebug dbg, const QShader &bs)
625{
626 const QShaderPrivate *d = bs.d;
627 QDebugStateSaver saver(dbg);
628
629 dbg.nospace() << "QShader("
630 << "stage=" << d->stage
631 << " shaders=" << d->shaders.keys()
632 << " desc.isValid=" << d->desc.isValid()
633 << ')';
634
635 return dbg;
636}
637
638QDebug operator<<(QDebug dbg, const QShaderKey &k)
639{
640 QDebugStateSaver saver(dbg);
641 dbg.nospace() << "ShaderKey(" << k.source()
642 << " " << k.sourceVersion()
643 << " " << k.sourceVariant() << ")";
644 return dbg;
645}
646
647QDebug operator<<(QDebug dbg, const QShaderVersion &v)
648{
649 QDebugStateSaver saver(dbg);
650 dbg.nospace() << "Version(" << v.version() << " " << v.flags() << ")";
651 return dbg;
652}
653#endif // QT_NO_DEBUG_STREAM
654
655/*!
656 \typedef QShader::NativeResourceBindingMap
657
658 Synonym for QHash<int, QPair<int, int>>.
659
660 The resource binding model QRhi assumes is based on SPIR-V. This means that
661 uniform buffers, storage buffers, combined image samplers, and storage
662 images share a common binding point space. The binding numbers in
663 QShaderDescription and QRhiShaderResourceBinding are expected to match the
664 \c binding layout qualifier in the Vulkan-compatible GLSL shader.
665
666 Graphics APIs other than Vulkan may use a resource binding model that is
667 not fully compatible with this. In addition, the generator of the shader
668 code translated from SPIR-V may choose not to take the SPIR-V binding
669 qualifiers into account, for various reasons. (this is the case with the
670 Metal backend of SPIRV-Cross, for example).
671
672 Therefore, a QShader may expose an additional map that describes what the
673 native binding point for a given SPIR-V binding is. The QRhi backends are
674 expected to use this map automatically, as appropriate. The value is a
675 pair, because combined image samplers may map to two native resources (a
676 texture and a sampler) in some shading languages. In that case the second
677 value refers to the sampler.
678
679 \note The native binding may be -1, in case there is no active binding for
680 the resource in the shader. (for example, there is a uniform block
681 declared, but it is not used in the shader code) The map is always
682 complete, meaning there is an entry for all declared uniform blocks,
683 storage blocks, image objects, and combined samplers, but the value will be
684 -1 for those that are not actually referenced in the shader functions.
685*/
686
687/*!
688 \return the native binding map for \a key or null if no extra mapping is
689 available, or is not applicable.
690 */
691const QShader::NativeResourceBindingMap *QShader::nativeResourceBindingMap(const QShaderKey &key) const
692{
693 auto it = d->bindings.constFind(akey: key);
694 if (it == d->bindings.cend())
695 return nullptr;
696
697 return &it.value();
698}
699
700/*!
701 Stores the given native resource binding \a map associated with \a key.
702
703 \sa nativeResourceBindingMap()
704 */
705void QShader::setResourceBindingMap(const QShaderKey &key, const NativeResourceBindingMap &map)
706{
707 detach();
708 d->bindings[key] = map;
709}
710
711/*!
712 Removes the native resource binding map for \a key.
713 */
714void QShader::removeResourceBindingMap(const QShaderKey &key)
715{
716 auto it = d->bindings.find(akey: key);
717 if (it == d->bindings.end())
718 return;
719
720 detach();
721 d->bindings.erase(it);
722}
723
724QT_END_NAMESPACE
725

source code of qtbase/src/gui/rhi/qshader.cpp