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

Provided by KDAB

Privacy Policy
Start learning QML with our Intro Training
Find out more

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