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 \fn QShader::QShader(QShader &&other) noexcept
303 \since 6.7
304
305 Move-constructs a new QShader from \a other.
306
307 \note The moved-from object \a other is placed in a
308 partially-formed state, in which the only valid operations are
309 destruction and assignment of a new value.
310*/
311
312/*!
313 \fn QShader &QShader::operator=(QShader &&other)
314 \since 6.7
315
316 Move-assigns \a other to this QShader instance.
317
318 \note The moved-from object \a other is placed in a
319 partially-formed state, in which the only valid operations are
320 destruction and assignment of a new value.
321*/
322
323/*!
324 Destructor.
325 */
326QShader::~QShader()
327{
328 if (d && !d->ref.deref())
329 delete d;
330}
331
332/*!
333 \fn void QShader::swap(QShader &other)
334 \since 6.7
335 \memberswap{shader}
336*/
337
338/*!
339 \return true if the QShader contains at least one shader version.
340 */
341bool QShader::isValid() const
342{
343 return d ? !d->shaders.isEmpty() : false;
344}
345
346/*!
347 \return the pipeline stage the shader is meant for.
348 */
349QShader::Stage QShader::stage() const
350{
351 return d ? d->stage : QShader::VertexStage;
352}
353
354/*!
355 Sets the pipeline \a stage.
356 */
357void QShader::setStage(Stage stage)
358{
359 if (!d || stage != d->stage) {
360 detach();
361 d->stage = stage;
362 }
363}
364
365/*!
366 \return the reflection metadata for the shader.
367 */
368QShaderDescription QShader::description() const
369{
370 return d ? d->desc : QShaderDescription();
371}
372
373/*!
374 Sets the reflection metadata to \a desc.
375 */
376void QShader::setDescription(const QShaderDescription &desc)
377{
378 detach();
379 d->desc = desc;
380}
381
382/*!
383 \return the list of available shader versions
384 */
385QList<QShaderKey> QShader::availableShaders() const
386{
387 return d ? d->shaders.keys().toVector() : QList<QShaderKey>();
388}
389
390/*!
391 \return the source or binary code for a given shader version specified by \a key.
392 */
393QShaderCode QShader::shader(const QShaderKey &key) const
394{
395 return d ? d->shaders.value(key) : QShaderCode();
396}
397
398/*!
399 Stores the source or binary \a shader code for a given shader version specified by \a key.
400 */
401void QShader::setShader(const QShaderKey &key, const QShaderCode &shader)
402{
403 if (d && d->shaders.value(key) == shader)
404 return;
405
406 detach();
407 d->shaders[key] = shader;
408}
409
410/*!
411 Removes the source or binary shader code for a given \a key.
412 Does nothing when not found.
413 */
414void QShader::removeShader(const QShaderKey &key)
415{
416 if (!d)
417 return;
418
419 auto it = d->shaders.find(key);
420 if (it == d->shaders.end())
421 return;
422
423 detach();
424 d->shaders.erase(it);
425}
426
427static void writeShaderKey(QDataStream *ds, const QShaderKey &k)
428{
429 *ds << int(k.source());
430 *ds << k.sourceVersion().version();
431 *ds << k.sourceVersion().flags();
432 *ds << int(k.sourceVariant());
433}
434
435/*!
436 \return a serialized binary version of all the data held by the
437 QShader, suitable for writing to files or other I/O devices.
438
439 By default the latest serialization format is used. Use \a version
440 parameter to serialize for a compatibility Qt version. Only when it is
441 known that the generated data stream must be made compatible with an older
442 Qt version at the expense of making it incompatible with features
443 introduced since that Qt version, should another value (for example,
444 \l{SerializedFormatVersion}{Qt_6_5} for Qt 6.5) be used.
445
446 \sa fromSerialized()
447 */
448QByteArray QShader::serialized(SerializedFormatVersion version) const
449{
450 static QShaderPrivate sd;
451 QShaderPrivate *dd = d ? d : &sd;
452
453 QBuffer buf;
454 QDataStream ds(&buf);
455 ds.setVersion(QDataStream::Qt_5_10);
456 if (!buf.open(openMode: QIODevice::WriteOnly))
457 return QByteArray();
458
459 const int qsbVersion = QShaderPrivate::qtQsbVersion(qtVersion: version);
460 ds << qsbVersion;
461
462 ds << int(dd->stage);
463 dd->desc.serialize(stream: &ds, version: qsbVersion);
464 ds << int(dd->shaders.size());
465 for (auto it = dd->shaders.cbegin(), itEnd = dd->shaders.cend(); it != itEnd; ++it) {
466 const QShaderKey &k(it.key());
467 writeShaderKey(ds: &ds, k);
468 const QShaderCode &shader(dd->shaders.value(key: k));
469 ds << shader.shader();
470 ds << shader.entryPoint();
471 }
472 ds << int(dd->bindings.size());
473 for (auto it = dd->bindings.cbegin(), itEnd = dd->bindings.cend(); it != itEnd; ++it) {
474 const QShaderKey &k(it.key());
475 writeShaderKey(ds: &ds, k);
476 const NativeResourceBindingMap &map(it.value());
477 ds << int(map.size());
478 for (auto mapIt = map.cbegin(), mapItEnd = map.cend(); mapIt != mapItEnd; ++mapIt) {
479 ds << mapIt.key();
480 ds << mapIt.value().first;
481 ds << mapIt.value().second;
482 }
483 }
484 ds << int(dd->combinedImageMap.size());
485 for (auto it = dd->combinedImageMap.cbegin(), itEnd = dd->combinedImageMap.cend(); it != itEnd; ++it) {
486 const QShaderKey &k(it.key());
487 writeShaderKey(ds: &ds, k);
488 const SeparateToCombinedImageSamplerMappingList &list(it.value());
489 ds << int(list.size());
490 for (auto listIt = list.cbegin(), listItEnd = list.cend(); listIt != listItEnd; ++listIt) {
491 ds << listIt->combinedSamplerName;
492 ds << listIt->textureBinding;
493 ds << listIt->samplerBinding;
494 }
495 }
496 if (qsbVersion > QShaderPrivate::QSB_VERSION_WITHOUT_NATIVE_SHADER_INFO) {
497 ds << int(dd->nativeShaderInfoMap.size());
498 for (auto it = dd->nativeShaderInfoMap.cbegin(), itEnd = dd->nativeShaderInfoMap.cend(); it != itEnd; ++it) {
499 const QShaderKey &k(it.key());
500 writeShaderKey(ds: &ds, k);
501 ds << it->flags;
502 ds << int(it->extraBufferBindings.size());
503 for (auto mapIt = it->extraBufferBindings.cbegin(), mapItEnd = it->extraBufferBindings.cend();
504 mapIt != mapItEnd; ++mapIt)
505 {
506 ds << mapIt.key();
507 ds << mapIt.value();
508 }
509 }
510 }
511
512 return qCompress(data: buf.buffer());
513}
514
515static void readShaderKey(QDataStream *ds, QShaderKey *k)
516{
517 int intVal;
518 *ds >> intVal;
519 k->setSource(QShader::Source(intVal));
520 QShaderVersion ver;
521 *ds >> intVal;
522 ver.setVersion(intVal);
523 *ds >> intVal;
524 ver.setFlags(QShaderVersion::Flags(intVal));
525 k->setSourceVersion(ver);
526 *ds >> intVal;
527 k->setSourceVariant(QShader::Variant(intVal));
528}
529
530/*!
531 Creates a new QShader instance from the given \a data.
532
533 If \a data cannot be deserialized successfully, the result is a default
534 constructed QShader for which isValid() returns \c false.
535
536 \sa serialized()
537 */
538QShader QShader::fromSerialized(const QByteArray &data)
539{
540 QByteArray udata = qUncompress(data);
541 QBuffer buf(&udata);
542 QDataStream ds(&buf);
543 ds.setVersion(QDataStream::Qt_5_10);
544 if (!buf.open(openMode: QIODevice::ReadOnly))
545 return QShader();
546
547 QShader bs;
548 bs.detach(); // to get d created
549 QShaderPrivate *d = QShaderPrivate::get(s: &bs);
550 Q_ASSERT(d->ref.loadRelaxed() == 1); // must be detached
551 int intVal;
552 ds >> intVal;
553 d->qsbVersion = intVal;
554 if (d->qsbVersion != QShaderPrivate::QSB_VERSION
555 && d->qsbVersion != QShaderPrivate::QSB_VERSION_WITHOUT_INPUT_OUTPUT_INTERFACE_BLOCKS
556 && d->qsbVersion != QShaderPrivate::QSB_VERSION_WITHOUT_EXTENDED_STORAGE_BUFFER_INFO
557 && d->qsbVersion != QShaderPrivate::QSB_VERSION_WITHOUT_NATIVE_SHADER_INFO
558 && d->qsbVersion != QShaderPrivate::QSB_VERSION_WITHOUT_SEPARATE_IMAGES_AND_SAMPLERS
559 && d->qsbVersion != QShaderPrivate::QSB_VERSION_WITHOUT_VAR_ARRAYDIMS
560 && d->qsbVersion != QShaderPrivate::QSB_VERSION_WITH_CBOR
561 && d->qsbVersion != QShaderPrivate::QSB_VERSION_WITH_BINARY_JSON
562 && d->qsbVersion != QShaderPrivate::QSB_VERSION_WITHOUT_BINDINGS)
563 {
564 qWarning(msg: "Attempted to deserialize QShader with unknown version %d.", d->qsbVersion);
565 return QShader();
566 }
567
568 ds >> intVal;
569 d->stage = Stage(intVal);
570 if (d->qsbVersion > QShaderPrivate::QSB_VERSION_WITH_CBOR) {
571 d->desc = QShaderDescription::deserialize(stream: &ds, version: d->qsbVersion);
572 } else if (d->qsbVersion > QShaderPrivate::QSB_VERSION_WITH_BINARY_JSON) {
573 qWarning(msg: "Can no longer load QShaderDescription from CBOR.");
574 d->desc = QShaderDescription();
575 } else {
576 qWarning(msg: "Can no longer load QShaderDescription from binary JSON.");
577 d->desc = QShaderDescription();
578 }
579 int count;
580 ds >> count;
581 for (int i = 0; i < count; ++i) {
582 QShaderKey k;
583 readShaderKey(ds: &ds, k: &k);
584 QShaderCode shader;
585 QByteArray s;
586 ds >> s;
587 shader.setShader(s);
588 ds >> s;
589 shader.setEntryPoint(s);
590 d->shaders[k] = shader;
591 }
592
593 if (d->qsbVersion > QShaderPrivate::QSB_VERSION_WITHOUT_BINDINGS) {
594 ds >> count;
595 for (int i = 0; i < count; ++i) {
596 QShaderKey k;
597 readShaderKey(ds: &ds, k: &k);
598 NativeResourceBindingMap map;
599 int mapSize;
600 ds >> mapSize;
601 for (int b = 0; b < mapSize; ++b) {
602 int binding;
603 ds >> binding;
604 int firstNativeBinding;
605 ds >> firstNativeBinding;
606 int secondNativeBinding;
607 ds >> secondNativeBinding;
608 map.insert(key: binding, value: { firstNativeBinding, secondNativeBinding });
609 }
610 d->bindings.insert(key: k, value: map);
611 }
612 }
613
614 if (d->qsbVersion > QShaderPrivate::QSB_VERSION_WITHOUT_SEPARATE_IMAGES_AND_SAMPLERS) {
615 ds >> count;
616 for (int i = 0; i < count; ++i) {
617 QShaderKey k;
618 readShaderKey(ds: &ds, k: &k);
619 SeparateToCombinedImageSamplerMappingList list;
620 int listSize;
621 ds >> listSize;
622 for (int b = 0; b < listSize; ++b) {
623 QByteArray combinedSamplerName;
624 ds >> combinedSamplerName;
625 int textureBinding;
626 ds >> textureBinding;
627 int samplerBinding;
628 ds >> samplerBinding;
629 list.append(t: { .combinedSamplerName: combinedSamplerName, .textureBinding: textureBinding, .samplerBinding: samplerBinding });
630 }
631 d->combinedImageMap.insert(key: k, value: list);
632 }
633 }
634
635 if (d->qsbVersion > QShaderPrivate::QSB_VERSION_WITHOUT_NATIVE_SHADER_INFO) {
636 ds >> count;
637 for (int i = 0; i < count; ++i) {
638 QShaderKey k;
639 readShaderKey(ds: &ds, k: &k);
640 int flags;
641 ds >> flags;
642 QMap<int, int> extraBufferBindings;
643 int mapSize;
644 ds >> mapSize;
645 for (int b = 0; b < mapSize; ++b) {
646 int k, v;
647 ds >> k;
648 ds >> v;
649 extraBufferBindings.insert(key: k, value: v);
650 }
651 d->nativeShaderInfoMap.insert(key: k, value: { .flags: flags, .extraBufferBindings: extraBufferBindings });
652 }
653 }
654
655 return bs;
656}
657
658/*!
659 \fn QShaderVersion::QShaderVersion() = default
660 */
661
662/*!
663 Constructs a new QShaderVersion with version \a v and flags \a f.
664 */
665QShaderVersion::QShaderVersion(int v, Flags f)
666 : m_version(v), m_flags(f)
667{
668}
669
670/*!
671 \fn int QShaderVersion::version() const
672 \return the version.
673 */
674
675/*!
676 \fn void QShaderVersion::setVersion(int v)
677 Sets the shading language version to \a v.
678 */
679
680/*!
681 \fn QShaderVersion::Flags QShaderVersion::flags() const
682 \return the flags.
683 */
684
685/*!
686 \fn void QShaderVersion::setFlags(Flags f)
687 Sets the flags \a f.
688 */
689
690/*!
691 \fn QShaderCode::QShaderCode() = default
692 */
693
694/*!
695 Constructs a new QShaderCode with the specified shader source \a code and
696 \a entry point name.
697 */
698QShaderCode::QShaderCode(const QByteArray &code, const QByteArray &entry)
699 : m_shader(code), m_entryPoint(entry)
700{
701}
702
703/*!
704 \fn QByteArray QShaderCode::shader() const
705 \return the shader source or bytecode.
706 */
707
708/*!
709 \fn void QShaderCode::setShader(const QByteArray &code)
710 Sets the shader source or byte \a code.
711 */
712
713/*!
714 \fn QByteArray QShaderCode::entryPoint() const
715 \return the entry point name.
716 */
717
718/*!
719 \fn void QShaderCode::setEntryPoint(const QByteArray &entry)
720 Sets the \a entry point name.
721 */
722
723/*!
724 \fn QShaderKey::QShaderKey() = default
725 */
726
727/*!
728 Constructs a new QShaderKey with shader type \a s, version \a sver, and
729 variant \a svar.
730 */
731QShaderKey::QShaderKey(QShader::Source s,
732 const QShaderVersion &sver,
733 QShader::Variant svar)
734 : m_source(s),
735 m_sourceVersion(sver),
736 m_sourceVariant(svar)
737{
738}
739
740/*!
741 \fn QShader::Source QShaderKey::source() const
742 \return the shader type.
743 */
744
745/*!
746 \fn void QShaderKey::setSource(QShader::Source s)
747 Sets the shader type \a s.
748 */
749
750/*!
751 \fn QShaderVersion QShaderKey::sourceVersion() const
752 \return the shading language version.
753 */
754
755/*!
756 \fn void QShaderKey::setSourceVersion(const QShaderVersion &sver)
757 Sets the shading language version \a sver.
758 */
759
760/*!
761 \fn QShader::Variant QShaderKey::sourceVariant() const
762 \return the type of the variant to use.
763 */
764
765/*!
766 \fn void QShaderKey::setSourceVariant(QShader::Variant svar)
767 Sets the type of variant to use to \a svar.
768 */
769
770/*!
771 Returns \c true if the two QShader objects \a lhs and \a rhs are equal,
772 meaning they are for the same stage with matching sets of shader source or
773 binary code.
774
775 \relates QShader
776 */
777bool operator==(const QShader &lhs, const QShader &rhs) noexcept
778{
779 if (!lhs.d || !rhs.d)
780 return lhs.d == rhs.d;
781
782 return lhs.d->stage == rhs.d->stage
783 && lhs.d->shaders == rhs.d->shaders
784 && lhs.d->bindings == rhs.d->bindings;
785}
786
787/*!
788 \fn bool operator!=(const QShader &lhs, const QShader &rhs)
789
790 Returns \c false if the values in the two QShader objects \a lhs and \a rhs
791 are equal; otherwise returns \c true.
792
793 \relates QShader
794 */
795
796/*!
797 \fn size_t qHash(const QShader &key, size_t seed)
798 \qhashold{QShader}
799 */
800size_t qHash(const QShader &s, size_t seed) noexcept
801{
802 if (s.d) {
803 QtPrivate::QHashCombine hash;
804 seed = hash(seed, s.stage());
805 if (!s.d->shaders.isEmpty()) {
806 seed = hash(seed, s.d->shaders.firstKey());
807 seed = hash(seed, std::as_const(t&: s.d->shaders).first());
808 }
809 }
810 return seed;
811}
812
813/*!
814 Returns \c true if the two QShaderVersion objects \a lhs and \a rhs are
815 equal.
816
817 \relates QShaderVersion
818 */
819bool operator==(const QShaderVersion &lhs, const QShaderVersion &rhs) noexcept
820{
821 return lhs.version() == rhs.version() && lhs.flags() == rhs.flags();
822}
823
824#ifdef Q_OS_INTEGRITY
825size_t qHash(const QShaderVersion &s, size_t seed) noexcept
826{
827 return qHashMulti(seed, s.version(), s.flags());
828}
829#endif
830
831/*!
832 \return true if \a lhs is smaller than \a rhs.
833
834 Establishes a sorting order between the two QShaderVersion \a lhs and \a rhs.
835
836 \relates QShaderVersion
837 */
838bool operator<(const QShaderVersion &lhs, const QShaderVersion &rhs) noexcept
839{
840 if (lhs.version() < rhs.version())
841 return true;
842
843 if (lhs.version() == rhs.version())
844 return int(lhs.flags()) < int(rhs.flags());
845
846 return false;
847}
848
849/*!
850 \fn bool operator!=(const QShaderVersion &lhs, const QShaderVersion &rhs)
851
852 Returns \c false if the values in the two QShaderVersion objects \a lhs
853 and \a rhs are equal; otherwise returns \c true.
854
855 \relates QShaderVersion
856 */
857
858/*!
859 Returns \c true if the two QShaderKey objects \a lhs and \a rhs are equal.
860
861 \relates QShaderKey
862 */
863bool operator==(const QShaderKey &lhs, const QShaderKey &rhs) noexcept
864{
865 return lhs.source() == rhs.source() && lhs.sourceVersion() == rhs.sourceVersion()
866 && lhs.sourceVariant() == rhs.sourceVariant();
867}
868
869/*!
870 \return true if \a lhs is smaller than \a rhs.
871
872 Establishes a sorting order between the two keys \a lhs and \a rhs.
873
874 \relates QShaderKey
875 */
876bool operator<(const QShaderKey &lhs, const QShaderKey &rhs) noexcept
877{
878 if (int(lhs.source()) < int(rhs.source()))
879 return true;
880
881 if (int(lhs.source()) == int(rhs.source())) {
882 if (lhs.sourceVersion() < rhs.sourceVersion())
883 return true;
884 if (lhs.sourceVersion() == rhs.sourceVersion()) {
885 if (int(lhs.sourceVariant()) < int(rhs.sourceVariant()))
886 return true;
887 }
888 }
889
890 return false;
891}
892
893/*!
894 \fn bool operator!=(const QShaderKey &lhs, const QShaderKey &rhs)
895
896 Returns \c false if the values in the two QShaderKey objects \a lhs
897 and \a rhs are equal; otherwise returns \c true.
898
899 \relates QShaderKey
900 */
901
902/*!
903 \fn size_t qHash(const QShaderKey &key, size_t seed)
904 \qhashold{QShaderKey}
905 */
906size_t qHash(const QShaderKey &k, size_t seed) noexcept
907{
908 return qHashMulti(seed,
909 args: k.source(),
910 args: k.sourceVersion().version(),
911 args: k.sourceVersion().flags(),
912 args: k.sourceVariant());
913}
914
915/*!
916 Returns \c true if the two QShaderCode objects \a lhs and \a rhs are equal.
917
918 \relates QShaderCode
919 */
920bool operator==(const QShaderCode &lhs, const QShaderCode &rhs) noexcept
921{
922 return lhs.shader() == rhs.shader() && lhs.entryPoint() == rhs.entryPoint();
923}
924
925/*!
926 \fn bool operator!=(const QShaderCode &lhs, const QShaderCode &rhs)
927
928 Returns \c false if the values in the two QShaderCode objects \a lhs
929 and \a rhs are equal; otherwise returns \c true.
930
931 \relates QShaderCode
932 */
933
934/*!
935 \fn size_t qHash(const QShaderCode &key, size_t seed)
936 \qhashold{QShaderCode}
937 */
938size_t qHash(const QShaderCode &k, size_t seed) noexcept
939{
940 return qHash(key: k.shader(), seed);
941}
942
943#ifndef QT_NO_DEBUG_STREAM
944QDebug operator<<(QDebug dbg, const QShader &bs)
945{
946 const QShaderPrivate *d = bs.d;
947 QDebugStateSaver saver(dbg);
948
949 if (d) {
950 dbg.nospace() << "QShader("
951 << "stage=" << d->stage
952 << " shaders=" << d->shaders.keys()
953 << " desc.isValid=" << d->desc.isValid()
954 << ')';
955 } else {
956 dbg.nospace() << "QShader()";
957 }
958
959 return dbg;
960}
961
962QDebug operator<<(QDebug dbg, const QShaderKey &k)
963{
964 QDebugStateSaver saver(dbg);
965 dbg.nospace() << "ShaderKey(" << k.source()
966 << " " << k.sourceVersion()
967 << " " << k.sourceVariant() << ")";
968 return dbg;
969}
970
971QDebug operator<<(QDebug dbg, const QShaderVersion &v)
972{
973 QDebugStateSaver saver(dbg);
974 dbg.nospace() << "Version(" << v.version() << " " << v.flags() << ")";
975 return dbg;
976}
977#endif // QT_NO_DEBUG_STREAM
978
979/*!
980 \typedef QShader::NativeResourceBindingMap
981
982 Synonym for QMap<int, QPair<int, int>>.
983
984 The resource binding model QRhi assumes is based on SPIR-V. This means that
985 uniform buffers, storage buffers, combined image samplers, and storage
986 images share a common binding point space. The binding numbers in
987 QShaderDescription and QRhiShaderResourceBinding are expected to match the
988 \c binding layout qualifier in the Vulkan-compatible GLSL shader.
989
990 Graphics APIs other than Vulkan may use a resource binding model that is
991 not fully compatible with this. The generator of the shader code translated
992 from SPIR-V may choose not to take the SPIR-V binding qualifiers into
993 account, for various reasons. This is the case with the Metal backend of
994 SPIRV-Cross, for example. In addition, even when an automatic, implicit
995 translation is mostly possible (e.g. by using SPIR-V binding points as HLSL
996 resource register indices), assigning resource bindings without being
997 constrained by the SPIR-V binding points can lead to better results.
998
999 Therefore, a QShader may expose an additional map that describes what the
1000 native binding point for a given SPIR-V binding is. The QRhi backends, for
1001 which this is relevant, are expected to use this map automatically, as
1002 appropriate. The value is a pair, because combined image samplers may map
1003 to two native resources (a texture and a sampler) in some shading
1004 languages. In that case the second value refers to the sampler.
1005
1006 \note The native binding may be -1, in case there is no active binding for
1007 the resource in the shader. (for example, there is a uniform block
1008 declared, but it is not used in the shader code) The map is always
1009 complete, meaning there is an entry for all declared uniform blocks,
1010 storage blocks, image objects, and combined samplers, but the value will be
1011 -1 for those that are not actually referenced in the shader functions.
1012*/
1013
1014/*!
1015 \return the native binding map for \a key. The map is empty if no mapping
1016 is available for \a key (for example, because the map is not applicable for
1017 the API and shading language described by \a key).
1018 */
1019QShader::NativeResourceBindingMap QShader::nativeResourceBindingMap(const QShaderKey &key) const
1020{
1021 if (!d)
1022 return {};
1023
1024 auto it = d->bindings.constFind(key);
1025 if (it == d->bindings.cend())
1026 return {};
1027
1028 return it.value();
1029}
1030
1031/*!
1032 Stores the given native resource binding \a map associated with \a key.
1033
1034 \sa nativeResourceBindingMap()
1035 */
1036void QShader::setResourceBindingMap(const QShaderKey &key, const NativeResourceBindingMap &map)
1037{
1038 detach();
1039 d->bindings[key] = map;
1040}
1041
1042/*!
1043 Removes the native resource binding map for \a key.
1044 */
1045void QShader::removeResourceBindingMap(const QShaderKey &key)
1046{
1047 if (!d)
1048 return;
1049
1050 auto it = d->bindings.find(key);
1051 if (it == d->bindings.end())
1052 return;
1053
1054 detach();
1055 d->bindings.erase(it);
1056}
1057
1058/*!
1059 \typedef QShader::SeparateToCombinedImageSamplerMappingList
1060
1061 Synonym for QList<QShader::SeparateToCombinedImageSamplerMapping>.
1062 */
1063
1064/*!
1065 \struct QShader::SeparateToCombinedImageSamplerMapping
1066 \inmodule QtGui
1067 \brief Mapping metadata for sampler uniforms.
1068
1069 Describes a mapping from a traditional combined image sampler uniform to
1070 binding points for a separate texture and sampler.
1071
1072 For example, if \c combinedImageSampler is \c{"_54"}, \c textureBinding is
1073 \c 1, and \c samplerBinding is \c 2, this means that the GLSL shader code
1074 contains a \c sampler2D (or sampler3D, etc.) uniform with the name of
1075 \c{_54} which corresponds to two separate resource bindings (\c 1 and \c 2)
1076 in the original shader.
1077
1078 \note This is a RHI API with limited compatibility guarantees, see \l QShader
1079 for details.
1080 */
1081
1082/*!
1083 \variable QShader::SeparateToCombinedImageSamplerMapping::combinedSamplerName
1084*/
1085
1086/*!
1087 \variable QShader::SeparateToCombinedImageSamplerMapping::textureBinding
1088*/
1089
1090/*!
1091 \variable QShader::SeparateToCombinedImageSamplerMapping::samplerBinding
1092*/
1093
1094/*!
1095 \return the combined image sampler mapping list for \a key, or an empty
1096 list if there is no data available for \a key, for example because such a
1097 mapping is not applicable for the shading language.
1098 */
1099QShader::SeparateToCombinedImageSamplerMappingList QShader::separateToCombinedImageSamplerMappingList(const QShaderKey &key) const
1100{
1101 if (!d)
1102 return {};
1103
1104 auto it = d->combinedImageMap.constFind(key);
1105 if (it == d->combinedImageMap.cend())
1106 return {};
1107
1108 return it.value();
1109}
1110
1111/*!
1112 Stores the given combined image sampler mapping \a list associated with \a key.
1113
1114 \sa separateToCombinedImageSamplerMappingList()
1115 */
1116void QShader::setSeparateToCombinedImageSamplerMappingList(const QShaderKey &key,
1117 const SeparateToCombinedImageSamplerMappingList &list)
1118{
1119 detach();
1120 d->combinedImageMap[key] = list;
1121}
1122
1123/*!
1124 Removes the combined image sampler mapping list for \a key.
1125 */
1126void QShader::removeSeparateToCombinedImageSamplerMappingList(const QShaderKey &key)
1127{
1128 if (!d)
1129 return;
1130
1131 auto it = d->combinedImageMap.find(key);
1132 if (it == d->combinedImageMap.end())
1133 return;
1134
1135 detach();
1136 d->combinedImageMap.erase(it);
1137}
1138
1139/*!
1140 \struct QShader::NativeShaderInfo
1141 \inmodule QtGui
1142 \brief Additional metadata about the native shader code.
1143
1144 Describes information about the native shader code, if applicable. This
1145 becomes relevant with certain shader languages for certain shader stages,
1146 in case the translation from SPIR-V involves the introduction of
1147 additional, "magic" inputs, outputs, or resources in the generated shader.
1148 Such additions may be dependent on the original source code (i.e. the usage
1149 of various GLSL language constructs or built-ins), and therefore it needs
1150 to be indicated in a dynamic manner if certain features got added to the
1151 generated shader code.
1152
1153 As an example, consider a tessellation control shader with a per-patch (not
1154 per-vertex) output variable. This is translated to a Metal compute shader
1155 outputting (among others) into an spvPatchOut buffer. But this buffer would
1156 not be present at all if per-patch output variables were not used. The fact
1157 that the shader code relies on such a buffer present can be indicated by
1158 the data in this struct.
1159
1160 \note This is a RHI API with limited compatibility guarantees, see \l QShader
1161 for details.
1162 */
1163
1164/*!
1165 \variable QShader::NativeShaderInfo::flags
1166*/
1167
1168/*!
1169 \variable QShader::NativeShaderInfo::extraBufferBindings
1170*/
1171
1172/*!
1173 \return the native shader info struct for \a key, or an empty object if
1174 there is no data available for \a key, for example because such a mapping
1175 is not applicable for the shading language or the shader stage.
1176 */
1177QShader::NativeShaderInfo QShader::nativeShaderInfo(const QShaderKey &key) const
1178{
1179 if (!d)
1180 return {};
1181
1182 auto it = d->nativeShaderInfoMap.constFind(key);
1183 if (it == d->nativeShaderInfoMap.cend())
1184 return {};
1185
1186 return it.value();
1187}
1188
1189/*!
1190 Stores the given native shader \a info associated with \a key.
1191
1192 \sa nativeShaderInfo()
1193 */
1194void QShader::setNativeShaderInfo(const QShaderKey &key, const NativeShaderInfo &info)
1195{
1196 detach();
1197 d->nativeShaderInfoMap[key] = info;
1198}
1199
1200/*!
1201 Removes the native shader information for \a key.
1202 */
1203void QShader::removeNativeShaderInfo(const QShaderKey &key)
1204{
1205 if (!d)
1206 return;
1207
1208 auto it = d->nativeShaderInfoMap.find(key);
1209 if (it == d->nativeShaderInfoMap.end())
1210 return;
1211
1212 detach();
1213 d->nativeShaderInfoMap.erase(it);
1214}
1215
1216QT_END_NAMESPACE
1217

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