1// Copyright (C) 2023 The Qt Company Ltd.
2// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
3
4#include "qshader_p.h"
5#include <QDataStream>
6#include <QBuffer>
7
8QT_BEGIN_NAMESPACE
9
10/*!
11 \class QShader
12 \ingroup painting-3D
13 \inmodule QtGui
14 \since 6.6
15
16 \brief Contains multiple versions of a shader translated to multiple shading languages,
17 together with reflection metadata.
18
19 QShader is the entry point to shader code in the graphics API agnostic
20 Qt world. Instead of using GLSL shader sources, as was the custom with Qt
21 5.x, new graphics systems with backends for multiple graphics APIs, such
22 as, Vulkan, Metal, Direct3D, and OpenGL, take QShader as their input
23 whenever a shader needs to be specified.
24
25 \warning The QRhi family of classes in the Qt Gui module, including QShader
26 and QShaderDescription, offer limited compatibility guarantees. There are
27 no source or binary compatibility guarantees for these classes, meaning the
28 API is only guaranteed to work with the Qt version the application was
29 developed against. Source incompatible changes are however aimed to be kept
30 at a minimum and will only be made in minor releases (6.7, 6.8, and so on).
31 To use these classes in an application, link to
32 \c{Qt::GuiPrivate} (if using CMake), and include the headers with the \c
33 rhi prefix, for example \c{#include <rhi/qshader.h>}.
34
35 A QShader instance is empty and thus invalid by default. To get a useful
36 instance, the two typical methods are:
37
38 \list
39
40 \li Generate the contents offline, during build time or earlier, using the
41 \c qsb command line tool. The result is a binary file that is shipped with
42 the application, read via QIODevice::readAll(), and then deserialized via
43 fromSerialized(). For more information, see QShaderBaker.
44
45 \li Generate at run time via QShaderBaker. This is an expensive operation,
46 but allows applications to use user-provided or dynamically generated
47 shader source strings.
48
49 \endlist
50
51 When used together with the Qt Rendering Hardware Interface and its
52 classes, like QRhiGraphicsPipeline, no further action is needed from the
53 application's side as these classes are prepared to consume a QShader
54 whenever a shader needs to be specified for a given stage of the graphics
55 pipeline.
56
57 Alternatively, applications can access
58
59 \list
60
61 \li the source or byte code for any of the shading language versions that
62 are included in the QShader,
63
64 \li the name of the entry point for the shader,
65
66 \li the reflection metadata containing a description of the shader's
67 inputs, outputs and resources like uniform blocks. This is essential when
68 an application or framework needs to discover the inputs of a shader at
69 runtime due to not having advance knowledge of the vertex attributes or the
70 layout of the uniform buffers used by the shader.
71
72 \endlist
73
74 QShader makes no assumption about the shading language that was used
75 as the source for generating the various versions and variants that are
76 included in it.
77
78 QShader uses implicit sharing similarly to many core Qt types, and so
79 can be returned or passed by value. Detach happens implicitly when calling
80 a setter.
81
82 For reference, a typical, portable QRhi expects that a QShader suitable for
83 all its backends contains at least the following. (this excludes support
84 for core profile OpenGL contexts, add GLSL 150 or newer for that)
85
86 \list
87
88 \li SPIR-V 1.0 bytecode suitable for Vulkan 1.0 or newer
89
90 \li GLSL/ES 100 source code suitable for OpenGL ES 2.0 or newer
91
92 \li GLSL 120 source code suitable for OpenGL 2.1 or newer
93
94 \li HLSL Shader Model 5.0 source code or the corresponding DXBC bytecode suitable for Direct3D 11/12
95
96 \li Metal Shading Language 1.2 source code or the corresponding bytecode suitable for Metal 1.2 or newer
97
98 \endlist
99
100 \sa QShaderBaker
101 */
102
103/*!
104 \enum QShader::Stage
105 Describes the stage of the graphics pipeline the shader is suitable for.
106
107 \value VertexStage Vertex shader
108 \value TessellationControlStage Tessellation control (hull) shader
109 \value TessellationEvaluationStage Tessellation evaluation (domain) shader
110 \value GeometryStage Geometry shader
111 \value FragmentStage Fragment (pixel) shader
112 \value ComputeStage Compute shader
113 */
114
115/*!
116 \class QShaderVersion
117 \inmodule QtGui
118 \since 6.6
119
120 \brief Specifies the shading language version.
121
122 While languages like SPIR-V or the Metal Shading Language use traditional
123 version numbers, shaders for other APIs can use slightly different
124 versioning schemes. All those are mapped to a single version number in
125 here, however. For HLSL, the version refers to the Shader Model version,
126 like 5.0, 5.1, or 6.0. For GLSL an additional flag is needed to choose
127 between GLSL and GLSL/ES.
128
129 Below is a list with the most common examples of shader versions for
130 different graphics APIs:
131
132 \list
133
134 \li Vulkan (SPIR-V): 100
135 \li OpenGL: 120, 330, 440, etc.
136 \li OpenGL ES: 100 with GlslEs, 300 with GlslEs, etc.
137 \li Direct3D: 50, 51, 60
138 \li Metal: 12, 20
139 \endlist
140
141 A default constructed QShaderVersion contains a version of 100 and no
142 flags set.
143
144 \note This is a RHI API with limited compatibility guarantees, see \l QShader
145 for details.
146 */
147
148/*!
149 \enum QShaderVersion::Flag
150
151 Describes the flags that can be set.
152
153 \value GlslEs Indicates that GLSL/ES is meant in combination with GlslShader
154 */
155
156/*!
157 \class QShaderKey
158 \inmodule QtGui
159 \since 6.6
160
161 \brief Specifies the shading language, the version with flags, and the variant.
162
163 A default constructed QShaderKey has source set to SpirvShader and
164 sourceVersion set to 100. sourceVariant defaults to StandardShader.
165
166 \note This is a RHI API with limited compatibility guarantees, see \l QShader
167 for details.
168 */
169
170/*!
171 \enum QShader::Source
172 Describes what kind of shader code an entry contains.
173
174 \value SpirvShader SPIR-V
175 \value GlslShader GLSL
176 \value HlslShader HLSL
177 \value DxbcShader Direct3D bytecode (HLSL compiled by \c fxc)
178 \value MslShader Metal Shading Language
179 \value DxilShader Direct3D bytecode (HLSL compiled by \c dxc)
180 \value MetalLibShader Pre-compiled Metal bytecode
181 \value WgslShader WGSL
182 */
183
184/*!
185 \enum QShader::Variant
186 Describes what kind of shader code an entry contains.
187
188 \value StandardShader The normal, unmodified version of the shader code.
189
190 \value BatchableVertexShader Vertex shader rewritten to be suitable for Qt Quick scenegraph batching.
191
192 \value UInt16IndexedVertexAsComputeShader A vertex shader meant to be used
193 in a Metal pipeline with tessellation in combination with indexed draw
194 calls sourcing index data from a uint16 index buffer. To support the Metal
195 tessellation pipeline, the vertex shader is translated to a compute shader
196 that may be dependent on the index buffer usage in the draw calls (e.g. if
197 the shader is using gl_VertexIndex), hence the need for three dedicated
198 variants.
199
200 \value UInt32IndexedVertexAsComputeShader A vertex shader meant to be used
201 in a Metal pipeline with tessellation in combination with indexed draw
202 calls sourcing index data from a uint32 index buffer. To support the Metal
203 tessellation pipeline, the vertex shader is translated to a compute shader
204 that may be dependent on the index buffer usage in the draw calls (e.g. if
205 the shader is using gl_VertexIndex), hence the need for three dedicated
206 variants.
207
208 \value NonIndexedVertexAsComputeShader A vertex shader meant to be used in
209 a Metal pipeline with tessellation in combination with non-indexed draw
210 calls. To support the Metal tessellation pipeline, the vertex shader is
211 translated to a compute shader that may be dependent on the index buffer
212 usage in the draw calls (e.g. if the shader is using gl_VertexIndex), hence
213 the need for three dedicated variants.
214 */
215
216/*!
217 \enum QShader::SerializedFormatVersion
218 Describes the desired output format when serializing the QShader.
219
220 The default value for the \c version argument of serialized() is \c Latest.
221 This is sufficient in the vast majority of cases. Specifying another value
222 is needed only when the intention is to generate serialized data that can
223 be loaded by earlier Qt versions. For example, the \c qsb tool uses these
224 enum values when the \c{--qsbversion} command-line argument is given.
225
226 \note Targeting earlier versions will make certain features disfunctional
227 with the generated asset. This is not an issue when using the asset with
228 the specified, older Qt version, given that that Qt version does not have
229 the newer features in newer Qt versions that rely on additional data
230 generated in the QShader and the serialized data stream, but may become a
231 problem if the generated asset is then used with a newer Qt version.
232
233 \value Latest The current Qt version
234 \value Qt_6_5 Qt 6.5
235 \value Qt_6_4 Qt 6.4
236 */
237
238/*!
239 \class QShaderCode
240 \inmodule QtGui
241 \since 6.6
242
243 \brief Contains source or binary code for a shader and additional metadata.
244
245 When shader() is empty after retrieving a QShaderCode instance from
246 QShader, it indicates no shader code was found for the requested key.
247
248 \note This is a RHI API with limited compatibility guarantees, see \l QShader
249 for details.
250 */
251
252/*!
253 Constructs a new, empty (and thus invalid) QShader instance.
254 */
255QShader::QShader()
256 : d(nullptr)
257{
258}
259
260/*!
261 \internal
262 */
263void QShader::detach()
264{
265 if (d)
266 qAtomicDetach(d);
267 else
268 d = new QShaderPrivate;
269}
270
271/*!
272 Constructs a copy of \a other.
273 */
274QShader::QShader(const QShader &other)
275 : d(other.d)
276{
277 if (d)
278 d->ref.ref();
279}
280
281/*!
282 Assigns \a other to this object.
283 */
284QShader &QShader::operator=(const QShader &other)
285{
286 if (d) {
287 if (other.d) {
288 qAtomicAssign(d, x: other.d);
289 } else {
290 if (!d->ref.deref())
291 delete d;
292 d = nullptr;
293 }
294 } else if (other.d) {
295 other.d->ref.ref();
296 d = other.d;
297 }
298 return *this;
299}
300
301/*!
302 Destructor.
303 */
304QShader::~QShader()
305{
306 if (d && !d->ref.deref())
307 delete d;
308}
309
310/*!
311 \return true if the QShader contains at least one shader version.
312 */
313bool QShader::isValid() const
314{
315 return d ? !d->shaders.isEmpty() : false;
316}
317
318/*!
319 \return the pipeline stage the shader is meant for.
320 */
321QShader::Stage QShader::stage() const
322{
323 return d ? d->stage : QShader::VertexStage;
324}
325
326/*!
327 Sets the pipeline \a stage.
328 */
329void QShader::setStage(Stage stage)
330{
331 if (!d || stage != d->stage) {
332 detach();
333 d->stage = stage;
334 }
335}
336
337/*!
338 \return the reflection metadata for the shader.
339 */
340QShaderDescription QShader::description() const
341{
342 return d ? d->desc : QShaderDescription();
343}
344
345/*!
346 Sets the reflection metadata to \a desc.
347 */
348void QShader::setDescription(const QShaderDescription &desc)
349{
350 detach();
351 d->desc = desc;
352}
353
354/*!
355 \return the list of available shader versions
356 */
357QList<QShaderKey> QShader::availableShaders() const
358{
359 return d ? d->shaders.keys().toVector() : QList<QShaderKey>();
360}
361
362/*!
363 \return the source or binary code for a given shader version specified by \a key.
364 */
365QShaderCode QShader::shader(const QShaderKey &key) const
366{
367 return d ? d->shaders.value(key) : QShaderCode();
368}
369
370/*!
371 Stores the source or binary \a shader code for a given shader version specified by \a key.
372 */
373void QShader::setShader(const QShaderKey &key, const QShaderCode &shader)
374{
375 if (d && d->shaders.value(key) == shader)
376 return;
377
378 detach();
379 d->shaders[key] = shader;
380}
381
382/*!
383 Removes the source or binary shader code for a given \a key.
384 Does nothing when not found.
385 */
386void QShader::removeShader(const QShaderKey &key)
387{
388 if (!d)
389 return;
390
391 auto it = d->shaders.find(key);
392 if (it == d->shaders.end())
393 return;
394
395 detach();
396 d->shaders.erase(it);
397}
398
399static void writeShaderKey(QDataStream *ds, const QShaderKey &k)
400{
401 *ds << int(k.source());
402 *ds << k.sourceVersion().version();
403 *ds << k.sourceVersion().flags();
404 *ds << int(k.sourceVariant());
405}
406
407/*!
408 \return a serialized binary version of all the data held by the
409 QShader, suitable for writing to files or other I/O devices.
410
411 By default the latest serialization format is used. Use \a version
412 parameter to serialize for a compatibility Qt version. Only when it is
413 known that the generated data stream must be made compatible with an older
414 Qt version at the expense of making it incompatible with features
415 introduced since that Qt version, should another value (for example,
416 \l{SerializedFormatVersion}{Qt_6_5} for Qt 6.5) be used.
417
418 \sa fromSerialized()
419 */
420QByteArray QShader::serialized(SerializedFormatVersion version) const
421{
422 static QShaderPrivate sd;
423 QShaderPrivate *dd = d ? d : &sd;
424
425 QBuffer buf;
426 QDataStream ds(&buf);
427 ds.setVersion(QDataStream::Qt_5_10);
428 if (!buf.open(openMode: QIODevice::WriteOnly))
429 return QByteArray();
430
431 const int qsbVersion = QShaderPrivate::qtQsbVersion(qtVersion: version);
432 ds << qsbVersion;
433
434 ds << int(dd->stage);
435 dd->desc.serialize(stream: &ds, version: qsbVersion);
436 ds << int(dd->shaders.size());
437 for (auto it = dd->shaders.cbegin(), itEnd = dd->shaders.cend(); it != itEnd; ++it) {
438 const QShaderKey &k(it.key());
439 writeShaderKey(ds: &ds, k);
440 const QShaderCode &shader(dd->shaders.value(key: k));
441 ds << shader.shader();
442 ds << shader.entryPoint();
443 }
444 ds << int(dd->bindings.size());
445 for (auto it = dd->bindings.cbegin(), itEnd = dd->bindings.cend(); it != itEnd; ++it) {
446 const QShaderKey &k(it.key());
447 writeShaderKey(ds: &ds, k);
448 const NativeResourceBindingMap &map(it.value());
449 ds << int(map.size());
450 for (auto mapIt = map.cbegin(), mapItEnd = map.cend(); mapIt != mapItEnd; ++mapIt) {
451 ds << mapIt.key();
452 ds << mapIt.value().first;
453 ds << mapIt.value().second;
454 }
455 }
456 ds << int(dd->combinedImageMap.size());
457 for (auto it = dd->combinedImageMap.cbegin(), itEnd = dd->combinedImageMap.cend(); it != itEnd; ++it) {
458 const QShaderKey &k(it.key());
459 writeShaderKey(ds: &ds, k);
460 const SeparateToCombinedImageSamplerMappingList &list(it.value());
461 ds << int(list.size());
462 for (auto listIt = list.cbegin(), listItEnd = list.cend(); listIt != listItEnd; ++listIt) {
463 ds << listIt->combinedSamplerName;
464 ds << listIt->textureBinding;
465 ds << listIt->samplerBinding;
466 }
467 }
468 if (qsbVersion > QShaderPrivate::QSB_VERSION_WITHOUT_NATIVE_SHADER_INFO) {
469 ds << int(dd->nativeShaderInfoMap.size());
470 for (auto it = dd->nativeShaderInfoMap.cbegin(), itEnd = dd->nativeShaderInfoMap.cend(); it != itEnd; ++it) {
471 const QShaderKey &k(it.key());
472 writeShaderKey(ds: &ds, k);
473 ds << it->flags;
474 ds << int(it->extraBufferBindings.size());
475 for (auto mapIt = it->extraBufferBindings.cbegin(), mapItEnd = it->extraBufferBindings.cend();
476 mapIt != mapItEnd; ++mapIt)
477 {
478 ds << mapIt.key();
479 ds << mapIt.value();
480 }
481 }
482 }
483
484 return qCompress(data: buf.buffer());
485}
486
487static void readShaderKey(QDataStream *ds, QShaderKey *k)
488{
489 int intVal;
490 *ds >> intVal;
491 k->setSource(QShader::Source(intVal));
492 QShaderVersion ver;
493 *ds >> intVal;
494 ver.setVersion(intVal);
495 *ds >> intVal;
496 ver.setFlags(QShaderVersion::Flags(intVal));
497 k->setSourceVersion(ver);
498 *ds >> intVal;
499 k->setSourceVariant(QShader::Variant(intVal));
500}
501
502/*!
503 Creates a new QShader instance from the given \a data.
504
505 If \a data cannot be deserialized successfully, the result is a default
506 constructed QShader for which isValid() returns \c false.
507
508 \sa serialized()
509 */
510QShader QShader::fromSerialized(const QByteArray &data)
511{
512 QByteArray udata = qUncompress(data);
513 QBuffer buf(&udata);
514 QDataStream ds(&buf);
515 ds.setVersion(QDataStream::Qt_5_10);
516 if (!buf.open(openMode: QIODevice::ReadOnly))
517 return QShader();
518
519 QShader bs;
520 bs.detach(); // to get d created
521 QShaderPrivate *d = QShaderPrivate::get(s: &bs);
522 Q_ASSERT(d->ref.loadRelaxed() == 1); // must be detached
523 int intVal;
524 ds >> intVal;
525 d->qsbVersion = intVal;
526 if (d->qsbVersion != QShaderPrivate::QSB_VERSION
527 && d->qsbVersion != QShaderPrivate::QSB_VERSION_WITHOUT_INPUT_OUTPUT_INTERFACE_BLOCKS
528 && d->qsbVersion != QShaderPrivate::QSB_VERSION_WITHOUT_EXTENDED_STORAGE_BUFFER_INFO
529 && d->qsbVersion != QShaderPrivate::QSB_VERSION_WITHOUT_NATIVE_SHADER_INFO
530 && d->qsbVersion != QShaderPrivate::QSB_VERSION_WITHOUT_SEPARATE_IMAGES_AND_SAMPLERS
531 && d->qsbVersion != QShaderPrivate::QSB_VERSION_WITHOUT_VAR_ARRAYDIMS
532 && d->qsbVersion != QShaderPrivate::QSB_VERSION_WITH_CBOR
533 && d->qsbVersion != QShaderPrivate::QSB_VERSION_WITH_BINARY_JSON
534 && d->qsbVersion != QShaderPrivate::QSB_VERSION_WITHOUT_BINDINGS)
535 {
536 qWarning(msg: "Attempted to deserialize QShader with unknown version %d.", d->qsbVersion);
537 return QShader();
538 }
539
540 ds >> intVal;
541 d->stage = Stage(intVal);
542 if (d->qsbVersion > QShaderPrivate::QSB_VERSION_WITH_CBOR) {
543 d->desc = QShaderDescription::deserialize(stream: &ds, version: d->qsbVersion);
544 } else if (d->qsbVersion > QShaderPrivate::QSB_VERSION_WITH_BINARY_JSON) {
545 qWarning(msg: "Can no longer load QShaderDescription from CBOR.");
546 d->desc = QShaderDescription();
547 } else {
548 qWarning(msg: "Can no longer load QShaderDescription from binary JSON.");
549 d->desc = QShaderDescription();
550 }
551 int count;
552 ds >> count;
553 for (int i = 0; i < count; ++i) {
554 QShaderKey k;
555 readShaderKey(ds: &ds, k: &k);
556 QShaderCode shader;
557 QByteArray s;
558 ds >> s;
559 shader.setShader(s);
560 ds >> s;
561 shader.setEntryPoint(s);
562 d->shaders[k] = shader;
563 }
564
565 if (d->qsbVersion > QShaderPrivate::QSB_VERSION_WITHOUT_BINDINGS) {
566 ds >> count;
567 for (int i = 0; i < count; ++i) {
568 QShaderKey k;
569 readShaderKey(ds: &ds, k: &k);
570 NativeResourceBindingMap map;
571 int mapSize;
572 ds >> mapSize;
573 for (int b = 0; b < mapSize; ++b) {
574 int binding;
575 ds >> binding;
576 int firstNativeBinding;
577 ds >> firstNativeBinding;
578 int secondNativeBinding;
579 ds >> secondNativeBinding;
580 map.insert(key: binding, value: { firstNativeBinding, secondNativeBinding });
581 }
582 d->bindings.insert(key: k, value: map);
583 }
584 }
585
586 if (d->qsbVersion > QShaderPrivate::QSB_VERSION_WITHOUT_SEPARATE_IMAGES_AND_SAMPLERS) {
587 ds >> count;
588 for (int i = 0; i < count; ++i) {
589 QShaderKey k;
590 readShaderKey(ds: &ds, k: &k);
591 SeparateToCombinedImageSamplerMappingList list;
592 int listSize;
593 ds >> listSize;
594 for (int b = 0; b < listSize; ++b) {
595 QByteArray combinedSamplerName;
596 ds >> combinedSamplerName;
597 int textureBinding;
598 ds >> textureBinding;
599 int samplerBinding;
600 ds >> samplerBinding;
601 list.append(t: { .combinedSamplerName: combinedSamplerName, .textureBinding: textureBinding, .samplerBinding: samplerBinding });
602 }
603 d->combinedImageMap.insert(key: k, value: list);
604 }
605 }
606
607 if (d->qsbVersion > QShaderPrivate::QSB_VERSION_WITHOUT_NATIVE_SHADER_INFO) {
608 ds >> count;
609 for (int i = 0; i < count; ++i) {
610 QShaderKey k;
611 readShaderKey(ds: &ds, k: &k);
612 int flags;
613 ds >> flags;
614 QMap<int, int> extraBufferBindings;
615 int mapSize;
616 ds >> mapSize;
617 for (int b = 0; b < mapSize; ++b) {
618 int k, v;
619 ds >> k;
620 ds >> v;
621 extraBufferBindings.insert(key: k, value: v);
622 }
623 d->nativeShaderInfoMap.insert(key: k, value: { .flags: flags, .extraBufferBindings: extraBufferBindings });
624 }
625 }
626
627 return bs;
628}
629
630/*!
631 \fn QShaderVersion::QShaderVersion() = default
632 */
633
634/*!
635 Constructs a new QShaderVersion with version \a v and flags \a f.
636 */
637QShaderVersion::QShaderVersion(int v, Flags f)
638 : m_version(v), m_flags(f)
639{
640}
641
642/*!
643 \fn int QShaderVersion::version() const
644 \return the version.
645 */
646
647/*!
648 \fn void QShaderVersion::setVersion(int v)
649 Sets the shading language version to \a v.
650 */
651
652/*!
653 \fn QShaderVersion::Flags QShaderVersion::flags() const
654 \return the flags.
655 */
656
657/*!
658 \fn void QShaderVersion::setFlags(Flags f)
659 Sets the flags \a f.
660 */
661
662/*!
663 \fn QShaderCode::QShaderCode() = default
664 */
665
666/*!
667 Constructs a new QShaderCode with the specified shader source \a code and
668 \a entry point name.
669 */
670QShaderCode::QShaderCode(const QByteArray &code, const QByteArray &entry)
671 : m_shader(code), m_entryPoint(entry)
672{
673}
674
675/*!
676 \fn QByteArray QShaderCode::shader() const
677 \return the shader source or bytecode.
678 */
679
680/*!
681 \fn void QShaderCode::setShader(const QByteArray &code)
682 Sets the shader source or byte \a code.
683 */
684
685/*!
686 \fn QByteArray QShaderCode::entryPoint() const
687 \return the entry point name.
688 */
689
690/*!
691 \fn void QShaderCode::setEntryPoint(const QByteArray &entry)
692 Sets the \a entry point name.
693 */
694
695/*!
696 \fn QShaderKey::QShaderKey() = default
697 */
698
699/*!
700 Constructs a new QShaderKey with shader type \a s, version \a sver, and
701 variant \a svar.
702 */
703QShaderKey::QShaderKey(QShader::Source s,
704 const QShaderVersion &sver,
705 QShader::Variant svar)
706 : m_source(s),
707 m_sourceVersion(sver),
708 m_sourceVariant(svar)
709{
710}
711
712/*!
713 \fn QShader::Source QShaderKey::source() const
714 \return the shader type.
715 */
716
717/*!
718 \fn void QShaderKey::setSource(QShader::Source s)
719 Sets the shader type \a s.
720 */
721
722/*!
723 \fn QShaderVersion QShaderKey::sourceVersion() const
724 \return the shading language version.
725 */
726
727/*!
728 \fn void QShaderKey::setSourceVersion(const QShaderVersion &sver)
729 Sets the shading language version \a sver.
730 */
731
732/*!
733 \fn QShader::Variant QShaderKey::sourceVariant() const
734 \return the type of the variant to use.
735 */
736
737/*!
738 \fn void QShaderKey::setSourceVariant(QShader::Variant svar)
739 Sets the type of variant to use to \a svar.
740 */
741
742/*!
743 Returns \c true if the two QShader objects \a lhs and \a rhs are equal,
744 meaning they are for the same stage with matching sets of shader source or
745 binary code.
746
747 \relates QShader
748 */
749bool operator==(const QShader &lhs, const QShader &rhs) noexcept
750{
751 if (!lhs.d || !rhs.d)
752 return lhs.d == rhs.d;
753
754 return lhs.d->stage == rhs.d->stage
755 && lhs.d->shaders == rhs.d->shaders
756 && lhs.d->bindings == rhs.d->bindings;
757}
758
759/*!
760 \fn bool operator!=(const QShader &lhs, const QShader &rhs)
761
762 Returns \c false if the values in the two QShader objects \a lhs and \a rhs
763 are equal; otherwise returns \c true.
764
765 \relates QShader
766 */
767
768/*!
769 Returns the hash value for \a s, using \a seed to seed the calculation.
770
771 \relates QShader
772 */
773size_t qHash(const QShader &s, size_t seed) noexcept
774{
775 if (s.d) {
776 QtPrivate::QHashCombine hash;
777 seed = hash(seed, s.stage());
778 if (!s.d->shaders.isEmpty()) {
779 seed = hash(seed, s.d->shaders.firstKey());
780 seed = hash(seed, s.d->shaders.first());
781 }
782 }
783 return seed;
784}
785
786/*!
787 Returns \c true if the two QShaderVersion objects \a lhs and \a rhs are
788 equal.
789
790 \relates QShaderVersion
791 */
792bool operator==(const QShaderVersion &lhs, const QShaderVersion &rhs) noexcept
793{
794 return lhs.version() == rhs.version() && lhs.flags() == rhs.flags();
795}
796
797#ifdef Q_OS_INTEGRITY
798size_t qHash(const QShaderVersion &s, size_t seed) noexcept
799{
800 return qHashMulti(seed, s.version(), s.flags());
801}
802#endif
803
804/*!
805 \return true if \a lhs is smaller than \a rhs.
806
807 Establishes a sorting order between the two QShaderVersion \a lhs and \a rhs.
808
809 \relates QShaderVersion
810 */
811bool operator<(const QShaderVersion &lhs, const QShaderVersion &rhs) noexcept
812{
813 if (lhs.version() < rhs.version())
814 return true;
815
816 if (lhs.version() == rhs.version())
817 return int(lhs.flags()) < int(rhs.flags());
818
819 return false;
820}
821
822/*!
823 \fn bool operator!=(const QShaderVersion &lhs, const QShaderVersion &rhs)
824
825 Returns \c false if the values in the two QShaderVersion objects \a lhs
826 and \a rhs are equal; otherwise returns \c true.
827
828 \relates QShaderVersion
829 */
830
831/*!
832 Returns \c true if the two QShaderKey objects \a lhs and \a rhs are equal.
833
834 \relates QShaderKey
835 */
836bool operator==(const QShaderKey &lhs, const QShaderKey &rhs) noexcept
837{
838 return lhs.source() == rhs.source() && lhs.sourceVersion() == rhs.sourceVersion()
839 && lhs.sourceVariant() == rhs.sourceVariant();
840}
841
842/*!
843 \return true if \a lhs is smaller than \a rhs.
844
845 Establishes a sorting order between the two keys \a lhs and \a rhs.
846
847 \relates QShaderKey
848 */
849bool operator<(const QShaderKey &lhs, const QShaderKey &rhs) noexcept
850{
851 if (int(lhs.source()) < int(rhs.source()))
852 return true;
853
854 if (int(lhs.source()) == int(rhs.source())) {
855 if (lhs.sourceVersion() < rhs.sourceVersion())
856 return true;
857 if (lhs.sourceVersion() == rhs.sourceVersion()) {
858 if (int(lhs.sourceVariant()) < int(rhs.sourceVariant()))
859 return true;
860 }
861 }
862
863 return false;
864}
865
866/*!
867 \fn bool operator!=(const QShaderKey &lhs, const QShaderKey &rhs)
868
869 Returns \c false if the values in the two QShaderKey objects \a lhs
870 and \a rhs are equal; otherwise returns \c true.
871
872 \relates QShaderKey
873 */
874
875/*!
876 Returns the hash value for \a k, using \a seed to seed the calculation.
877
878 \relates QShaderKey
879 */
880size_t qHash(const QShaderKey &k, size_t seed) noexcept
881{
882 return qHashMulti(seed,
883 args: k.source(),
884 args: k.sourceVersion().version(),
885 args: k.sourceVersion().flags(),
886 args: k.sourceVariant());
887}
888
889/*!
890 Returns \c true if the two QShaderCode objects \a lhs and \a rhs are equal.
891
892 \relates QShaderCode
893 */
894bool operator==(const QShaderCode &lhs, const QShaderCode &rhs) noexcept
895{
896 return lhs.shader() == rhs.shader() && lhs.entryPoint() == rhs.entryPoint();
897}
898
899/*!
900 \fn bool operator!=(const QShaderCode &lhs, const QShaderCode &rhs)
901
902 Returns \c false if the values in the two QShaderCode objects \a lhs
903 and \a rhs are equal; otherwise returns \c true.
904
905 \relates QShaderCode
906 */
907
908/*!
909 Returns the hash value for \a k, using \a seed to seed the calculation.
910
911 \relates QShaderCode
912 */
913size_t qHash(const QShaderCode &k, size_t seed) noexcept
914{
915 return qHash(key: k.shader(), seed);
916}
917
918#ifndef QT_NO_DEBUG_STREAM
919QDebug operator<<(QDebug dbg, const QShader &bs)
920{
921 const QShaderPrivate *d = bs.d;
922 QDebugStateSaver saver(dbg);
923
924 if (d) {
925 dbg.nospace() << "QShader("
926 << "stage=" << d->stage
927 << " shaders=" << d->shaders.keys()
928 << " desc.isValid=" << d->desc.isValid()
929 << ')';
930 } else {
931 dbg.nospace() << "QShader()";
932 }
933
934 return dbg;
935}
936
937QDebug operator<<(QDebug dbg, const QShaderKey &k)
938{
939 QDebugStateSaver saver(dbg);
940 dbg.nospace() << "ShaderKey(" << k.source()
941 << " " << k.sourceVersion()
942 << " " << k.sourceVariant() << ")";
943 return dbg;
944}
945
946QDebug operator<<(QDebug dbg, const QShaderVersion &v)
947{
948 QDebugStateSaver saver(dbg);
949 dbg.nospace() << "Version(" << v.version() << " " << v.flags() << ")";
950 return dbg;
951}
952#endif // QT_NO_DEBUG_STREAM
953
954/*!
955 \typedef QShader::NativeResourceBindingMap
956
957 Synonym for QMap<int, QPair<int, int>>.
958
959 The resource binding model QRhi assumes is based on SPIR-V. This means that
960 uniform buffers, storage buffers, combined image samplers, and storage
961 images share a common binding point space. The binding numbers in
962 QShaderDescription and QRhiShaderResourceBinding are expected to match the
963 \c binding layout qualifier in the Vulkan-compatible GLSL shader.
964
965 Graphics APIs other than Vulkan may use a resource binding model that is
966 not fully compatible with this. The generator of the shader code translated
967 from SPIR-V may choose not to take the SPIR-V binding qualifiers into
968 account, for various reasons. This is the case with the Metal backend of
969 SPIRV-Cross, for example. In addition, even when an automatic, implicit
970 translation is mostly possible (e.g. by using SPIR-V binding points as HLSL
971 resource register indices), assigning resource bindings without being
972 constrained by the SPIR-V binding points can lead to better results.
973
974 Therefore, a QShader may expose an additional map that describes what the
975 native binding point for a given SPIR-V binding is. The QRhi backends, for
976 which this is relevant, are expected to use this map automatically, as
977 appropriate. The value is a pair, because combined image samplers may map
978 to two native resources (a texture and a sampler) in some shading
979 languages. In that case the second value refers to the sampler.
980
981 \note The native binding may be -1, in case there is no active binding for
982 the resource in the shader. (for example, there is a uniform block
983 declared, but it is not used in the shader code) The map is always
984 complete, meaning there is an entry for all declared uniform blocks,
985 storage blocks, image objects, and combined samplers, but the value will be
986 -1 for those that are not actually referenced in the shader functions.
987*/
988
989/*!
990 \return the native binding map for \a key. The map is empty if no mapping
991 is available for \a key (for example, because the map is not applicable for
992 the API and shading language described by \a key).
993 */
994QShader::NativeResourceBindingMap QShader::nativeResourceBindingMap(const QShaderKey &key) const
995{
996 if (!d)
997 return {};
998
999 auto it = d->bindings.constFind(key);
1000 if (it == d->bindings.cend())
1001 return {};
1002
1003 return it.value();
1004}
1005
1006/*!
1007 Stores the given native resource binding \a map associated with \a key.
1008
1009 \sa nativeResourceBindingMap()
1010 */
1011void QShader::setResourceBindingMap(const QShaderKey &key, const NativeResourceBindingMap &map)
1012{
1013 detach();
1014 d->bindings[key] = map;
1015}
1016
1017/*!
1018 Removes the native resource binding map for \a key.
1019 */
1020void QShader::removeResourceBindingMap(const QShaderKey &key)
1021{
1022 if (!d)
1023 return;
1024
1025 auto it = d->bindings.find(key);
1026 if (it == d->bindings.end())
1027 return;
1028
1029 detach();
1030 d->bindings.erase(it);
1031}
1032
1033/*!
1034 \typedef QShader::SeparateToCombinedImageSamplerMappingList
1035
1036 Synonym for QList<QShader::SeparateToCombinedImageSamplerMapping>.
1037 */
1038
1039/*!
1040 \struct QShader::SeparateToCombinedImageSamplerMapping
1041 \inmodule QtGui
1042 \brief Mapping metadata for sampler uniforms.
1043
1044 Describes a mapping from a traditional combined image sampler uniform to
1045 binding points for a separate texture and sampler.
1046
1047 For example, if \c combinedImageSampler is \c{"_54"}, \c textureBinding is
1048 \c 1, and \c samplerBinding is \c 2, this means that the GLSL shader code
1049 contains a \c sampler2D (or sampler3D, etc.) uniform with the name of
1050 \c{_54} which corresponds to two separate resource bindings (\c 1 and \c 2)
1051 in the original shader.
1052
1053 \note This is a RHI API with limited compatibility guarantees, see \l QShader
1054 for details.
1055 */
1056
1057/*!
1058 \variable QShader::SeparateToCombinedImageSamplerMapping::combinedSamplerName
1059*/
1060
1061/*!
1062 \variable QShader::SeparateToCombinedImageSamplerMapping::textureBinding
1063*/
1064
1065/*!
1066 \variable QShader::SeparateToCombinedImageSamplerMapping::samplerBinding
1067*/
1068
1069/*!
1070 \return the combined image sampler mapping list for \a key, or an empty
1071 list if there is no data available for \a key, for example because such a
1072 mapping is not applicable for the shading language.
1073 */
1074QShader::SeparateToCombinedImageSamplerMappingList QShader::separateToCombinedImageSamplerMappingList(const QShaderKey &key) const
1075{
1076 if (!d)
1077 return {};
1078
1079 auto it = d->combinedImageMap.constFind(key);
1080 if (it == d->combinedImageMap.cend())
1081 return {};
1082
1083 return it.value();
1084}
1085
1086/*!
1087 Stores the given combined image sampler mapping \a list associated with \a key.
1088
1089 \sa separateToCombinedImageSamplerMappingList()
1090 */
1091void QShader::setSeparateToCombinedImageSamplerMappingList(const QShaderKey &key,
1092 const SeparateToCombinedImageSamplerMappingList &list)
1093{
1094 detach();
1095 d->combinedImageMap[key] = list;
1096}
1097
1098/*!
1099 Removes the combined image sampler mapping list for \a key.
1100 */
1101void QShader::removeSeparateToCombinedImageSamplerMappingList(const QShaderKey &key)
1102{
1103 if (!d)
1104 return;
1105
1106 auto it = d->combinedImageMap.find(key);
1107 if (it == d->combinedImageMap.end())
1108 return;
1109
1110 detach();
1111 d->combinedImageMap.erase(it);
1112}
1113
1114/*!
1115 \struct QShader::NativeShaderInfo
1116 \inmodule QtGui
1117 \brief Additional metadata about the native shader code.
1118
1119 Describes information about the native shader code, if applicable. This
1120 becomes relevant with certain shader languages for certain shader stages,
1121 in case the translation from SPIR-V involves the introduction of
1122 additional, "magic" inputs, outputs, or resources in the generated shader.
1123 Such additions may be dependent on the original source code (i.e. the usage
1124 of various GLSL language constructs or built-ins), and therefore it needs
1125 to be indicated in a dynamic manner if certain features got added to the
1126 generated shader code.
1127
1128 As an example, consider a tessellation control shader with a per-patch (not
1129 per-vertex) output variable. This is translated to a Metal compute shader
1130 outputting (among others) into an spvPatchOut buffer. But this buffer would
1131 not be present at all if per-patch output variables were not used. The fact
1132 that the shader code relies on such a buffer present can be indicated by
1133 the data in this struct.
1134
1135 \note This is a RHI API with limited compatibility guarantees, see \l QShader
1136 for details.
1137 */
1138
1139/*!
1140 \variable QShader::NativeShaderInfo::flags
1141*/
1142
1143/*!
1144 \variable QShader::NativeShaderInfo::extraBufferBindings
1145*/
1146
1147/*!
1148 \return the native shader info struct for \a key, or an empty object if
1149 there is no data available for \a key, for example because such a mapping
1150 is not applicable for the shading language or the shader stage.
1151 */
1152QShader::NativeShaderInfo QShader::nativeShaderInfo(const QShaderKey &key) const
1153{
1154 if (!d)
1155 return {};
1156
1157 auto it = d->nativeShaderInfoMap.constFind(key);
1158 if (it == d->nativeShaderInfoMap.cend())
1159 return {};
1160
1161 return it.value();
1162}
1163
1164/*!
1165 Stores the given native shader \a info associated with \a key.
1166
1167 \sa nativeShaderInfo()
1168 */
1169void QShader::setNativeShaderInfo(const QShaderKey &key, const NativeShaderInfo &info)
1170{
1171 detach();
1172 d->nativeShaderInfoMap[key] = info;
1173}
1174
1175/*!
1176 Removes the native shader information for \a key.
1177 */
1178void QShader::removeNativeShaderInfo(const QShaderKey &key)
1179{
1180 if (!d)
1181 return;
1182
1183 auto it = d->nativeShaderInfoMap.find(key);
1184 if (it == d->nativeShaderInfoMap.end())
1185 return;
1186
1187 detach();
1188 d->nativeShaderInfoMap.erase(it);
1189}
1190
1191QT_END_NAMESPACE
1192

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