1// Copyright (C) 2016 The Qt Company Ltd.
2// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
3
4#include "qsgmaterial.h"
5#include "qsgrenderer_p.h"
6
7QT_BEGIN_NAMESPACE
8
9Q_DECLARE_LOGGING_CATEGORY(lcQsgLeak)
10
11#ifndef QT_NO_DEBUG
12bool qsg_material_failure = false;
13bool qsg_test_and_clear_material_failure()
14{
15 bool fail = qsg_material_failure;
16 qsg_material_failure = false;
17 return fail;
18}
19
20void qsg_set_material_failure()
21{
22 qsg_material_failure = true;
23}
24#endif
25
26/*!
27 \group qtquick-scenegraph-materials
28 \title Qt Quick Scene Graph Material Classes
29 \brief classes used to define materials in the Qt Quick Scene Graph.
30
31 This page lists the material classes in \l {Qt Quick}'s
32 \l {scene graph}{Qt Quick Scene Graph}.
33 */
34
35#ifndef QT_NO_DEBUG
36static int qt_material_count = 0;
37
38static void qt_print_material_count()
39{
40 qCDebug(lcQsgLeak, "Number of leaked materials: %i", qt_material_count);
41 qt_material_count = -1;
42}
43#endif
44
45/*!
46 \class QSGMaterialType
47 \brief The QSGMaterialType class is used as a unique type token in combination with QSGMaterial.
48 \inmodule QtQuick
49 \ingroup qtquick-scenegraph-materials
50
51 It serves no purpose outside the QSGMaterial::type() function.
52
53 \note All classes with QSG prefix should be used solely on the scene graph's
54 rendering thread. See \l {Scene Graph and Rendering} for more information.
55 */
56
57/*!
58 \class QSGMaterial
59 \brief The QSGMaterial class encapsulates rendering state for a shader program.
60 \inmodule QtQuick
61 \ingroup qtquick-scenegraph-materials
62
63 QSGMaterial and QSGMaterialShader subclasses form a tight relationship. For
64 one scene graph (including nested graphs), there is one unique
65 QSGMaterialShader instance which encapsulates the shaders the scene graph
66 uses to render that material, such as a shader to flat coloring of
67 geometry. Each QSGGeometryNode can have a unique QSGMaterial containing the
68 how the shader should be configured when drawing that node, such as the
69 actual color to used to render the geometry.
70
71 QSGMaterial has two virtual functions that both need to be implemented. The
72 function type() should return a unique instance for all instances of a
73 specific subclass. The createShader() function should return a new instance
74 of QSGMaterialShader, specific to that subclass of QSGMaterial.
75
76 A minimal QSGMaterial implementation could look like this:
77 \code
78 class Material : public QSGMaterial
79 {
80 public:
81 QSGMaterialType *type() const override { static QSGMaterialType type; return &type; }
82 QSGMaterialShader *createShader(QSGRendererInterface::RenderMode) const override { return new Shader; }
83 };
84 \endcode
85
86 See the \l{Scene Graph - Custom Material}{Custom Material example} for an introduction
87 on implementing a QQuickItem subclass backed by a QSGGeometryNode and a custom
88 material.
89
90 \note createShader() is called only once per QSGMaterialType, to reduce
91 redundant work with shader preparation. If a QSGMaterial is backed by
92 multiple sets of vertex and fragment shader combinations, the implementation
93 of type() must return a different, unique QSGMaterialType pointer for each
94 combination of shaders.
95
96 \note All classes with QSG prefix should be used solely on the scene graph's
97 rendering thread. See \l {Scene Graph and Rendering} for more information.
98
99 \sa QSGMaterialShader, {Scene Graph - Custom Material}, {Scene Graph - Two Texture Providers}, {Scene Graph - Graph}
100 */
101
102/*!
103 \internal
104 */
105
106QSGMaterial::QSGMaterial()
107{
108 Q_UNUSED(m_reserved);
109#ifndef QT_NO_DEBUG
110 if (lcQsgLeak().isDebugEnabled()) {
111 ++qt_material_count;
112 static bool atexit_registered = false;
113 if (!atexit_registered) {
114 atexit(func: qt_print_material_count);
115 atexit_registered = true;
116 }
117 }
118#endif
119}
120
121
122/*!
123 \internal
124 */
125
126QSGMaterial::~QSGMaterial()
127{
128#ifndef QT_NO_DEBUG
129 if (lcQsgLeak().isDebugEnabled()) {
130 --qt_material_count;
131 if (qt_material_count < 0)
132 qCDebug(lcQsgLeak, "Material destroyed after qt_print_material_count() was called.");
133 }
134#endif
135}
136
137
138
139/*!
140 \enum QSGMaterial::Flag
141
142 \value Blending Set this flag to true if the material requires blending to be
143 enabled during rendering.
144
145 \value RequiresDeterminant Set this flag to true if the material relies on
146 the determinant of the matrix of the geometry nodes for rendering.
147
148 \value RequiresFullMatrixExceptTranslate Set this flag to true if the material
149 relies on the full matrix of the geometry nodes for rendering, except the translation part.
150
151 \value RequiresFullMatrix Set this flag to true if the material relies on
152 the full matrix of the geometry nodes for rendering.
153
154 \value NoBatching Set this flag to true if the material uses shaders that are
155 incompatible with the \l{Qt Quick Scene Graph Default Renderer}{scene graph's batching
156 mechanism}. This is relevant in certain advanced usages, such as, directly
157 manipulating \c{gl_Position.z} in the vertex shader. Such solutions are often tied to
158 a specific scene structure, and are likely not safe to use with arbitrary contents in
159 a scene. Thus this flag should only be set after appropriate investigation, and will
160 never be needed for the vast majority of materials. Setting this flag can lead to
161 reduced performance due to having to issue more draw calls. This flag was introduced
162 in Qt 6.3.
163
164 \value CustomCompileStep In Qt 6 this flag is identical to NoBatching. Prefer using
165 NoBatching instead.
166
167 \omitvalue MultiView2
168 \omitvalue MultiView3
169 \omitvalue MultiView4
170 */
171
172/*!
173 \fn QSGMaterial::Flags QSGMaterial::flags() const
174
175 Returns the material's flags.
176 */
177
178
179
180/*!
181 Sets the flags \a flags on this material if \a on is true;
182 otherwise clears the attribute.
183*/
184
185void QSGMaterial::setFlag(Flags flags, bool on)
186{
187 if (on)
188 m_flags |= flags;
189 else
190 m_flags &= ~flags;
191}
192
193
194
195/*!
196 Compares this material to \a other and returns 0 if they are equal; -1 if
197 this material should sort before \a other and 1 if \a other should sort
198 before.
199
200 The scene graph can reorder geometry nodes to minimize state changes.
201 The compare function is called during the sorting process so that
202 the materials can be sorted to minimize state changes in each
203 call to QSGMaterialShader::updateState().
204
205 The this pointer and \a other is guaranteed to have the same type().
206 */
207
208int QSGMaterial::compare(const QSGMaterial *other) const
209{
210 Q_ASSERT(other && type() == other->type());
211 const qintptr diff = qintptr(this) - qintptr(other);
212 return diff < 0 ? -1 : (diff > 0 ? 1 : 0);
213}
214
215
216
217/*!
218 \fn QSGMaterialType *QSGMaterial::type() const
219
220 This function is called by the scene graph to query an identifier that is
221 unique to the QSGMaterialShader instantiated by createShader().
222
223 For many materials, the typical approach will be to return a pointer to a
224 static, and so globally available, QSGMaterialType instance. The
225 QSGMaterialType is an opaque object. Its purpose is only to serve as a
226 type-safe, simple way to generate unique material identifiers.
227 \code
228 QSGMaterialType *type() const override
229 {
230 static QSGMaterialType type;
231 return &type;
232 }
233 \endcode
234 */
235
236
237
238/*!
239 \fn QSGMaterialShader *QSGMaterial::createShader(QSGRendererInterface::RenderMode renderMode) const
240
241 This function returns a new instance of a the QSGMaterialShader
242 implementation used to render geometry for a specific implementation
243 of QSGMaterial.
244
245 The function will be called only once for each combination of material type and \a renderMode
246 and will be cached internally.
247
248 For most materials, the \a renderMode can be ignored. A few materials may need
249 custom handling for specific render modes. For instance if the material implements
250 antialiasing in a way that needs to account for perspective transformations when
251 RenderMode3D is in use.
252*/
253
254/*!
255 \return The number of views in case of the material is used in multiview
256 rendering.
257
258 \note The return value is valid only when called from createShader(), and
259 afterwards. The value is not necessarily up-to-date before createShader()
260 is invokved by the scene graph.
261
262 Normally the return value is \c 1. A view count greater than 2 implies a
263 \e{multiview render pass}. Materials that support multiview are expected to
264 query viewCount() in createShader(), or in their QSGMaterialShader
265 constructor, and ensure the appropriate shaders are picked. The vertex
266 shader is then expected to use
267 \c{gl_ViewIndex} to index the modelview-projection matrix array as there
268 are multiple matrices in multiview mode. (one for each view)
269
270 As an example, take the following simple vertex shader:
271
272 \badcode
273 #version 440
274
275 layout(location = 0) in vec4 vertexCoord;
276 layout(location = 1) in vec4 vertexColor;
277
278 layout(location = 0) out vec4 color;
279
280 layout(std140, binding = 0) uniform buf {
281 mat4 matrix[2];
282 float opacity;
283 };
284
285 void main()
286 {
287 gl_Position = matrix[gl_ViewIndex] * vertexCoord;
288 color = vertexColor * opacity;
289 }
290 \endcode
291
292 This shader is prepared to handle 2 views, and 2 views only. It is not
293 compatible with other view counts. When conditioning the shader, the \c qsb
294 tool has to be invoked with \c{--view-count 2} or, if using the CMake
295 integration,
296 \c{VIEW_COUNT 2} must be specified in the \c{qt_add_shaders()} command.
297
298 \note A line with \c{#extension GL_EXT_multiview : require} is injected
299 automatically by \c qsb whenever a view count of 2 or greater is set.
300
301 Developers are encouraged to use the automatically injected preprocessor
302 variable \c{QSHADER_VIEW_COUNT} to simplify the handling of the different
303 number of views. For example, if there is a need to support both
304 non-multiview and multiview with a view count of 2 in the same source file,
305 the following could be done:
306
307 \badcode
308 #version 440
309
310 layout(location = 0) in vec4 vertexCoord;
311 layout(location = 1) in vec4 vertexColor;
312
313 layout(location = 0) out vec4 color;
314
315 layout(std140, binding = 0) uniform buf {
316 #if QSHADER_VIEW_COUNT >= 2
317 mat4 matrix[QSHADER_VIEW_COUNT];
318 #else
319 mat4 matrix;
320 #endif
321 float opacity;
322 };
323
324 void main()
325 {
326 #if QSHADER_VIEW_COUNT >= 2
327 gl_Position = matrix[gl_ViewIndex] * vertexCoord;
328 #else
329 gl_Position = matrix * vertexCoord;
330 #endif
331 color = vertexColor * opacity;
332 }
333 \endcode
334
335 The same source file can now be run through \c qsb or \c{qt_add_shaders()}
336 twice, once without specify the view count, and once with the view count
337 set to 2. The material can then pick the appropriate .qsb file based on
338 viewCount() at run time.
339
340 With CMake, this could looks similar to the following. With this example
341 the corresponding QSGMaterialShader is expected to choose between
342 \c{:/shaders/example.vert.qsb} and \c{:/shaders/multiview/example.vert.qsb}
343 based on the value of viewCount(). (same goes for the fragment shader)
344
345 \badcode
346 qt_add_shaders(application "application_shaders"
347 PREFIX
348 /
349 FILES
350 shaders/example.vert
351 shaders/example.frag
352 )
353
354 qt_add_shaders(application "application_multiview_shaders"
355 GLSL
356 330,300es
357 HLSL
358 61
359 MSL
360 12
361 VIEW_COUNT
362 2
363 PREFIX
364 /
365 FILES
366 shaders/example.vert
367 shaders/example.frag
368 OUTPUTS
369 shaders/multiview/example.vert
370 shaders/multiview/example.frag
371 )
372 \endcode
373
374 \note The fragment shader should be treated the same way the vertex shader
375 is, even though the fragment shader code cannot have any dependency on the
376 view count (\c{gl_ViewIndex}), for maximum portability. There are two
377 reasons for including fragment shaders too in the multiview set. One is that
378 mixing different shader versions within the same graphics pipeline can be
379 problematic, depending on the underlying graphics API: with D3D12 for
380 example, mixing HLSL shaders for shader model 5.0 and 6.1 would generate an
381 error. The other is that having \c QSHADER_VIEW_COUNT defined in fragment
382 shaders can be very useful, for example when sharing a uniform buffer layout
383 between the vertex and fragment stages.
384
385 \note For OpenGL the minimum GLSL version for vertex shaders relying on
386 \c{gl_ViewIndex} is \c 330. Lower versions may be accepted at build time,
387 but may lead to an error at run time, depending on the OpenGL implementation.
388
389 As a convenience, there is also a \c MULTIVIEW option for qt_add_shaders().
390 This first runs the \c qsb tool normally, then overrides \c VIEW_COUNT to
391 \c 2, sets \c GLSL, \c HLSL, \c MSL to some suitable defaults, and runs \c
392 qsb again, this time outputting .qsb files with a suffix added. The material
393 implementation can then use the \l QSGMaterialShader::setShaderFileName()
394 overload taking a \c viewCount argument, that automatically picks the
395 correct .qsb file.
396
397 The following is therefore mostly equivalent to the example call shown
398 above, except that no manually managed output files need to be specified.
399 Note that there can be cases when the automatically chosen shading language
400 versions are not sufficient, in which case applications should continue
401 specify everything explicitly.
402
403 \badcode
404 qt_add_shaders(application "application_multiview_shaders"
405 MULTIVIEW
406 PREFIX
407 /
408 FILES
409 shaders/example.vert
410 shaders/example.frag
411 )
412 \endcode
413
414 See \l QRhi::MultiView, \l QRhiColorAttachment::setMultiViewCount(), and
415 \l QRhiGraphicsPipeline::setMultiViewCount() for further, lower-level details
416 on multiview support in Qt. The Qt Quick scene graph renderer is prepared to
417 recognize multiview render targets, when specified via \l
418 QQuickRenderTarget::fromRhiRenderTarget() or the 3D API specific functions,
419 such as \l{QQuickRenderTarget::}{fromVulkanImage()} with an \c arraySize
420 argument greater than 1. The renderer will then propagate the view count to
421 graphics pipelines and the materials.
422
423 \since 6.8
424 */
425int QSGMaterial::viewCount() const
426{
427 if (m_flags.testFlag(flag: MultiView4))
428 return 4;
429 if (m_flags.testFlag(flag: MultiView3))
430 return 3;
431 if (m_flags.testFlag(flag: MultiView2))
432 return 2;
433 return 1;
434}
435
436QT_END_NAMESPACE
437

Provided by KDAB

Privacy Policy
Start learning QML with our Intro Training
Find out more

source code of qtdeclarative/src/quick/scenegraph/coreapi/qsgmaterial.cpp