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