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
qquick3dshaderutils.cpp
Go to the documentation of this file.
1// Copyright (C) 2020 The Qt Company Ltd.
2// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
3// Qt-Security score:significant reason:default
4
5
7
8#include <QtCore/qfile.h>
9#include <QtQml/qqmlcontext.h>
10#include <QtQml/qqmlfile.h>
11
16#include <ssg/qssgrenderextensions.h>
17#include <ssg/qquick3dextensionhelpers.h>
18
19#include <QtQuick3DRuntimeRender/private/qssgrenderimage_p.h>
20
21#include <QtCore/QLoggingCategory>
22
24
25Q_LOGGING_CATEGORY(lcSubRenderPass, "qt.quick3d.subrenderpass")
26
27/*!
28 \qmltype Shader
29 \inherits QtObject
30 \inqmlmodule QtQuick3D
31 \brief Container component for defining shader code used by post-processing effects.
32
33 The Shader type is used for populating the \l{Pass::shaders}{shaders} list in the
34 render \l{Pass}{pass} of an \l Effect.
35
36 A shader is code which is executed directly on the graphic hardware at a particular
37 \l{Shader::stage}{stage} of the rendering pipeline.
38
39 \sa Effect
40*/
41/*!
42 \qmlproperty url Shader::shader
43 Specifies the name of the shader source file. For details on how to write shader code,
44 see the \l Effect documentation.
45
46 \warning Shader snippets are assumed to be trusted content. Application
47 developers are advised to carefully consider the potential implications
48 before allowing the loading of user-provided content that is not part of the
49 application.
50*/
51/*!
52 \qmlproperty enumeration Shader::stage
53 Specifies the stage of the rendering pipeline when the shader code will be executed.
54 The default is \c Shader.Fragment
55
56 \value Shader.Vertex The shader is a vertex shader. This code is run once per vertex
57 in the input geometry and can be used to modify it before the geometry is rasterized
58 (scan converted). In the case of effects, the input geometry is always a quad (four
59 vertexes representing the corners of the render target).
60 \value Shader.Fragment The shader is a fragment shader. After vertex processing,
61 the modified geometry is turned into fragments (rasterization). Then a fragment shader
62 is executed for each fragment, assigning a color to it. Fragments are a related concept
63 to pixels, but with additional information attached. Also, as a result of some
64 anti-aliasing strategies, there may be more than one fragment for each pixel in the
65 output.
66*/
67
68/*!
69 \qmltype TextureInput
70 \inherits QtObject
71 \inqmlmodule QtQuick3D
72 \brief Specifies a texture exposed to the shaders of a CustomMaterial or Effect.
73
74 This is a type which can be used for exposing a \l Texture to a shader, either
75 in the \l{Pass}{render pass} of an \l Effect, or in a \l CustomMaterial. It exists
76 primarily to assign a local name to the \l Texture that can be referenced from
77 shaders.
78
79 When a TextureInput property is declared in an \l Effect or a \l CustomMaterial,
80 it will automatically be available as a sampler in all shaders by its property
81 name.
82*/
83/*!
84 \qmlproperty Texture TextureInput::texture
85 The texture for which this TextureInput serves as an indirection.
86*/
87/*!
88 \qmlproperty bool TextureInput::enabled
89 The property determines if this TextureInput is enabled. The default value
90 is true. When disabled, the shaders of the effect sample a dummy, opaque
91 black texture instead of the one specified by \l texture.
92*/
93
94/*!
95 \qmltype Pass
96 \inherits QtObject
97 \inqmlmodule QtQuick3D
98 \brief Defines a render pass in an Effect.
99
100 An \l Effect may consist of multiple render passes. Each render pass has a
101 setup phase where the list of \l{Pass::commands}{render commands} are executed,
102 a \l{Pass::output}{output buffer} and a list of \l{Pass::shaders}{shaders} to
103 use for rendering the effect.
104
105 See the documentation for \l Effect for more details on how to set up multiple
106 rendering passes.
107*/
108/*!
109 \qmlproperty Buffer Pass::output
110 Specifies the output \l {Buffer}{buffer} of the pass.
111*/
112/*!
113 \qmlproperty list Pass::commands
114 Specifies the list of render \l {Command}{commands} of the pass.
115*/
116/*!
117 \qmlproperty list Pass::shaders
118 Specifies the list of \l {Shader}{shaders} of the pass.
119*/
120
121/*!
122 \qmltype Command
123 \inherits QtObject
124 \inqmlmodule QtQuick3D
125 \brief Supertype of commands to be performed as part of a pass in an Effect.
126
127 The Command type should not be instantiated by itself, but only exists as a
128 polymorphic supertype for the different actions that can be performed as part
129 of a \l{Pass}{render pass}.
130
131 \sa BufferInput, SetUniformValue, Effect
132*/
133
134/*!
135 \qmltype BufferInput
136 \inherits Command
137 \inqmlmodule QtQuick3D
138 \brief Defines an input buffer to be used as input for a pass of an Effect.
139
140 BufferInput is a \l Command which can be added to the list of commands in the \l Pass of
141 an \l Effect. When executed, it will expose the buffer as a sample to the shaders
142 in the render pass. The shaders must declare a sampler with the name given in the
143 BufferInput's \c sampler property.
144
145 This can be used for sharing intermediate results between the different passes of an
146 effect.
147
148 \sa TextureInput
149*/
150/*!
151 \qmlproperty Buffer BufferInput::buffer
152 Specifies the \l {Buffer}{buffer} which should be exposed to the shader.
153*/
154/*!
155 \qmlproperty string BufferInput::sampler
156 Specifies the name under which the buffer is exposed to the shader.
157 When this property is not set, the buffer is exposed with the built-in name \c INPUT.
158*/
159
160/*!
161 \qmltype Buffer
162 \inherits QtObject
163 \inqmlmodule QtQuick3D
164 \brief Creates or references a color buffer to be used for a pass of an Effect.
165
166 A Buffer can be used to create intermediate buffers to share data between
167 \l{Pass}{render passes} in an \l Effect.
168
169 \note If the \l name property of the Buffer is empty, it will reference the
170 default output texture of the render pass.
171*/
172/*!
173 \qmlproperty enumeration Buffer::format
174 Specifies the texture format. The default value is Buffer.RGBA8.
175
176 \value Buffer.RGBA8
177 \value Buffer.RGBA16F
178 \value Buffer.RGBA32F
179 \value Buffer.R8
180 \value Buffer.R16
181 \value Buffer.R16F
182 \value Buffer.R32F
183*/
184/*!
185 \qmlproperty enumeration Buffer::textureFilterOperation
186 Specifies the texture filtering mode when sampling the contents of the
187 Buffer. The default value is Buffer.Linear.
188
189 \value Buffer.Nearest Use nearest-neighbor filtering.
190 \value Buffer.Linear Use linear filtering.
191*/
192/*!
193 \qmlproperty enumeration Buffer::textureCoordOperation
194 Specifies the behavior for texture coordinates when sampling outside the [0, 1] range.
195 The default is Buffer.ClampToEdge.
196
197 \value Buffer.ClampToEdge Clamp coordinates to the edges.
198 \value Buffer.Repeat Wrap the coordinates at the edges to tile the texture.
199 \value Buffer.MirroredRepeat Wrap the coordinate at the edges, but mirror the texture
200 when tiling it.
201*/
202/*!
203 \qmlproperty real Buffer::sizeMultiplier
204 Specifies the size multiplier of the buffer. For instance, a value of \c 1.0 creates
205 a buffer with the same size as the effect's input texture while \c 0.5 creates buffer
206 where both width and height is half as big. The default value is 1.0.
207*/
208/*!
209 \qmlproperty enumeration Buffer::bufferFlags
210 Specifies the buffer allocation flags. The default is Buffer.None.
211
212 \value Buffer.None No special behavior.
213 \value Buffer.SceneLifetime The buffer is allocated for the whole lifetime of the scene.
214*/
215/*!
216 \qmlproperty string Buffer::name
217 Specifies the name of the buffer.
218
219 \note When this property is empty, the Buffer will refer to the default output texture
220 of the \l{Pass}{render pass} instead of allocating a buffer. This can be useful to
221 override certain settings of the output, such as the texture format, without introducing
222 a new, separate intermediate texture.
223*/
224
225/*!
226 \qmltype SetUniformValue
227 \inherits Command
228 \inqmlmodule QtQuick3D
229 \brief Defines a value to be set during a single \l {Pass}{pass}.
230 \since 5.15
231
232 SetUniformValue is a \l Command which can be added to the list of commands in a \l Pass. When
233 executed, it will set the uniform given by the \l{SetUniformValue::target}{target} property
234 to \l{SetUniformValue::value}{value}.
235
236 \note The value set by this command is will only be set during the \l {Pass}{pass} it occurs in.
237 For consecutive passes the value will be revert to the initial value of the uniform as it
238 was defined in the \l Effect item.
239
240 \sa BufferInput
241*/
242/*!
243 \qmlproperty string SetUniformValue::target
244 Specifies the name of the uniform that will have its value changed during the \l {Pass}{pass}.
245 This must match the name of an existing property in the \l Effect.
246*/
247/*!
248 \qmlproperty Variant SetUniformValue::value
249 Specifies the value that will be set on the \c target uniform.
250*/
251
252/*!
253 \qmlvaluetype renderTargetBlend
254 \inqmlmodule QtQuick3D
255 \brief Defines blending parameters for a single color attachment of a render pass.
256 \since 6.11
257
258 The renderTargetBlend type is used to specify blending parameters for a single
259 color attachment of a \l RenderPass. An instance of renderTargetBlend can be
260 assigned to one of the \l PipelineStateOverride \c targetBlend0 through \c targetBlend7
261 properties, where the index corresponds to the color attachment slot.
262
263 The color blend equation is:
264 \badcode
265 result.rgb = srcColor * src.rgb opColor dstColor * dst.rgb
266 result.a = srcAlpha * src.a opAlpha dstAlpha * dst.a
267 \endcode
268*/
269
270/*!
271 \qmlproperty bool renderTargetBlend::blendEnabled
272 If set to \c true, enables blending for the color attachment. If set to \c false,
273 disables blending and the output color is written directly to the attachment.
274 The default value is \c false.
275*/
276
277/*!
278 \qmlproperty enumeration renderTargetBlend::colorWrite
279 A bitmask that controls which color channels are written to the color attachment.
280 Multiple values can be combined with the \c | operator.
281 The default value is \c {R | G | B | A}.
282
283 \value RenderTargetBlend.R Write to the red channel.
284 \value RenderTargetBlend.G Write to the green channel.
285 \value RenderTargetBlend.B Write to the blue channel.
286 \value RenderTargetBlend.A Write to the alpha channel.
287*/
288
289/*!
290 \qmlproperty enumeration renderTargetBlend::srcColor
291 Sets the source color blend factor. This value scales the RGB components of the
292 fragment shader output before the blend operation. The default value is \c One.
293
294 \value RenderTargetBlend.Zero Factor is (0, 0, 0).
295 \value RenderTargetBlend.One Factor is (1, 1, 1).
296 \value RenderTargetBlend.SrcColor Factor is the source color (r, g, b).
297 \value RenderTargetBlend.OneMinusSrcColor Factor is (1-r, 1-g, 1-b) of the source.
298 \value RenderTargetBlend.DstColor Factor is the destination color (r, g, b).
299 \value RenderTargetBlend.OneMinusDstColor Factor is (1-r, 1-g, 1-b) of the destination.
300 \value RenderTargetBlend.SrcAlpha Factor is the source alpha (a, a, a).
301 \value RenderTargetBlend.OneMinusSrcAlpha Factor is (1-a, 1-a, 1-a) of the source.
302 \value RenderTargetBlend.DstAlpha Factor is the destination alpha (a, a, a).
303 \value RenderTargetBlend.OneMinusDstAlpha Factor is (1-a, 1-a, 1-a) of the destination.
304 \value RenderTargetBlend.ConstantColor Factor is the constant color set on the pipeline.
305 \value RenderTargetBlend.OneMinusConstantColor Factor is one minus the constant color.
306 \value RenderTargetBlend.ConstantAlpha Factor is the constant alpha.
307 \value RenderTargetBlend.OneMinusConstantAlpha Factor is one minus the constant alpha.
308 \value RenderTargetBlend.SrcAlphaSaturate Factor is min(src.a, 1-dst.a) for RGB, 1 for A.
309 \value RenderTargetBlend.Src1Color Factor is from the second color output of the fragment shader.
310 \value RenderTargetBlend.OneMinusSrc1Color Factor is one minus the second color output.
311 \value RenderTargetBlend.Src1Alpha Factor is the alpha of the second color output.
312 \value RenderTargetBlend.OneMinusSrc1Alpha Factor is one minus the alpha of the second output.
313*/
314
315/*!
316 \qmlproperty enumeration renderTargetBlend::dstColor
317 Sets the destination color blend factor. This value scales the RGB components of the
318 current value in the color attachment before the blend operation.
319 The default value is \c OneMinusSrcAlpha.
320 See \c srcColor for the list of available values.
321*/
322
323/*!
324 \qmlproperty enumeration renderTargetBlend::opColor
325 Sets the arithmetic operation used to combine the scaled source and destination
326 color components. The default value is \c Add.
327
328 \value RenderTargetBlend.Add Result = src + dst.
329 \value RenderTargetBlend.Subtract Result = src - dst.
330 \value RenderTargetBlend.ReverseSubtract Result = dst - src.
331 \value RenderTargetBlend.Min Result = min(src, dst).
332 \value RenderTargetBlend.Max Result = max(src, dst).
333*/
334
335/*!
336 \qmlproperty enumeration renderTargetBlend::srcAlpha
337 Sets the source alpha blend factor. This value scales the alpha component of the
338 fragment shader output before the blend operation. The default value is \c One.
339 See \c srcColor for the list of available values.
340*/
341
342/*!
343 \qmlproperty enumeration renderTargetBlend::dstAlpha
344 Sets the destination alpha blend factor. This value scales the alpha component of the
345 current value in the color attachment before the blend operation.
346 The default value is \c OneMinusSrcAlpha.
347 See \c srcColor for the list of available values.
348*/
349
350/*!
351 \qmlproperty enumeration renderTargetBlend::opAlpha
352 Sets the arithmetic operation used to combine the scaled source and destination
353 alpha components. The default value is \c Add.
354 See \c opColor for the list of available values.
355*/
356
357/*!
358 \qmltype PipelineStateOverride
359 \inherits Command
360 \inqmlmodule QtQuick3D
361 \brief Defines pipeline state overrides for a single \l {RenderPass}{pass}.
362 \since 6.11
363
364 PipelineStateOverride is a \l Command which can be added to the list of commands in a \l RenderPass.
365 When executed, it will override the pipeline state in the render pass according to the properties set on
366 the PipelineStateOverride. Only values that are set will override the existing pipeline state's values.
367 If you want to reset a value that has been overridden to the default, then set the property
368 to \c undefined.
369
370 \sa renderTargetBlend
371*/
372
373/*!
374 \qmltype DepthStencilAttachment
375 \inherits Command
376 \inqmlmodule QtQuick3D
377 \brief Defines a depth-stencil attachment for a RenderPass.
378 \since 6.11
379
380 DepthStencilAttachment adds an implicit depth/stencil render buffer to a
381 \l RenderPass. The render buffer is automatically created and managed by the
382 rendering system. Because it is backed by an opaque render buffer rather
383 than a texture, its contents cannot be sampled in shaders. If you need to
384 read back depth values in a later pass, use \l DepthTextureAttachment instead.
385
386 Use DepthStencilAttachment when you need depth testing and/or stencil
387 operations in a pass but do not need to access the depth data afterwards.
388
389 \qml
390 RenderPass {
391 commands: [
392 ColorAttachment { name: "color0"; target: colorTexture },
393 // Add a depth buffer so depth testing works in this pass
394 DepthStencilAttachment {}
395 ]
396 }
397 \endqml
398
399 \sa DepthTextureAttachment, RenderPassTexture
400*/
401
403
405
410
411QByteArray resolveShader(const QUrl &fileUrl, const QQmlContext *context, QByteArray &shaderPathKey)
412{
414 QByteArray shaderData;
415 if (resolveShaderOverride(fileUrl, context, shaderData, shaderPathKey))
416 return shaderData;
417 }
418
419 if (!shaderPathKey.isEmpty())
420 shaderPathKey.append('>');
421
422 const QUrl loadUrl = context ? context->resolvedUrl(fileUrl) : fileUrl;
423 const QString filePath = QQmlFile::urlToLocalFileOrQrc(loadUrl);
424
425 QFile f(filePath);
426 if (f.open(QIODevice::ReadOnly | QIODevice::Text)) {
427 shaderPathKey += loadUrl.fileName().toUtf8();
428 return f.readAll();
429 } else {
430 qWarning("Failed to read shader code from %s", qPrintable(filePath));
431 }
432
433 return QByteArray();
434}
435
436// These are the QMetaTypes that we convert into uniforms.
454
455template<>
457{
459 static QByteArray name() { return QByteArrayLiteral("float"); }
460};
461
462template<>
464{
466 static QByteArray name() { return QByteArrayLiteral("bool"); }
467};
468
469template<>
471{
473 static QByteArray name() { return QByteArrayLiteral("int"); }
474};
475
476template<>
478{
480 static QByteArray name() { return QByteArrayLiteral("vec2"); }
481};
482
483template<>
485{
487 static QByteArray name() { return QByteArrayLiteral("vec3"); }
488};
489
490template<>
492{
494 static QByteArray name() { return QByteArrayLiteral("vec4"); }
495};
496
497template<>
499{
501 static QByteArray name() { return QByteArrayLiteral("vec4"); }
502};
503
504template<>
506{
508 static QByteArray name() { return QByteArrayLiteral("vec2"); }
509};
510
511template<>
513{
515 static QByteArray name() { return QByteArrayLiteral("vec2"); }
516};
517
518template<>
520{
522 static QByteArray name() { return QByteArrayLiteral("vec2"); }
523};
524
525template<>
527{
529 static QByteArray name() { return QByteArrayLiteral("vec2"); }
530};
531
532template<>
534{
536 static QByteArray name() { return QByteArrayLiteral("vec4"); }
537};
538
539template<>
541{
543 static QByteArray name() { return QByteArrayLiteral("vec4"); }
544};
545
546template<>
548{
550 static QByteArray name() { return QByteArrayLiteral("vec4"); }
551};
552
553template<>
555{
557 static QByteArray name() { return QByteArrayLiteral("mat4"); }
558};
559
561{
562 switch (type.id()) {
563 case QMetaType::Double:
564 case QMetaType::Float:
565 return ShaderType<QMetaType::Double>::name();
566 case QMetaType::Bool:
567 return ShaderType<QMetaType::Bool>::name();
568 case QMetaType::QVector2D:
569 return ShaderType<QMetaType::QVector2D>::name();
570 case QMetaType::QVector3D:
571 return ShaderType<QMetaType::QVector3D>::name();
572 case QMetaType::QVector4D:
573 return ShaderType<QMetaType::QVector4D>::name();
574 case QMetaType::Int:
575 return ShaderType<QMetaType::Int>::name();
576 case QMetaType::QColor:
577 return ShaderType<QMetaType::QColor>::name();
578 case QMetaType::QSize:
579 return ShaderType<QMetaType::QSize>::name();
580 case QMetaType::QSizeF:
581 return ShaderType<QMetaType::QSizeF>::name();
582 case QMetaType::QPoint:
583 return ShaderType<QMetaType::QPoint>::name();
584 case QMetaType::QPointF:
585 return ShaderType<QMetaType::QPointF>::name();
586 case QMetaType::QRect:
587 return ShaderType<QMetaType::QRect>::name();
588 case QMetaType::QRectF:
589 return ShaderType<QMetaType::QRectF>::name();
590 case QMetaType::QQuaternion:
591 return ShaderType<QMetaType::QQuaternion>::name();
592 case QMetaType::QMatrix4x4:
593 return ShaderType<QMetaType::QMatrix4x4>::name();
594 default:
595 return QByteArray();
596 }
597}
598
599QByteArray uniformTypeName(QSSGRenderShaderValue::Type type)
600{
601 switch (type) {
602 case QSSGRenderShaderValue::Float:
603 return ShaderType<QMetaType::Double>::name();
604 case QSSGRenderShaderValue::Boolean:
605 return ShaderType<QMetaType::Bool>::name();
606 case QSSGRenderShaderValue::Integer:
607 return ShaderType<QMetaType::Int>::name();
608 case QSSGRenderShaderValue::Vec2:
609 return ShaderType<QMetaType::QVector2D>::name();
610 case QSSGRenderShaderValue::Vec3:
611 return ShaderType<QMetaType::QVector3D>::name();
612 case QSSGRenderShaderValue::Vec4:
613 return ShaderType<QMetaType::QVector4D>::name();
614 case QSSGRenderShaderValue::Rgba:
615 return ShaderType<QMetaType::QColor>::name();
616 case QSSGRenderShaderValue::Size:
617 return ShaderType<QMetaType::QSize>::name();
618 case QSSGRenderShaderValue::SizeF:
619 return ShaderType<QMetaType::QSizeF>::name();
620 case QSSGRenderShaderValue::Point:
621 return ShaderType<QMetaType::QPoint>::name();
622 case QSSGRenderShaderValue::PointF:
623 return ShaderType<QMetaType::QPointF>::name();
624 case QSSGRenderShaderValue::Rect:
625 return ShaderType<QMetaType::QRect>::name();
626 case QSSGRenderShaderValue::RectF:
627 return ShaderType<QMetaType::QRectF>::name();
628 case QSSGRenderShaderValue::Quaternion:
629 return ShaderType<QMetaType::QQuaternion>::name();
630 case QSSGRenderShaderValue::Matrix4x4:
631 return ShaderType<QMetaType::QMatrix4x4>::name();
632 default:
633 return QByteArray();
634 }
635}
636
638{
639 switch (type.id()) {
640 case QMetaType::Double:
641 case QMetaType::Float:
642 return ShaderType<QMetaType::Double>::type();
643 case QMetaType::Bool:
644 return ShaderType<QMetaType::Bool>::type();
645 case QMetaType::QVector2D:
646 return ShaderType<QMetaType::QVector2D>::type();
647 case QMetaType::QVector3D:
648 return ShaderType<QMetaType::QVector3D>::type();
649 case QMetaType::QVector4D:
650 return ShaderType<QMetaType::QVector4D>::type();
651 case QMetaType::Int:
652 return ShaderType<QMetaType::Int>::type();
653 case QMetaType::QColor:
654 return ShaderType<QMetaType::QColor>::type();
655 case QMetaType::QSize:
656 return ShaderType<QMetaType::QSize>::type();
657 case QMetaType::QSizeF:
658 return ShaderType<QMetaType::QSizeF>::type();
659 case QMetaType::QPoint:
660 return ShaderType<QMetaType::QPoint>::type();
661 case QMetaType::QPointF:
662 return ShaderType<QMetaType::QPointF>::type();
663 case QMetaType::QRect:
664 return ShaderType<QMetaType::QRect>::type();
665 case QMetaType::QRectF:
666 return ShaderType<QMetaType::QRectF>::type();
667 case QMetaType::QQuaternion:
668 return ShaderType<QMetaType::QQuaternion>::type();
669 case QMetaType::QMatrix4x4:
670 return ShaderType<QMetaType::QMatrix4x4>::type();
671 default:
672 return QSSGRenderShaderValue::Unknown;
673 }
674}
675
677{
678 return {std::begin(qssg_metatype_list), std::end(qssg_metatype_list)};
679}
680
681}
682
683QQuick3DShaderUtilsBuffer::TextureFormat QQuick3DShaderUtilsBuffer::mapRenderTextureFormat(QSSGRenderTextureFormat::Format fmt)
684{
685 using TextureFormat = QQuick3DShaderUtilsBuffer::TextureFormat;
686 switch (fmt) {
687 case QSSGRenderTextureFormat::RGBA8: return TextureFormat::RGBA8;
688 case QSSGRenderTextureFormat::RGBA16F: return TextureFormat::RGBA16F;
689 case QSSGRenderTextureFormat::RGBA32F: return TextureFormat::RGBA32F;
690 case QSSGRenderTextureFormat::R8: return TextureFormat::R8;
691 case QSSGRenderTextureFormat::R16: return TextureFormat::R16;
692 case QSSGRenderTextureFormat::R16F: return TextureFormat::R16F;
693 case QSSGRenderTextureFormat::R32F: return TextureFormat::R32F;
694 default:
695 break;
696 }
697 return TextureFormat::Unknown;
698}
699
700QSSGRenderTextureFormat::Format QQuick3DShaderUtilsBuffer::mapTextureFormat(QQuick3DShaderUtilsBuffer::TextureFormat fmt)
701{
702 using TextureFormat = QQuick3DShaderUtilsBuffer::TextureFormat;
703 switch (fmt) {
704 case TextureFormat::RGBA8: return QSSGRenderTextureFormat::RGBA8;
705 case TextureFormat::RGBA16F: return QSSGRenderTextureFormat::RGBA16F;
706 case TextureFormat::RGBA32F: return QSSGRenderTextureFormat::RGBA32F;
707 case TextureFormat::R8: return QSSGRenderTextureFormat::R8;
708 case TextureFormat::R16: return QSSGRenderTextureFormat::R16;
709 case TextureFormat::R16F: return QSSGRenderTextureFormat::R16F;
710 case TextureFormat::R32F: return QSSGRenderTextureFormat::R32F;
711 default:
712 break;
713 }
714 return QSSGRenderTextureFormat::Unknown;
715}
716
717QQuick3DShaderUtilsBuffer::TextureFormat QQuick3DShaderUtilsBuffer::format() const
718{
719 return mapRenderTextureFormat(command.m_format.format);
720}
721
722void QQuick3DShaderUtilsBuffer::setFormat(TextureFormat format)
723{
724 QSSGRenderTextureFormat::Format mappedTextureFormat = mapTextureFormat(format);
725 if (command.m_format == mappedTextureFormat)
726 return;
727
728 command.m_format = mappedTextureFormat;
729 emit changed();
730}
731
732void QQuick3DShaderUtilsBuffer::setTextureFilterOperation(TextureFilterOperation op)
733{
734 if (command.m_filterOp == QSSGRenderTextureFilterOp(op))
735 return;
736
737 command.m_filterOp = QSSGRenderTextureFilterOp(op);
738 emit changed();
739}
740
741void QQuick3DShaderUtilsBuffer::setTextureCoordOperation(TextureCoordOperation texCoordOp)
742{
743 if (command.m_texCoordOp == QSSGRenderTextureCoordOp(texCoordOp))
744 return;
745
746 command.m_texCoordOp = QSSGRenderTextureCoordOp(texCoordOp);
747 emit changed();
748}
749
750void QQuick3DShaderUtilsBuffer::setBufferFlags(AllocateBufferFlagValues flag)
751{
752 if (quint32(command.m_bufferFlags) == quint32(flag))
753 return;
754
755 command.m_bufferFlags = quint32(flag);
756 emit changed();
757}
758
759QQuick3DShaderUtilsRenderPass::~QQuick3DShaderUtilsRenderPass()
760{
761
762}
763
764void QQuick3DShaderUtilsRenderPass::qmlAppendCommand(QQmlListProperty<QQuick3DShaderUtilsRenderCommand> *list,
765 QQuick3DShaderUtilsRenderCommand *command)
766{
767 if (!command)
768 return;
769
770 QQuick3DShaderUtilsRenderPass *that = qobject_cast<QQuick3DShaderUtilsRenderPass *>(list->object);
771 that->m_commands.push_back(command);
772 emit that->changed();
773}
774
775QQuick3DShaderUtilsRenderCommand *QQuick3DShaderUtilsRenderPass::qmlCommandAt(QQmlListProperty<QQuick3DShaderUtilsRenderCommand> *list,
776 qsizetype index)
777{
778 QQuick3DShaderUtilsRenderPass *that = qobject_cast<QQuick3DShaderUtilsRenderPass *>(list->object);
779 return that->m_commands.at(index);
780}
781
782qsizetype QQuick3DShaderUtilsRenderPass::qmlCommandCount(QQmlListProperty<QQuick3DShaderUtilsRenderCommand> *list)
783{
784 QQuick3DShaderUtilsRenderPass *that = qobject_cast<QQuick3DShaderUtilsRenderPass *>(list->object);
785 return that->m_commands.size();
786}
787
788void QQuick3DShaderUtilsRenderPass::qmlCommandClear(QQmlListProperty<QQuick3DShaderUtilsRenderCommand> *list)
789{
790 QQuick3DShaderUtilsRenderPass *that = qobject_cast<QQuick3DShaderUtilsRenderPass *>(list->object);
791 that->m_commands.clear();
792 emit that->changed();
793}
794
795QQmlListProperty<QQuick3DShaderUtilsRenderCommand> QQuick3DShaderUtilsRenderPass::commands()
796{
797 return QQmlListProperty<QQuick3DShaderUtilsRenderCommand>(this,
798 nullptr,
799 QQuick3DShaderUtilsRenderPass::qmlAppendCommand,
800 QQuick3DShaderUtilsRenderPass::qmlCommandCount,
801 QQuick3DShaderUtilsRenderPass::qmlCommandAt,
802 QQuick3DShaderUtilsRenderPass::qmlCommandClear);
803}
804
805void QQuick3DShaderUtilsRenderPass::qmlAppendShader(QQmlListProperty<QQuick3DShaderUtilsShader> *list,
806 QQuick3DShaderUtilsShader *shader)
807{
808 if (!shader)
809 return;
810
811 QQuick3DShaderUtilsRenderPass *that = qobject_cast<QQuick3DShaderUtilsRenderPass *>(list->object);
812
813 // An append implementation CANNOT rely on the object (shader in this case)
814 // being complete. When the list references a Shader object living under
815 // another Effect, its properties may not be set at the point of this
816 // function being called, so accessing shader->stage is not allowed since
817 // it may still have its default value, not what is set from QML...
818
819 // the only thing we can do is to append to our list, do not try to be clever
820 that->m_shaders.append(shader);
821
822 connect(shader, &QQuick3DShaderUtilsShader::shaderChanged, that, &QQuick3DShaderUtilsRenderPass::changed);
823 connect(shader, &QQuick3DShaderUtilsShader::stageChanged, that, &QQuick3DShaderUtilsRenderPass::changed);
824
825 emit that->changed();
826}
827
828QQuick3DShaderUtilsShader *QQuick3DShaderUtilsRenderPass::qmlShaderAt(QQmlListProperty<QQuick3DShaderUtilsShader> *list,
829 qsizetype index)
830{
831 QQuick3DShaderUtilsRenderPass *that = qobject_cast<QQuick3DShaderUtilsRenderPass *>(list->object);
832 return that->m_shaders.at(index);
833}
834
835qsizetype QQuick3DShaderUtilsRenderPass::qmlShaderCount(QQmlListProperty<QQuick3DShaderUtilsShader> *list)
836{
837 QQuick3DShaderUtilsRenderPass *that = qobject_cast<QQuick3DShaderUtilsRenderPass *>(list->object);
838 return that->m_shaders.size();
839}
840
841void QQuick3DShaderUtilsRenderPass::qmlShaderClear(QQmlListProperty<QQuick3DShaderUtilsShader> *list)
842{
843 QQuick3DShaderUtilsRenderPass *that = qobject_cast<QQuick3DShaderUtilsRenderPass *>(list->object);
844
845 for (QQuick3DShaderUtilsShader *shader : that->m_shaders)
846 shader->disconnect(that);
847
848 that->m_shaders.clear();
849
850 emit that->changed();
851}
852
853QQmlListProperty<QQuick3DShaderUtilsShader> QQuick3DShaderUtilsRenderPass::shaders()
854{
855 return QQmlListProperty<QQuick3DShaderUtilsShader>(this,
856 nullptr,
857 QQuick3DShaderUtilsRenderPass::qmlAppendShader,
858 QQuick3DShaderUtilsRenderPass::qmlShaderCount,
859 QQuick3DShaderUtilsRenderPass::qmlShaderAt,
860 QQuick3DShaderUtilsRenderPass::qmlShaderClear);
861}
862
863QQuick3DShaderUtilsTextureInput::QQuick3DShaderUtilsTextureInput(QQuick3DObject *p) : QQuick3DObject(p) {}
864
865QQuick3DShaderUtilsTextureInput::~QQuick3DShaderUtilsTextureInput()
866{
867}
868
869void QQuick3DShaderUtilsTextureInput::setTexture(QQuick3DTexture *texture)
870{
871 if (m_texture == texture)
872 return;
873
874 QObject *p = parent();
875 while (p != nullptr) {
876 if (QQuick3DCustomMaterial *mat = qobject_cast<QQuick3DCustomMaterial *>(p)) {
877 mat->setDynamicTextureMap(this);
878 QQuick3DObjectPrivate::attachWatcherPriv(mat, this, &QQuick3DShaderUtilsTextureInput::setTexture, texture, m_texture);
879 break;
880 } else if (QQuick3DEffect *efx = qobject_cast<QQuick3DEffect *>(p)) {
881 efx->setDynamicTextureMap(this);
882 QQuick3DObjectPrivate::attachWatcherPriv(efx, this, &QQuick3DShaderUtilsTextureInput::setTexture, texture, m_texture);
883 break;
884 }
885 p = p->parent();
886 }
887
888 if (p == nullptr) {
889 qWarning("A TextureInput was defined without a CustomMaterial or Effect ancestor. This should be avoided.");
890 }
891
892 m_texture = texture;
893 Q_EMIT textureChanged();
894}
895
896/*!
897 \qmltype RenderablesFilter
898 \inherits Command
899 \inqmlmodule QtQuick3D
900 \brief Defines a filter for selecting which renderables to affect in a \l {RenderPass}{pass}.
901 \since 6.11
902
903 The RenderablesFilter type is used to specify which renderables in the scene
904 should be affected by a \l RenderPass. By setting the \c renderableTypes property,
905 you can control whether the pass affects opaque objects, transparent objects, or
906 no objects at all.
907
908 Setting \c renderableTypes to \c None is useful when a RenderPass acts as a container
909 for \l SubRenderPass commands and should not render any scene objects itself.
910
911 In addition to filtering by renderable types, you can use the \l {RenderablesFilter::layerMask}{layerMask}
912 to further refine which renderables are affected based on their assigned \l{Node::layers}{layers}.
913
914 \qml
915 // A pass that renders only opaque scene objects
916 RenderPass {
917 id: opaquePass
918 commands: [
919 ColorAttachment { name: "color0"; target: opaqueColorTexture },
920 DepthTextureAttachment { target: depthTexture },
921 RenderablesFilter {
922 renderableTypes: RenderablesFilter.Opaque
923 }
924 ]
925 }
926
927 // A pass that renders only transparent objects on top
928 RenderPass {
929 id: transparentPass
930 commands: [
931 ColorAttachment { name: "color0"; target: transparentColorTexture },
932 RenderablesFilter {
933 renderableTypes: RenderablesFilter.Transparent
934 }
935 ]
936 }
937 \endqml
938*/
939
940/*!
941 \qmlproperty int RenderablesFilter::layerMask
942 Sets the layer mask for the filter. Only renderables on the specified layers will be affected by the filter.
943
944 \sa Node::layers
945*/
946
947/*!
948 \qmlproperty enumeration RenderablesFilter::renderableTypes
949 Sets the types of renderables that the filter will affect.
950
951 \value RenderablesFilter.None No renderables will be rendered. Useful for container passes that only have SubRenderPasses.
952 \value RenderablesFilter.Opaque Only opaque renderables will be rendered.
953 \value RenderablesFilter.Transparent Only transparent renderables will be rendered.
954
955 \note Multiple values can be combined using the | operator.
956
957 \default RenderablesFilter.Opaque | RenderablesFilter.Transparent
958*/
959QQuick3DShaderUtilsRenderablesFilter::RenderableTypes QQuick3DShaderUtilsRenderablesFilter::renderableTypes() const
960{
961 return static_cast<QQuick3DShaderUtilsRenderablesFilter::RenderableTypes>(command.renderableTypes);
962}
963
964QQuick3DShaderUtilsRenderablesFilter::~QQuick3DShaderUtilsRenderablesFilter()
965{
966
967}
968
969void QQuick3DShaderUtilsRenderablesFilter::setRenderableTypes(RenderableTypes types)
970{
971 command.renderableTypes = static_cast<QSSGRenderablesFilterCommand::RenderableTypeT>(types.toInt());
972}
973
974QQuick3DShaderUtilsPipelineStateOverride::~QQuick3DShaderUtilsPipelineStateOverride()
975{
976
977}
978
979/*!
980 \qmlproperty bool PipelineStateOverride::depthTestEnabled
981 If set to \c true, enables depth testing for the render pass. If set to \c false, disables depth testing.
982 Setting this property to \c true requires a depth attachment for the render pass.
983*/
984bool QQuick3DShaderUtilsPipelineStateOverride::depthTestEnabled() const
985{
986 if (command.m_depthTestEnabled)
987 return *command.m_depthTestEnabled;
988 return false;
989}
990
991void QQuick3DShaderUtilsPipelineStateOverride::setDepthTestEnabled(bool newDepthTestEnabled)
992{
993 if (command.m_depthTestEnabled && *command.m_depthTestEnabled == newDepthTestEnabled)
994 return;
995 command.m_depthTestEnabled = newDepthTestEnabled;
996 emit depthTestEnabledChanged();
997}
998
999void QQuick3DShaderUtilsPipelineStateOverride::resetDepthTestEnabled()
1000{
1001 command.m_depthTestEnabled.reset();
1002 emit depthTestEnabledChanged();
1003}
1004
1005/*!
1006 \qmlproperty bool PipelineStateOverride::depthWriteEnabled
1007 If set to \c true, enables depth writing for the render pass. If set to \c false, disables depth writing.
1008 Setting this property to \c true requires a depth attachment for the render pass.
1009*/
1010bool QQuick3DShaderUtilsPipelineStateOverride::depthWriteEnabled() const
1011{
1012 if (command.m_depthWriteEnabled)
1013 return *command.m_depthWriteEnabled;
1014 return false;
1015}
1016
1017void QQuick3DShaderUtilsPipelineStateOverride::setDepthWriteEnabled(bool newDepthWriteEnabled)
1018{
1019 if (command.m_depthWriteEnabled && *command.m_depthWriteEnabled == newDepthWriteEnabled)
1020 return;
1021 command.m_depthWriteEnabled = newDepthWriteEnabled;
1022 emit depthWriteEnabledChanged();
1023}
1024
1025void QQuick3DShaderUtilsPipelineStateOverride::resetDepthWriteEnabled()
1026{
1027 command.m_depthWriteEnabled.reset();
1028 emit depthWriteEnabledChanged();
1029}
1030
1031/*!
1032 \qmlproperty bool PipelineStateOverride::blendEnabled
1033 If set to \c true, enables blending for the render pass. If set to \c false, disables blending.
1034 For per-attachment blend settings, use the \c targetBlend0 through \c targetBlend7 properties.
1035*/
1036bool QQuick3DShaderUtilsPipelineStateOverride::blendEnabled() const
1037{
1038 if (command.m_blendEnabled)
1039 return *command.m_blendEnabled;
1040 return false;
1041}
1042
1043void QQuick3DShaderUtilsPipelineStateOverride::setBlendEnabled(bool newBlendEnabled)
1044{
1045 if (command.m_blendEnabled && *command.m_blendEnabled == newBlendEnabled)
1046 return;
1047 command.m_blendEnabled = newBlendEnabled;
1048 emit blendEnabledChanged();
1049}
1050
1051void QQuick3DShaderUtilsPipelineStateOverride::resetBlendEnabled()
1052{
1053 command.m_blendEnabled.reset();
1054 emit blendEnabledChanged();
1055}
1056
1057/*!
1058 \qmlproperty bool PipelineStateOverride::usesStencilReference
1059 If set to \c true, enables the use of the stencil reference value for the render pass. If set to \c false,
1060 disables the use of the stencil reference value.
1061*/
1062bool QQuick3DShaderUtilsPipelineStateOverride::usesStencilReference() const
1063{
1064 if (command.m_usesStencilReference)
1065 return *command.m_usesStencilReference;
1066 return false;
1067}
1068
1069void QQuick3DShaderUtilsPipelineStateOverride::setUsesStencilReference(bool newUsesStencilReference)
1070{
1071 if (command.m_usesStencilReference && *command.m_usesStencilReference == newUsesStencilReference)
1072 return;
1073 command.m_usesStencilReference = newUsesStencilReference;
1074 emit usesStencilReferenceChanged();
1075}
1076
1077void QQuick3DShaderUtilsPipelineStateOverride::resetUsesStencilReference()
1078{
1079 command.m_usesStencilReference.reset();
1080 emit usesStencilReferenceChanged();
1081}
1082
1083/*!
1084 \qmlproperty bool PipelineStateOverride::usesScissor
1085 If set to \c true, enables scissor testing for the render pass. If set to \c false,
1086 disables the scissor test. Use the \c scissor property to set the scissor rectangle.
1087*/
1088bool QQuick3DShaderUtilsPipelineStateOverride::usesScissor() const
1089{
1090 if (command.m_usesScissor)
1091 return *command.m_usesScissor;
1092 return false;
1093}
1094
1095void QQuick3DShaderUtilsPipelineStateOverride::setUsesScissor(bool newUsesScissor)
1096{
1097 if (command.m_usesScissor && *command.m_usesScissor == newUsesScissor)
1098 return;
1099 command.m_usesScissor = newUsesScissor;
1100 emit usesScissorChanged();
1101}
1102
1103void QQuick3DShaderUtilsPipelineStateOverride::resetUsesScissor()
1104{
1105 command.m_usesScissor.reset();
1106 emit usesScissorChanged();
1107}
1108
1109/*!
1110 \qmlproperty enumeration PipelineStateOverride::depthFunction
1111 Sets the depth comparison function for the render pass.
1112
1113 \value PipelineStateOverride.Never The depth test never passes.
1114 \value PipelineStateOverride.Less The depth test passes when the incoming depth is less than the stored depth.
1115 \value PipelineStateOverride.Equal The depth test passes when the incoming depth equals the stored depth.
1116 \value PipelineStateOverride.LessOrEqual The depth test passes when the incoming depth is less than or equal to the stored depth.
1117 \value PipelineStateOverride.Greater The depth test passes when the incoming depth is greater than the stored depth.
1118 \value PipelineStateOverride.NotEqual The depth test passes when the incoming depth does not equal the stored depth.
1119 \value PipelineStateOverride.GreaterOrEqual The depth test passes when the incoming depth is greater than or equal to the stored depth.
1120 \value PipelineStateOverride.Always The depth test always passes.
1121*/
1122QQuick3DShaderUtilsPipelineStateOverride::CompareOperation QQuick3DShaderUtilsPipelineStateOverride::depthFunction() const
1123{
1124 if (command.m_depthFunction)
1125 return CompareOperation(*command.m_depthFunction);
1126 return CompareOperation::Less;
1127}
1128
1129void QQuick3DShaderUtilsPipelineStateOverride::setDepthFunction(CompareOperation newDepthFunction)
1130{
1131 if (command.m_depthFunction && *command.m_depthFunction == QRhiGraphicsPipeline::CompareOp(newDepthFunction))
1132 return;
1133 command.m_depthFunction = QRhiGraphicsPipeline::CompareOp(newDepthFunction);
1134 emit depthFunctionChanged();
1135}
1136
1137void QQuick3DShaderUtilsPipelineStateOverride::resetDepthFunction()
1138{
1139 command.m_depthFunction.reset();
1140 emit depthFunctionChanged();
1141}
1142
1143/*!
1144 \qmlproperty enumeration PipelineStateOverride::cullMode
1145 Sets the face culling mode for the render pass.
1146
1147 \value PipelineStateOverride.None No face culling.
1148 \value PipelineStateOverride.Front Front-facing polygons are culled.
1149 \value PipelineStateOverride.Back Back-facing polygons are culled.
1150*/
1151QQuick3DShaderUtilsPipelineStateOverride::CullMode QQuick3DShaderUtilsPipelineStateOverride::cullMode() const
1152{
1153 if (command.m_cullMode)
1154 return CullMode(*command.m_cullMode);
1155 return CullMode::Back;
1156}
1157
1158void QQuick3DShaderUtilsPipelineStateOverride::setCullMode(CullMode newCullMode)
1159{
1160 if (command.m_cullMode && *command.m_cullMode == QRhiGraphicsPipeline::CullMode(newCullMode))
1161 return;
1162 command.m_cullMode = QRhiGraphicsPipeline::CullMode(newCullMode);
1163 emit cullModeChanged();
1164}
1165
1166void QQuick3DShaderUtilsPipelineStateOverride::resetCullMode()
1167{
1168 command.m_cullMode.reset();
1169 emit cullModeChanged();
1170}
1171
1172/*!
1173 \qmlproperty enumeration PipelineStateOverride::polygonMode
1174 Sets the polygon rasterization mode for the render pass.
1175
1176 \value PipelineStateOverride.Fill Polygons are filled (default).
1177 \value PipelineStateOverride.Line Polygon edges are drawn as lines (wireframe).
1178*/
1179QQuick3DShaderUtilsPipelineStateOverride::PolygonMode QQuick3DShaderUtilsPipelineStateOverride::polygonMode() const
1180{
1181 if (command.m_polygonMode)
1182 return PolygonMode(*command.m_polygonMode);
1183 return PolygonMode::Fill;
1184}
1185
1186void QQuick3DShaderUtilsPipelineStateOverride::setPolygonMode(PolygonMode newPolygonMode)
1187{
1188 if (command.m_polygonMode && *command.m_polygonMode == QRhiGraphicsPipeline::PolygonMode(newPolygonMode))
1189 return;
1190 command.m_polygonMode = QRhiGraphicsPipeline::PolygonMode(newPolygonMode);
1191 emit polygonModeChanged();
1192}
1193
1194void QQuick3DShaderUtilsPipelineStateOverride::resetPolygonMode()
1195{
1196 command.m_polygonMode.reset();
1197 emit polygonModeChanged();
1198}
1199
1200/*!
1201 \qmlproperty uint PipelineStateOverride::stencilWriteMask
1202 Sets the stencil write mask for the render pass. Each bit controls whether the corresponding
1203 bit in the stencil buffer can be written.
1204*/
1205quint32 QQuick3DShaderUtilsPipelineStateOverride::stencilWriteMask() const
1206{
1207 if (command.m_stencilWriteMask)
1208 return *command.m_stencilWriteMask;
1209 return 0;
1210}
1211
1212void QQuick3DShaderUtilsPipelineStateOverride::setStencilWriteMask(quint32 newStencilWriteMask)
1213{
1214 if (command.m_stencilWriteMask && *command.m_stencilWriteMask == newStencilWriteMask)
1215 return;
1216 command.m_stencilWriteMask = newStencilWriteMask;
1217 emit stencilWriteMaskChanged();
1218}
1219
1220void QQuick3DShaderUtilsPipelineStateOverride::resetStencilWriteMask()
1221{
1222 command.m_stencilWriteMask.reset();
1223 emit stencilWriteMaskChanged();
1224}
1225
1226/*!
1227 \qmlproperty uint PipelineStateOverride::stencilReference
1228 Sets the stencil reference value for the render pass. This value is used in stencil
1229 comparison operations when \c usesStencilReference is \c true.
1230*/
1231quint32 QQuick3DShaderUtilsPipelineStateOverride::stencilReference() const
1232{
1233 if (command.m_stencilReference)
1234 return *command.m_stencilReference;
1235 return 0;
1236}
1237
1238void QQuick3DShaderUtilsPipelineStateOverride::setStencilReference(quint32 newStencilReference)
1239{
1240 if (command.m_stencilReference && *command.m_stencilReference == newStencilReference)
1241 return;
1242 command.m_stencilReference = newStencilReference;
1243 emit stencilReferenceChanged();
1244}
1245
1246void QQuick3DShaderUtilsPipelineStateOverride::resetStencilReference()
1247{
1248 command.m_stencilReference.reset();
1249 emit stencilReferenceChanged();
1250}
1251
1252/*!
1253 \qmlproperty rect PipelineStateOverride::viewport
1254 Sets the viewport rectangle for the render pass. The rectangle specifies the region
1255 of the render target to draw into, in pixels (x, y, width, height).
1256 If not set, the full render target dimensions are used.
1257*/
1258QRectF QQuick3DShaderUtilsPipelineStateOverride::viewport() const
1259{
1260 if (command.m_viewport) {
1261 const QRhiViewport &vp = *command.m_viewport;
1262 return QRectF(vp.viewport()[0], vp.viewport()[1], vp.viewport()[2], vp.viewport()[3]);
1263 }
1264 return QRectF();
1265}
1266
1267void QQuick3DShaderUtilsPipelineStateOverride::setViewport(const QRectF &newViewport)
1268{
1269 if (command.m_viewport) {
1270 const QRhiViewport &vp = *command.m_viewport;
1271 if (vp.viewport()[0] == newViewport.x() &&
1272 vp.viewport()[1] == newViewport.y() &&
1273 vp.viewport()[2] == newViewport.width() &&
1274 vp.viewport()[3] == newViewport.height())
1275 return;
1276 }
1277 command.m_viewport = QRhiViewport(newViewport.x(), newViewport.y(), newViewport.width(), newViewport.height());
1278 emit viewportChanged();
1279}
1280
1281void QQuick3DShaderUtilsPipelineStateOverride::resetViewport()
1282{
1283 command.m_viewport.reset();
1284 emit viewportChanged();
1285}
1286
1287/*!
1288 \qmlproperty rect PipelineStateOverride::scissor
1289 Sets the scissor rectangle for the render pass. Fragments outside this rectangle are
1290 discarded. Requires \c usesScissor to be \c true.
1291*/
1292QRect QQuick3DShaderUtilsPipelineStateOverride::scissor() const
1293{
1294 if (command.m_scissor) {
1295 const QRhiScissor &sc = *command.m_scissor;
1296 return QRect(sc.scissor()[0], sc.scissor()[1], sc.scissor()[2], sc.scissor()[3]);
1297 }
1298 return QRect();
1299}
1300
1301void QQuick3DShaderUtilsPipelineStateOverride::setScissor(const QRect &newScissor)
1302{
1303 if (command.m_viewport) {
1304 const QRhiScissor &sc = *command.m_scissor;
1305 if (sc.scissor()[0] == newScissor.x() &&
1306 sc.scissor()[1] == newScissor.y() &&
1307 sc.scissor()[2] == newScissor.width() &&
1308 sc.scissor()[3] == newScissor.height())
1309 return;
1310 }
1311 command.m_scissor = QRhiScissor(newScissor.x(), newScissor.y(), newScissor.width(), newScissor.height());
1312 emit scissorChanged();
1313}
1314
1315void QQuick3DShaderUtilsPipelineStateOverride::resetScissor()
1316{
1317 command.m_scissor.reset();
1318}
1319
1320/*!
1321 \qmlproperty renderTargetBlend PipelineStateOverride::targetBlend0
1322 Sets the blending parameters for color attachment 0 of the render pass.
1323 \sa renderTargetBlend
1324*/
1325QQuick3DRenderPassTargetBlend QQuick3DShaderUtilsPipelineStateOverride::targetBlend0() const
1326{
1327 if (command.m_targetBlend0)
1328 return *command.m_targetBlend0;
1329 return {};
1330}
1331
1332void QQuick3DShaderUtilsPipelineStateOverride::setTargetBlend0(const QQuick3DRenderPassTargetBlend &newTargetBlend0)
1333{
1334 if (command.m_targetBlend0 && QQuick3DRenderPassTargetBlend(*command.m_targetBlend0) == newTargetBlend0)
1335 return;
1336
1337 command.m_targetBlend0 = newTargetBlend0.toRhiTargetBlend();;
1338 emit targetBlend0Changed();
1339}
1340
1341void QQuick3DShaderUtilsPipelineStateOverride::resetTargetBlend0()
1342{
1343 command.m_targetBlend0.reset();
1344 emit targetBlend0Changed();
1345}
1346
1347/*!
1348 \qmlproperty renderTargetBlend PipelineStateOverride::targetBlend1
1349 Sets the blending parameters for color attachment 1 of the render pass.
1350 \sa renderTargetBlend
1351*/
1352QQuick3DRenderPassTargetBlend QQuick3DShaderUtilsPipelineStateOverride::targetBlend1() const
1353{
1354 if (command.m_targetBlend1)
1355 return *command.m_targetBlend1;
1356 return {};
1357}
1358
1359void QQuick3DShaderUtilsPipelineStateOverride::setTargetBlend1(const QQuick3DRenderPassTargetBlend &newTargetBlend1)
1360{
1361 if (command.m_targetBlend1 && QQuick3DRenderPassTargetBlend(*command.m_targetBlend1) == newTargetBlend1)
1362 return;
1363 command.m_targetBlend1 = newTargetBlend1.toRhiTargetBlend();
1364 emit targetBlend1Changed();
1365}
1366
1367void QQuick3DShaderUtilsPipelineStateOverride::resetTargetBlend1()
1368{
1369 command.m_targetBlend1.reset();
1370 emit targetBlend1Changed();
1371}
1372
1373/*!
1374 \qmlproperty renderTargetBlend PipelineStateOverride::targetBlend2
1375 Sets the blending parameters for color attachment 2 of the render pass.
1376 \sa renderTargetBlend
1377*/
1378QQuick3DRenderPassTargetBlend QQuick3DShaderUtilsPipelineStateOverride::targetBlend2() const
1379{
1380 if (command.m_targetBlend2)
1381 return *command.m_targetBlend2;
1382 return {};
1383}
1384
1385void QQuick3DShaderUtilsPipelineStateOverride::setTargetBlend2(const QQuick3DRenderPassTargetBlend &newTargetBlend2)
1386{
1387 if (command.m_targetBlend2 && QQuick3DRenderPassTargetBlend(*command.m_targetBlend2) == newTargetBlend2)
1388 return;
1389 command.m_targetBlend2 = newTargetBlend2.toRhiTargetBlend();
1390 emit targetBlend2Changed();
1391}
1392
1393void QQuick3DShaderUtilsPipelineStateOverride::resetTargetBlend2()
1394{
1395 command.m_targetBlend2.reset();
1396 emit targetBlend2Changed();
1397}
1398
1399/*!
1400 \qmlproperty renderTargetBlend PipelineStateOverride::targetBlend3
1401 Sets the blending parameters for color attachment 3 of the render pass.
1402 \sa renderTargetBlend
1403*/
1404QQuick3DRenderPassTargetBlend QQuick3DShaderUtilsPipelineStateOverride::targetBlend3() const
1405{
1406 if (command.m_targetBlend3)
1407 return *command.m_targetBlend3;
1408 return {};
1409}
1410
1411void QQuick3DShaderUtilsPipelineStateOverride::setTargetBlend3(const QQuick3DRenderPassTargetBlend &newTargetBlend3)
1412{
1413 if (command.m_targetBlend3 && QQuick3DRenderPassTargetBlend(*command.m_targetBlend3) == newTargetBlend3)
1414 return;
1415 command.m_targetBlend3 = newTargetBlend3.toRhiTargetBlend();
1416 emit targetBlend3Changed();
1417}
1418
1419void QQuick3DShaderUtilsPipelineStateOverride::resetTargetBlend3()
1420{
1421 command.m_targetBlend3.reset();
1422 emit targetBlend3Changed();
1423}
1424
1425/*!
1426 \qmlproperty renderTargetBlend PipelineStateOverride::targetBlend4
1427 Sets the blending parameters for color attachment 4 of the render pass.
1428 \sa renderTargetBlend
1429*/
1430QQuick3DRenderPassTargetBlend QQuick3DShaderUtilsPipelineStateOverride::targetBlend4() const
1431{
1432 if (command.m_targetBlend4)
1433 return *command.m_targetBlend4;
1434 return {};
1435}
1436
1437void QQuick3DShaderUtilsPipelineStateOverride::setTargetBlend4(const QQuick3DRenderPassTargetBlend &newTargetBlend4)
1438{
1439 if (command.m_targetBlend4 && QQuick3DRenderPassTargetBlend(*command.m_targetBlend4) == newTargetBlend4)
1440 return;
1441 command.m_targetBlend4 = newTargetBlend4.toRhiTargetBlend();
1442 emit targetBlend4Changed();
1443}
1444
1445void QQuick3DShaderUtilsPipelineStateOverride::resetTargetBlend4()
1446{
1447 command.m_targetBlend4.reset();
1448 emit targetBlend4Changed();
1449}
1450
1451/*!
1452 \qmlproperty renderTargetBlend PipelineStateOverride::targetBlend5
1453 Sets the blending parameters for color attachment 5 of the render pass.
1454 \sa renderTargetBlend
1455*/
1456QQuick3DRenderPassTargetBlend QQuick3DShaderUtilsPipelineStateOverride::targetBlend5() const
1457{
1458 if (command.m_targetBlend5)
1459 return *command.m_targetBlend5;
1460 return {};
1461}
1462
1463void QQuick3DShaderUtilsPipelineStateOverride::setTargetBlend5(const QQuick3DRenderPassTargetBlend &newTargetBlend5)
1464{
1465 if (command.m_targetBlend5 && QQuick3DRenderPassTargetBlend(*command.m_targetBlend5) == newTargetBlend5)
1466 return;
1467 command.m_targetBlend5 = newTargetBlend5.toRhiTargetBlend();
1468 emit targetBlend5Changed();
1469}
1470
1471void QQuick3DShaderUtilsPipelineStateOverride::resetTargetBlend5()
1472{
1473 command.m_targetBlend5.reset();
1474 emit targetBlend5Changed();
1475}
1476
1477/*!
1478 \qmlproperty renderTargetBlend PipelineStateOverride::targetBlend6
1479 Sets the blending parameters for color attachment 6 of the render pass.
1480 \sa renderTargetBlend
1481*/
1482QQuick3DRenderPassTargetBlend QQuick3DShaderUtilsPipelineStateOverride::targetBlend6() const
1483{
1484 if (command.m_targetBlend6)
1485 return *command.m_targetBlend6;
1486 return {};
1487}
1488
1489void QQuick3DShaderUtilsPipelineStateOverride::setTargetBlend6(const QQuick3DRenderPassTargetBlend &newTargetBlend6)
1490{
1491 if (command.m_targetBlend6 && QQuick3DRenderPassTargetBlend(*command.m_targetBlend6) == newTargetBlend6)
1492 return;
1493 command.m_targetBlend6 = newTargetBlend6.toRhiTargetBlend();
1494 emit targetBlend6Changed();
1495}
1496
1497void QQuick3DShaderUtilsPipelineStateOverride::resetTargetBlend6()
1498{
1499 command.m_targetBlend6.reset();
1500 emit targetBlend6Changed();
1501}
1502
1503/*!
1504 \qmlproperty renderTargetBlend PipelineStateOverride::targetBlend7
1505 Sets the blending parameters for color attachment 7 of the render pass.
1506 \sa renderTargetBlend
1507*/
1508QQuick3DRenderPassTargetBlend QQuick3DShaderUtilsPipelineStateOverride::targetBlend7() const
1509{
1510 if (command.m_targetBlend7)
1511 return *command.m_targetBlend7;
1512 return {};
1513}
1514
1515void QQuick3DShaderUtilsPipelineStateOverride::setTargetBlend7(const QQuick3DRenderPassTargetBlend &newTargetBlend7)
1516{
1517 if (command.m_targetBlend7 && QQuick3DRenderPassTargetBlend(*command.m_targetBlend7) == newTargetBlend7)
1518 return;
1519 command.m_targetBlend7 = newTargetBlend7.toRhiTargetBlend();
1520 emit targetBlend7Changed();
1521}
1522
1523void QQuick3DShaderUtilsPipelineStateOverride::resetTargetBlend7()
1524{
1525 command.m_targetBlend7.reset();
1526 emit targetBlend7Changed();
1527}
1528
1529/*!
1530 \qmltype RenderPassTexture
1531 \inherits Object3D
1532 \inqmlmodule QtQuick3D
1533 \brief Defines a texture to be used as a render target in a \l {RenderPass}{pass}.
1534 \since 6.11
1535
1536 RenderPassTexture declares an off-screen texture that a \l RenderPass can
1537 render into. The texture is automatically created and managed by the
1538 rendering system. Its size tracks the \l View3D viewport by default.
1539
1540 Once declared, attach the texture to a pass via \l ColorAttachment (for
1541 color output) or \l DepthTextureAttachment (for depth output). The
1542 resulting texture can then be accessed from a subsequent pass or a
1543 \l Texture component using \l RenderOutputProvider.
1544
1545 \qml
1546 // Declare a float color buffer
1547 RenderPassTexture {
1548 id: hdrColorTexture
1549 format: RenderPassTexture.RGBA16F
1550 }
1551
1552 RenderPass {
1553 commands: [
1554 ColorAttachment {
1555 name: "color0"
1556 target: hdrColorTexture
1557 }
1558 ]
1559 }
1560 \endqml
1561
1562 \sa ColorAttachment, DepthTextureAttachment, RenderOutputProvider
1563*/
1564
1565/*!
1566 \qmlproperty enumeration RenderPassTexture::format
1567 Sets the format of the render target texture.
1568
1569 Available formats are:
1570
1571 \value RenderPassTexture.Unknown
1572 \value RenderPassTexture.RGBA8
1573 \value RenderPassTexture.RGBA16F
1574 \value RenderPassTexture.RGBA32F
1575 \value RenderPassTexture.R8
1576 \value RenderPassTexture.R16
1577 \value RenderPassTexture.R16F
1578 \value RenderPassTexture.R32F
1579 \value RenderPassTexture.Depth16
1580 \value RenderPassTexture.Depth24
1581 \value RenderPassTexture.Depth32
1582 \value RenderPassTexture.Depth24Stencil8
1583*/
1584
1585QQuick3DShaderUtilsRenderPassTexture::TextureFormat QQuick3DShaderUtilsRenderPassTexture::format() const
1586{
1587 return fromRenderTextureFormat(command->format());
1588}
1589
1590QQuick3DShaderUtilsRenderPassTexture::~QQuick3DShaderUtilsRenderPassTexture()
1591{
1592
1593}
1594
1595void QQuick3DShaderUtilsRenderPassTexture::setFormat(TextureFormat newFormat)
1596{
1597 if (!command)
1598 command = std::make_shared<QSSGAllocateTexture>();
1599 command->setFormat(asRenderTextureFormat(newFormat));
1600}
1601
1602QSSGRenderTextureFormat QQuick3DShaderUtilsRenderPassTexture::asRenderTextureFormat(TextureFormat fmt)
1603{
1604 switch (fmt) {
1605 case TextureFormat::Unknown: return QSSGRenderTextureFormat::Unknown;
1606 case TextureFormat::RGBA8: return QSSGRenderTextureFormat::RGBA8;
1607 case TextureFormat::RGBA16F: return QSSGRenderTextureFormat::RGBA16F;
1608 case TextureFormat::RGBA32F: return QSSGRenderTextureFormat::RGBA32F;
1609 case TextureFormat::R8: return QSSGRenderTextureFormat::R8;
1610 case TextureFormat::R16: return QSSGRenderTextureFormat::R16;
1611 case TextureFormat::R16F: return QSSGRenderTextureFormat::R16F;
1612 case TextureFormat::R32F: return QSSGRenderTextureFormat::R32F;
1613 case TextureFormat::Depth16: return QSSGRenderTextureFormat::Depth16;
1614 case TextureFormat::Depth24: return QSSGRenderTextureFormat::Depth24;
1615 case TextureFormat::Depth32: return QSSGRenderTextureFormat::Depth32;
1616 case TextureFormat::Depth24Stencil8: return QSSGRenderTextureFormat::Depth24Stencil8;
1617 default:
1618 break;
1619 }
1620 return QSSGRenderTextureFormat::Unknown;
1621}
1622
1623QQuick3DShaderUtilsRenderPassTexture::TextureFormat QQuick3DShaderUtilsRenderPassTexture::fromRenderTextureFormat(QSSGRenderTextureFormat fmt)
1624{
1625 switch (fmt.format) {
1626 case QSSGRenderTextureFormat::Unknown: return TextureFormat::Unknown;
1627 case QSSGRenderTextureFormat::RGBA8: return TextureFormat::RGBA8;
1628 case QSSGRenderTextureFormat::RGBA16F: return TextureFormat::RGBA16F;
1629 case QSSGRenderTextureFormat::RGBA32F: return TextureFormat::RGBA32F;
1630 case QSSGRenderTextureFormat::R8: return TextureFormat::R8;
1631 case QSSGRenderTextureFormat::R16: return TextureFormat::R16;
1632 case QSSGRenderTextureFormat::R16F: return TextureFormat::R16F;
1633 case QSSGRenderTextureFormat::R32F: return TextureFormat::R32F;
1634 case QSSGRenderTextureFormat::Depth16: return TextureFormat::Depth16;
1635 case QSSGRenderTextureFormat::Depth24: return TextureFormat::Depth24;
1636 case QSSGRenderTextureFormat::Depth32: return TextureFormat::Depth32;
1637 case QSSGRenderTextureFormat::Depth24Stencil8: return TextureFormat::Depth24Stencil8;
1638 default:
1639 break;
1640 }
1641 return TextureFormat::Unknown;
1642}
1643
1644/*!
1645 \qmltype ColorAttachment
1646 \inherits Command
1647 \inqmlmodule QtQuick3D
1648 \brief Defines a color attachment for a \l {RenderPass}{pass}.
1649 \since 6.11
1650
1651 The ColorAttachment type is used to specify a color attachment for a \l RenderPass.
1652 The \l name property is used to identify the attachment within the render pass.
1653 If the \l {RenderPass.AugmentMaterial}{AugmentMaterial} mode is used, the name will be
1654 exposed as an output with \c name in the fragment shader.
1655
1656 \sa RenderPassTexture
1657
1658 \qml
1659 RenderPass {
1660 // Define a render target texture
1661 RenderPassTexture {
1662 id: colorTexture
1663 format: RenderPassTexture.RGBA16F
1664 }
1665
1666 commands: [
1667 // Define a color attachment using the texture
1668 ColorAttachment {
1669 name: "color0"
1670 target: colorTexture
1671 }
1672 ]
1673 }
1674 \endqml
1675*/
1676
1677/*!
1678 \qmlproperty RenderPassTexture ColorAttachment::target
1679 The \l RenderPassTexture that will be used as the color attachment. The texture
1680 must have a color-compatible format (e.g. \c RGBA8, \c RGBA16F).
1681*/
1682
1683/*!
1684 \qmlproperty string ColorAttachment::name
1685 The name used to identify this color attachment within the render pass. When
1686 \l {RenderPass::materialMode}{materialMode} is set to \c AugmentMaterial, this name
1687 is exposed as a fragment shader output variable, allowing custom shader code to
1688 write to this attachment.
1689*/
1690
1691QQuick3DShaderUtilsRenderPassColorAttachment::~QQuick3DShaderUtilsRenderPassColorAttachment()
1692{
1693
1694}
1695
1696QByteArray QQuick3DShaderUtilsRenderPassColorAttachment::name() const
1697{
1698 return m_name;
1699}
1700
1701void QQuick3DShaderUtilsRenderPassColorAttachment::setName(const QByteArray &newName)
1702{
1703 m_name = newName;
1704}
1705
1706QSSGCommand *QQuick3DShaderUtilsRenderPassColorAttachment::cloneCommand()
1707{
1708 if (target) {
1709 QSSGColorAttachment *cmd = new QSSGColorAttachment(m_name);
1710 cmd->m_textureCmd = target->command;
1711 return cmd;
1712 }
1713
1714 return nullptr;
1715}
1716
1717QQuick3DPropertyChangedTracker::~QQuick3DPropertyChangedTracker()
1718{
1719
1720}
1721
1722std::optional<QQuick3DPropertyChangedTracker::UniformProperty> QQuick3DPropertyChangedTracker::createOrUpdateTrackedProperty(const QMetaProperty property,
1723 bool addWatchers)
1724{
1725 Q_ASSERT(property.isValid());
1726
1727 std::optional<QQuick3DPropertyChangedTracker::UniformProperty> result;
1728 const int propertyIndex = property.propertyIndex();
1729
1730 static auto getSamplerHint = [](const QQuick3DTexture &texture) {
1731 if (auto *po = QQuick3DObjectPrivate::get(&texture)) {
1732 if (po->type == QQuick3DObjectPrivate::Type::TextureProvider) {
1733 auto textureProvider = static_cast<QQuick3DTextureProviderExtension *>(texture.textureProvider());
1734 switch (textureProvider->samplerHint()) {
1735 case QQuick3DTextureProviderExtension::SamplerHint::Sampler2D:
1736 return QSSGRenderSamplerType::Sampler2D;
1737 case QQuick3DTextureProviderExtension::SamplerHint::Sampler2DArray:
1738 return QSSGRenderSamplerType::Sampler2DArray;
1739 case QQuick3DTextureProviderExtension::SamplerHint::Sampler3D:
1740 return QSSGRenderSamplerType::Sampler3D;
1741 case QQuick3DTextureProviderExtension::SamplerHint::SamplerCube:
1742 return QSSGRenderSamplerType::SamplerCube;
1743 case QQuick3DTextureProviderExtension::SamplerHint::SamplerCubeArray:
1744 return QSSGRenderSamplerType::SamplerCubeArray;
1745 case QQuick3DTextureProviderExtension::SamplerHint::SamplerBuffer:
1746 return QSSGRenderSamplerType::SamplerBuffer;
1747 }
1748 } else if (po->type == QQuick3DObjectPrivate::Type::ImageCube) {
1749 return QSSGRenderSamplerType::SamplerCube;
1750 } else if (texture.textureData() && texture.textureData()->depth() > 0) {
1751 return QSSGRenderSamplerType::Sampler3D;
1752 }
1753 }
1754
1755 return QSSGRenderSamplerType::Sampler2D;
1756 };
1757
1758 const auto addTextureToUniforms =
1759 [&](const char *name, QQuick3DTexture *texture, int propertyIndex) -> QQuick3DPropertyChangedTracker::UniformProperty {
1760 QSSGRenderImage *ri = static_cast<QSSGRenderImage *>(QQuick3DObjectPrivate::get(texture)->spatialNode);
1761 auto samplerName = QSSGBaseTypeHelpers::toString(getSamplerHint(*texture));
1762 if (addWatchers) {
1763 QObject::connect(texture, &QQuick3DTexture::textureProviderChanged, m_owner, [this, property, texture]() {
1764 addPropertyWatcher(property, DirtyPropertyHint::Reference, texture);
1765 });
1766 }
1767
1768 return UniformProperty(name, samplerName, QVariant::fromValue(ri), QSSGRenderShaderValue::Texture, propertyIndex);
1769 };
1770
1771 // The TextureInput type needs extra watchers for its properties...
1772 const auto addTextureInputWatchers = [&](QMetaProperty property, QQuick3DShaderUtilsTextureInput *textureInput) {
1773 QObject::connect(textureInput, &QQuick3DShaderUtilsTextureInput::enabledChanged, m_owner, [this, property, textureInput]() {
1774 addPropertyWatcher(property, DirtyPropertyHint::Reference, textureInput);
1775 });
1776 QObject::connect(textureInput, &QQuick3DShaderUtilsTextureInput::textureChanged, m_owner, [this, property, textureInput]() {
1777 addPropertyWatcher(property, DirtyPropertyHint::Reference, textureInput);
1778 });
1779 };
1780
1781 const char *name = property.name();
1782 QMetaType propType = property.metaType();
1783 QVariant propValue = property.read(m_owner);
1784 if (propType == QMetaType(QMetaType::QVariant))
1785 propType = propValue.metaType();
1786
1787 const auto type = QSSGShaderUtils::uniformType(propType);
1788 if (type != QSSGRenderShaderValue::Unknown) {
1789 result = UniformProperty(name, QSSGShaderUtils::uniformTypeName(propType), propValue, QSSGShaderUtils::uniformType(propType), propertyIndex);
1790 if (addWatchers)
1791 addPropertyWatcher(property, DirtyPropertyHint::Value);
1792 } else {
1793 if (propType.id() >= QMetaType::User) {
1794 if (propType.id() == qMetaTypeId<QQuick3DTexture *>()) {
1795 if (QQuick3DTexture *texture = property.read(m_owner).value<QQuick3DTexture *>()) {
1796 result = addTextureToUniforms(name, texture, propertyIndex);
1797 if (addWatchers)
1798 addPropertyWatcher(property, DirtyPropertyHint::Reference, texture);
1799 }
1800 } else if (propType.id() == qMetaTypeId<QQuick3DShaderUtilsTextureInput *>()) { // For compatibility, also check for texture input types
1801 if (QQuick3DShaderUtilsTextureInput *textureInput = property.read(m_owner).value<QQuick3DShaderUtilsTextureInput *>();
1802 textureInput && textureInput->texture()) {
1803 result = addTextureToUniforms(property.name(), textureInput->texture(), propertyIndex);
1804 if (addWatchers) {
1805 addTextureInputWatchers(property, textureInput);
1806 addPropertyWatcher(property, DirtyPropertyHint::Reference, textureInput);
1807 }
1808 }
1809 }
1810 } else if (propType == QMetaType(QMetaType::QObjectStar)) {
1811 if (QQuick3DTexture *texture = qobject_cast<QQuick3DTexture *>(propValue.value<QObject *>())) {
1812 result = addTextureToUniforms(name, texture, propertyIndex);
1813 if (addWatchers)
1814 addPropertyWatcher(property, DirtyPropertyHint::Reference, texture);
1815 } else if (QQuick3DShaderUtilsTextureInput *textureInput = qobject_cast<QQuick3DShaderUtilsTextureInput *>(
1816 propValue.value<QObject *>());
1817 textureInput && textureInput->texture()) {
1818 result = addTextureToUniforms(property.name(), textureInput->texture(), propertyIndex);
1819 if (addWatchers) {
1820 addTextureInputWatchers(property, textureInput);
1821 addPropertyWatcher(property, DirtyPropertyHint::Reference, textureInput);
1822 }
1823 }
1824 }
1825 }
1826
1827 return result;
1828}
1829
1830QList<QQuick3DPropertyChangedTracker::UniformProperty> QQuick3DPropertyChangedTracker::extractProperties()
1831{
1832 if (!m_dirtyProperties.empty()) {
1833 for (int propertyIndex : std::as_const(m_dirtyProperties)) {
1834 auto it = std::lower_bound(m_propertyList.begin(),
1835 m_propertyList.end(),
1836 propertyIndex,
1837 [](const UniformProperty &p, int index) { return p.pid < index; });
1838 if (it == m_propertyList.end()) {
1839 Q_ASSERT(false);
1840 continue;
1841 }
1842
1843 QMetaProperty property = m_owner->metaObject()->property(propertyIndex);
1844 if (Q_UNLIKELY(!property.isValid())) {
1845 m_propertyList.erase(it);
1846 continue;
1847 }
1848
1849 if (auto trackedProperty = createOrUpdateTrackedProperty(property, false); trackedProperty.has_value())
1850 *it = *trackedProperty;
1851 else
1852 m_propertyList.erase(it);
1853 }
1854 m_dirtyProperties.clear();
1855 }
1856
1857 if (m_extracted)
1858 return m_propertyList;
1859
1860 auto metaObject = m_owner->metaObject();
1861
1862 // Properties -> uniforms
1863 const int propCount = metaObject->propertyCount();
1864 int propOffset = metaObject->propertyOffset();
1865 m_propertyList.reserve(propCount);
1866
1867 // Classes can have multilayered inheritance structure, so find the actual propOffset by
1868 // walking up the inheritance chain.
1869 const QMetaObject *superClass = metaObject->superClass();
1870 while (superClass && qstrcmp(superClass->className(), m_superClassName) != 0) {
1871 propOffset = superClass->propertyOffset();
1872 superClass = superClass->superClass();
1873 }
1874
1875 for (int i = propOffset; i != propCount; ++i) {
1876 const QMetaProperty property = metaObject->property(i);
1877 if (Q_UNLIKELY(!property.isValid()))
1878 continue;
1879 if (auto uniformProperty = createOrUpdateTrackedProperty(property, true); uniformProperty.has_value()) {
1880 m_propertyList.push_back(*uniformProperty);
1881 }
1882 }
1883
1884 m_dirtyProperties.reserve(m_propertyList.size());
1885
1886 m_extracted = true;
1887 return m_propertyList;
1888}
1889
1890void QQuick3DPropertyChangedTracker::addPropertyWatcher(QMetaProperty property, DirtyPropertyHint hint, QQuick3DObject *object)
1891{
1892 if (property.isValid() && property.hasNotifySignal()) {
1893 // Check if we're already watching this property.
1894 const auto pid = property.propertyIndex();
1895 Q_ASSERT(pid != -1);
1896 auto it = m_trackedProperties.find(pid);
1897 const bool found = (it != m_trackedProperties.end());
1898 QQuick3DObject *oldObj = nullptr;
1899 if (!found) {
1900 QQuick3DPropertyWatcher *watcher = new QQuick3DPropertyWatcher(this, property);
1901 it = m_trackedProperties.insert(pid, Tracked { watcher, object, pid });
1902 } else {
1903 oldObj = it->object;
1904 it->object = object;
1905 }
1906
1907 if (hint == DirtyPropertyHint::Reference) {
1908 const auto &sm = QQuick3DObjectPrivate::get(m_owner)->sceneManager;
1909
1910 // First check if the object changed
1911 const bool changed = (oldObj != object);
1912
1913 // Deref old object
1914 if (changed && oldObj) {
1915 QQuick3DObjectPrivate::get(oldObj)->derefSceneManager();
1916 QObject::disconnect(oldObj, &QObject::destroyed, m_owner, nullptr);
1917 }
1918
1919 // Ref new object
1920 if (changed && object) {
1921 QQuick3DObjectPrivate::get(object)->refSceneManager(*sm);
1922 QObject::connect(object, &QObject::destroyed, m_owner, [this, property](QObject *obj) {
1923 Q_UNUSED(obj);
1924 addPropertyWatcher(property, DirtyPropertyHint::Reference, nullptr);
1925 });
1926 }
1927 }
1928
1929 m_dirtyProperties.push_back(property.propertyIndex());
1930 markTrackedPropertyDirty(property, hint);
1931 }
1932}
1933
1934void QQuick3DPropertyChangedTracker::markTrackedPropertyDirty(QMetaProperty property, DirtyPropertyHint hint)
1935{
1936 Q_UNUSED(property);
1937 Q_UNUSED(hint);
1938 QSSG_CHECK_X(false, "QQuick3DPropertyChangedTracker::onPropertyDirty implementation missing");
1939}
1940
1941QQuick3DPropertyWatcher::QQuick3DPropertyWatcher(QQuick3DPropertyChangedTracker *tracker, QMetaProperty property)
1942 : m_tracker(tracker)
1943 , m_property(property)
1944{
1945 Q_ASSERT(tracker != nullptr);
1946 Q_ASSERT(property.isValid() && property.hasNotifySignal());
1947
1948 const bool isPointerType = (property.metaType().flags().testFlag(QMetaType::IsPointer));
1949
1950 if (!isPointerType) {
1951 // Value change notification
1952 const auto idx = staticMetaObject.indexOfSlot("onValuePropertyChanged()");
1953 if (QSSG_GUARD_X(idx != -1, "Method not found!")) {
1954 auto onPropertyChangedMethod = staticMetaObject.method(idx);
1955 connect(m_tracker->m_owner, property.notifySignal(), this, onPropertyChangedMethod);
1956 }
1957 } else {
1958 // Pointer value change notification
1959 const auto idx = staticMetaObject.indexOfSlot("onPointerPropertyChanged()");
1960 if (QSSG_GUARD_X(idx != -1, "Method not found!")) {
1961 auto onPointerPropertyChangedMethod = staticMetaObject.method(idx);
1962 connect(m_tracker->m_owner, property.notifySignal(), this, onPointerPropertyChangedMethod);
1963 }
1964 }
1965}
1966
1967void QQuick3DPropertyWatcher::onValuePropertyChanged()
1968{
1969 m_tracker->m_dirtyProperties.push_back(m_property.propertyIndex());
1970 m_tracker->markTrackedPropertyDirty(m_property, QQuick3DPropertyChangedTracker::DirtyPropertyHint::Value);
1971}
1972
1974{
1975 m_tracker->m_dirtyProperties.push_back(m_property.propertyIndex());
1976 m_tracker->markTrackedPropertyDirty(m_property, QQuick3DPropertyChangedTracker::DirtyPropertyHint::Reference);;
1977}
1978
1979/*!
1980 \qmltype AddDefine
1981 \inherits Command
1982 \inqmlmodule QtQuick3D
1983 \brief Adds a preprocessor define to the shader compilation for a \l {RenderPass}{pass}.
1984 \since 6.11
1985
1986 The AddDefine type is used to inject a \c {#define} into the shader
1987 compilation for a \l RenderPass. This lets you branch shader behavior
1988 at compile time depending on which pass is being executed.
1989
1990 In the shader code, the define can be used with the standard preprocessor
1991 \c {#ifdef} / \c {#if} directives:
1992 \badcode
1993 #ifdef CUSTOM_PASS_MODE
1994 // code compiled only when this define is present
1995 #endif
1996 \endcode
1997
1998 \qml
1999 RenderPass {
2000 commands: [
2001 ColorAttachment { name: "color0"; target: colorTexture },
2002 AddDefine { name: "CUSTOM_PASS_MODE" },
2003 AddDefine { name: "OUTPUT_VERSION"; value: 2 }
2004 ]
2005 }
2006 \endqml
2007*/
2008
2009/*!
2010 \qmlproperty string AddDefine::name
2011 The name of the preprocessor macro to define. This becomes the identifier
2012 used in \c {#ifdef} or \c {#if} checks in the shader code.
2013*/
2014
2015/*!
2016 \qmlproperty int AddDefine::value
2017 The integer value assigned to the preprocessor macro. Defaults to \c 1.
2018 When non-zero, \c {#if NAME} evaluates to \c true in the shader.
2019*/
2020
2021QQuick3DShaderUtilsRenderPassAddDefine::QQuick3DShaderUtilsRenderPassAddDefine()
2022{
2023
2024}
2025
2026QQuick3DShaderUtilsRenderPassAddDefine::~QQuick3DShaderUtilsRenderPassAddDefine() = default;
2027
2028QSSGCommand *QQuick3DShaderUtilsRenderPassAddDefine::cloneCommand() {
2029 QSSGAddShaderDefine *cmd = new QSSGAddShaderDefine(command);
2030 return cmd;
2031}
2032
2033/*!
2034 \qmltype SubRenderPass
2035 \inherits Command
2036 \inqmlmodule QtQuick3D
2037 \brief Renders a sub-section of work within the same render target as the parent RenderPass.
2038 \since 6.11
2039
2040 SubRenderPass is a \l Command that subdivides a \l RenderPass into
2041 smaller, independently configured pieces while sharing the same render
2042 target. Only the parent \l RenderPass controls which textures are
2043 rendered into (via \l ColorAttachment and \l DepthTextureAttachment)
2044 and when the pass begins (and therefore when the render target is
2045 cleared). A SubRenderPass cannot set its own render target or clear
2046 settings — those are always inherited from the parent pass.
2047
2048 What a SubRenderPass \e can control is \e what gets rendered and
2049 \e how: it has its own \l{RenderPass::commands}{commands}, so it can
2050 carry a \l RenderablesFilter to select a subset of objects, a
2051 \l PipelineStateOverride to change depth/blend state, a different
2052 \l{RenderPass::materialMode}{materialMode}, and so on.
2053
2054 A typical use is a forward-rendering pipeline where a single color
2055 pass uses separate SubRenderPasses for the background, opaque
2056 objects, and transparent objects. All three sub-passes write into the
2057 same color and depth textures, but each has different pipeline state:
2058
2059 \qml
2060 RenderPass {
2061 id: mainColorPass
2062 commands: [
2063 // Render targets are set at the parent RenderPass level only
2064 ColorAttachment { name: "color0"; target: colorTexture },
2065 DepthTextureAttachment { target: depthTexture },
2066
2067 // Sub-pass 1: render the skybox background
2068 SubRenderPass { renderPass: skyboxSubPass },
2069
2070 // Sub-pass 2: opaque objects with depth testing and writing
2071 SubRenderPass { renderPass: opaqueSubPass },
2072
2073 // Sub-pass 3: transparent objects with blending, no depth write
2074 SubRenderPass { renderPass: transparentSubPass }
2075 ]
2076 }
2077
2078 // Sub-passes share mainColorPass's render target
2079 RenderPass {
2080 id: skyboxSubPass
2081 passMode: RenderPass.SkyboxPass
2082 }
2083
2084 RenderPass {
2085 id: opaqueSubPass
2086 commands: [
2087 RenderablesFilter { renderableTypes: RenderablesFilter.Opaque }
2088 ]
2089 }
2090
2091 RenderPass {
2092 id: transparentSubPass
2093 commands: [
2094 RenderablesFilter { renderableTypes: RenderablesFilter.Transparent },
2095 PipelineStateOverride {
2096 blendEnabled: true
2097 depthWriteEnabled: false
2098 }
2099 ]
2100 }
2101 \endqml
2102
2103 \sa RenderPass, RenderablesFilter, PipelineStateOverride
2104*/
2105
2106/*!
2107 \qmlproperty RenderPass SubRenderPass::renderPass
2108 The RenderPass that will be executed as a sub-pass when this command is processed.
2109*/
2110
2111QQuick3DShaderUtilsSubRenderPass::~QQuick3DShaderUtilsSubRenderPass()
2112{
2113
2114}
2115
2116QSSGCommand *QQuick3DShaderUtilsSubRenderPass::cloneCommand()
2117{
2118 QSSGSubRenderPass *cmd = nullptr;
2119
2120 if (!m_renderPass) {
2121 if (!m_hasWarnedAboutInvalidId) {
2122 qCWarning(lcSubRenderPass, "SubRenderPass: No render pass specified. Set the 'renderPass' property.");
2123 m_hasWarnedAboutInvalidId = true;
2124 }
2125 return nullptr;
2126 }
2127
2128 QSSGResourceId userPassId = QQuick3DExtensionHelpers::getResourceId(*m_renderPass);
2129 // Ensure we have a valid resource id before continuing.
2130 if (userPassId != QSSGResourceId::Invalid) {
2131 cmd = new QSSGSubRenderPass();
2132 cmd->setSubPass(userPassId);
2133 // Reset warning flag on success
2134 m_hasWarnedAboutInvalidId = false;
2135 } else {
2136 // Resource ID is not ready yet - this is expected during initialization
2137 qCDebug(lcSubRenderPass, "SubRenderPass: Render pass resource ID not yet available, will retry.");
2138 update();
2139 }
2140
2141 return cmd;
2142}
2143
2144QSSGRenderGraphObject *QQuick3DShaderUtilsSubRenderPass::updateSpatialNode(QSSGRenderGraphObject *node)
2145{
2146 return node;
2147}
2148
2149void QQuick3DShaderUtilsSubRenderPass::itemChange(ItemChange change, const ItemChangeData &value)
2150{
2151 if (change == QQuick3DObject::ItemSceneChange)
2152 updateSceneManager(value.sceneManager);
2153}
2154
2155void QQuick3DShaderUtilsSubRenderPass::updateSceneManager(QQuick3DSceneManager *sceneManager)
2156{
2157 if (sceneManager)
2158 QQuick3DObjectPrivate::refSceneManager(m_renderPass, *sceneManager);
2159 else
2160 QQuick3DObjectPrivate::derefSceneManager(m_renderPass);
2161}
2162
2163QQuick3DRenderPass *QQuick3DShaderUtilsSubRenderPass::renderPass() const
2164{
2165 return m_renderPass;
2166}
2167
2168void QQuick3DShaderUtilsSubRenderPass::setRenderPass(QQuick3DRenderPass *newRenderPass)
2169{
2170 if (m_renderPass == newRenderPass)
2171 return;
2172
2173 QQuick3DObjectPrivate::attachWatcher(this, &QQuick3DShaderUtilsSubRenderPass::setRenderPass, newRenderPass, m_renderPass);
2174
2175 if (newRenderPass)
2176 newRenderPass->update();
2177
2178 m_renderPass = newRenderPass;
2179 m_hasWarnedAboutInvalidId = false; // Reset warning flag when property changes
2180 emit renderPassChanged();
2181}
2182
2183/*!
2184 \qmltype DepthTextureAttachment
2185 \inherits Command
2186 \inqmlmodule QtQuick3D
2187 \brief Defines a depth texture attachment for a \l {RenderPass}{pass}.
2188 \since 6.11
2189
2190 DepthTextureAttachment attaches a \l RenderPassTexture with a depth
2191 format as the depth buffer for a \l RenderPass. Unlike
2192 \l DepthStencilAttachment (which uses an opaque render buffer), the
2193 resulting depth data is stored in a texture that can be read by
2194 subsequent passes via \l RenderOutputProvider.
2195
2196 \qml
2197 RenderPassTexture {
2198 id: depthTexture
2199 format: RenderPassTexture.Depth24
2200 }
2201
2202 RenderPass {
2203 commands: [
2204 ColorAttachment { name: "color0"; target: colorTexture },
2205 DepthTextureAttachment { target: depthTexture }
2206 ]
2207 }
2208
2209 // Expose the depth texture to a material via RenderOutputProvider
2210 Texture {
2211 textureProvider: RenderOutputProvider {
2212 textureSource: RenderOutputProvider.UserPassTexture
2213 renderPass: myRenderPass
2214 }
2215 }
2216 \endqml
2217
2218 \sa RenderPassTexture, DepthStencilAttachment, RenderOutputProvider
2219*/
2220
2221/*!
2222 \qmlproperty RenderPassTexture DepthTextureAttachment::target
2223 The \l RenderPassTexture that will be used as the depth attachment. The texture
2224 must have a depth-compatible format (e.g. \c Depth16, \c Depth24, \c Depth32,
2225 or \c Depth24Stencil8).
2226*/
2227
2228QQuick3DShaderUtilsRenderPassDepthTextureAttachment::~QQuick3DShaderUtilsRenderPassDepthTextureAttachment()
2229{
2230
2231}
2232
2233QSSGCommand *QQuick3DShaderUtilsRenderPassDepthTextureAttachment::cloneCommand()
2234{
2235 if (target) {
2236 QSSGDepthTextureAttachment *cmd = new QSSGDepthTextureAttachment(QByteArrayLiteral("__depth__"));
2237 cmd->m_textureCmd = target->command;
2238 return cmd;
2239 }
2240
2241 return nullptr;
2242}
2243
2244QQuick3DShaderUtilsRenderPassDepthStencilAttachment::~QQuick3DShaderUtilsRenderPassDepthStencilAttachment()
2245{
2246
2247}
2248
2249QSSGCommand *QQuick3DShaderUtilsRenderPassDepthStencilAttachment::cloneCommand()
2250{
2251 QSSGDepthStencilAttachment *cmd = new QSSGDepthStencilAttachment;
2252 return cmd;
2253}
2254
2255QT_END_NAMESPACE
\qmltype Shader \inherits QtObject \inqmlmodule QtQuick3D
ResolveFunction resolveShaderOverride
QSSGRenderShaderValue::Type uniformType(QMetaType type)
QByteArray uniformTypeName(QMetaType type)
bool(*)(const QUrl &url, const QQmlContext *context, QByteArray &shaderData, QByteArray &shaderPathKey) ResolveFunction
static constexpr QMetaType::Type qssg_metatype_list[]
QByteArray resolveShader(const QUrl &fileUrl, const QQmlContext *context, QByteArray &shaderPathKey)
MetaTypeList supportedMetatypes()
void setResolveFunction(ResolveFunction fn)
Combined button and popup list for selecting options.