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 | |
42 | QT_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 | */ |
278 | QShaderDescription::QShaderDescription() |
279 | : d(new QShaderDescriptionPrivate) |
280 | { |
281 | } |
282 | |
283 | /*! |
284 | \internal |
285 | */ |
286 | void QShaderDescription::detach() |
287 | { |
288 | qAtomicDetach(d); |
289 | } |
290 | |
291 | /*! |
292 | \internal |
293 | */ |
294 | QShaderDescription::QShaderDescription(const QShaderDescription &other) |
295 | : d(other.d) |
296 | { |
297 | d->ref.ref(); |
298 | } |
299 | |
300 | /*! |
301 | \internal |
302 | */ |
303 | QShaderDescription &QShaderDescription::operator=(const QShaderDescription &other) |
304 | { |
305 | qAtomicAssign(d, other.d); |
306 | return *this; |
307 | } |
308 | |
309 | /*! |
310 | Destructor. |
311 | */ |
312 | QShaderDescription::~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 | */ |
322 | bool 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 | */ |
334 | QByteArray 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 | */ |
346 | QByteArray 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 | */ |
355 | QShaderDescription 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 | */ |
367 | QVector<QShaderDescription::InOutVariable> QShaderDescription::inputVariables() const |
368 | { |
369 | return d->inVars; |
370 | } |
371 | |
372 | /*! |
373 | \return the list of output variables. |
374 | */ |
375 | QVector<QShaderDescription::InOutVariable> QShaderDescription::outputVariables() const |
376 | { |
377 | return d->outVars; |
378 | } |
379 | |
380 | /*! |
381 | \return the list of uniform blocks. |
382 | */ |
383 | QVector<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 | */ |
395 | QVector<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 | */ |
466 | QVector<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 | */ |
493 | QVector<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 | */ |
520 | QVector<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 | */ |
535 | std::array<uint, 3> QShaderDescription::computeShaderLocalSize() const |
536 | { |
537 | return d->localSize; |
538 | } |
539 | |
540 | static 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 | |
617 | static 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 | |
626 | static 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 | |
635 | static 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 | |
681 | static 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 | |
690 | static 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 |
700 | QDebug 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 | |
722 | QDebug 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 | |
740 | QDebug 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 | |
759 | QDebug 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 | |
771 | QDebug 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 | |
778 | QDebug 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 | |
791 | static const QString nameKey = QLatin1String("name" ); |
792 | static const QString typeKey = QLatin1String("type" ); |
793 | static const QString locationKey = QLatin1String("location" ); |
794 | static const QString bindingKey = QLatin1String("binding" ); |
795 | static const QString setKey = QLatin1String("set" ); |
796 | static const QString imageFormatKey = QLatin1String("imageFormat" ); |
797 | static const QString imageFlagsKey = QLatin1String("imageFlags" ); |
798 | static const QString offsetKey = QLatin1String("offset" ); |
799 | static const QString arrayDimsKey = QLatin1String("arrayDims" ); |
800 | static const QString arrayStrideKey = QLatin1String("arrayStride" ); |
801 | static const QString matrixStrideKey = QLatin1String("matrixStride" ); |
802 | static const QString matrixRowMajorKey = QLatin1String("matrixRowMajor" ); |
803 | static const QString structMembersKey = QLatin1String("structMembers" ); |
804 | static const QString membersKey = QLatin1String("members" ); |
805 | static const QString inputsKey = QLatin1String("inputs" ); |
806 | static const QString outputsKey = QLatin1String("outputs" ); |
807 | static const QString uniformBlocksKey = QLatin1String("uniformBlocks" ); |
808 | static const QString blockNameKey = QLatin1String("blockName" ); |
809 | static const QString structNameKey = QLatin1String("structName" ); |
810 | static const QString instanceNameKey = QLatin1String("instanceName" ); |
811 | static const QString sizeKey = QLatin1String("size" ); |
812 | static const QString knownSizeKey = QLatin1String("knownSize" ); |
813 | static const QString pushConstantBlocksKey = QLatin1String("pushConstantBlocks" ); |
814 | static const QString storageBlocksKey = QLatin1String("storageBlocks" ); |
815 | static const QString combinedImageSamplersKey = QLatin1String("combinedImageSamplers" ); |
816 | static const QString storageImagesKey = QLatin1String("storageImages" ); |
817 | static const QString localSizeKey = QLatin1String("localSize" ); |
818 | |
819 | static 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 | |
833 | static 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 | |
842 | static 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 | |
870 | QJsonDocument 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 | |
968 | static 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 | |
986 | static 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 | |
1012 | void 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 | |
1116 | QT_END_NAMESPACE |
1117 | |