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