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