Coverage Report

Created: 2026-05-16 07:21

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