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 "qshader_p_p.h"
38#include <QDataStream>
39#include <QBuffer>
40
41QT_BEGIN_NAMESPACE
42
43/*!
44 \class QShader
45 \inmodule QtRhi
46
47 \brief Contains multiple versions of a shader translated to multiple shading languages,
48 together with reflection metadata.
49
50 QShader is the entry point to shader code in the graphics API agnostic
51 Qt world. Instead of using GLSL shader sources, as was the custom with Qt
52 5.x, new graphics systems with backends for multiple graphics APIs, such
53 as, Vulkan, Metal, Direct3D, and OpenGL, take QShader as their input
54 whenever a shader needs to be specified.
55
56 A QShader instance is empty and thus invalid by default. To get a useful
57 instance, the two typical methods are:
58
59 \list
60
61 \li Generate the contents offline, during build time or earlier, using the
62 \c qsb command line tool. The result is a binary file that is shipped with
63 the application, read via QIODevice::readAll(), and then deserialized via
64 fromSerialized(). For more information, see QShaderBaker.
65
66 \li Generate at run time via QShaderBaker. This is an expensive operation,
67 but allows applications to use user-provided or dynamically generated
68 shader source strings.
69
70 \endlist
71
72 When used together with the Qt Rendering Hardware Interface and its
73 classes, like QRhiGraphicsPipeline, no further action is needed from the
74 application's side as these classes are prepared to consume a QShader
75 whenever a shader needs to be specified for a given stage of the graphics
76 pipeline.
77
78 Alternatively, applications can access
79
80 \list
81
82 \li the source or byte code for any of the shading language versions that
83 are included in the QShader,
84
85 \li the name of the entry point for the shader,
86
87 \li the reflection metadata containing a description of the shader's
88 inputs, outputs and resources like uniform blocks. This is essential when
89 an application or framework needs to discover the inputs of a shader at
90 runtime due to not having advance knowledge of the vertex attributes or the
91 layout of the uniform buffers used by the shader.
92
93 \endlist
94
95 QShader makes no assumption about the shading language that was used
96 as the source for generating the various versions and variants that are
97 included in it.
98
99 QShader uses implicit sharing similarly to many core Qt types, and so
100 can be returned or passed by value. Detach happens implicitly when calling
101 a setter.
102
103 For reference, QRhi expects that a QShader suitable for all its
104 backends contains at least the following:
105
106 \list
107
108 \li SPIR-V 1.0 bytecode suitable for Vulkan 1.0 or newer
109
110 \li GLSL/ES 100 source code suitable for OpenGL ES 2.0 or newer
111
112 \li GLSL 120 source code suitable for OpenGL 2.1
113
114 \li HLSL Shader Model 5.0 source code or the corresponding DXBC bytecode suitable for Direct3D 11
115
116 \li Metal Shading Language 1.2 source code or the corresponding bytecode suitable for Metal
117
118 \endlist
119
120 \sa QShaderBaker
121 */
122
123/*!
124 \enum QShader::Stage
125 Describes the stage of the graphics pipeline the shader is suitable for.
126
127 \value VertexStage Vertex shader
128 \value TessellationControlStage Tessellation control (hull) shader
129 \value TessellationEvaluationStage Tessellation evaluation (domain) shader
130 \value GeometryStage Geometry shader
131 \value FragmentStage Fragment (pixel) shader
132 \value ComputeStage Compute shader
133 */
134
135/*!
136 \class QShaderVersion
137 \inmodule QtRhi
138
139 \brief Specifies the shading language version.
140
141 While languages like SPIR-V or the Metal Shading Language use traditional
142 version numbers, shaders for other APIs can use slightly different
143 versioning schemes. All those are mapped to a single version number in
144 here, however. For HLSL, the version refers to the Shader Model version,
145 like 5.0, 5.1, or 6.0. For GLSL an additional flag is needed to choose
146 between GLSL and GLSL/ES.
147
148 Below is a list with the most common examples of shader versions for
149 different graphics APIs:
150
151 \list
152
153 \li Vulkan (SPIR-V): 100
154 \li OpenGL: 120, 330, 440, etc.
155 \li OpenGL ES: 100 with GlslEs, 300 with GlslEs, etc.
156 \li Direct3D: 50, 51, 60
157 \li Metal: 12, 20
158 \endlist
159
160 A default constructed QShaderVersion contains a version of 100 and no
161 flags set.
162 */
163
164/*!
165 \enum QShaderVersion::Flag
166
167 Describes the flags that can be set.
168
169 \value GlslEs Indicates that GLSL/ES is meant in combination with GlslShader
170 */
171
172/*!
173 \class QShaderKey
174 \inmodule QtRhi
175
176 \brief Specifies the shading language, the version with flags, and the variant.
177
178 A default constructed QShaderKey has source set to SpirvShader and
179 sourceVersion set to 100. sourceVariant defaults to StandardShader.
180 */
181
182/*!
183 \enum QShader::Source
184 Describes what kind of shader code an entry contains.
185
186 \value SpirvShader SPIR-V
187 \value GlslShader GLSL
188 \value HlslShader HLSL
189 \value DxbcShader Direct3D bytecode (HLSL compiled by \c fxc)
190 \value MslShader Metal Shading Language
191 \value DxilShader Direct3D bytecode (HLSL compiled by \c dxc)
192 \value MetalLibShader Pre-compiled Metal bytecode
193 */
194
195/*!
196 \enum QShader::Variant
197 Describes what kind of shader code an entry contains.
198
199 \value StandardShader The normal, unmodified version of the shader code.
200 \value BatchableVertexShader Vertex shader rewritten to be suitable for Qt Quick scenegraph batching.
201 */
202
203/*!
204 \class QShaderCode
205 \inmodule QtRhi
206
207 \brief Contains source or binary code for a shader and additional metadata.
208
209 When shader() is empty after retrieving a QShaderCode instance from
210 QShader, it indicates no shader code was found for the requested key.
211 */
212
213static const int QSB_VERSION = 1;
214
215/*!
216 Constructs a new, empty (and thus invalid) QShader instance.
217 */
218QShader::QShader()
219 : d(new QShaderPrivate)
220{
221}
222
223/*!
224 \internal
225 */
226void QShader::detach()
227{
228 qAtomicDetach(d);
229}
230
231/*!
232 \internal
233 */
234QShader::QShader(const QShader &other)
235 : d(other.d)
236{
237 d->ref.ref();
238}
239
240/*!
241 \internal
242 */
243QShader &QShader::operator=(const QShader &other)
244{
245 qAtomicAssign(d, other.d);
246 return *this;
247}
248
249/*!
250 Destructor.
251 */
252QShader::~QShader()
253{
254 if (!d->ref.deref())
255 delete d;
256}
257
258/*!
259 \return true if the QShader contains at least one shader version.
260 */
261bool QShader::isValid() const
262{
263 return !d->shaders.isEmpty();
264}
265
266/*!
267 \return the pipeline stage the shader is meant for.
268 */
269QShader::Stage QShader::stage() const
270{
271 return d->stage;
272}
273
274/*!
275 Sets the pipeline \a stage.
276 */
277void QShader::setStage(Stage stage)
278{
279 if (stage != d->stage) {
280 detach();
281 d->stage = stage;
282 }
283}
284
285/*!
286 \return the reflection metadata for the shader.
287 */
288QShaderDescription QShader::description() const
289{
290 return d->desc;
291}
292
293/*!
294 Sets the reflection metadata to \a desc.
295 */
296void QShader::setDescription(const QShaderDescription &desc)
297{
298 detach();
299 d->desc = desc;
300}
301
302/*!
303 \return the list of available shader versions
304 */
305QVector<QShaderKey> QShader::availableShaders() const
306{
307 return d->shaders.keys().toVector();
308}
309
310/*!
311 \return the source or binary code for a given shader version specified by \a key.
312 */
313QShaderCode QShader::shader(const QShaderKey &key) const
314{
315 return d->shaders.value(key);
316}
317
318/*!
319 Stores the source or binary \a shader code for a given shader version specified by \a key.
320 */
321void QShader::setShader(const QShaderKey &key, const QShaderCode &shader)
322{
323 if (d->shaders.value(key) == shader)
324 return;
325
326 detach();
327 d->shaders[key] = shader;
328}
329
330/*!
331 Removes the source or binary shader code for a given \a key.
332 Does nothing when not found.
333 */
334void QShader::removeShader(const QShaderKey &key)
335{
336 auto it = d->shaders.find(key);
337 if (it == d->shaders.end())
338 return;
339
340 detach();
341 d->shaders.erase(it);
342}
343
344/*!
345 \return a serialized binary version of all the data held by the
346 QShader, suitable for writing to files or other I/O devices.
347
348 \sa fromSerialized()
349 */
350QByteArray QShader::serialized() const
351{
352 QBuffer buf;
353 QDataStream ds(&buf);
354 ds.setVersion(QDataStream::Qt_5_10);
355 if (!buf.open(QIODevice::WriteOnly))
356 return QByteArray();
357
358 ds << QSB_VERSION;
359 ds << d->stage;
360 ds << d->desc.toBinaryJson();
361 ds << d->shaders.count();
362 for (auto it = d->shaders.cbegin(), itEnd = d->shaders.cend(); it != itEnd; ++it) {
363 const QShaderKey &k(it.key());
364 ds << k.source();
365 ds << k.sourceVersion().version();
366 ds << k.sourceVersion().flags();
367 ds << k.sourceVariant();
368 const QShaderCode &shader(d->shaders.value(k));
369 ds << shader.shader();
370 ds << shader.entryPoint();
371 }
372
373 return qCompress(buf.buffer());
374}
375
376/*!
377 Creates a new QShader instance from the given \a data.
378
379 \sa serialized()
380 */
381QShader QShader::fromSerialized(const QByteArray &data)
382{
383 QByteArray udata = qUncompress(data);
384 QBuffer buf(&udata);
385 QDataStream ds(&buf);
386 ds.setVersion(QDataStream::Qt_5_10);
387 if (!buf.open(QIODevice::ReadOnly))
388 return QShader();
389
390 QShader bs;
391 QShaderPrivate *d = QShaderPrivate::get(&bs);
392 Q_ASSERT(d->ref.loadRelaxed() == 1); // must be detached
393 int intVal;
394 ds >> intVal;
395 if (intVal != QSB_VERSION)
396 return QShader();
397
398 ds >> intVal;
399 d->stage = Stage(intVal);
400 QByteArray descBin;
401 ds >> descBin;
402 d->desc = QShaderDescription::fromBinaryJson(descBin);
403 int count;
404 ds >> count;
405 for (int i = 0; i < count; ++i) {
406 QShaderKey k;
407 ds >> intVal;
408 k.setSource(Source(intVal));
409 QShaderVersion ver;
410 ds >> intVal;
411 ver.setVersion(intVal);
412 ds >> intVal;
413 ver.setFlags(QShaderVersion::Flags(intVal));
414 k.setSourceVersion(ver);
415 ds >> intVal;
416 k.setSourceVariant(Variant(intVal));
417 QShaderCode shader;
418 QByteArray s;
419 ds >> s;
420 shader.setShader(s);
421 ds >> s;
422 shader.setEntryPoint(s);
423 d->shaders[k] = shader;
424 }
425
426 return bs;
427}
428
429QShaderVersion::QShaderVersion(int v, Flags f)
430 : m_version(v), m_flags(f)
431{
432}
433
434QShaderCode::QShaderCode(const QByteArray &code, const QByteArray &entry)
435 : m_shader(code), m_entryPoint(entry)
436{
437}
438
439QShaderKey::QShaderKey(QShader::Source s,
440 const QShaderVersion &sver,
441 QShader::Variant svar)
442 : m_source(s),
443 m_sourceVersion(sver),
444 m_sourceVariant(svar)
445{
446}
447
448/*!
449 Returns \c true if the two QShader objects \a a and \a b are equal,
450 meaning they are for the same stage with matching sets of shader source or
451 binary code.
452
453 \relates QShader
454 */
455bool operator==(const QShader &lhs, const QShader &rhs) Q_DECL_NOTHROW
456{
457 return lhs.d->stage == rhs.d->stage
458 && lhs.d->shaders == rhs.d->shaders;
459 // do not bother with desc, if the shader code is the same, the description must match too
460}
461
462/*!
463 \fn bool operator!=(const QShader &lhs, const QShader &rhs)
464
465 Returns \c false if the values in the two QShader objects \a a and \a b
466 are equal; otherwise returns \c true.
467
468 \relates QShader
469 */
470
471/*!
472 Returns the hash value for \a s, using \a seed to seed the calculation.
473
474 \relates QShader
475 */
476uint qHash(const QShader &s, uint seed) Q_DECL_NOTHROW
477{
478 uint h = s.stage();
479 for (auto it = s.d->shaders.constBegin(), itEnd = s.d->shaders.constEnd(); it != itEnd; ++it)
480 h += qHash(it.key(), seed) + qHash(it.value().shader(), seed);
481 return h;
482}
483
484/*!
485 Returns \c true if the two QShaderVersion objects \a a and \a b are
486 equal.
487
488 \relates QShaderVersion
489 */
490bool operator==(const QShaderVersion &lhs, const QShaderVersion &rhs) Q_DECL_NOTHROW
491{
492 return lhs.version() == rhs.version() && lhs.flags() == rhs.flags();
493}
494
495/*!
496 \fn bool operator!=(const QShaderVersion &lhs, const QShaderVersion &rhs)
497
498 Returns \c false if the values in the two QShaderVersion objects \a a
499 and \a b are equal; otherwise returns \c true.
500
501 \relates QShaderVersion
502 */
503
504/*!
505 Returns \c true if the two QShaderKey objects \a a and \a b are equal.
506
507 \relates QShaderKey
508 */
509bool operator==(const QShaderKey &lhs, const QShaderKey &rhs) Q_DECL_NOTHROW
510{
511 return lhs.source() == rhs.source() && lhs.sourceVersion() == rhs.sourceVersion()
512 && lhs.sourceVariant() == rhs.sourceVariant();
513}
514
515/*!
516 \fn bool operator!=(const QShaderKey &lhs, const QShaderKey &rhs)
517
518 Returns \c false if the values in the two QShaderKey objects \a a
519 and \a b are equal; otherwise returns \c true.
520
521 \relates QShaderKey
522 */
523
524/*!
525 Returns the hash value for \a k, using \a seed to seed the calculation.
526
527 \relates QShaderKey
528 */
529uint qHash(const QShaderKey &k, uint seed) Q_DECL_NOTHROW
530{
531 return seed + 10 * k.source() + k.sourceVersion().version() + k.sourceVersion().flags() + k.sourceVariant();
532}
533
534/*!
535 Returns \c true if the two QShaderCode objects \a a and \a b are equal.
536
537 \relates QShaderCode
538 */
539bool operator==(const QShaderCode &lhs, const QShaderCode &rhs) Q_DECL_NOTHROW
540{
541 return lhs.shader() == rhs.shader() && lhs.entryPoint() == rhs.entryPoint();
542}
543
544/*!
545 \fn bool operator!=(const QShaderCode &lhs, const QShaderCode &rhs)
546
547 Returns \c false if the values in the two QShaderCode objects \a a
548 and \a b are equal; otherwise returns \c true.
549
550 \relates QShaderCode
551 */
552
553#ifndef QT_NO_DEBUG_STREAM
554QDebug operator<<(QDebug dbg, const QShader &bs)
555{
556 const QShaderPrivate *d = bs.d;
557 QDebugStateSaver saver(dbg);
558
559 dbg.nospace() << "QShader("
560 << "stage=" << d->stage
561 << " shaders=" << d->shaders.keys()
562 << " desc.isValid=" << d->desc.isValid()
563 << ')';
564
565 return dbg;
566}
567
568QDebug operator<<(QDebug dbg, const QShaderKey &k)
569{
570 QDebugStateSaver saver(dbg);
571 dbg.nospace() << "ShaderKey(" << k.source()
572 << " " << k.sourceVersion()
573 << " " << k.sourceVariant() << ")";
574 return dbg;
575}
576
577QDebug operator<<(QDebug dbg, const QShaderVersion &v)
578{
579 QDebugStateSaver saver(dbg);
580 dbg.nospace() << "Version(" << v.version() << " " << v.flags() << ")";
581 return dbg;
582}
583#endif // QT_NO_DEBUG_STREAM
584
585QT_END_NAMESPACE
586