1 | // Copyright (C) 2019 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 | #include "qsgmaterialshader_p.h" |
7 | #include <QtCore/QFile> |
8 | |
9 | QT_BEGIN_NAMESPACE |
10 | |
11 | /*! |
12 | \class QSGMaterialShader |
13 | \brief The QSGMaterialShader class represents a graphics API independent shader program. |
14 | \inmodule QtQuick |
15 | \ingroup qtquick-scenegraph-materials |
16 | \since 5.14 |
17 | |
18 | QSGMaterialShader represents a combination of vertex and fragment shaders, |
19 | data that define the graphics pipeline state changes, and logic that |
20 | updates graphics resources, such as uniform buffers and textures. |
21 | |
22 | \note All classes with QSG prefix should be used solely on the scene graph's |
23 | rendering thread. See \l {Scene Graph and Rendering} for more information. |
24 | |
25 | The QSGMaterial and QSGMaterialShader form a tight relationship. For one |
26 | scene graph (including nested graphs), there is one unique |
27 | QSGMaterialShader instance that encapsulates the shaders and other data |
28 | the scene graph uses to render an object with that material. Each |
29 | QSGGeometryNode can have a unique QSGMaterial that defines how the graphics |
30 | pipeline must be configured while drawing the node. An instance of |
31 | QSGMaterialShader is never created explicitly by the user, it will be |
32 | created on demand by the scene graph through QSGMaterial::createShader(). |
33 | The scene graph creates an instance of QSGMaterialShader by calling the |
34 | QSGMaterial::createShader() method, ensuring that there is only one |
35 | instance of each shader implementation. |
36 | |
37 | In Qt 5, QSGMaterialShader was tied to OpenGL. It was built directly on |
38 | QOpenGLShaderProgram and had functions like \c updateState() that could |
39 | issue arbitrary OpenGL commands. This is no longer the case in Qt 6. |
40 | QSGMaterialShader is not strictly data-oriented, meaning it provides data |
41 | (shaders and the desired pipeline state changes) together with logic that |
42 | updates data in a uniform buffer. Graphics API access is not provided. This |
43 | means that a QSGMaterialShader cannot make OpenGL, Vulkan, Metal, or Direct |
44 | 3D calls on its own. Together with the unified shader management, this |
45 | allows a QSGMaterialShader to be written once, and be functional with any of |
46 | the supported graphics APIs at run time. |
47 | |
48 | The shaders set by calling the protected setShaderFileName() function |
49 | control what material does with the vertex data from the geometry, and how |
50 | the fragments are shaded. A QSGMaterialShader will typically set a vertex |
51 | and a fragment shader during construction. Changing the shaders afterwards |
52 | may not lead to the desired effect and must be avoided. |
53 | |
54 | In Qt 6, the default approach is to ship \c{.qsb} files with the application, |
55 | typically embedded via the resource system, and referenced when calling |
56 | setShaderFileName(). The \c{.qsb} files are generated offline, or at latest |
57 | at application build time, from Vulkan-style GLSL source code using the \c |
58 | qsb tool from the Qt Shader Tools module. |
59 | |
60 | There are three virtuals that can be overridden. These provide the data, or |
61 | the logic to generate the data, for uniform buffers, textures, and pipeline |
62 | state changes. |
63 | |
64 | updateUniformData() is the function that is most commonly reimplemented in |
65 | subclasses. This function is expected to update the contents of a |
66 | QByteArray that will then be exposed to the shaders as a uniform buffer. |
67 | Any QSGMaterialShader that has a uniform block in its vertex or fragment |
68 | shader must reimplement updateUniformData(). |
69 | |
70 | updateSampledImage() is relevant when the shader code samples textures. The |
71 | function will be invoked for each sampler (or combined image sampler, in |
72 | APIs where relevant), giving it the option to specify which QSGTexture |
73 | should be exposed to the shader. |
74 | |
75 | The shader pipeline state changes are less often used. One use case is |
76 | materials that wish to use a specific blend mode. The relevant function is |
77 | updateGraphicsPipelineState(). This function is not called unless the |
78 | QSGMaterialShader has opted in by setting the flag |
79 | UpdatesGraphicsPipelineState. The task of the function is to update the |
80 | GraphicsPipelineState struct instance that is passed to it with the |
81 | desired changes. Currently only blending and culling-related features are |
82 | available, other states cannot be controlled by materials. |
83 | |
84 | A minimal example, that also includes texture support, could be the |
85 | following. Here we assume that Material is the QSGMaterial that creates an |
86 | instance of Shader in its \l{QSGMaterial::createShader()}{createShader()}, |
87 | and that it holds a QSGTexture we want to sample in the fragment shader. The |
88 | vertex shader relies only on the modelview-projection matrix. |
89 | |
90 | \code |
91 | class Shader : public QSGMaterialShader |
92 | { |
93 | public: |
94 | Shader() |
95 | { |
96 | setShaderFileName(VertexStage, QLatin1String(":/materialshader.vert.qsb")); |
97 | setShaderFileName(FragmentStage, QLatin1String(":/materialshader.frag.qsb")); |
98 | } |
99 | |
100 | bool updateUniformData(RenderState &state, QSGMaterial *, QSGMaterial *) |
101 | { |
102 | bool changed = false; |
103 | QByteArray *buf = state.uniformData(); |
104 | if (state.isMatrixDirty()) { |
105 | const QMatrix4x4 m = state.combinedMatrix(); |
106 | memcpy(buf->data(), m.constData(), 64); |
107 | changed = true; |
108 | } |
109 | return changed; |
110 | } |
111 | |
112 | void updateSampledImage(RenderState &, int binding, QSGTexture **texture, QSGMaterial *newMaterial, QSGMaterial *) |
113 | { |
114 | Material *mat = static_cast<Material *>(newMaterial); |
115 | if (binding == 1) |
116 | *texture = mat->texture(); |
117 | } |
118 | }; |
119 | \endcode |
120 | |
121 | The Vulkan-style GLSL source code for the shaders could look like the |
122 | following. These are expected to be preprocessed offline using the \c qsb |
123 | tool, which generates the \c{.qsb} files referenced in the Shader() |
124 | constructor. |
125 | |
126 | \badcode |
127 | #version 440 |
128 | layout(location = 0) in vec4 aVertex; |
129 | layout(location = 1) in vec2 aTexCoord; |
130 | layout(location = 0) out vec2 vTexCoord; |
131 | layout(std140, binding = 0) uniform buf { |
132 | mat4 qt_Matrix; |
133 | } ubuf; |
134 | out gl_PerVertex { vec4 gl_Position; }; |
135 | void main() { |
136 | gl_Position = ubuf.qt_Matrix * aVertex; |
137 | vTexCoord = aTexCoord; |
138 | } |
139 | \endcode |
140 | |
141 | \badcode |
142 | #version 440 |
143 | layout(location = 0) in vec2 vTexCoord; |
144 | layout(location = 0) out vec4 fragColor; |
145 | layout(binding = 1) uniform sampler2D srcTex; |
146 | void main() { |
147 | vec4 c = texture(srcTex, vTexCoord); |
148 | fragColor = vec4(c.rgb * 0.5, 1.0); |
149 | } |
150 | \endcode |
151 | |
152 | \note All classes with QSG prefix should be used solely on the scene graph's |
153 | rendering thread. See \l {Scene Graph and Rendering} for more information. |
154 | |
155 | \sa QSGMaterial, {Scene Graph - Custom Material}, {Scene Graph - Two Texture Providers}, {Scene Graph - Graph} |
156 | */ |
157 | |
158 | /*! |
159 | \enum QSGMaterialShader::Flag |
160 | Flag values to indicate special material properties. |
161 | |
162 | \value UpdatesGraphicsPipelineState Setting this flag enables calling |
163 | updateGraphicsPipelineState(). |
164 | */ |
165 | |
166 | QShader QSGMaterialShaderPrivate::loadShader(const QString &filename) |
167 | { |
168 | QFile f(filename); |
169 | if (!f.open(flags: QIODevice::ReadOnly)) { |
170 | qWarning() << "Failed to find shader"<< filename; |
171 | return QShader(); |
172 | } |
173 | return QShader::fromSerialized(data: f.readAll()); |
174 | } |
175 | |
176 | void QSGMaterialShaderPrivate::clearCachedRendererData() |
177 | { |
178 | for (int i = 0; i < MAX_SHADER_RESOURCE_BINDINGS; ++i) |
179 | textureBindingTable[i].clear(); |
180 | for (int i = 0; i < MAX_SHADER_RESOURCE_BINDINGS; ++i) |
181 | samplerBindingTable[i].clear(); |
182 | } |
183 | |
184 | static inline QRhiShaderResourceBinding::StageFlags toSrbStage(QShader::Stage stage) |
185 | { |
186 | switch (stage) { |
187 | case QShader::VertexStage: |
188 | return QRhiShaderResourceBinding::VertexStage; |
189 | case QShader::FragmentStage: |
190 | return QRhiShaderResourceBinding::FragmentStage; |
191 | default: |
192 | Q_UNREACHABLE(); |
193 | break; |
194 | } |
195 | return { }; |
196 | } |
197 | |
198 | void QSGMaterialShaderPrivate::prepare(QShader::Variant vertexShaderVariant) |
199 | { |
200 | ubufBinding = -1; |
201 | ubufSize = 0; |
202 | ubufStages = { }; |
203 | memset(s: static_cast<void *>(combinedImageSamplerBindings), c: 0, n: sizeof(combinedImageSamplerBindings)); |
204 | memset(s: static_cast<void *>(combinedImageSamplerCount), c: 0, n: sizeof(combinedImageSamplerCount)); |
205 | vertexShader = fragmentShader = nullptr; |
206 | masterUniformData.clear(); |
207 | |
208 | clearCachedRendererData(); |
209 | |
210 | for (QShader::Stage stage : { QShader::VertexStage, QShader::FragmentStage }) { |
211 | auto it = shaderFileNames.constFind(key: stage); |
212 | if (it != shaderFileNames.cend()) { |
213 | QString fn = *it; |
214 | const QShader s = loadShader(filename: *it); |
215 | if (!s.isValid()) |
216 | continue; |
217 | shaders[stage] = ShaderStageData(s); |
218 | // load only once, subsequent prepare() calls will have it all in shaders already |
219 | shaderFileNames.erase(it); |
220 | } |
221 | } |
222 | |
223 | auto vsIt = shaders.find(key: QShader::VertexStage); |
224 | if (vsIt != shaders.end()) { |
225 | vsIt->shaderVariant = vertexShaderVariant; |
226 | vsIt->vertexInputLocations.clear(); |
227 | vsIt->qt_order_attrib_location = -1; |
228 | |
229 | const QShaderDescription desc = vsIt->shader.description(); |
230 | const QVector<QShaderDescription::InOutVariable> vertexInputs = desc.inputVariables(); |
231 | for (const QShaderDescription::InOutVariable &v : vertexInputs) { |
232 | if (vertexShaderVariant == QShader::BatchableVertexShader |
233 | && v.name == QByteArrayLiteral("_qt_order")) { |
234 | vsIt->qt_order_attrib_location = v.location; |
235 | } else { |
236 | vsIt->vertexInputLocations.append(t: v.location); |
237 | } |
238 | } |
239 | |
240 | if (vsIt->vertexInputLocations.contains(t: vsIt->qt_order_attrib_location)) { |
241 | qWarning(msg: "Vertex input clash in rewritten (batchable) vertex shader at input location %d. " |
242 | "Vertex shaders must avoid using this location.", vsIt->qt_order_attrib_location); |
243 | } |
244 | } |
245 | |
246 | for (auto it = shaders.begin(); it != shaders.end(); ++it) { |
247 | const QShaderDescription desc = it->shader.description(); |
248 | |
249 | const QVector<QShaderDescription::UniformBlock> ubufs = desc.uniformBlocks(); |
250 | const int ubufCount = ubufs.size(); |
251 | if (ubufCount > 1) { |
252 | qWarning(msg: "Multiple uniform blocks found in shader. " |
253 | "This should be avoided as Qt Quick supports only one."); |
254 | } |
255 | for (int i = 0; i < ubufCount; ++i) { |
256 | const QShaderDescription::UniformBlock &ubuf(ubufs[i]); |
257 | if (ubufBinding == -1 && ubuf.binding >= 0) { |
258 | ubufBinding = ubuf.binding; |
259 | ubufSize = ubuf.size; |
260 | ubufStages |= toSrbStage(stage: it->shader.stage()); |
261 | masterUniformData.fill(c: '\0', size: ubufSize); |
262 | } else if (ubufBinding == ubuf.binding && ubuf.binding >= 0) { |
263 | if (ubuf.size > ubufSize) { |
264 | ubufSize = ubuf.size; |
265 | masterUniformData.fill(c: '\0', size: ubufSize); |
266 | } |
267 | ubufStages |= toSrbStage(stage: it->shader.stage()); |
268 | } else { |
269 | qWarning(msg: "Uniform block %s (binding %d) ignored", ubuf.blockName.constData(), |
270 | ubuf.binding); |
271 | } |
272 | } |
273 | |
274 | const QVector<QShaderDescription::InOutVariable> imageSamplers = desc.combinedImageSamplers(); |
275 | const int imageSamplersCount = imageSamplers.size(); |
276 | for (int i = 0; i < imageSamplersCount; ++i) { |
277 | const QShaderDescription::InOutVariable &var(imageSamplers[i]); |
278 | |
279 | if (var.binding < 0) |
280 | continue; |
281 | |
282 | if (var.binding < MAX_SHADER_RESOURCE_BINDINGS) { |
283 | combinedImageSamplerBindings[var.binding] |= toSrbStage(stage: it->shader.stage()); |
284 | |
285 | int count = 1; |
286 | for (int dim : var.arrayDims) |
287 | count *= dim; |
288 | |
289 | combinedImageSamplerCount[var.binding] = count; |
290 | } else { |
291 | qWarning(msg: "Encountered invalid combined image sampler (%s) binding %d", |
292 | var.name.constData(), var.binding); |
293 | } |
294 | } |
295 | |
296 | if (it.key() == QShader::VertexStage) |
297 | vertexShader = &it.value(); |
298 | else if (it.key() == QShader::FragmentStage) |
299 | fragmentShader = &it.value(); |
300 | } |
301 | |
302 | if (vertexShader && vertexShaderVariant == QShader::BatchableVertexShader && vertexShader->qt_order_attrib_location == -1) |
303 | qWarning(msg: "No rewriter-inserted attribute found, this should not happen."); |
304 | } |
305 | |
306 | /*! |
307 | Constructs a new QSGMaterialShader. |
308 | */ |
309 | QSGMaterialShader::QSGMaterialShader() |
310 | : d_ptr(new QSGMaterialShaderPrivate(this)) |
311 | { |
312 | } |
313 | |
314 | /*! |
315 | \internal |
316 | */ |
317 | QSGMaterialShader::QSGMaterialShader(QSGMaterialShaderPrivate &dd) |
318 | : d_ptr(&dd) |
319 | { |
320 | } |
321 | |
322 | /*! |
323 | \internal |
324 | */ |
325 | QSGMaterialShader::~QSGMaterialShader() |
326 | { |
327 | } |
328 | |
329 | // We have our own enum as QShader is not initially public. Internally |
330 | // everything works with QShader::Stage however. So convert. |
331 | static inline QShader::Stage toShaderStage(QSGMaterialShader::Stage stage) |
332 | { |
333 | switch (stage) { |
334 | case QSGMaterialShader::VertexStage: |
335 | return QShader::VertexStage; |
336 | case QSGMaterialShader::FragmentStage: |
337 | return QShader::FragmentStage; |
338 | default: |
339 | Q_UNREACHABLE_RETURN(QShader::VertexStage); |
340 | } |
341 | } |
342 | |
343 | /*! |
344 | Sets the \a shader for the specified \a stage. |
345 | */ |
346 | void QSGMaterialShader::setShader(Stage stage, const QShader &shader) |
347 | { |
348 | Q_D(QSGMaterialShader); |
349 | d->shaders[toShaderStage(stage)] = QSGMaterialShaderPrivate::ShaderStageData(shader); |
350 | } |
351 | |
352 | /*! |
353 | Sets the \a filename for the shader for the specified \a stage. |
354 | |
355 | The file is expected to contain a serialized QShader. |
356 | */ |
357 | void QSGMaterialShader::setShaderFileName(Stage stage, const QString &filename) |
358 | { |
359 | Q_D(QSGMaterialShader); |
360 | d->shaderFileNames[toShaderStage(stage)] = filename; |
361 | } |
362 | |
363 | /*! |
364 | Sets the \a filename for the shader for the specified \a stage. |
365 | |
366 | The file is expected to contain a serialized QShader. |
367 | |
368 | This overload is used when enabling \l{QSGMaterial::viewCount()}{multiview} |
369 | rendering, in particular when the \l{Qt Shader Tools Build System |
370 | Integration}{build system's MULTIVIEW convenience option} is used. |
371 | |
372 | \a viewCount should be 2, 3, or 4. The \a filename is adjusted automatically |
373 | based on this. |
374 | |
375 | \since 6.8 |
376 | */ |
377 | void QSGMaterialShader::setShaderFileName(Stage stage, const QString &filename, int viewCount) |
378 | { |
379 | Q_D(QSGMaterialShader); |
380 | if (viewCount == 2) |
381 | d->shaderFileNames[toShaderStage(stage)] = filename + QStringLiteral(".mv2qsb"); |
382 | else if (viewCount == 3) |
383 | d->shaderFileNames[toShaderStage(stage)] = filename + QStringLiteral(".mv3qsb"); |
384 | else if (viewCount == 4) |
385 | d->shaderFileNames[toShaderStage(stage)] = filename + QStringLiteral(".mv4qsb"); |
386 | else |
387 | d->shaderFileNames[toShaderStage(stage)] = filename; |
388 | } |
389 | |
390 | /*! |
391 | \return the currently set flags for this material shader. |
392 | */ |
393 | QSGMaterialShader::Flags QSGMaterialShader::flags() const |
394 | { |
395 | Q_D(const QSGMaterialShader); |
396 | return d->flags; |
397 | } |
398 | |
399 | /*! |
400 | Sets the \a flags on this material shader if \a on is true; |
401 | otherwise clears the specified flags. |
402 | */ |
403 | void QSGMaterialShader::setFlag(Flags flags, bool on) |
404 | { |
405 | Q_D(QSGMaterialShader); |
406 | if (on) |
407 | d->flags |= flags; |
408 | else |
409 | d->flags &= ~flags; |
410 | } |
411 | |
412 | /*! |
413 | Sets the \a flags for this material shader. |
414 | */ |
415 | void QSGMaterialShader::setFlags(Flags flags) |
416 | { |
417 | Q_D(QSGMaterialShader); |
418 | d->flags = flags; |
419 | } |
420 | |
421 | /*! |
422 | Returns the number of elements in the combined image sampler variable at \a |
423 | binding. This value is introspected from the shader code. The variable may |
424 | be an array, and may have more than one dimension. |
425 | |
426 | The count reflects the total number of combined image sampler items in the |
427 | variable. In the following example, the count for \c{srcA} is 1, \c{srcB} |
428 | is 4, and \c{srcC} is 6. |
429 | |
430 | \badcode |
431 | layout (binding = 0) uniform sampler2D srcA; |
432 | layout (binding = 1) uniform sampler2D srcB[4]; |
433 | layout (binding = 2) uniform sampler2D srcC[2][3]; |
434 | \endcode |
435 | |
436 | This count is the number of QSGTexture pointers in the texture parameter |
437 | of \l{QSGMaterialShader::updateSampledImage}. |
438 | |
439 | \sa QSGMaterialShader::updateSampledImage |
440 | \since 6.4 |
441 | */ |
442 | int QSGMaterialShader::combinedImageSamplerCount(int binding) const |
443 | { |
444 | Q_D(const QSGMaterialShader); |
445 | |
446 | if (binding >= 0 && binding < d->MAX_SHADER_RESOURCE_BINDINGS) |
447 | return d->combinedImageSamplerCount[binding]; |
448 | |
449 | return 0; |
450 | } |
451 | |
452 | /*! |
453 | This function is called by the scene graph to get the contents of the |
454 | shader program's uniform buffer updated. The implementation is not expected |
455 | to perform any real graphics operations, it is merely responsible for |
456 | copying data to the QByteArray returned from RenderState::uniformData(). |
457 | The scene graph takes care of making that buffer visible in the shaders. |
458 | |
459 | The current rendering \a state is passed from the scene graph. If the state |
460 | indicates that any relevant state is dirty, the implementation must update |
461 | the appropriate region in the buffer data that is accessible via |
462 | RenderState::uniformData(). When a state, such as, matrix or opacity, is |
463 | not dirty, there is no need to touch the corresponding region since the |
464 | data is persistent. |
465 | |
466 | The return value must be \c true whenever any change was made to the uniform data. |
467 | |
468 | The subclass specific state, such as the color of a flat color material, |
469 | should be extracted from \a newMaterial to update the relevant regions in |
470 | the buffer accordingly. |
471 | |
472 | \a oldMaterial can be used to minimize buffer changes (which are typically |
473 | memcpy calls) when updating material states. When \a oldMaterial is null, |
474 | this shader was just activated. |
475 | */ |
476 | bool QSGMaterialShader::updateUniformData(RenderState &state, |
477 | QSGMaterial *newMaterial, |
478 | QSGMaterial *oldMaterial) |
479 | { |
480 | Q_UNUSED(state); |
481 | Q_UNUSED(newMaterial); |
482 | Q_UNUSED(oldMaterial); |
483 | return false; |
484 | } |
485 | |
486 | /*! |
487 | This function is called by the scene graph to prepare use of sampled images |
488 | in the shader, typically in the form of combined image samplers. |
489 | |
490 | \a binding is the binding number of the sampler. The function is called for |
491 | each combined image sampler variable in the shader code associated with the |
492 | QSGMaterialShader. |
493 | |
494 | \a{texture} is an array of QSGTexture pointers. The number of elements in |
495 | the array matches the number of elements in the image sampler variable |
496 | specified in the shader code. This variable may be an array, and may have |
497 | more than one dimension. The number of elements in the array may be |
498 | found via \l{QSGMaterialShader::combinedImageSamplerCount} |
499 | |
500 | When an element in \a{texture} is null, it must be set to a valid |
501 | QSGTexture pointer before returning. When non-null, it is up to the |
502 | material to decide if a new \c{QSGTexture *} is stored to it, or if it |
503 | updates some parameters on the already known QSGTexture. The ownership of |
504 | the QSGTexture is not transferred. |
505 | |
506 | The current rendering \a state is passed from the scene graph. Where |
507 | relevant, it is up to the material to trigger enqueuing texture data |
508 | uploads via QSGTexture::commitTextureOperations(). |
509 | |
510 | The subclass specific state can be extracted from \a newMaterial. |
511 | |
512 | \a oldMaterial can be used to minimize changes. When \a oldMaterial is null, |
513 | this shader was just activated. |
514 | |
515 | \sa QSGMaterialShader::combinedImageSamplerCount |
516 | */ |
517 | void QSGMaterialShader::updateSampledImage(RenderState &state, |
518 | int binding, |
519 | QSGTexture **texture, |
520 | QSGMaterial *newMaterial, |
521 | QSGMaterial *oldMaterial) |
522 | { |
523 | Q_UNUSED(state); |
524 | Q_UNUSED(binding); |
525 | Q_UNUSED(texture); |
526 | Q_UNUSED(newMaterial); |
527 | Q_UNUSED(oldMaterial); |
528 | } |
529 | |
530 | /*! |
531 | This function is called by the scene graph to enable the material to |
532 | provide a custom set of graphics state. The set of states that are |
533 | customizable by material is limited to blending and related settings. |
534 | |
535 | \note This function is only called when the UpdatesGraphicsPipelineState |
536 | flag was enabled via setFlags(). By default it is not set, and so this |
537 | function is never called. |
538 | |
539 | The return value must be \c true whenever a change was made to any of the |
540 | members in \a ps. |
541 | |
542 | \note The contents of \a ps is not persistent between invocations of this |
543 | function. |
544 | |
545 | The current rendering \a state is passed from the scene graph. |
546 | |
547 | The subclass specific state can be extracted from \a newMaterial. When \a |
548 | oldMaterial is null, this shader was just activated. |
549 | */ |
550 | bool QSGMaterialShader::updateGraphicsPipelineState(RenderState &state, GraphicsPipelineState *ps, |
551 | QSGMaterial *newMaterial, QSGMaterial *oldMaterial) |
552 | { |
553 | Q_UNUSED(state); |
554 | Q_UNUSED(ps); |
555 | Q_UNUSED(newMaterial); |
556 | Q_UNUSED(oldMaterial); |
557 | return false; |
558 | } |
559 | |
560 | /*! |
561 | \class QSGMaterialShader::RenderState |
562 | |
563 | \brief Encapsulates the current rendering state during a call to |
564 | QSGMaterialShader::updateUniformData() and the other \c update type of |
565 | functions. |
566 | |
567 | \inmodule QtQuick |
568 | \since 5.14 |
569 | |
570 | The render state contains a number of accessors that the shader needs to |
571 | respect in order to conform to the current state of the scene graph. |
572 | */ |
573 | |
574 | /*! |
575 | \enum QSGMaterialShader::RenderState::DirtyState |
576 | |
577 | \value DirtyMatrix Used to indicate that the matrix has changed and must be |
578 | updated. |
579 | |
580 | \value DirtyOpacity Used to indicate that the opacity has changed and must |
581 | be updated. |
582 | |
583 | \value DirtyCachedMaterialData Used to indicate that the cached material |
584 | state has changed and must be updated. |
585 | |
586 | \value DirtyAll Used to indicate that everything needs to be updated. |
587 | */ |
588 | |
589 | /*! |
590 | \fn bool QSGMaterialShader::RenderState::isMatrixDirty() const |
591 | |
592 | Returns \c true if the dirtyStates() contain the dirty matrix state, |
593 | otherwise returns \c false. |
594 | */ |
595 | |
596 | /*! |
597 | \fn bool QSGMaterialShader::RenderState::isOpacityDirty() const |
598 | |
599 | Returns \c true if the dirtyStates() contains the dirty opacity state, |
600 | otherwise returns \c false. |
601 | */ |
602 | |
603 | /*! |
604 | \fn QSGMaterialShader::RenderState::DirtyStates QSGMaterialShader::RenderState::dirtyStates() const |
605 | |
606 | Returns which rendering states that have changed and needs to be updated |
607 | for geometry rendered with this material to conform to the current |
608 | rendering state. |
609 | */ |
610 | |
611 | /*! |
612 | \class QSGMaterialShader::GraphicsPipelineState |
613 | |
614 | \brief Describes state changes that the material wants to apply to the |
615 | currently active graphics pipeline state. |
616 | |
617 | \inmodule QtQuick |
618 | \since 5.14 |
619 | |
620 | Unlike QSGMaterialShader, directly issuing state change commands with the |
621 | underlying graphics API is not possible with QSGMaterialShader. This is |
622 | mainly because the concept of individually changeable states is considered |
623 | deprecated and not supported with modern graphics APIs. |
624 | |
625 | Therefore, it is up to QSGMaterialShader to expose a data structure with |
626 | the set of supported states, which the material can change in its |
627 | updatePipelineState() implementation, if there is one. The scenegraph will |
628 | then internally apply these changes to the active graphics pipeline state, |
629 | then rolling them back as appropriate. |
630 | |
631 | When updateGraphicsPipelineState() is called, the struct has all members |
632 | set to a valid value to reflect the renderer's current state. Not changing |
633 | any values (or not reimplementing the function) indicates that the material |
634 | is fine with the defaults (which are dynamic however, depending on |
635 | QSGMaterial flags, for example). |
636 | */ |
637 | |
638 | /*! |
639 | \enum QSGMaterialShader::GraphicsPipelineState::BlendFactor |
640 | \since 5.14 |
641 | |
642 | \value Zero |
643 | \value One |
644 | \value SrcColor |
645 | \value OneMinusSrcColor |
646 | \value DstColor |
647 | \value OneMinusDstColor |
648 | \value SrcAlpha |
649 | \value OneMinusSrcAlpha |
650 | \value DstAlpha |
651 | \value OneMinusDstAlpha |
652 | \value ConstantColor |
653 | \value OneMinusConstantColor |
654 | \value ConstantAlpha |
655 | \value OneMinusConstantAlpha |
656 | \value SrcAlphaSaturate |
657 | \value Src1Color |
658 | \value OneMinusSrc1Color |
659 | \value Src1Alpha |
660 | \value OneMinusSrc1Alpha |
661 | */ |
662 | |
663 | /*! |
664 | \enum QSGMaterialShader::GraphicsPipelineState::BlendOp |
665 | \since 6.8 |
666 | |
667 | \value Add |
668 | \value Subtract |
669 | \value ReverseSubtract |
670 | \value Min |
671 | \value Max |
672 | */ |
673 | |
674 | /*! |
675 | \enum QSGMaterialShader::GraphicsPipelineState::ColorMaskComponent |
676 | \since 5.14 |
677 | |
678 | \value R |
679 | \value G |
680 | \value B |
681 | \value A |
682 | */ |
683 | |
684 | /*! |
685 | \enum QSGMaterialShader::GraphicsPipelineState::CullMode |
686 | \since 5.14 |
687 | |
688 | \value CullNone |
689 | \value CullFront |
690 | \value CullBack |
691 | */ |
692 | |
693 | /*! |
694 | \enum QSGMaterialShader::GraphicsPipelineState::PolygonMode |
695 | \since 6.4 |
696 | \brief Specifies the polygon rasterization mode |
697 | |
698 | Polygon Mode (Triangle Fill Mode in Metal, Fill Mode in D3D) specifies |
699 | the fill mode used when rasterizing polygons. Polygons may be drawn as |
700 | solids (Fill), or as a wire mesh (Line). |
701 | |
702 | \warning OpenGL ES does not support the \c{Line} polygon mode. OpenGL ES |
703 | will rasterize all polygons as filled no matter what polygon mode is set. |
704 | Using \c{Line} will make your application non-portable. |
705 | |
706 | \value Fill The interior of the polygon is filled (default) |
707 | \value Line Boundary edges of the polygon are drawn as line segments. |
708 | */ |
709 | |
710 | /*! |
711 | \variable QSGMaterialShader::GraphicsPipelineState::blendEnable |
712 | \since 5.14 |
713 | \brief Enables blending. |
714 | |
715 | \note Changing this flag should be done with care, and is best avoided. |
716 | Rather, materials should always use the QSGMaterial::Blend flag to indicate |
717 | that they wish to use blending. Changing this value from false to true for |
718 | a material that did not declare QSGMaterial::Blend can lead to unexpected |
719 | visual results. |
720 | */ |
721 | |
722 | /*! |
723 | \variable QSGMaterialShader::GraphicsPipelineState::srcColor |
724 | \since 5.14 |
725 | \brief Source blending factor, either RGB or RGBA depending on separateBlendFactors. |
726 | */ |
727 | |
728 | /*! |
729 | \variable QSGMaterialShader::GraphicsPipelineState::dstColor |
730 | \since 5.14 |
731 | \brief Destination blending factor, either RGB or RGBA depending on separateBlendFactors. |
732 | */ |
733 | |
734 | /*! |
735 | \variable QSGMaterialShader::GraphicsPipelineState::colorWrite |
736 | \since 5.14 |
737 | \brief Color write mask. |
738 | */ |
739 | |
740 | /*! |
741 | \variable QSGMaterialShader::GraphicsPipelineState::blendConstant |
742 | \since 5.14 |
743 | \brief Blend constant applicable when a blending factor is set to use a constant value. |
744 | */ |
745 | |
746 | /*! |
747 | \variable QSGMaterialShader::GraphicsPipelineState::cullMode |
748 | \since 5.14 |
749 | \brief Cull mode. |
750 | */ |
751 | |
752 | /*! |
753 | \variable QSGMaterialShader::GraphicsPipelineState::polygonMode |
754 | \since 6.4 |
755 | \brief Polygon rasterization mode. |
756 | */ |
757 | |
758 | /*! |
759 | \variable QSGMaterialShader::GraphicsPipelineState::separateBlendFactors |
760 | \since 6.5 |
761 | \brief Indicates that alpha blending factors are specified separately. |
762 | |
763 | False by default, meaning both RGB and alpha blending factors are defined |
764 | by srcColor and dstColor. When set to true, the alpha blending factors are |
765 | taken from srcAlpha and dstAlpha instead, and srcColor and dstColor applies |
766 | only to RGB. |
767 | */ |
768 | |
769 | /*! |
770 | \variable QSGMaterialShader::GraphicsPipelineState::srcAlpha |
771 | \since 6.5 |
772 | \brief Source alpha blending factor. |
773 | |
774 | Applies only when separateBlendFactors is set to true. |
775 | */ |
776 | |
777 | /*! |
778 | \variable QSGMaterialShader::GraphicsPipelineState::dstAlpha |
779 | \since 6.5 |
780 | \brief Destination alpha blending factor. |
781 | |
782 | Applies only when separateBlendFactors is set to true. |
783 | */ |
784 | |
785 | /*! |
786 | \variable QSGMaterialShader::GraphicsPipelineState::opColor |
787 | \since 6.8 |
788 | \brief RGB blending operation. |
789 | */ |
790 | |
791 | /*! |
792 | \variable QSGMaterialShader::GraphicsPipelineState::opAlpha |
793 | \since 6.8 |
794 | \brief Alpha blending operation. |
795 | */ |
796 | |
797 | /*! |
798 | Returns the accumulated opacity to be used for rendering. |
799 | */ |
800 | float QSGMaterialShader::RenderState::opacity() const |
801 | { |
802 | Q_ASSERT(m_data); |
803 | return float(static_cast<const QSGRenderer *>(m_data)->currentOpacity()); |
804 | } |
805 | |
806 | /*! |
807 | Returns the modelview determinant to be used for rendering. |
808 | */ |
809 | float QSGMaterialShader::RenderState::determinant() const |
810 | { |
811 | Q_ASSERT(m_data); |
812 | return float(static_cast<const QSGRenderer *>(m_data)->determinant()); |
813 | } |
814 | |
815 | /*! |
816 | Returns the matrix combined of modelview matrix and project matrix. |
817 | */ |
818 | QMatrix4x4 QSGMaterialShader::RenderState::combinedMatrix() const |
819 | { |
820 | Q_ASSERT(m_data); |
821 | return static_cast<const QSGRenderer *>(m_data)->currentCombinedMatrix(index: 0); |
822 | } |
823 | |
824 | /*! |
825 | \internal |
826 | */ |
827 | QMatrix4x4 QSGMaterialShader::RenderState::combinedMatrix(qsizetype index) const |
828 | { |
829 | Q_ASSERT(m_data); |
830 | return static_cast<const QSGRenderer *>(m_data)->currentCombinedMatrix(index); |
831 | } |
832 | |
833 | /*! |
834 | Returns the ratio between physical pixels and device-independent pixels |
835 | to be used for rendering. |
836 | */ |
837 | float QSGMaterialShader::RenderState::devicePixelRatio() const |
838 | { |
839 | Q_ASSERT(m_data); |
840 | return float(static_cast<const QSGRenderer *>(m_data)->devicePixelRatio()); |
841 | } |
842 | |
843 | /*! |
844 | Returns the model view matrix. |
845 | |
846 | If the material has the RequiresFullMatrix flag set, this is guaranteed to |
847 | be the complete transform matrix calculated from the scenegraph. |
848 | |
849 | However, if this flag is not set, the renderer may choose to alter this |
850 | matrix. For example, it may pre-transform vertices on the CPU and set this |
851 | matrix to identity. |
852 | |
853 | In a situation such as the above, it is still possible to retrieve the |
854 | actual matrix determinant by setting the RequiresDeterminant flag in the |
855 | material and calling the determinant() accessor. |
856 | */ |
857 | QMatrix4x4 QSGMaterialShader::RenderState::modelViewMatrix() const |
858 | { |
859 | Q_ASSERT(m_data); |
860 | return static_cast<const QSGRenderer *>(m_data)->currentModelViewMatrix(); |
861 | } |
862 | |
863 | /*! |
864 | Returns the projection matrix. |
865 | */ |
866 | QMatrix4x4 QSGMaterialShader::RenderState::projectionMatrix() const |
867 | { |
868 | Q_ASSERT(m_data); |
869 | return static_cast<const QSGRenderer *>(m_data)->currentProjectionMatrix(index: 0); |
870 | } |
871 | |
872 | /*! |
873 | \internal |
874 | */ |
875 | QMatrix4x4 QSGMaterialShader::RenderState::projectionMatrix(qsizetype index) const |
876 | { |
877 | Q_ASSERT(m_data); |
878 | return static_cast<const QSGRenderer *>(m_data)->currentProjectionMatrix(index); |
879 | } |
880 | |
881 | /*! |
882 | \internal |
883 | */ |
884 | qsizetype QSGMaterialShader::RenderState::projectionMatrixCount() const |
885 | { |
886 | Q_ASSERT(m_data); |
887 | return static_cast<const QSGRenderer *>(m_data)->projectionMatrixCount(); |
888 | } |
889 | |
890 | /*! |
891 | Returns the viewport rect of the surface being rendered to. |
892 | */ |
893 | QRect QSGMaterialShader::RenderState::viewportRect() const |
894 | { |
895 | Q_ASSERT(m_data); |
896 | return static_cast<const QSGRenderer *>(m_data)->viewportRect(); |
897 | } |
898 | |
899 | /*! |
900 | Returns the device rect of the surface being rendered to |
901 | */ |
902 | QRect QSGMaterialShader::RenderState::deviceRect() const |
903 | { |
904 | Q_ASSERT(m_data); |
905 | return static_cast<const QSGRenderer *>(m_data)->deviceRect(); |
906 | } |
907 | |
908 | /*! |
909 | Returns a pointer to the data for the uniform (constant) buffer in the |
910 | shader. Uniform data must only be updated from |
911 | QSGMaterialShader::updateUniformData(). The return value is null in the |
912 | other reimplementable functions, such as, |
913 | QSGMaterialShader::updateSampledImage(). |
914 | |
915 | \note It is strongly recommended to declare the uniform block with \c |
916 | std140 in the shader, and to carefully study the standard uniform block |
917 | layout as described in section 7.6.2.2 of the OpenGL specification. It is |
918 | up to the QSGMaterialShader implementation to ensure data gets placed |
919 | at the right location in this QByteArray, taking alignment requirements |
920 | into account. Shader code translated to other shading languages is expected |
921 | to use the same offsets for block members, even when the target language |
922 | uses different packing rules by default. |
923 | |
924 | \note Avoid copying from C++ POD types, such as, structs, in order to |
925 | update multiple members at once, unless it has been verified that the |
926 | layouts of the C++ struct and the GLSL uniform block match. |
927 | */ |
928 | QByteArray *QSGMaterialShader::RenderState::uniformData() |
929 | { |
930 | Q_ASSERT(m_data); |
931 | return static_cast<const QSGRenderer *>(m_data)->currentUniformData(); |
932 | } |
933 | |
934 | /*! |
935 | Returns a resource update batch to which upload and copy operatoins can be |
936 | queued. This is typically used by |
937 | QSGMaterialShader::updateSampledImage() to enqueue texture image |
938 | content updates. |
939 | */ |
940 | QRhiResourceUpdateBatch *QSGMaterialShader::RenderState::resourceUpdateBatch() |
941 | { |
942 | Q_ASSERT(m_data); |
943 | return static_cast<const QSGRenderer *>(m_data)->currentResourceUpdateBatch(); |
944 | } |
945 | |
946 | /*! |
947 | Returns the current QRhi. |
948 | */ |
949 | QRhi *QSGMaterialShader::RenderState::rhi() |
950 | { |
951 | Q_ASSERT(m_data); |
952 | return static_cast<const QSGRenderer *>(m_data)->currentRhi(); |
953 | } |
954 | |
955 | QT_END_NAMESPACE |
956 |
Definitions
- loadShader
- clearCachedRendererData
- toSrbStage
- prepare
- QSGMaterialShader
- QSGMaterialShader
- ~QSGMaterialShader
- toShaderStage
- setShader
- setShaderFileName
- setShaderFileName
- flags
- setFlag
- setFlags
- combinedImageSamplerCount
- updateUniformData
- updateSampledImage
- updateGraphicsPipelineState
- opacity
- determinant
- combinedMatrix
- combinedMatrix
- devicePixelRatio
- modelViewMatrix
- projectionMatrix
- projectionMatrix
- projectionMatrixCount
- viewportRect
- deviceRect
- uniformData
- resourceUpdateBatch
Start learning QML with our Intro Training
Find out more