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
1721
1722void QQuick3DPropertyChangedTracker::extractProperties(UniformPropertyList &outUniforms)
1723{
1724 // Ensure we start with a clean list
1725 outUniforms.clear();
1726
1727 auto metaObject = m_owner->metaObject();
1728
1729 // Properties -> uniforms
1730 const int propCount = metaObject->propertyCount();
1731 int propOffset = metaObject->propertyOffset();
1732
1733 // Classes can have multilayered inheritance structure, so find the actual propOffset by
1734 // walking up the inheritance chain.
1735 const QMetaObject *superClass = metaObject->superClass();
1736 while (superClass && qstrcmp(superClass->className(), m_superClassName) != 0) {
1737 propOffset = superClass->propertyOffset();
1738 superClass = superClass->superClass();
1739 }
1740
1741 static auto getSamplerHint = [](const QQuick3DTexture &texture) {
1742 if (auto *po = QQuick3DObjectPrivate::get(&texture)) {
1743 if (po->type == QQuick3DObjectPrivate::Type::TextureProvider) {
1744 auto textureProvider = static_cast<QQuick3DTextureProviderExtension *>(texture.textureProvider());
1745 switch (textureProvider->samplerHint()) {
1746 case QQuick3DTextureProviderExtension::SamplerHint::Sampler2D:
1747 return QSSGRenderSamplerType::Sampler2D;
1748 case QQuick3DTextureProviderExtension::SamplerHint::Sampler2DArray:
1749 return QSSGRenderSamplerType::Sampler2DArray;
1750 case QQuick3DTextureProviderExtension::SamplerHint::Sampler3D:
1751 return QSSGRenderSamplerType::Sampler3D;
1752 case QQuick3DTextureProviderExtension::SamplerHint::SamplerCube:
1753 return QSSGRenderSamplerType::SamplerCube;
1754 case QQuick3DTextureProviderExtension::SamplerHint::SamplerCubeArray:
1755 return QSSGRenderSamplerType::SamplerCubeArray;
1756 case QQuick3DTextureProviderExtension::SamplerHint::SamplerBuffer:
1757 return QSSGRenderSamplerType::SamplerBuffer;
1758 }
1759 } else if (po->type == QQuick3DObjectPrivate::Type::ImageCube) {
1760 return QSSGRenderSamplerType::SamplerCube;
1761 } else if (texture.textureData() && texture.textureData()->depth() > 0) {
1762 return QSSGRenderSamplerType::Sampler3D;
1763 }
1764 }
1765
1766 return QSSGRenderSamplerType::Sampler2D;
1767 };
1768
1769 const auto addTextureToUniforms = [&](const char *name, QQuick3DTexture *texture, int propertyIndex) {
1770 QSSGRenderImage *ri = static_cast<QSSGRenderImage *>(QQuick3DObjectPrivate::get(texture)->spatialNode);
1771 auto samplerName = QSSGBaseTypeHelpers::toString(getSamplerHint(*texture));
1772 outUniforms.emplace_back(name, samplerName, QVariant::fromValue(ri), QSSGRenderShaderValue::Texture, propertyIndex);
1773 };
1774
1775 // The TextureInput type needs extra watchers for its properties...
1776 const auto addTextureInputWatchers = [&](QMetaProperty property, QQuick3DShaderUtilsTextureInput *textureInput) {
1777 QObject::connect(textureInput, &QQuick3DShaderUtilsTextureInput::enabledChanged, m_owner, [this, property, textureInput](){ addPropertyWatcher(property, DirtyPropertyHint::Reference, textureInput); }, Qt::UniqueConnection);
1778 QObject::connect(textureInput, &QQuick3DShaderUtilsTextureInput::textureChanged, m_owner, [this, property, textureInput](){ addPropertyWatcher(property, DirtyPropertyHint::Reference, textureInput); }, Qt::UniqueConnection);
1779 if (auto *texture = textureInput->texture())
1780 addTextureToUniforms(property.name(), texture, property.propertyIndex());
1781 };
1782
1783 for (int i = propOffset; i != propCount; ++i) {
1784 const QMetaProperty property = metaObject->property(i);
1785 if (Q_UNLIKELY(!property.isValid()))
1786 continue;
1787
1788 const char *name = property.name();
1789 QMetaType propType = property.metaType();
1790 QVariant propValue = property.read(m_owner);
1791 if (propType == QMetaType(QMetaType::QVariant))
1792 propType = propValue.metaType();
1793
1794 const auto type = QSSGShaderUtils::uniformType(propType);
1795 if (type != QSSGRenderShaderValue::Unknown) {
1796 outUniforms.emplace_back(name, QSSGShaderUtils::uniformTypeName(propType),
1797 propValue, QSSGShaderUtils::uniformType(propType), i);
1798 addPropertyWatcher(property, DirtyPropertyHint::Value);
1799 } else {
1800 if (propType.id() >= QMetaType::User) {
1801 if (propType.id() == qMetaTypeId<QQuick3DTexture *>()) {
1802 if (QQuick3DTexture *texture = property.read(m_owner).value<QQuick3DTexture *>()) {
1803 addTextureToUniforms(name, texture, i);
1804 addPropertyWatcher(property, DirtyPropertyHint::Reference, texture);
1805 }
1806 } else if (propType.id() == qMetaTypeId<QQuick3DShaderUtilsTextureInput *>()) { // For compatibility, also check for texture input types
1807 if (QQuick3DShaderUtilsTextureInput *textureInput = property.read(m_owner).value<QQuick3DShaderUtilsTextureInput *>(); textureInput && textureInput->texture()) {
1808 addTextureInputWatchers(property, textureInput);
1809 addPropertyWatcher(property, DirtyPropertyHint::Reference, textureInput);
1810 }
1811 }
1812 } else if (propType == QMetaType(QMetaType::QObjectStar)) {
1813 if (QQuick3DTexture *texture = qobject_cast<QQuick3DTexture *>(propValue.value<QObject *>())) {
1814 addTextureToUniforms(name, texture, i);
1815 addPropertyWatcher(property, DirtyPropertyHint::Reference, texture);
1816 } else if (QQuick3DShaderUtilsTextureInput *textureInput = qobject_cast<QQuick3DShaderUtilsTextureInput *>(propValue.value<QObject *>()); textureInput && textureInput->texture()) {
1817 addTextureInputWatchers(property, textureInput);
1818 addPropertyWatcher(property, DirtyPropertyHint::Reference, textureInput);
1819 }
1820 }
1821 }
1822 }
1823}
1824
1825void QQuick3DPropertyChangedTracker::addPropertyWatcher(QMetaProperty property, DirtyPropertyHint hint, QQuick3DObject *object)
1826{
1827 if (property.isValid() && property.hasNotifySignal()) {
1828 // Check if we're already watching this property.
1829 const auto pid = property.propertyIndex();
1830 Q_ASSERT(pid != -1);
1831 auto it = std::find_if(m_trackedProperties.begin(), m_trackedProperties.end(), [pid](const Tracked &tp) { return tp.pid == pid; });
1832 const bool found = (it != m_trackedProperties.end());
1833 QQuick3DObject *oldObj = nullptr;
1834 if (!found) {
1835 QQuick3DPropertyWatcher *watcher = new QQuick3DPropertyWatcher(this, property);
1836 m_trackedProperties.push_back({watcher, object, pid});
1837 it = std::prev(m_trackedProperties.end());
1838 } else {
1839 oldObj = it->object;
1840 it->object = object;
1841 }
1842
1843 if (hint == DirtyPropertyHint::Reference) {
1844 const auto &sm = QQuick3DObjectPrivate::get(m_owner)->sceneManager;
1845
1846 // First check if the object changed
1847 const bool changed = (oldObj != object);
1848
1849 // Deref old object
1850 if (changed && oldObj) {
1851 QQuick3DObjectPrivate::get(oldObj)->derefSceneManager();
1852 QObject::disconnect(oldObj, &QObject::destroyed, m_owner, nullptr);
1853 }
1854
1855 // Ref new object
1856 if (changed && object) {
1857 QQuick3DObjectPrivate::get(object)->refSceneManager(*sm);
1858 QObject::connect(object, &QObject::destroyed, m_owner, [this, property](QObject *obj) {
1859 Q_UNUSED(obj);
1860 addPropertyWatcher(property, DirtyPropertyHint::Reference, nullptr);
1861 });
1862 }
1863 }
1864
1865 markTrackedPropertyDirty(property, hint);
1866 }
1867}
1868
1870{
1871 Q_UNUSED(property);
1872 Q_UNUSED(hint);
1873 QSSG_CHECK_X(false, "QQuick3DPropertyChangedTracker::onPropertyDirty implementation missing");
1874}
1875
1876QQuick3DPropertyWatcher::QQuick3DPropertyWatcher(QQuick3DPropertyChangedTracker *tracker, QMetaProperty property)
1877 : m_tracker(tracker)
1878 , m_property(property)
1879{
1880 Q_ASSERT(tracker != nullptr);
1881 Q_ASSERT(property.isValid() && property.hasNotifySignal());
1882
1883 const bool isPointerType = (property.metaType().flags().testFlag(QMetaType::IsPointer));
1884
1885 if (!isPointerType) {
1886 // Value change notification
1887 const auto idx = staticMetaObject.indexOfSlot("onValuePropertyChanged()");
1888 if (QSSG_GUARD_X(idx != -1, "Method not found!")) {
1889 auto onPropertyChangedMethod = staticMetaObject.method(idx);
1890 connect(m_tracker->m_owner, property.notifySignal(), this, onPropertyChangedMethod);
1891 }
1892 } else {
1893 // Pointer value change notification
1894 const auto idx = staticMetaObject.indexOfSlot("onPointerPropertyChanged()");
1895 if (QSSG_GUARD_X(idx != -1, "Method not found!")) {
1896 auto onPointerPropertyChangedMethod = staticMetaObject.method(idx);
1897 connect(m_tracker->m_owner, property.notifySignal(), this, onPointerPropertyChangedMethod);
1898 }
1899 }
1900}
1901
1902void QQuick3DPropertyWatcher::onValuePropertyChanged()
1903{
1904 m_tracker->markTrackedPropertyDirty(m_property, QQuick3DPropertyChangedTracker::DirtyPropertyHint::Value);
1905}
1906
1908{
1909 m_tracker->markTrackedPropertyDirty(m_property, QQuick3DPropertyChangedTracker::DirtyPropertyHint::Reference);;
1910}
1911
1912/*!
1913 \qmltype AddDefine
1914 \inherits Command
1915 \inqmlmodule QtQuick3D
1916 \brief Adds a preprocessor define to the shader compilation for a \l {RenderPass}{pass}.
1917 \since 6.11
1918
1919 The AddDefine type is used to inject a \c {#define} into the shader
1920 compilation for a \l RenderPass. This lets you branch shader behavior
1921 at compile time depending on which pass is being executed.
1922
1923 In the shader code, the define can be used with the standard preprocessor
1924 \c {#ifdef} / \c {#if} directives:
1925 \badcode
1926 #ifdef CUSTOM_PASS_MODE
1927 // code compiled only when this define is present
1928 #endif
1929 \endcode
1930
1931 \qml
1932 RenderPass {
1933 commands: [
1934 ColorAttachment { name: "color0"; target: colorTexture },
1935 AddDefine { name: "CUSTOM_PASS_MODE" },
1936 AddDefine { name: "OUTPUT_VERSION"; value: 2 }
1937 ]
1938 }
1939 \endqml
1940*/
1941
1942/*!
1943 \qmlproperty string AddDefine::name
1944 The name of the preprocessor macro to define. This becomes the identifier
1945 used in \c {#ifdef} or \c {#if} checks in the shader code.
1946*/
1947
1948/*!
1949 \qmlproperty int AddDefine::value
1950 The integer value assigned to the preprocessor macro. Defaults to \c 1.
1951 When non-zero, \c {#if NAME} evaluates to \c true in the shader.
1952*/
1953
1954QQuick3DShaderUtilsRenderPassAddDefine::QQuick3DShaderUtilsRenderPassAddDefine()
1955{
1956
1957}
1958
1959QQuick3DShaderUtilsRenderPassAddDefine::~QQuick3DShaderUtilsRenderPassAddDefine() = default;
1960
1961QSSGCommand *QQuick3DShaderUtilsRenderPassAddDefine::cloneCommand() {
1962 QSSGAddShaderDefine *cmd = new QSSGAddShaderDefine(command);
1963 return cmd;
1964}
1965
1966/*!
1967 \qmltype SubRenderPass
1968 \inherits Command
1969 \inqmlmodule QtQuick3D
1970 \brief Renders a sub-section of work within the same render target as the parent RenderPass.
1971 \since 6.11
1972
1973 SubRenderPass is a \l Command that subdivides a \l RenderPass into
1974 smaller, independently configured pieces while sharing the same render
1975 target. Only the parent \l RenderPass controls which textures are
1976 rendered into (via \l ColorAttachment and \l DepthTextureAttachment)
1977 and when the pass begins (and therefore when the render target is
1978 cleared). A SubRenderPass cannot set its own render target or clear
1979 settings — those are always inherited from the parent pass.
1980
1981 What a SubRenderPass \e can control is \e what gets rendered and
1982 \e how: it has its own \l{RenderPass::commands}{commands}, so it can
1983 carry a \l RenderablesFilter to select a subset of objects, a
1984 \l PipelineStateOverride to change depth/blend state, a different
1985 \l{RenderPass::materialMode}{materialMode}, and so on.
1986
1987 A typical use is a forward-rendering pipeline where a single color
1988 pass uses separate SubRenderPasses for the background, opaque
1989 objects, and transparent objects. All three sub-passes write into the
1990 same color and depth textures, but each has different pipeline state:
1991
1992 \qml
1993 RenderPass {
1994 id: mainColorPass
1995 commands: [
1996 // Render targets are set at the parent RenderPass level only
1997 ColorAttachment { name: "color0"; target: colorTexture },
1998 DepthTextureAttachment { target: depthTexture },
1999
2000 // Sub-pass 1: render the skybox background
2001 SubRenderPass { renderPass: skyboxSubPass },
2002
2003 // Sub-pass 2: opaque objects with depth testing and writing
2004 SubRenderPass { renderPass: opaqueSubPass },
2005
2006 // Sub-pass 3: transparent objects with blending, no depth write
2007 SubRenderPass { renderPass: transparentSubPass }
2008 ]
2009 }
2010
2011 // Sub-passes share mainColorPass's render target
2012 RenderPass {
2013 id: skyboxSubPass
2014 passMode: RenderPass.SkyboxPass
2015 }
2016
2017 RenderPass {
2018 id: opaqueSubPass
2019 commands: [
2020 RenderablesFilter { renderableTypes: RenderablesFilter.Opaque }
2021 ]
2022 }
2023
2024 RenderPass {
2025 id: transparentSubPass
2026 commands: [
2027 RenderablesFilter { renderableTypes: RenderablesFilter.Transparent },
2028 PipelineStateOverride {
2029 blendEnabled: true
2030 depthWriteEnabled: false
2031 }
2032 ]
2033 }
2034 \endqml
2035
2036 \sa RenderPass, RenderablesFilter, PipelineStateOverride
2037*/
2038
2039/*!
2040 \qmlproperty RenderPass SubRenderPass::renderPass
2041 The RenderPass that will be executed as a sub-pass when this command is processed.
2042*/
2043
2044QQuick3DShaderUtilsSubRenderPass::~QQuick3DShaderUtilsSubRenderPass()
2045{
2046
2047}
2048
2049QSSGCommand *QQuick3DShaderUtilsSubRenderPass::cloneCommand()
2050{
2051 QSSGSubRenderPass *cmd = nullptr;
2052
2053 if (!m_renderPass) {
2054 if (!m_hasWarnedAboutInvalidId) {
2055 qCWarning(lcSubRenderPass, "SubRenderPass: No render pass specified. Set the 'renderPass' property.");
2056 m_hasWarnedAboutInvalidId = true;
2057 }
2058 return nullptr;
2059 }
2060
2061 QSSGResourceId userPassId = QQuick3DExtensionHelpers::getResourceId(*m_renderPass);
2062 // Ensure we have a valid resource id before continuing.
2063 if (userPassId != QSSGResourceId::Invalid) {
2064 cmd = new QSSGSubRenderPass();
2065 cmd->setSubPass(userPassId);
2066 // Reset warning flag on success
2067 m_hasWarnedAboutInvalidId = false;
2068 } else {
2069 // Resource ID is not ready yet - this is expected during initialization
2070 qCDebug(lcSubRenderPass, "SubRenderPass: Render pass resource ID not yet available, will retry.");
2071 update();
2072 }
2073
2074 return cmd;
2075}
2076
2077QSSGRenderGraphObject *QQuick3DShaderUtilsSubRenderPass::updateSpatialNode(QSSGRenderGraphObject *node)
2078{
2079 return node;
2080}
2081
2082void QQuick3DShaderUtilsSubRenderPass::itemChange(ItemChange change, const ItemChangeData &value)
2083{
2084 if (change == QQuick3DObject::ItemSceneChange)
2085 updateSceneManager(value.sceneManager);
2086}
2087
2088void QQuick3DShaderUtilsSubRenderPass::updateSceneManager(QQuick3DSceneManager *sceneManager)
2089{
2090 if (sceneManager)
2091 QQuick3DObjectPrivate::refSceneManager(m_renderPass, *sceneManager);
2092 else
2093 QQuick3DObjectPrivate::derefSceneManager(m_renderPass);
2094}
2095
2096QQuick3DRenderPass *QQuick3DShaderUtilsSubRenderPass::renderPass() const
2097{
2098 return m_renderPass;
2099}
2100
2101void QQuick3DShaderUtilsSubRenderPass::setRenderPass(QQuick3DRenderPass *newRenderPass)
2102{
2103 if (m_renderPass == newRenderPass)
2104 return;
2105
2106 QQuick3DObjectPrivate::attachWatcher(this, &QQuick3DShaderUtilsSubRenderPass::setRenderPass, newRenderPass, m_renderPass);
2107
2108 if (newRenderPass)
2109 newRenderPass->update();
2110
2111 m_renderPass = newRenderPass;
2112 m_hasWarnedAboutInvalidId = false; // Reset warning flag when property changes
2113 emit renderPassChanged();
2114}
2115
2116/*!
2117 \qmltype DepthTextureAttachment
2118 \inherits Command
2119 \inqmlmodule QtQuick3D
2120 \brief Defines a depth texture attachment for a \l {RenderPass}{pass}.
2121 \since 6.11
2122
2123 DepthTextureAttachment attaches a \l RenderPassTexture with a depth
2124 format as the depth buffer for a \l RenderPass. Unlike
2125 \l DepthStencilAttachment (which uses an opaque render buffer), the
2126 resulting depth data is stored in a texture that can be read by
2127 subsequent passes via \l RenderOutputProvider.
2128
2129 \qml
2130 RenderPassTexture {
2131 id: depthTexture
2132 format: RenderPassTexture.Depth24
2133 }
2134
2135 RenderPass {
2136 commands: [
2137 ColorAttachment { name: "color0"; target: colorTexture },
2138 DepthTextureAttachment { target: depthTexture }
2139 ]
2140 }
2141
2142 // Expose the depth texture to a material via RenderOutputProvider
2143 Texture {
2144 textureProvider: RenderOutputProvider {
2145 textureSource: RenderOutputProvider.UserPassTexture
2146 renderPass: myRenderPass
2147 }
2148 }
2149 \endqml
2150
2151 \sa RenderPassTexture, DepthStencilAttachment, RenderOutputProvider
2152*/
2153
2154/*!
2155 \qmlproperty RenderPassTexture DepthTextureAttachment::target
2156 The \l RenderPassTexture that will be used as the depth attachment. The texture
2157 must have a depth-compatible format (e.g. \c Depth16, \c Depth24, \c Depth32,
2158 or \c Depth24Stencil8).
2159*/
2160
2161QQuick3DShaderUtilsRenderPassDepthTextureAttachment::~QQuick3DShaderUtilsRenderPassDepthTextureAttachment()
2162{
2163
2164}
2165
2166QSSGCommand *QQuick3DShaderUtilsRenderPassDepthTextureAttachment::cloneCommand()
2167{
2168 if (target) {
2169 QSSGDepthTextureAttachment *cmd = new QSSGDepthTextureAttachment(QByteArrayLiteral("__depth__"));
2170 cmd->m_textureCmd = target->command;
2171 return cmd;
2172 }
2173
2174 return nullptr;
2175}
2176
2177QQuick3DShaderUtilsRenderPassDepthStencilAttachment::~QQuick3DShaderUtilsRenderPassDepthStencilAttachment()
2178{
2179
2180}
2181
2182QSSGCommand *QQuick3DShaderUtilsRenderPassDepthStencilAttachment::cloneCommand()
2183{
2184 QSSGDepthStencilAttachment *cmd = new QSSGDepthStencilAttachment;
2185 return cmd;
2186}
2187
2188QT_END_NAMESPACE
void extractProperties(UniformPropertyList &outUniforms)
void addPropertyWatcher(QMetaProperty property, DirtyPropertyHint hint, QQuick3DObject *object=nullptr)
virtual void markTrackedPropertyDirty(QMetaProperty property, DirtyPropertyHint hint)=0
\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.