1/****************************************************************************
2**
3** Copyright (C) 2019 The Qt Company Ltd.
4** Contact: http://www.qt.io/licensing/
5**
6** This file is part of the Qt Gui module
7**
8** $QT_BEGIN_LICENSE:LGPL3$
9** Commercial License Usage
10** Licensees holding valid commercial Qt licenses may use this file in
11** accordance with the commercial license agreement provided with the
12** Software or, alternatively, in accordance with the terms contained in
13** a written agreement between you and The Qt Company. For licensing terms
14** and conditions see http://www.qt.io/terms-conditions. For further
15** information use the contact form at http://www.qt.io/contact-us.
16**
17** GNU Lesser General Public License Usage
18** Alternatively, this file may be used under the terms of the GNU Lesser
19** General Public License version 3 as published by the Free Software
20** Foundation and appearing in the file LICENSE.LGPLv3 included in the
21** packaging of this file. Please review the following information to
22** ensure the GNU Lesser General Public License version 3 requirements
23** will be met: https://www.gnu.org/licenses/lgpl.html.
24**
25** GNU General Public License Usage
26** Alternatively, this file may be used under the terms of the GNU
27** General Public License version 2.0 or later as published by the Free
28** Software Foundation and appearing in the file LICENSE.GPL included in
29** the packaging of this file. Please review the following information to
30** ensure the GNU General Public License version 2.0 requirements will be
31** met: http://www.gnu.org/licenses/gpl-2.0.html.
32**
33** $QT_END_LICENSE$
34**
35****************************************************************************/
36
37#include "qshaderdescription_p_p.h"
38#include <QDebug>
39#include <QJsonObject>
40#include <QJsonArray>
41
42QT_BEGIN_NAMESPACE
43
44/*!
45 \class QShaderDescription
46 \inmodule QtRhi
47
48 \brief Describes the interface of a shader.
49
50 A shader typically has a set of inputs and outputs. A vertex shader for
51 example has a number of input variables and may use one or more uniform
52 buffers to access data (e.g. a modelview matrix) provided by the
53 application. The shader for the fragment stage receives data from the
54 vertex stage (in a simple setup) and may also rely on data from uniform
55 buffers, images, and samplers.
56
57 When it comes to vertex inputs and the layout of the uniform buffers (what
58 are the names of the members? what is there size, offset, and so on),
59 applications and frameworks may need to discover this dynamically at run
60 time. This is typical when the shader is not built-in but provided by an
61 external entity, like the user.
62
63 Modern and lean graphics APIs may no longer provide a way to query shader
64 reflection information at run time. Therefore, such data is now
65 automatically generated by QShaderBaker and is provided as a
66 QShaderDescription object for each and every QShader.
67
68 \section2 Example
69
70 Take the following vertex shader:
71
72 \badcode
73 #version 440
74
75 layout(location = 0) in vec4 position;
76 layout(location = 1) in vec3 color;
77 layout(location = 0) out vec3 v_color;
78
79 layout(std140, binding = 0) uniform buf {
80 mat4 mvp;
81 float opacity;
82 } ubuf;
83
84 out gl_PerVertex { vec4 gl_Position; };
85
86 void main()
87 {
88 v_color = color;
89 gl_Position = ubuf.mvp * position;
90 }
91 \endcode
92
93 This shader has two inputs: \c position at location 0 with a type of \c
94 vec4, and \c color at location 1 with a type of \c vec3. It has one output:
95 \c v_color, although this is typically not interesting for applications.
96 What is more important, there is a uniform block at binding 0 with a size
97 of 68 bytes and two members, a 4x4 matrix named \c mvp at offset 0, and a
98 float \c opacity at offset 64.
99
100 All this is described by a QShaderDescription object. QShaderDescription
101 can also be serialized to JSON and binary JSON, and can be deserialized
102 from binary JSON. In practice this is rarely needed since QShader
103 takes care of the associated QShaderDescription automatically, but if the
104 QShaderDescription of the above shader would be written out as JSON, it
105 would look like the following:
106
107 \badcode
108 {
109 "inputs": [
110 {
111 "location": 1,
112 "name": "color",
113 "type": "vec3"
114 },
115 {
116 "location": 0,
117 "name": "position",
118 "type": "vec4"
119 }
120 ],
121 "outputs": [
122 {
123 "location": 0,
124 "name": "v_color",
125 "type": "vec3"
126 }
127 ],
128 "uniformBlocks": [
129 {
130 "binding": 0,
131 "blockName": "buf",
132 "members": [
133 {
134 "matrixStride": 16,
135 "name": "mvp",
136 "offset": 0,
137 "size": 64,
138 "type": "mat4"
139 },
140 {
141 "name": "opacity",
142 "offset": 64,
143 "size": 4,
144 "type": "float"
145 }
146 ],
147 "set": 0,
148 "size": 68,
149 "structName": "ubuf"
150 }
151 ]
152 }
153 \endcode
154
155 The C++ API allows accessing a data structure like the above. For
156 simplicity the inner structs only contain public data members, also
157 considering that their layout is unlikely to change in the future.
158
159 \sa QShaderBaker, QShader
160 */
161
162/*!
163 \enum QShaderDescription::VariableType
164 Represents the type of a variable or block member.
165
166 \value Unknown
167 \value Float
168 \value Vec2
169 \value Vec3
170 \value Vec4
171 \value Mat2
172 \value Mat2x3
173 \value Mat2x4
174 \value Mat3
175 \value Mat3x2
176 \value Mat3x4
177 \value Mat4
178 \value Mat4x2
179 \value Mat4x3
180 \value Int
181 \value Int2
182 \value Int3
183 \value Int4
184 \value Uint
185 \value Uint2
186 \value Uint3
187 \value Uint4
188 \value Bool
189 \value Bool2
190 \value Bool3
191 \value Bool4
192 \value Double
193 \value Double2
194 \value Double3
195 \value Double4
196 \value DMat2
197 \value DMat2x3
198 \value DMat2x4
199 \value DMat3
200 \value DMat3x2
201 \value DMat3x4
202 \value DMat4
203 \value DMat4x2
204 \value DMat4x3
205 \value Sampler1D
206 \value Sampler2D
207 \value Sampler2DMS
208 \value Sampler3D
209 \value SamplerCube
210 \value Sampler1DArray
211 \value Sampler2DArray
212 \value Sampler2DMSArray
213 \value Sampler3DArray
214 \value SamplerCubeArray
215 \value SamplerRect
216 \value SamplerBuffer
217 \value Image1D
218 \value Image2D
219 \value Image2DMS
220 \value Image3D
221 \value ImageCube
222 \value Image1DArray
223 \value Image2DArray
224 \value Image2DMSArray
225 \value Image3DArray
226 \value ImageCubeArray
227 \value ImageRect
228 \value ImageBuffer
229 \value Struct
230 */
231
232/*!
233 \class QShaderDescription::InOutVariable
234 \inmodule QtRhi
235
236 \brief Describes an input or output variable in the shader.
237 */
238
239/*!
240 \class QShaderDescription::BlockVariable
241 \inmodule QtRhi
242
243 \brief Describes a member of a uniform or push constant block.
244 */
245
246/*!
247 \class QShaderDescription::UniformBlock
248 \inmodule QtRhi
249
250 \brief Describes a uniform block.
251
252 \note When translating to shading languages without uniform block support
253 (like GLSL 120 or GLSL/ES 100), uniform blocks are replaced with ordinary
254 uniforms in a struct. The name of the struct, and so the prefix for the
255 uniforms generated from the block members, is given by structName.
256 */
257
258/*!
259 \class QShaderDescription::PushConstantBlock
260 \inmodule QtRhi
261
262 \brief Describes a push constant block.
263 */
264
265/*!
266 \class QShaderDescription::StorageBlock
267 \inmodule QtRhi
268
269 \brief Describes a shader storage block.
270 */
271
272/*!
273 Constructs a new, empty QShaderDescription.
274
275 \note Being empty implies that isValid() returns \c false for the
276 newly constructed instance.
277 */
278QShaderDescription::QShaderDescription()
279 : d(new QShaderDescriptionPrivate)
280{
281}
282
283/*!
284 \internal
285 */
286void QShaderDescription::detach()
287{
288 qAtomicDetach(d);
289}
290
291/*!
292 \internal
293 */
294QShaderDescription::QShaderDescription(const QShaderDescription &other)
295 : d(other.d)
296{
297 d->ref.ref();
298}
299
300/*!
301 \internal
302 */
303QShaderDescription &QShaderDescription::operator=(const QShaderDescription &other)
304{
305 qAtomicAssign(d, other.d);
306 return *this;
307}
308
309/*!
310 Destructor.
311 */
312QShaderDescription::~QShaderDescription()
313{
314 if (!d->ref.deref())
315 delete d;
316}
317
318/*!
319 \return true if the QShaderDescription contains at least one entry in one of
320 the variable and block lists.
321 */
322bool QShaderDescription::isValid() const
323{
324 return !d->inVars.isEmpty() || !d->outVars.isEmpty()
325 || !d->uniformBlocks.isEmpty() || !d->pushConstantBlocks.isEmpty() || !d->storageBlocks.isEmpty()
326 || !d->combinedImageSamplers.isEmpty() || !d->storageImages.isEmpty();
327}
328
329/*!
330 \return a serialized binary version of the data.
331
332 \sa toJson()
333 */
334QByteArray QShaderDescription::toBinaryJson() const
335{
336 return d->makeDoc().toBinaryData();
337}
338
339/*!
340 \return a serialized JSON text version of the data.
341
342 \note There is no deserialization method provided for JSON text.
343
344 \sa toBinaryJson()
345 */
346QByteArray QShaderDescription::toJson() const
347{
348 return d->makeDoc().toJson();
349}
350
351/*!
352 Deserializes the given binary JSON \a data and returns a new
353 QShaderDescription.
354 */
355QShaderDescription QShaderDescription::fromBinaryJson(const QByteArray &data)
356{
357 QShaderDescription desc;
358 QShaderDescriptionPrivate::get(&desc)->loadDoc(QJsonDocument::fromBinaryData(data));
359 return desc;
360}
361
362/*!
363 \return the list of input variables. This includes vertex inputs (sometimes
364 called attributes) for the vertex stage, and inputs for other stages
365 (sometimes called varyings).
366 */
367QVector<QShaderDescription::InOutVariable> QShaderDescription::inputVariables() const
368{
369 return d->inVars;
370}
371
372/*!
373 \return the list of output variables.
374 */
375QVector<QShaderDescription::InOutVariable> QShaderDescription::outputVariables() const
376{
377 return d->outVars;
378}
379
380/*!
381 \return the list of uniform blocks.
382 */
383QVector<QShaderDescription::UniformBlock> QShaderDescription::uniformBlocks() const
384{
385 return d->uniformBlocks;
386}
387
388/*!
389 \return the list of push constant blocks.
390
391 \note Avoid relying on push constant blocks for shaders that are to be used
392 in combination with the Qt Rendering Hardware Interface since that
393 currently has no support for them.
394 */
395QVector<QShaderDescription::PushConstantBlock> QShaderDescription::pushConstantBlocks() const
396{
397 return d->pushConstantBlocks;
398}
399
400/*!
401 \return the list of shader storage blocks.
402
403 For example, with GLSL/Vulkan shaders as source, the declaration
404
405 \badcode
406 struct Stuff {
407 vec2 a;
408 vec2 b;
409 };
410 layout(std140, binding = 0) buffer StuffSsbo {
411 vec4 whatever;
412 Stuff stuff[];
413 } buf;
414 \endcode
415
416 generates the following: (shown as textual JSON here)
417
418 \badcode
419 "storageBlocks": [ {
420 "binding": 0,
421 "blockName": "StuffSsbo",
422 "instanceName": "buf",
423 "knownSize": 16,
424 "members": [
425 {
426 "name": "whatever",
427 "offset": 0,
428 "size": 16,
429 "type": "vec4"
430 },
431 {
432 "arrayDims": [
433 0
434 ],
435 "name": "stuff",
436 "offset": 16,
437 "size": 0,
438 "structMembers": [
439 {
440 "name": "a",
441 "offset": 0,
442 "size": 8,
443 "type": "vec2"
444 },
445 {
446 "name": "b",
447 "offset": 8,
448 "size": 8,
449 "type": "vec2"
450 }
451 ],
452 "type": "struct"
453 }
454 ],
455 "set": 0
456 } ]
457 \endcode
458
459 \note The size of the last member in the storage block is undefined. This shows
460 up as \c size 0 and an array dimension of \c{[0]}. The storage block's \c knownSize
461 excludes the size of the last member since that will only be known at run time.
462
463 \note SSBOs are not available with some graphics APIs, such as, OpenGL 2.x or
464 OpenGL ES older than 3.1.
465 */
466QVector<QShaderDescription::StorageBlock> QShaderDescription::storageBlocks() const
467{
468 return d->storageBlocks;
469}
470
471/*!
472 \return the list of combined image samplers
473
474 With GLSL/Vulkan shaders as source a \c{layout(binding = 1) uniform sampler2D tex;}
475 uniform generates the following: (shown as textual JSON here)
476
477 \badcode
478 "combinedImageSamplers": [
479 {
480 "binding": 1,
481 "name": "tex",
482 "set": 0,
483 "type": "sampler2D"
484 }
485 ]
486 \endcode
487
488 This does not mean that other language versions of the shader must also use
489 a combined image sampler, especially considering that the concept may not
490 exist everywhere. For instance, a HLSL version will likely just use a
491 Texture2D and SamplerState object with registers t1 and s1, respectively.
492 */
493QVector<QShaderDescription::InOutVariable> QShaderDescription::combinedImageSamplers() const
494{
495 return d->combinedImageSamplers;
496}
497
498/*!
499 \return the list of image variables.
500
501 These will likely occur in compute shaders. For example,
502 \c{layout (binding = 0, rgba8) uniform readonly image2D inputImage;}
503 generates the following: (shown as textual JSON here)
504
505 \badcode
506 "storageImages": [
507 {
508 "binding": 0,
509 "imageFormat": "rgba8",
510 "name": "inputImage",
511 "set": 0,
512 "type": "image2D"
513 }
514 ]
515 \endcode
516
517 \note Separate image objects are not compatible with some graphics APIs,
518 such as, OpenGL 2.x or OpenGL ES older than 3.1.
519 */
520QVector<QShaderDescription::InOutVariable> QShaderDescription::storageImages() const
521{
522 return d->storageImages;
523}
524
525/*!
526 Returns the local size of a compute shader.
527
528 For example, for a compute shader with the following declaration the
529 function returns { 256, 16, 1}.
530
531 \badcode
532 layout(local_size_x = 256, local_size_y = 16, local_size_z = 1) in;
533 \endcode
534 */
535std::array<uint, 3> QShaderDescription::computeShaderLocalSize() const
536{
537 return d->localSize;
538}
539
540static struct TypeTab {
541 QString k;
542 QShaderDescription::VariableType v;
543} typeTab[] = {
544 { QLatin1String("float"), QShaderDescription::Float },
545 { QLatin1String("vec2"), QShaderDescription::Vec2 },
546 { QLatin1String("vec3"), QShaderDescription::Vec3 },
547 { QLatin1String("vec4"), QShaderDescription::Vec4 },
548 { QLatin1String("mat2"), QShaderDescription::Mat2 },
549 { QLatin1String("mat3"), QShaderDescription::Mat3 },
550 { QLatin1String("mat4"), QShaderDescription::Mat4 },
551
552 { QLatin1String("struct"), QShaderDescription::Struct },
553
554 { QLatin1String("sampler1D"), QShaderDescription::Sampler1D },
555 { QLatin1String("sampler2D"), QShaderDescription::Sampler2D },
556 { QLatin1String("sampler2DMS"), QShaderDescription::Sampler2DMS },
557 { QLatin1String("sampler3D"), QShaderDescription::Sampler3D },
558 { QLatin1String("samplerCube"), QShaderDescription::SamplerCube },
559 { QLatin1String("sampler1DArray"), QShaderDescription::Sampler1DArray },
560 { QLatin1String("sampler2DArray"), QShaderDescription::Sampler2DArray },
561 { QLatin1String("sampler2DMSArray"), QShaderDescription::Sampler2DMSArray },
562 { QLatin1String("sampler3DArray"), QShaderDescription::Sampler3DArray },
563 { QLatin1String("samplerCubeArray"), QShaderDescription::SamplerCubeArray },
564 { QLatin1String("samplerRect"), QShaderDescription::SamplerRect },
565 { QLatin1String("samplerBuffer"), QShaderDescription::SamplerBuffer },
566
567 { QLatin1String("mat2x3"), QShaderDescription::Mat2x3 },
568 { QLatin1String("mat2x4"), QShaderDescription::Mat2x4 },
569 { QLatin1String("mat3x2"), QShaderDescription::Mat3x2 },
570 { QLatin1String("mat3x4"), QShaderDescription::Mat3x4 },
571 { QLatin1String("mat4x2"), QShaderDescription::Mat4x2 },
572 { QLatin1String("mat4x3"), QShaderDescription::Mat4x3 },
573
574 { QLatin1String("int"), QShaderDescription::Int },
575 { QLatin1String("ivec2"), QShaderDescription::Int2 },
576 { QLatin1String("ivec3"), QShaderDescription::Int3 },
577 { QLatin1String("ivec4"), QShaderDescription::Int4 },
578
579 { QLatin1String("uint"), QShaderDescription::Uint },
580 { QLatin1String("uvec2"), QShaderDescription::Uint2 },
581 { QLatin1String("uvec3"), QShaderDescription::Uint3 },
582 { QLatin1String("uvec4"), QShaderDescription::Uint4 },
583
584 { QLatin1String("bool"), QShaderDescription::Bool },
585 { QLatin1String("bvec2"), QShaderDescription::Bool2 },
586 { QLatin1String("bvec3"), QShaderDescription::Bool3 },
587 { QLatin1String("bvec4"), QShaderDescription::Bool4 },
588
589 { QLatin1String("double"), QShaderDescription::Double },
590 { QLatin1String("dvec2"), QShaderDescription::Double2 },
591 { QLatin1String("dvec3"), QShaderDescription::Double3 },
592 { QLatin1String("dvec4"), QShaderDescription::Double4 },
593 { QLatin1String("dmat2"), QShaderDescription::DMat2 },
594 { QLatin1String("dmat3"), QShaderDescription::DMat3 },
595 { QLatin1String("dmat4"), QShaderDescription::DMat4 },
596 { QLatin1String("dmat2x3"), QShaderDescription::DMat2x3 },
597 { QLatin1String("dmat2x4"), QShaderDescription::DMat2x4 },
598 { QLatin1String("dmat3x2"), QShaderDescription::DMat3x2 },
599 { QLatin1String("dmat3x4"), QShaderDescription::DMat3x4 },
600 { QLatin1String("dmat4x2"), QShaderDescription::DMat4x2 },
601 { QLatin1String("dmat4x3"), QShaderDescription::DMat4x3 },
602
603 { QLatin1String("image1D"), QShaderDescription::Image1D },
604 { QLatin1String("image2D"), QShaderDescription::Image2D },
605 { QLatin1String("image2DMS"), QShaderDescription::Image2DMS },
606 { QLatin1String("image3D"), QShaderDescription::Image3D },
607 { QLatin1String("imageCube"), QShaderDescription::ImageCube },
608 { QLatin1String("image1DArray"), QShaderDescription::Image1DArray },
609 { QLatin1String("image2DArray"), QShaderDescription::Image2DArray },
610 { QLatin1String("image2DMSArray"), QShaderDescription::Image2DMSArray },
611 { QLatin1String("image3DArray"), QShaderDescription::Image3DArray },
612 { QLatin1String("imageCubeArray"), QShaderDescription::ImageCubeArray },
613 { QLatin1String("imageRect"), QShaderDescription::ImageRect },
614 { QLatin1String("imageBuffer"), QShaderDescription::ImageBuffer }
615};
616
617static QString typeStr(const QShaderDescription::VariableType &t)
618{
619 for (size_t i = 0; i < sizeof(typeTab) / sizeof(TypeTab); ++i) {
620 if (typeTab[i].v == t)
621 return typeTab[i].k;
622 }
623 return QString();
624}
625
626static QShaderDescription::VariableType mapType(const QString &t)
627{
628 for (size_t i = 0; i < sizeof(typeTab) / sizeof(TypeTab); ++i) {
629 if (typeTab[i].k == t)
630 return typeTab[i].v;
631 }
632 return QShaderDescription::Unknown;
633}
634
635static struct ImageFormatTab {
636 QString k;
637 QShaderDescription::ImageFormat v;
638} imageFormatTab[] {
639 { QLatin1String("unknown"), QShaderDescription::ImageFormatUnknown },
640 { QLatin1String("rgba32f"), QShaderDescription::ImageFormatRgba32f },
641 { QLatin1String("rgba16"), QShaderDescription::ImageFormatRgba16f },
642 { QLatin1String("r32f"), QShaderDescription::ImageFormatR32f },
643 { QLatin1String("rgba8"), QShaderDescription::ImageFormatRgba8 },
644 { QLatin1String("rgba8_snorm"), QShaderDescription::ImageFormatRgba8Snorm },
645 { QLatin1String("rg32f"), QShaderDescription::ImageFormatRg32f },
646 { QLatin1String("rg16f"), QShaderDescription::ImageFormatRg16f },
647 { QLatin1String("r11f_g11f_b10f"), QShaderDescription::ImageFormatR11fG11fB10f },
648 { QLatin1String("r16f"), QShaderDescription::ImageFormatR16f },
649 { QLatin1String("rgba16"), QShaderDescription::ImageFormatRgba16 },
650 { QLatin1String("rgb10_a2"), QShaderDescription::ImageFormatRgb10A2 },
651 { QLatin1String("rg16"), QShaderDescription::ImageFormatRg16 },
652 { QLatin1String("rg8"), QShaderDescription::ImageFormatRg8 },
653 { QLatin1String("r16"), QShaderDescription::ImageFormatR16 },
654 { QLatin1String("r8"), QShaderDescription::ImageFormatR8 },
655 { QLatin1String("rgba16_snorm"), QShaderDescription::ImageFormatRgba16Snorm },
656 { QLatin1String("rg16_snorm"), QShaderDescription::ImageFormatRg16Snorm },
657 { QLatin1String("rg8_snorm"), QShaderDescription::ImageFormatRg8Snorm },
658 { QLatin1String("r16_snorm"), QShaderDescription::ImageFormatR16Snorm },
659 { QLatin1String("r8_snorm"), QShaderDescription::ImageFormatR8Snorm },
660 { QLatin1String("rgba32i"), QShaderDescription::ImageFormatRgba32i },
661 { QLatin1String("rgba16i"), QShaderDescription::ImageFormatRgba16i },
662 { QLatin1String("rgba8i"), QShaderDescription::ImageFormatRgba8i },
663 { QLatin1String("r32i"), QShaderDescription::ImageFormatR32i },
664 { QLatin1String("rg32i"), QShaderDescription::ImageFormatRg32i },
665 { QLatin1String("rg16i"), QShaderDescription::ImageFormatRg16i },
666 { QLatin1String("rg8i"), QShaderDescription::ImageFormatRg8i },
667 { QLatin1String("r16i"), QShaderDescription::ImageFormatR16i },
668 { QLatin1String("r8i"), QShaderDescription::ImageFormatR8i },
669 { QLatin1String("rgba32ui"), QShaderDescription::ImageFormatRgba32ui },
670 { QLatin1String("rgba16ui"), QShaderDescription::ImageFormatRgba16ui },
671 { QLatin1String("rgba8ui"), QShaderDescription::ImageFormatRgba8ui },
672 { QLatin1String("r32ui"), QShaderDescription::ImageFormatR32ui },
673 { QLatin1String("rgb10_a2ui"), QShaderDescription::ImageFormatRgb10a2ui },
674 { QLatin1String("rg32ui"), QShaderDescription::ImageFormatRg32ui },
675 { QLatin1String("rg16ui"), QShaderDescription::ImageFormatRg16ui },
676 { QLatin1String("rg8ui"), QShaderDescription::ImageFormatRg8ui },
677 { QLatin1String("r16ui"), QShaderDescription::ImageFormatR16ui },
678 { QLatin1String("r8ui"), QShaderDescription::ImageFormatR8ui }
679};
680
681static QString imageFormatStr(const QShaderDescription::ImageFormat &f)
682{
683 for (size_t i = 0; i < sizeof(imageFormatTab) / sizeof(ImageFormatTab); ++i) {
684 if (imageFormatTab[i].v == f)
685 return imageFormatTab[i].k;
686 }
687 return QString();
688}
689
690static QShaderDescription::ImageFormat mapImageFormat(const QString &f)
691{
692 for (size_t i = 0; i < sizeof(imageFormatTab) / sizeof(ImageFormatTab); ++i) {
693 if (imageFormatTab[i].k == f)
694 return imageFormatTab[i].v;
695 }
696 return QShaderDescription::ImageFormatUnknown;
697}
698
699#ifndef QT_NO_DEBUG_STREAM
700QDebug operator<<(QDebug dbg, const QShaderDescription &sd)
701{
702 const QShaderDescriptionPrivate *d = sd.d;
703 QDebugStateSaver saver(dbg);
704
705 if (sd.isValid()) {
706 dbg.nospace() << "QShaderDescription("
707 << "inVars " << d->inVars
708 << " outVars " << d->outVars
709 << " uniformBlocks " << d->uniformBlocks
710 << " pcBlocks " << d->pushConstantBlocks
711 << " storageBlocks " << d->storageBlocks
712 << " combinedSamplers " << d->combinedImageSamplers
713 << " images " << d->storageImages
714 << ')';
715 } else {
716 dbg.nospace() << "QShaderDescription(null)";
717 }
718
719 return dbg;
720}
721
722QDebug operator<<(QDebug dbg, const QShaderDescription::InOutVariable &var)
723{
724 QDebugStateSaver saver(dbg);
725 dbg.nospace() << "InOutVariable(" << typeStr(var.type) << ' ' << var.name;
726 if (var.location >= 0)
727 dbg.nospace() << " location=" << var.location;
728 if (var.binding >= 0)
729 dbg.nospace() << " binding=" << var.binding;
730 if (var.descriptorSet >= 0)
731 dbg.nospace() << " set=" << var.descriptorSet;
732 if (var.imageFormat != QShaderDescription::ImageFormatUnknown)
733 dbg.nospace() << " imageFormat=" << imageFormatStr(var.imageFormat);
734 if (var.imageFlags)
735 dbg.nospace() << " imageFlags=" << var.imageFlags;
736 dbg.nospace() << ')';
737 return dbg;
738}
739
740QDebug operator<<(QDebug dbg, const QShaderDescription::BlockVariable &var)
741{
742 QDebugStateSaver saver(dbg);
743 dbg.nospace() << "BlockVariable(" << typeStr(var.type) << ' ' << var.name
744 << " offset=" << var.offset << " size=" << var.size;
745 if (!var.arrayDims.isEmpty())
746 dbg.nospace() << " array=" << var.arrayDims;
747 if (var.arrayStride)
748 dbg.nospace() << " arrayStride=" << var.arrayStride;
749 if (var.matrixStride)
750 dbg.nospace() << " matrixStride=" << var.matrixStride;
751 if (var.matrixIsRowMajor)
752 dbg.nospace() << " [rowmaj]";
753 if (!var.structMembers.isEmpty())
754 dbg.nospace() << " structMembers=" << var.structMembers;
755 dbg.nospace() << ')';
756 return dbg;
757}
758
759QDebug operator<<(QDebug dbg, const QShaderDescription::UniformBlock &blk)
760{
761 QDebugStateSaver saver(dbg);
762 dbg.nospace() << "UniformBlock(" << blk.blockName << ' ' << blk.structName << " size=" << blk.size;
763 if (blk.binding >= 0)
764 dbg.nospace() << " binding=" << blk.binding;
765 if (blk.descriptorSet >= 0)
766 dbg.nospace() << " set=" << blk.descriptorSet;
767 dbg.nospace() << ' ' << blk.members << ')';
768 return dbg;
769}
770
771QDebug operator<<(QDebug dbg, const QShaderDescription::PushConstantBlock &blk)
772{
773 QDebugStateSaver saver(dbg);
774 dbg.nospace() << "PushConstantBlock(" << blk.name << " size=" << blk.size << ' ' << blk.members << ')';
775 return dbg;
776}
777
778QDebug operator<<(QDebug dbg, const QShaderDescription::StorageBlock &blk)
779{
780 QDebugStateSaver saver(dbg);
781 dbg.nospace() << "StorageBlock(" << blk.blockName << ' ' << blk.instanceName << " knownSize=" << blk.knownSize;
782 if (blk.binding >= 0)
783 dbg.nospace() << " binding=" << blk.binding;
784 if (blk.descriptorSet >= 0)
785 dbg.nospace() << " set=" << blk.descriptorSet;
786 dbg.nospace() << ' ' << blk.members << ')';
787 return dbg;
788}
789#endif
790
791static const QString nameKey = QLatin1String("name");
792static const QString typeKey = QLatin1String("type");
793static const QString locationKey = QLatin1String("location");
794static const QString bindingKey = QLatin1String("binding");
795static const QString setKey = QLatin1String("set");
796static const QString imageFormatKey = QLatin1String("imageFormat");
797static const QString imageFlagsKey = QLatin1String("imageFlags");
798static const QString offsetKey = QLatin1String("offset");
799static const QString arrayDimsKey = QLatin1String("arrayDims");
800static const QString arrayStrideKey = QLatin1String("arrayStride");
801static const QString matrixStrideKey = QLatin1String("matrixStride");
802static const QString matrixRowMajorKey = QLatin1String("matrixRowMajor");
803static const QString structMembersKey = QLatin1String("structMembers");
804static const QString membersKey = QLatin1String("members");
805static const QString inputsKey = QLatin1String("inputs");
806static const QString outputsKey = QLatin1String("outputs");
807static const QString uniformBlocksKey = QLatin1String("uniformBlocks");
808static const QString blockNameKey = QLatin1String("blockName");
809static const QString structNameKey = QLatin1String("structName");
810static const QString instanceNameKey = QLatin1String("instanceName");
811static const QString sizeKey = QLatin1String("size");
812static const QString knownSizeKey = QLatin1String("knownSize");
813static const QString pushConstantBlocksKey = QLatin1String("pushConstantBlocks");
814static const QString storageBlocksKey = QLatin1String("storageBlocks");
815static const QString combinedImageSamplersKey = QLatin1String("combinedImageSamplers");
816static const QString storageImagesKey = QLatin1String("storageImages");
817static const QString localSizeKey = QLatin1String("localSize");
818
819static void addDeco(QJsonObject *obj, const QShaderDescription::InOutVariable &v)
820{
821 if (v.location >= 0)
822 (*obj)[locationKey] = v.location;
823 if (v.binding >= 0)
824 (*obj)[bindingKey] = v.binding;
825 if (v.descriptorSet >= 0)
826 (*obj)[setKey] = v.descriptorSet;
827 if (v.imageFormat != QShaderDescription::ImageFormatUnknown)
828 (*obj)[imageFormatKey] = imageFormatStr(v.imageFormat);
829 if (v.imageFlags)
830 (*obj)[imageFlagsKey] = int(v.imageFlags);
831}
832
833static QJsonObject inOutObject(const QShaderDescription::InOutVariable &v)
834{
835 QJsonObject obj;
836 obj[nameKey] = v.name;
837 obj[typeKey] = typeStr(v.type);
838 addDeco(&obj, v);
839 return obj;
840}
841
842static QJsonObject blockMemberObject(const QShaderDescription::BlockVariable &v)
843{
844 QJsonObject obj;
845 obj[nameKey] = v.name;
846 obj[typeKey] = typeStr(v.type);
847 obj[offsetKey] = v.offset;
848 obj[sizeKey] = v.size;
849 if (!v.arrayDims.isEmpty()) {
850 QJsonArray dimArr;
851 for (int dim : v.arrayDims)
852 dimArr.append(dim);
853 obj[arrayDimsKey] = dimArr;
854 }
855 if (v.arrayStride)
856 obj[arrayStrideKey] = v.arrayStride;
857 if (v.matrixStride)
858 obj[matrixStrideKey] = v.matrixStride;
859 if (v.matrixIsRowMajor)
860 obj[matrixRowMajorKey] = true;
861 if (!v.structMembers.isEmpty()) {
862 QJsonArray arr;
863 for (const QShaderDescription::BlockVariable &sv : v.structMembers)
864 arr.append(blockMemberObject(sv));
865 obj[structMembersKey] = arr;
866 }
867 return obj;
868}
869
870QJsonDocument QShaderDescriptionPrivate::makeDoc()
871{
872 QJsonObject root;
873
874 QJsonArray jinputs;
875 for (const QShaderDescription::InOutVariable &v : qAsConst(inVars))
876 jinputs.append(inOutObject(v));
877 if (!jinputs.isEmpty())
878 root[inputsKey] = jinputs;
879
880 QJsonArray joutputs;
881 for (const QShaderDescription::InOutVariable &v : qAsConst(outVars))
882 joutputs.append(inOutObject(v));
883 if (!joutputs.isEmpty())
884 root[outputsKey] = joutputs;
885
886 QJsonArray juniformBlocks;
887 for (const QShaderDescription::UniformBlock &b : uniformBlocks) {
888 QJsonObject juniformBlock;
889 juniformBlock[blockNameKey] = b.blockName;
890 juniformBlock[structNameKey] = b.structName;
891 juniformBlock[sizeKey] = b.size;
892 if (b.binding >= 0)
893 juniformBlock[bindingKey] = b.binding;
894 if (b.descriptorSet >= 0)
895 juniformBlock[setKey] = b.descriptorSet;
896 QJsonArray members;
897 for (const QShaderDescription::BlockVariable &v : b.members)
898 members.append(blockMemberObject(v));
899 juniformBlock[membersKey] = members;
900 juniformBlocks.append(juniformBlock);
901 }
902 if (!juniformBlocks.isEmpty())
903 root[uniformBlocksKey] = juniformBlocks;
904
905 QJsonArray jpushConstantBlocks;
906 for (const QShaderDescription::PushConstantBlock &b : pushConstantBlocks) {
907 QJsonObject jpushConstantBlock;
908 jpushConstantBlock[nameKey] = b.name;
909 jpushConstantBlock[sizeKey] = b.size;
910 QJsonArray members;
911 for (const QShaderDescription::BlockVariable &v : b.members)
912 members.append(blockMemberObject(v));
913 jpushConstantBlock[membersKey] = members;
914 jpushConstantBlocks.append(jpushConstantBlock);
915 }
916 if (!jpushConstantBlocks.isEmpty())
917 root[pushConstantBlocksKey] = jpushConstantBlocks;
918
919 QJsonArray jstorageBlocks;
920 for (const QShaderDescription::StorageBlock &b : storageBlocks) {
921 QJsonObject jstorageBlock;
922 jstorageBlock[blockNameKey] = b.blockName;
923 jstorageBlock[instanceNameKey] = b.instanceName;
924 jstorageBlock[knownSizeKey] = b.knownSize;
925 if (b.binding >= 0)
926 jstorageBlock[bindingKey] = b.binding;
927 if (b.descriptorSet >= 0)
928 jstorageBlock[setKey] = b.descriptorSet;
929 QJsonArray members;
930 for (const QShaderDescription::BlockVariable &v : b.members)
931 members.append(blockMemberObject(v));
932 jstorageBlock[membersKey] = members;
933 jstorageBlocks.append(jstorageBlock);
934 }
935 if (!jstorageBlocks.isEmpty())
936 root[storageBlocksKey] = jstorageBlocks;
937
938 QJsonArray jcombinedSamplers;
939 for (const QShaderDescription::InOutVariable &v : qAsConst(combinedImageSamplers)) {
940 QJsonObject sampler;
941 sampler[nameKey] = v.name;
942 sampler[typeKey] = typeStr(v.type);
943 addDeco(&sampler, v);
944 jcombinedSamplers.append(sampler);
945 }
946 if (!jcombinedSamplers.isEmpty())
947 root[combinedImageSamplersKey] = jcombinedSamplers;
948
949 QJsonArray jstorageImages;
950 for (const QShaderDescription::InOutVariable &v : qAsConst(storageImages)) {
951 QJsonObject image;
952 image[nameKey] = v.name;
953 image[typeKey] = typeStr(v.type);
954 addDeco(&image, v);
955 jstorageImages.append(image);
956 }
957 if (!jstorageImages.isEmpty())
958 root[storageImagesKey] = jstorageImages;
959
960 QJsonArray jlocalSize;
961 for (int i = 0; i < 3; ++i)
962 jlocalSize.append(QJsonValue(int(localSize[i])));
963 root[localSizeKey] = jlocalSize;
964
965 return QJsonDocument(root);
966}
967
968static QShaderDescription::InOutVariable inOutVar(const QJsonObject &obj)
969{
970 QShaderDescription::InOutVariable var;
971 var.name = obj[nameKey].toString();
972 var.type = mapType(obj[typeKey].toString());
973 if (obj.contains(locationKey))
974 var.location = obj[locationKey].toInt();
975 if (obj.contains(bindingKey))
976 var.binding = obj[bindingKey].toInt();
977 if (obj.contains(setKey))
978 var.descriptorSet = obj[setKey].toInt();
979 if (obj.contains(imageFormatKey))
980 var.imageFormat = mapImageFormat(obj[imageFormatKey].toString());
981 if (obj.contains(imageFlagsKey))
982 var.imageFlags = QShaderDescription::ImageFlags(obj[imageFlagsKey].toInt());
983 return var;
984}
985
986static QShaderDescription::BlockVariable blockVar(const QJsonObject &obj)
987{
988 QShaderDescription::BlockVariable var;
989 var.name = obj[nameKey].toString();
990 var.type = mapType(obj[typeKey].toString());
991 var.offset = obj[offsetKey].toInt();
992 var.size = obj[sizeKey].toInt();
993 if (obj.contains(arrayDimsKey)) {
994 QJsonArray dimArr = obj[arrayDimsKey].toArray();
995 for (int i = 0; i < dimArr.count(); ++i)
996 var.arrayDims.append(dimArr.at(i).toInt());
997 }
998 if (obj.contains(arrayStrideKey))
999 var.arrayStride = obj[arrayStrideKey].toInt();
1000 if (obj.contains(matrixStrideKey))
1001 var.matrixStride = obj[matrixStrideKey].toInt();
1002 if (obj.contains(matrixRowMajorKey))
1003 var.matrixIsRowMajor = obj[matrixRowMajorKey].toBool();
1004 if (obj.contains(structMembersKey)) {
1005 QJsonArray arr = obj[structMembersKey].toArray();
1006 for (int i = 0; i < arr.count(); ++i)
1007 var.structMembers.append(blockVar(arr.at(i).toObject()));
1008 }
1009 return var;
1010}
1011
1012void QShaderDescriptionPrivate::loadDoc(const QJsonDocument &doc)
1013{
1014 if (doc.isNull()) {
1015 qWarning("QShaderDescription: JSON document is empty");
1016 return;
1017 }
1018
1019 Q_ASSERT(ref.load() == 1); // must be detached
1020
1021 inVars.clear();
1022 outVars.clear();
1023 uniformBlocks.clear();
1024 pushConstantBlocks.clear();
1025 storageBlocks.clear();
1026 combinedImageSamplers.clear();
1027 storageImages.clear();
1028
1029 QJsonObject root = doc.object();
1030
1031 if (root.contains(inputsKey)) {
1032 QJsonArray inputs = root[inputsKey].toArray();
1033 for (int i = 0; i < inputs.count(); ++i)
1034 inVars.append(inOutVar(inputs[i].toObject()));
1035 }
1036
1037 if (root.contains(outputsKey)) {
1038 QJsonArray outputs = root[outputsKey].toArray();
1039 for (int i = 0; i < outputs.count(); ++i)
1040 outVars.append(inOutVar(outputs[i].toObject()));
1041 }
1042
1043 if (root.contains(uniformBlocksKey)) {
1044 QJsonArray ubs = root[uniformBlocksKey].toArray();
1045 for (int i = 0; i < ubs.count(); ++i) {
1046 QJsonObject ubObj = ubs[i].toObject();
1047 QShaderDescription::UniformBlock ub;
1048 ub.blockName = ubObj[blockNameKey].toString();
1049 ub.structName = ubObj[structNameKey].toString();
1050 ub.size = ubObj[sizeKey].toInt();
1051 if (ubObj.contains(bindingKey))
1052 ub.binding = ubObj[bindingKey].toInt();
1053 if (ubObj.contains(setKey))
1054 ub.descriptorSet = ubObj[setKey].toInt();
1055 QJsonArray members = ubObj[membersKey].toArray();
1056 for (const QJsonValue &member : members)
1057 ub.members.append(blockVar(member.toObject()));
1058 uniformBlocks.append(ub);
1059 }
1060 }
1061
1062 if (root.contains(pushConstantBlocksKey)) {
1063 QJsonArray pcs = root[pushConstantBlocksKey].toArray();
1064 for (int i = 0; i < pcs.count(); ++i) {
1065 QJsonObject pcObj = pcs[i].toObject();
1066 QShaderDescription::PushConstantBlock pc;
1067 pc.name = pcObj[nameKey].toString();
1068 pc.size = pcObj[sizeKey].toInt();
1069 QJsonArray members = pcObj[membersKey].toArray();
1070 for (const QJsonValue &member : members)
1071 pc.members.append(blockVar(member.toObject()));
1072 pushConstantBlocks.append(pc);
1073 }
1074 }
1075
1076 if (root.contains(storageBlocksKey)) {
1077 QJsonArray ubs = root[storageBlocksKey].toArray();
1078 for (int i = 0; i < ubs.count(); ++i) {
1079 QJsonObject sbObj = ubs[i].toObject();
1080 QShaderDescription::StorageBlock sb;
1081 sb.blockName = sbObj[blockNameKey].toString();
1082 sb.instanceName = sbObj[instanceNameKey].toString();
1083 sb.knownSize = sbObj[knownSizeKey].toInt();
1084 if (sbObj.contains(bindingKey))
1085 sb.binding = sbObj[bindingKey].toInt();
1086 if (sbObj.contains(setKey))
1087 sb.descriptorSet = sbObj[setKey].toInt();
1088 QJsonArray members = sbObj[membersKey].toArray();
1089 for (const QJsonValue &member : members)
1090 sb.members.append(blockVar(member.toObject()));
1091 storageBlocks.append(sb);
1092 }
1093 }
1094
1095 if (root.contains(combinedImageSamplersKey)) {
1096 QJsonArray samplers = root[combinedImageSamplersKey].toArray();
1097 for (int i = 0; i < samplers.count(); ++i)
1098 combinedImageSamplers.append(inOutVar(samplers[i].toObject()));
1099 }
1100
1101 if (root.contains(storageImagesKey)) {
1102 QJsonArray images = root[storageImagesKey].toArray();
1103 for (int i = 0; i < images.count(); ++i)
1104 storageImages.append(inOutVar(images[i].toObject()));
1105 }
1106
1107 if (root.contains(localSizeKey)) {
1108 QJsonArray localSizeArr = root[localSizeKey].toArray();
1109 if (localSizeArr.count() == 3) {
1110 for (int i = 0; i < 3; ++i)
1111 localSize[i] = localSizeArr[i].toInt();
1112 }
1113 }
1114}
1115
1116QT_END_NAMESPACE
1117