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 | |
7 | QT_BEGIN_NAMESPACE |
8 | |
9 | Q_DECLARE_LOGGING_CATEGORY(lcQsgLeak) |
10 | |
11 | #ifndef QT_NO_DEBUG |
12 | bool qsg_material_failure = false; |
13 | bool qsg_test_and_clear_material_failure() |
14 | { |
15 | bool fail = qsg_material_failure; |
16 | qsg_material_failure = false; |
17 | return fail; |
18 | } |
19 | |
20 | void 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 |
36 | static int qt_material_count = 0; |
37 | |
38 | static 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 | |
106 | QSGMaterial::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 | |
126 | QSGMaterial::~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 | |
185 | void 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 | |
208 | int 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 | */ |
425 | int 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 | |
436 | QT_END_NAMESPACE |
437 | |