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