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