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 // Forward all our own signals to the base class changed() signal so that
977 // QQuick3DRenderPass can connect once and be notified of any change.
978 const QMetaObject *mo = metaObject();
979 const QMetaMethod changedSignal = QMetaMethod::fromSignal(&QQuick3DShaderUtilsRenderCommand::changed);
980 for (int i = QQuick3DShaderUtilsRenderCommand::staticMetaObject.methodCount(); i < mo->methodCount(); ++i) {
981 const QMetaMethod mm = mo->method(i);
982 if (mm.methodType() == QMetaMethod::Signal)
983 connect(this, mm, this, changedSignal);
984 }
985}
986
987QQuick3DShaderUtilsPipelineStateOverride::~QQuick3DShaderUtilsPipelineStateOverride()
988{
989
990}
991
992/*!
993 \qmlproperty bool PipelineStateOverride::depthTestEnabled
994 If set to \c true, enables depth testing for the render pass. If set to \c false, disables depth testing.
995 Setting this property to \c true requires a depth attachment for the render pass.
996*/
997bool QQuick3DShaderUtilsPipelineStateOverride::depthTestEnabled() const
998{
999 if (command.m_depthTestEnabled)
1000 return *command.m_depthTestEnabled;
1001 return false;
1002}
1003
1004void QQuick3DShaderUtilsPipelineStateOverride::setDepthTestEnabled(bool newDepthTestEnabled)
1005{
1006 if (command.m_depthTestEnabled && *command.m_depthTestEnabled == newDepthTestEnabled)
1007 return;
1008 command.m_depthTestEnabled = newDepthTestEnabled;
1009 emit depthTestEnabledChanged();
1010}
1011
1012void QQuick3DShaderUtilsPipelineStateOverride::resetDepthTestEnabled()
1013{
1014 command.m_depthTestEnabled.reset();
1015 emit depthTestEnabledChanged();
1016}
1017
1018/*!
1019 \qmlproperty bool PipelineStateOverride::depthWriteEnabled
1020 If set to \c true, enables depth writing for the render pass. If set to \c false, disables depth writing.
1021 Setting this property to \c true requires a depth attachment for the render pass.
1022*/
1023bool QQuick3DShaderUtilsPipelineStateOverride::depthWriteEnabled() const
1024{
1025 if (command.m_depthWriteEnabled)
1026 return *command.m_depthWriteEnabled;
1027 return false;
1028}
1029
1030void QQuick3DShaderUtilsPipelineStateOverride::setDepthWriteEnabled(bool newDepthWriteEnabled)
1031{
1032 if (command.m_depthWriteEnabled && *command.m_depthWriteEnabled == newDepthWriteEnabled)
1033 return;
1034 command.m_depthWriteEnabled = newDepthWriteEnabled;
1035 emit depthWriteEnabledChanged();
1036}
1037
1038void QQuick3DShaderUtilsPipelineStateOverride::resetDepthWriteEnabled()
1039{
1040 command.m_depthWriteEnabled.reset();
1041 emit depthWriteEnabledChanged();
1042}
1043
1044/*!
1045 \qmlproperty bool PipelineStateOverride::blendEnabled
1046 If set to \c true, enables blending for the render pass. If set to \c false, disables blending.
1047 For per-attachment blend settings, use the \c targetBlend0 through \c targetBlend7 properties.
1048*/
1049bool QQuick3DShaderUtilsPipelineStateOverride::blendEnabled() const
1050{
1051 if (command.m_blendEnabled)
1052 return *command.m_blendEnabled;
1053 return false;
1054}
1055
1056void QQuick3DShaderUtilsPipelineStateOverride::setBlendEnabled(bool newBlendEnabled)
1057{
1058 if (command.m_blendEnabled && *command.m_blendEnabled == newBlendEnabled)
1059 return;
1060 command.m_blendEnabled = newBlendEnabled;
1061 emit blendEnabledChanged();
1062}
1063
1064void QQuick3DShaderUtilsPipelineStateOverride::resetBlendEnabled()
1065{
1066 command.m_blendEnabled.reset();
1067 emit blendEnabledChanged();
1068}
1069
1070/*!
1071 \qmlproperty bool PipelineStateOverride::usesStencilReference
1072 If set to \c true, enables the use of the stencil reference value for the render pass. If set to \c false,
1073 disables the use of the stencil reference value.
1074*/
1075bool QQuick3DShaderUtilsPipelineStateOverride::usesStencilReference() const
1076{
1077 if (command.m_usesStencilReference)
1078 return *command.m_usesStencilReference;
1079 return false;
1080}
1081
1082void QQuick3DShaderUtilsPipelineStateOverride::setUsesStencilReference(bool newUsesStencilReference)
1083{
1084 if (command.m_usesStencilReference && *command.m_usesStencilReference == newUsesStencilReference)
1085 return;
1086 command.m_usesStencilReference = newUsesStencilReference;
1087 emit usesStencilReferenceChanged();
1088}
1089
1090void QQuick3DShaderUtilsPipelineStateOverride::resetUsesStencilReference()
1091{
1092 command.m_usesStencilReference.reset();
1093 emit usesStencilReferenceChanged();
1094}
1095
1096/*!
1097 \qmlproperty bool PipelineStateOverride::usesScissor
1098 If set to \c true, enables scissor testing for the render pass. If set to \c false,
1099 disables the scissor test. Use the \c scissor property to set the scissor rectangle.
1100*/
1101bool QQuick3DShaderUtilsPipelineStateOverride::usesScissor() const
1102{
1103 if (command.m_usesScissor)
1104 return *command.m_usesScissor;
1105 return false;
1106}
1107
1108void QQuick3DShaderUtilsPipelineStateOverride::setUsesScissor(bool newUsesScissor)
1109{
1110 if (command.m_usesScissor && *command.m_usesScissor == newUsesScissor)
1111 return;
1112 command.m_usesScissor = newUsesScissor;
1113 emit usesScissorChanged();
1114}
1115
1116void QQuick3DShaderUtilsPipelineStateOverride::resetUsesScissor()
1117{
1118 command.m_usesScissor.reset();
1119 emit usesScissorChanged();
1120}
1121
1122/*!
1123 \qmlproperty enumeration PipelineStateOverride::depthFunction
1124 Sets the depth comparison function for the render pass.
1125
1126 \value PipelineStateOverride.Never The depth test never passes.
1127 \value PipelineStateOverride.Less The depth test passes when the incoming depth is less than the stored depth.
1128 \value PipelineStateOverride.Equal The depth test passes when the incoming depth equals the stored depth.
1129 \value PipelineStateOverride.LessOrEqual The depth test passes when the incoming depth is less than or equal to the stored depth.
1130 \value PipelineStateOverride.Greater The depth test passes when the incoming depth is greater than the stored depth.
1131 \value PipelineStateOverride.NotEqual The depth test passes when the incoming depth does not equal the stored depth.
1132 \value PipelineStateOverride.GreaterOrEqual The depth test passes when the incoming depth is greater than or equal to the stored depth.
1133 \value PipelineStateOverride.Always The depth test always passes.
1134*/
1135QQuick3DShaderUtilsPipelineStateOverride::CompareOperation QQuick3DShaderUtilsPipelineStateOverride::depthFunction() const
1136{
1137 if (command.m_depthFunction)
1138 return CompareOperation(*command.m_depthFunction);
1139 return CompareOperation::Less;
1140}
1141
1142void QQuick3DShaderUtilsPipelineStateOverride::setDepthFunction(CompareOperation newDepthFunction)
1143{
1144 if (command.m_depthFunction && *command.m_depthFunction == QRhiGraphicsPipeline::CompareOp(newDepthFunction))
1145 return;
1146 command.m_depthFunction = QRhiGraphicsPipeline::CompareOp(newDepthFunction);
1147 emit depthFunctionChanged();
1148}
1149
1150void QQuick3DShaderUtilsPipelineStateOverride::resetDepthFunction()
1151{
1152 command.m_depthFunction.reset();
1153 emit depthFunctionChanged();
1154}
1155
1156/*!
1157 \qmlproperty enumeration PipelineStateOverride::cullMode
1158 Sets the face culling mode for the render pass.
1159
1160 \value PipelineStateOverride.None No face culling.
1161 \value PipelineStateOverride.Front Front-facing polygons are culled.
1162 \value PipelineStateOverride.Back Back-facing polygons are culled.
1163*/
1164QQuick3DShaderUtilsPipelineStateOverride::CullMode QQuick3DShaderUtilsPipelineStateOverride::cullMode() const
1165{
1166 if (command.m_cullMode)
1167 return CullMode(*command.m_cullMode);
1168 return CullMode::Back;
1169}
1170
1171void QQuick3DShaderUtilsPipelineStateOverride::setCullMode(CullMode newCullMode)
1172{
1173 if (command.m_cullMode && *command.m_cullMode == QRhiGraphicsPipeline::CullMode(newCullMode))
1174 return;
1175 command.m_cullMode = QRhiGraphicsPipeline::CullMode(newCullMode);
1176 emit cullModeChanged();
1177}
1178
1179void QQuick3DShaderUtilsPipelineStateOverride::resetCullMode()
1180{
1181 command.m_cullMode.reset();
1182 emit cullModeChanged();
1183}
1184
1185/*!
1186 \qmlproperty enumeration PipelineStateOverride::polygonMode
1187 Sets the polygon rasterization mode for the render pass.
1188
1189 \value PipelineStateOverride.Fill Polygons are filled (default).
1190 \value PipelineStateOverride.Line Polygon edges are drawn as lines (wireframe).
1191*/
1192QQuick3DShaderUtilsPipelineStateOverride::PolygonMode QQuick3DShaderUtilsPipelineStateOverride::polygonMode() const
1193{
1194 if (command.m_polygonMode)
1195 return PolygonMode(*command.m_polygonMode);
1196 return PolygonMode::Fill;
1197}
1198
1199void QQuick3DShaderUtilsPipelineStateOverride::setPolygonMode(PolygonMode newPolygonMode)
1200{
1201 if (command.m_polygonMode && *command.m_polygonMode == QRhiGraphicsPipeline::PolygonMode(newPolygonMode))
1202 return;
1203 command.m_polygonMode = QRhiGraphicsPipeline::PolygonMode(newPolygonMode);
1204 emit polygonModeChanged();
1205}
1206
1207void QQuick3DShaderUtilsPipelineStateOverride::resetPolygonMode()
1208{
1209 command.m_polygonMode.reset();
1210 emit polygonModeChanged();
1211}
1212
1213/*!
1214 \qmlproperty uint PipelineStateOverride::stencilWriteMask
1215 Sets the stencil write mask for the render pass. Each bit controls whether the corresponding
1216 bit in the stencil buffer can be written.
1217*/
1218quint32 QQuick3DShaderUtilsPipelineStateOverride::stencilWriteMask() const
1219{
1220 if (command.m_stencilWriteMask)
1221 return *command.m_stencilWriteMask;
1222 return 0;
1223}
1224
1225void QQuick3DShaderUtilsPipelineStateOverride::setStencilWriteMask(quint32 newStencilWriteMask)
1226{
1227 if (command.m_stencilWriteMask && *command.m_stencilWriteMask == newStencilWriteMask)
1228 return;
1229 command.m_stencilWriteMask = newStencilWriteMask;
1230 emit stencilWriteMaskChanged();
1231}
1232
1233void QQuick3DShaderUtilsPipelineStateOverride::resetStencilWriteMask()
1234{
1235 command.m_stencilWriteMask.reset();
1236 emit stencilWriteMaskChanged();
1237}
1238
1239/*!
1240 \qmlproperty uint PipelineStateOverride::stencilReference
1241 Sets the stencil reference value for the render pass. This value is used in stencil
1242 comparison operations when \c usesStencilReference is \c true.
1243*/
1244quint32 QQuick3DShaderUtilsPipelineStateOverride::stencilReference() const
1245{
1246 if (command.m_stencilReference)
1247 return *command.m_stencilReference;
1248 return 0;
1249}
1250
1251void QQuick3DShaderUtilsPipelineStateOverride::setStencilReference(quint32 newStencilReference)
1252{
1253 if (command.m_stencilReference && *command.m_stencilReference == newStencilReference)
1254 return;
1255 command.m_stencilReference = newStencilReference;
1256 emit stencilReferenceChanged();
1257}
1258
1259void QQuick3DShaderUtilsPipelineStateOverride::resetStencilReference()
1260{
1261 command.m_stencilReference.reset();
1262 emit stencilReferenceChanged();
1263}
1264
1265/*!
1266 \qmlproperty rect PipelineStateOverride::viewport
1267 Sets the viewport rectangle for the render pass. The rectangle specifies the region
1268 of the render target to draw into, in pixels (x, y, width, height).
1269 If not set, the full render target dimensions are used.
1270*/
1271QRectF QQuick3DShaderUtilsPipelineStateOverride::viewport() const
1272{
1273 if (command.m_viewport) {
1274 const QRhiViewport &vp = *command.m_viewport;
1275 return QRectF(vp.viewport()[0], vp.viewport()[1], vp.viewport()[2], vp.viewport()[3]);
1276 }
1277 return QRectF();
1278}
1279
1280void QQuick3DShaderUtilsPipelineStateOverride::setViewport(const QRectF &newViewport)
1281{
1282 if (command.m_viewport) {
1283 const QRhiViewport &vp = *command.m_viewport;
1284 if (vp.viewport()[0] == newViewport.x() &&
1285 vp.viewport()[1] == newViewport.y() &&
1286 vp.viewport()[2] == newViewport.width() &&
1287 vp.viewport()[3] == newViewport.height())
1288 return;
1289 }
1290 command.m_viewport = QRhiViewport(newViewport.x(), newViewport.y(), newViewport.width(), newViewport.height());
1291 emit viewportChanged();
1292}
1293
1294void QQuick3DShaderUtilsPipelineStateOverride::resetViewport()
1295{
1296 command.m_viewport.reset();
1297 emit viewportChanged();
1298}
1299
1300/*!
1301 \qmlproperty rect PipelineStateOverride::scissor
1302 Sets the scissor rectangle for the render pass. Fragments outside this rectangle are
1303 discarded. Requires \c usesScissor to be \c true.
1304*/
1305QRect QQuick3DShaderUtilsPipelineStateOverride::scissor() const
1306{
1307 if (command.m_scissor) {
1308 const QRhiScissor &sc = *command.m_scissor;
1309 return QRect(sc.scissor()[0], sc.scissor()[1], sc.scissor()[2], sc.scissor()[3]);
1310 }
1311 return QRect();
1312}
1313
1314void QQuick3DShaderUtilsPipelineStateOverride::setScissor(const QRect &newScissor)
1315{
1316 if (command.m_viewport) {
1317 const QRhiScissor &sc = *command.m_scissor;
1318 if (sc.scissor()[0] == newScissor.x() &&
1319 sc.scissor()[1] == newScissor.y() &&
1320 sc.scissor()[2] == newScissor.width() &&
1321 sc.scissor()[3] == newScissor.height())
1322 return;
1323 }
1324 command.m_scissor = QRhiScissor(newScissor.x(), newScissor.y(), newScissor.width(), newScissor.height());
1325 emit scissorChanged();
1326}
1327
1328void QQuick3DShaderUtilsPipelineStateOverride::resetScissor()
1329{
1330 command.m_scissor.reset();
1331}
1332
1333/*!
1334 \qmlproperty renderTargetBlend PipelineStateOverride::targetBlend0
1335 Sets the blending parameters for color attachment 0 of the render pass.
1336 \sa renderTargetBlend
1337*/
1338QQuick3DRenderPassTargetBlend QQuick3DShaderUtilsPipelineStateOverride::targetBlend0() const
1339{
1340 if (command.m_targetBlend0)
1341 return *command.m_targetBlend0;
1342 return {};
1343}
1344
1345void QQuick3DShaderUtilsPipelineStateOverride::setTargetBlend0(const QQuick3DRenderPassTargetBlend &newTargetBlend0)
1346{
1347 if (command.m_targetBlend0 && QQuick3DRenderPassTargetBlend(*command.m_targetBlend0) == newTargetBlend0)
1348 return;
1349
1350 command.m_targetBlend0 = newTargetBlend0.toRhiTargetBlend();;
1351 emit targetBlend0Changed();
1352}
1353
1354void QQuick3DShaderUtilsPipelineStateOverride::resetTargetBlend0()
1355{
1356 command.m_targetBlend0.reset();
1357 emit targetBlend0Changed();
1358}
1359
1360/*!
1361 \qmlproperty renderTargetBlend PipelineStateOverride::targetBlend1
1362 Sets the blending parameters for color attachment 1 of the render pass.
1363 \sa renderTargetBlend
1364*/
1365QQuick3DRenderPassTargetBlend QQuick3DShaderUtilsPipelineStateOverride::targetBlend1() const
1366{
1367 if (command.m_targetBlend1)
1368 return *command.m_targetBlend1;
1369 return {};
1370}
1371
1372void QQuick3DShaderUtilsPipelineStateOverride::setTargetBlend1(const QQuick3DRenderPassTargetBlend &newTargetBlend1)
1373{
1374 if (command.m_targetBlend1 && QQuick3DRenderPassTargetBlend(*command.m_targetBlend1) == newTargetBlend1)
1375 return;
1376 command.m_targetBlend1 = newTargetBlend1.toRhiTargetBlend();
1377 emit targetBlend1Changed();
1378}
1379
1380void QQuick3DShaderUtilsPipelineStateOverride::resetTargetBlend1()
1381{
1382 command.m_targetBlend1.reset();
1383 emit targetBlend1Changed();
1384}
1385
1386/*!
1387 \qmlproperty renderTargetBlend PipelineStateOverride::targetBlend2
1388 Sets the blending parameters for color attachment 2 of the render pass.
1389 \sa renderTargetBlend
1390*/
1391QQuick3DRenderPassTargetBlend QQuick3DShaderUtilsPipelineStateOverride::targetBlend2() const
1392{
1393 if (command.m_targetBlend2)
1394 return *command.m_targetBlend2;
1395 return {};
1396}
1397
1398void QQuick3DShaderUtilsPipelineStateOverride::setTargetBlend2(const QQuick3DRenderPassTargetBlend &newTargetBlend2)
1399{
1400 if (command.m_targetBlend2 && QQuick3DRenderPassTargetBlend(*command.m_targetBlend2) == newTargetBlend2)
1401 return;
1402 command.m_targetBlend2 = newTargetBlend2.toRhiTargetBlend();
1403 emit targetBlend2Changed();
1404}
1405
1406void QQuick3DShaderUtilsPipelineStateOverride::resetTargetBlend2()
1407{
1408 command.m_targetBlend2.reset();
1409 emit targetBlend2Changed();
1410}
1411
1412/*!
1413 \qmlproperty renderTargetBlend PipelineStateOverride::targetBlend3
1414 Sets the blending parameters for color attachment 3 of the render pass.
1415 \sa renderTargetBlend
1416*/
1417QQuick3DRenderPassTargetBlend QQuick3DShaderUtilsPipelineStateOverride::targetBlend3() const
1418{
1419 if (command.m_targetBlend3)
1420 return *command.m_targetBlend3;
1421 return {};
1422}
1423
1424void QQuick3DShaderUtilsPipelineStateOverride::setTargetBlend3(const QQuick3DRenderPassTargetBlend &newTargetBlend3)
1425{
1426 if (command.m_targetBlend3 && QQuick3DRenderPassTargetBlend(*command.m_targetBlend3) == newTargetBlend3)
1427 return;
1428 command.m_targetBlend3 = newTargetBlend3.toRhiTargetBlend();
1429 emit targetBlend3Changed();
1430}
1431
1432void QQuick3DShaderUtilsPipelineStateOverride::resetTargetBlend3()
1433{
1434 command.m_targetBlend3.reset();
1435 emit targetBlend3Changed();
1436}
1437
1438/*!
1439 \qmlproperty renderTargetBlend PipelineStateOverride::targetBlend4
1440 Sets the blending parameters for color attachment 4 of the render pass.
1441 \sa renderTargetBlend
1442*/
1443QQuick3DRenderPassTargetBlend QQuick3DShaderUtilsPipelineStateOverride::targetBlend4() const
1444{
1445 if (command.m_targetBlend4)
1446 return *command.m_targetBlend4;
1447 return {};
1448}
1449
1450void QQuick3DShaderUtilsPipelineStateOverride::setTargetBlend4(const QQuick3DRenderPassTargetBlend &newTargetBlend4)
1451{
1452 if (command.m_targetBlend4 && QQuick3DRenderPassTargetBlend(*command.m_targetBlend4) == newTargetBlend4)
1453 return;
1454 command.m_targetBlend4 = newTargetBlend4.toRhiTargetBlend();
1455 emit targetBlend4Changed();
1456}
1457
1458void QQuick3DShaderUtilsPipelineStateOverride::resetTargetBlend4()
1459{
1460 command.m_targetBlend4.reset();
1461 emit targetBlend4Changed();
1462}
1463
1464/*!
1465 \qmlproperty renderTargetBlend PipelineStateOverride::targetBlend5
1466 Sets the blending parameters for color attachment 5 of the render pass.
1467 \sa renderTargetBlend
1468*/
1469QQuick3DRenderPassTargetBlend QQuick3DShaderUtilsPipelineStateOverride::targetBlend5() const
1470{
1471 if (command.m_targetBlend5)
1472 return *command.m_targetBlend5;
1473 return {};
1474}
1475
1476void QQuick3DShaderUtilsPipelineStateOverride::setTargetBlend5(const QQuick3DRenderPassTargetBlend &newTargetBlend5)
1477{
1478 if (command.m_targetBlend5 && QQuick3DRenderPassTargetBlend(*command.m_targetBlend5) == newTargetBlend5)
1479 return;
1480 command.m_targetBlend5 = newTargetBlend5.toRhiTargetBlend();
1481 emit targetBlend5Changed();
1482}
1483
1484void QQuick3DShaderUtilsPipelineStateOverride::resetTargetBlend5()
1485{
1486 command.m_targetBlend5.reset();
1487 emit targetBlend5Changed();
1488}
1489
1490/*!
1491 \qmlproperty renderTargetBlend PipelineStateOverride::targetBlend6
1492 Sets the blending parameters for color attachment 6 of the render pass.
1493 \sa renderTargetBlend
1494*/
1495QQuick3DRenderPassTargetBlend QQuick3DShaderUtilsPipelineStateOverride::targetBlend6() const
1496{
1497 if (command.m_targetBlend6)
1498 return *command.m_targetBlend6;
1499 return {};
1500}
1501
1502void QQuick3DShaderUtilsPipelineStateOverride::setTargetBlend6(const QQuick3DRenderPassTargetBlend &newTargetBlend6)
1503{
1504 if (command.m_targetBlend6 && QQuick3DRenderPassTargetBlend(*command.m_targetBlend6) == newTargetBlend6)
1505 return;
1506 command.m_targetBlend6 = newTargetBlend6.toRhiTargetBlend();
1507 emit targetBlend6Changed();
1508}
1509
1510void QQuick3DShaderUtilsPipelineStateOverride::resetTargetBlend6()
1511{
1512 command.m_targetBlend6.reset();
1513 emit targetBlend6Changed();
1514}
1515
1516/*!
1517 \qmlproperty renderTargetBlend PipelineStateOverride::targetBlend7
1518 Sets the blending parameters for color attachment 7 of the render pass.
1519 \sa renderTargetBlend
1520*/
1521QQuick3DRenderPassTargetBlend QQuick3DShaderUtilsPipelineStateOverride::targetBlend7() const
1522{
1523 if (command.m_targetBlend7)
1524 return *command.m_targetBlend7;
1525 return {};
1526}
1527
1528void QQuick3DShaderUtilsPipelineStateOverride::setTargetBlend7(const QQuick3DRenderPassTargetBlend &newTargetBlend7)
1529{
1530 if (command.m_targetBlend7 && QQuick3DRenderPassTargetBlend(*command.m_targetBlend7) == newTargetBlend7)
1531 return;
1532 command.m_targetBlend7 = newTargetBlend7.toRhiTargetBlend();
1533 emit targetBlend7Changed();
1534}
1535
1536void QQuick3DShaderUtilsPipelineStateOverride::resetTargetBlend7()
1537{
1538 command.m_targetBlend7.reset();
1539 emit targetBlend7Changed();
1540}
1541
1542/*!
1543 \qmltype RenderPassTexture
1544 \inherits Object3D
1545 \inqmlmodule QtQuick3D
1546 \brief Defines a texture to be used as a render target in a \l {RenderPass}{pass}.
1547 \since 6.11
1548
1549 RenderPassTexture declares an off-screen texture that a \l RenderPass can
1550 render into. The texture is automatically created and managed by the
1551 rendering system. Its size tracks the \l View3D viewport by default.
1552
1553 Once declared, attach the texture to a pass via \l ColorAttachment (for
1554 color output) or \l DepthTextureAttachment (for depth output). The
1555 resulting texture can then be accessed from a subsequent pass or a
1556 \l Texture component using \l RenderOutputProvider.
1557
1558 \qml
1559 // Declare a float color buffer
1560 RenderPassTexture {
1561 id: hdrColorTexture
1562 format: RenderPassTexture.RGBA16F
1563 }
1564
1565 RenderPass {
1566 commands: [
1567 ColorAttachment {
1568 name: "color0"
1569 target: hdrColorTexture
1570 }
1571 ]
1572 }
1573 \endqml
1574
1575 \sa ColorAttachment, DepthTextureAttachment, RenderOutputProvider
1576*/
1577
1578/*!
1579 \qmlproperty enumeration RenderPassTexture::format
1580 Sets the format of the render target texture.
1581
1582 Available formats are:
1583
1584 \value RenderPassTexture.Unknown
1585 \value RenderPassTexture.RGBA8
1586 \value RenderPassTexture.RGBA16F
1587 \value RenderPassTexture.RGBA32F
1588 \value RenderPassTexture.R8
1589 \value RenderPassTexture.R16
1590 \value RenderPassTexture.R16F
1591 \value RenderPassTexture.R32F
1592 \value RenderPassTexture.Depth16
1593 \value RenderPassTexture.Depth24
1594 \value RenderPassTexture.Depth32
1595 \value RenderPassTexture.Depth24Stencil8
1596*/
1597
1598QQuick3DShaderUtilsRenderPassTexture::TextureFormat QQuick3DShaderUtilsRenderPassTexture::format() const
1599{
1600 return fromRenderTextureFormat(command->format());
1601}
1602
1603QQuick3DShaderUtilsRenderPassTexture::~QQuick3DShaderUtilsRenderPassTexture()
1604{
1605
1606}
1607
1608void QQuick3DShaderUtilsRenderPassTexture::setFormat(TextureFormat newFormat)
1609{
1610 if (!command)
1611 command = std::make_shared<QSSGAllocateTexture>();
1612 command->setFormat(asRenderTextureFormat(newFormat));
1613}
1614
1615QSSGRenderTextureFormat QQuick3DShaderUtilsRenderPassTexture::asRenderTextureFormat(TextureFormat fmt)
1616{
1617 switch (fmt) {
1618 case TextureFormat::Unknown: return QSSGRenderTextureFormat::Unknown;
1619 case TextureFormat::RGBA8: return QSSGRenderTextureFormat::RGBA8;
1620 case TextureFormat::RGBA16F: return QSSGRenderTextureFormat::RGBA16F;
1621 case TextureFormat::RGBA32F: return QSSGRenderTextureFormat::RGBA32F;
1622 case TextureFormat::R8: return QSSGRenderTextureFormat::R8;
1623 case TextureFormat::R16: return QSSGRenderTextureFormat::R16;
1624 case TextureFormat::R16F: return QSSGRenderTextureFormat::R16F;
1625 case TextureFormat::R32F: return QSSGRenderTextureFormat::R32F;
1626 case TextureFormat::Depth16: return QSSGRenderTextureFormat::Depth16;
1627 case TextureFormat::Depth24: return QSSGRenderTextureFormat::Depth24;
1628 case TextureFormat::Depth32: return QSSGRenderTextureFormat::Depth32;
1629 case TextureFormat::Depth24Stencil8: return QSSGRenderTextureFormat::Depth24Stencil8;
1630 default:
1631 break;
1632 }
1633 return QSSGRenderTextureFormat::Unknown;
1634}
1635
1636QQuick3DShaderUtilsRenderPassTexture::TextureFormat QQuick3DShaderUtilsRenderPassTexture::fromRenderTextureFormat(QSSGRenderTextureFormat fmt)
1637{
1638 switch (fmt.format) {
1639 case QSSGRenderTextureFormat::Unknown: return TextureFormat::Unknown;
1640 case QSSGRenderTextureFormat::RGBA8: return TextureFormat::RGBA8;
1641 case QSSGRenderTextureFormat::RGBA16F: return TextureFormat::RGBA16F;
1642 case QSSGRenderTextureFormat::RGBA32F: return TextureFormat::RGBA32F;
1643 case QSSGRenderTextureFormat::R8: return TextureFormat::R8;
1644 case QSSGRenderTextureFormat::R16: return TextureFormat::R16;
1645 case QSSGRenderTextureFormat::R16F: return TextureFormat::R16F;
1646 case QSSGRenderTextureFormat::R32F: return TextureFormat::R32F;
1647 case QSSGRenderTextureFormat::Depth16: return TextureFormat::Depth16;
1648 case QSSGRenderTextureFormat::Depth24: return TextureFormat::Depth24;
1649 case QSSGRenderTextureFormat::Depth32: return TextureFormat::Depth32;
1650 case QSSGRenderTextureFormat::Depth24Stencil8: return TextureFormat::Depth24Stencil8;
1651 default:
1652 break;
1653 }
1654 return TextureFormat::Unknown;
1655}
1656
1657/*!
1658 \qmltype ColorAttachment
1659 \inherits Command
1660 \inqmlmodule QtQuick3D
1661 \brief Defines a color attachment for a \l {RenderPass}{pass}.
1662 \since 6.11
1663
1664 The ColorAttachment type is used to specify a color attachment for a \l RenderPass.
1665 The \l name property is used to identify the attachment within the render pass.
1666 If the \l {RenderPass.AugmentMaterial}{AugmentMaterial} mode is used, the name will be
1667 exposed as an output with \c name in the fragment shader.
1668
1669 \sa RenderPassTexture
1670
1671 \qml
1672 RenderPass {
1673 // Define a render target texture
1674 RenderPassTexture {
1675 id: colorTexture
1676 format: RenderPassTexture.RGBA16F
1677 }
1678
1679 commands: [
1680 // Define a color attachment using the texture
1681 ColorAttachment {
1682 name: "color0"
1683 target: colorTexture
1684 }
1685 ]
1686 }
1687 \endqml
1688*/
1689
1690/*!
1691 \qmlproperty RenderPassTexture ColorAttachment::target
1692 The \l RenderPassTexture that will be used as the color attachment. The texture
1693 must have a color-compatible format (e.g. \c RGBA8, \c RGBA16F).
1694*/
1695
1696/*!
1697 \qmlproperty string ColorAttachment::name
1698 The name used to identify this color attachment within the render pass. When
1699 \l {RenderPass::materialMode}{materialMode} is set to \c AugmentMaterial, this name
1700 is exposed as a fragment shader output variable, allowing custom shader code to
1701 write to this attachment.
1702*/
1703
1704QQuick3DShaderUtilsRenderPassColorAttachment::~QQuick3DShaderUtilsRenderPassColorAttachment()
1705{
1706
1707}
1708
1709QByteArray QQuick3DShaderUtilsRenderPassColorAttachment::name() const
1710{
1711 return m_name;
1712}
1713
1714void QQuick3DShaderUtilsRenderPassColorAttachment::setName(const QByteArray &newName)
1715{
1716 m_name = newName;
1717}
1718
1719QSSGCommand *QQuick3DShaderUtilsRenderPassColorAttachment::cloneCommand()
1720{
1721 if (target) {
1722 QSSGColorAttachment *cmd = new QSSGColorAttachment(m_name);
1723 cmd->m_textureCmd = target->command;
1724 return cmd;
1725 }
1726
1727 return nullptr;
1728}
1729
1730QQuick3DPropertyChangedTracker::~QQuick3DPropertyChangedTracker()
1731{
1732
1733}
1734
1735std::optional<QQuick3DPropertyChangedTracker::UniformProperty> QQuick3DPropertyChangedTracker::createOrUpdateTrackedProperty(const QMetaProperty property,
1736 bool addWatchers)
1737{
1738 Q_ASSERT(property.isValid());
1739
1740 std::optional<QQuick3DPropertyChangedTracker::UniformProperty> result;
1741 const int propertyIndex = property.propertyIndex();
1742
1743 static auto getSamplerHint = [](const QQuick3DTexture &texture) {
1744 if (auto *po = QQuick3DObjectPrivate::get(&texture)) {
1745 if (po->type == QQuick3DObjectPrivate::Type::TextureProvider) {
1746 auto textureProvider = static_cast<QQuick3DTextureProviderExtension *>(texture.textureProvider());
1747 switch (textureProvider->samplerHint()) {
1748 case QQuick3DTextureProviderExtension::SamplerHint::Sampler2D:
1749 return QSSGRenderSamplerType::Sampler2D;
1750 case QQuick3DTextureProviderExtension::SamplerHint::Sampler2DArray:
1751 return QSSGRenderSamplerType::Sampler2DArray;
1752 case QQuick3DTextureProviderExtension::SamplerHint::Sampler3D:
1753 return QSSGRenderSamplerType::Sampler3D;
1754 case QQuick3DTextureProviderExtension::SamplerHint::SamplerCube:
1755 return QSSGRenderSamplerType::SamplerCube;
1756 case QQuick3DTextureProviderExtension::SamplerHint::SamplerCubeArray:
1757 return QSSGRenderSamplerType::SamplerCubeArray;
1758 case QQuick3DTextureProviderExtension::SamplerHint::SamplerBuffer:
1759 return QSSGRenderSamplerType::SamplerBuffer;
1760 }
1761 } else if (po->type == QQuick3DObjectPrivate::Type::ImageCube) {
1762 return QSSGRenderSamplerType::SamplerCube;
1763 } else if (texture.textureData() && texture.textureData()->depth() > 0) {
1764 return QSSGRenderSamplerType::Sampler3D;
1765 }
1766 }
1767
1768 return QSSGRenderSamplerType::Sampler2D;
1769 };
1770
1771 const auto addTextureToUniforms =
1772 [&](const char *name, QQuick3DTexture *texture, int propertyIndex) -> QQuick3DPropertyChangedTracker::UniformProperty {
1773 QSSGRenderImage *ri = static_cast<QSSGRenderImage *>(QQuick3DObjectPrivate::get(texture)->spatialNode);
1774 auto samplerName = QSSGBaseTypeHelpers::toString(getSamplerHint(*texture));
1775 if (addWatchers) {
1776 if (auto textureProvider = qobject_cast<QQuick3DTextureProviderExtension *>(texture->textureProvider())) {
1777 // FIXME? We don't have a way of adding watchers after the initial extraction so switching
1778 // textureProvider during runtime will not give a callback.
1779 QObject::connect(textureProvider, &QQuick3DTextureProviderExtension::surfaceChanged, m_owner, [this, property, texture]() {
1780 addPropertyWatcher(property, DirtyPropertyHint::Reference, texture);
1781 });
1782 }
1783 QObject::connect(texture, &QQuick3DTexture::textureProviderChanged, m_owner, [this, property, texture]() {
1784 addPropertyWatcher(property, DirtyPropertyHint::Reference, texture);
1785 });
1786 // Re-register watcher when the texture backend node is (re)created
1787 QObject::connect(texture, &QQuick3DTexture::textureInternalChange, m_owner,
1788 [this, property, texture](QQuick3DTextureInternalChange change) {
1789 if (change.type == QQuick3DTextureInternalChange::Type::BackendNode)
1790 addPropertyWatcher(property, DirtyPropertyHint::Reference, texture);
1791 });
1792 }
1793
1794 return UniformProperty(name, samplerName, QVariant::fromValue(ri), QSSGRenderShaderValue::Texture, propertyIndex);
1795 };
1796
1797 // The TextureInput type needs extra watchers for its properties...
1798 const auto addTextureInputWatchers = [&](QMetaProperty property, QQuick3DShaderUtilsTextureInput *textureInput) {
1799 QObject::connect(textureInput, &QQuick3DShaderUtilsTextureInput::enabledChanged, m_owner, [this, property, textureInput]() {
1800 addPropertyWatcher(property, DirtyPropertyHint::Reference, textureInput);
1801 });
1802 QObject::connect(textureInput, &QQuick3DShaderUtilsTextureInput::textureChanged, m_owner, [this, property, textureInput]() {
1803 addPropertyWatcher(property, DirtyPropertyHint::Reference, textureInput);
1804 });
1805 };
1806
1807 const char *name = property.name();
1808 QMetaType propType = property.metaType();
1809 QVariant propValue = property.read(m_owner);
1810 if (propType == QMetaType(QMetaType::QVariant))
1811 propType = propValue.metaType();
1812
1813 const auto type = QSSGShaderUtils::uniformType(propType);
1814 if (type != QSSGRenderShaderValue::Unknown) {
1815 result = UniformProperty(name, QSSGShaderUtils::uniformTypeName(propType), propValue, QSSGShaderUtils::uniformType(propType), propertyIndex);
1816 if (addWatchers)
1817 addPropertyWatcher(property, DirtyPropertyHint::Value);
1818 } else {
1819 if (propType.id() >= QMetaType::User) {
1820 if (propType.id() == qMetaTypeId<QQuick3DTexture *>()) {
1821 if (QQuick3DTexture *texture = property.read(m_owner).value<QQuick3DTexture *>()) {
1822 result = addTextureToUniforms(name, texture, propertyIndex);
1823 if (addWatchers)
1824 addPropertyWatcher(property, DirtyPropertyHint::Reference, texture);
1825 }
1826 } else if (propType.id() == qMetaTypeId<QQuick3DShaderUtilsTextureInput *>()) { // For compatibility, also check for texture input types
1827 if (QQuick3DShaderUtilsTextureInput *textureInput = property.read(m_owner).value<QQuick3DShaderUtilsTextureInput *>();
1828 textureInput && textureInput->texture()) {
1829 result = addTextureToUniforms(property.name(), textureInput->texture(), propertyIndex);
1830 if (addWatchers) {
1831 addTextureInputWatchers(property, textureInput);
1832 addPropertyWatcher(property, DirtyPropertyHint::Reference, textureInput);
1833 }
1834 }
1835 }
1836 } else if (propType == QMetaType(QMetaType::QObjectStar)) {
1837 if (QQuick3DTexture *texture = qobject_cast<QQuick3DTexture *>(propValue.value<QObject *>())) {
1838 result = addTextureToUniforms(name, texture, propertyIndex);
1839 if (addWatchers)
1840 addPropertyWatcher(property, DirtyPropertyHint::Reference, texture);
1841 } else if (QQuick3DShaderUtilsTextureInput *textureInput = qobject_cast<QQuick3DShaderUtilsTextureInput *>(
1842 propValue.value<QObject *>());
1843 textureInput && textureInput->texture()) {
1844 result = addTextureToUniforms(property.name(), textureInput->texture(), propertyIndex);
1845 if (addWatchers) {
1846 addTextureInputWatchers(property, textureInput);
1847 addPropertyWatcher(property, DirtyPropertyHint::Reference, textureInput);
1848 }
1849 }
1850 }
1851 }
1852
1853 return result;
1854}
1855
1856QList<QQuick3DPropertyChangedTracker::UniformProperty> QQuick3DPropertyChangedTracker::extractProperties()
1857{
1858 if (!m_dirtyProperties.empty()) {
1859 for (int propertyIndex : std::as_const(m_dirtyProperties)) {
1860 auto it = std::lower_bound(m_propertyList.begin(),
1861 m_propertyList.end(),
1862 propertyIndex,
1863 [](const UniformProperty &p, int index) { return p.pid < index; });
1864 if (it == m_propertyList.end()) {
1865 Q_ASSERT(false);
1866 continue;
1867 }
1868
1869 QMetaProperty property = m_owner->metaObject()->property(propertyIndex);
1870 if (Q_UNLIKELY(!property.isValid())) {
1871 m_propertyList.erase(it);
1872 continue;
1873 }
1874
1875 if (auto trackedProperty = createOrUpdateTrackedProperty(property, false); trackedProperty.has_value())
1876 *it = *trackedProperty;
1877 else
1878 m_propertyList.erase(it);
1879 }
1880 m_dirtyProperties.clear();
1881 }
1882
1883 if (m_extracted)
1884 return m_propertyList;
1885
1886 auto metaObject = m_owner->metaObject();
1887
1888 // Properties -> uniforms
1889 const int propCount = metaObject->propertyCount();
1890 int propOffset = metaObject->propertyOffset();
1891 m_propertyList.reserve(propCount);
1892
1893 // Classes can have multilayered inheritance structure, so find the actual propOffset by
1894 // walking up the inheritance chain.
1895 const QMetaObject *superClass = metaObject->superClass();
1896 while (superClass && qstrcmp(superClass->className(), m_superClassName) != 0) {
1897 propOffset = superClass->propertyOffset();
1898 superClass = superClass->superClass();
1899 }
1900
1901 for (int i = propOffset; i != propCount; ++i) {
1902 const QMetaProperty property = metaObject->property(i);
1903 if (Q_UNLIKELY(!property.isValid()))
1904 continue;
1905 if (auto uniformProperty = createOrUpdateTrackedProperty(property, true); uniformProperty.has_value()) {
1906 m_propertyList.push_back(*uniformProperty);
1907 }
1908 }
1909
1910 m_dirtyProperties.reserve(m_propertyList.size());
1911
1912 m_extracted = true;
1913 return m_propertyList;
1914}
1915
1916void QQuick3DPropertyChangedTracker::addPropertyWatcher(QMetaProperty property, DirtyPropertyHint hint, QQuick3DObject *object)
1917{
1918 if (property.isValid() && property.hasNotifySignal()) {
1919 // Check if we're already watching this property.
1920 const auto pid = property.propertyIndex();
1921 Q_ASSERT(pid != -1);
1922 auto it = m_trackedProperties.find(pid);
1923 const bool found = (it != m_trackedProperties.end());
1924 QQuick3DObject *oldObj = nullptr;
1925 if (!found) {
1926 QQuick3DPropertyWatcher *watcher = new QQuick3DPropertyWatcher(this, property);
1927 it = m_trackedProperties.insert(pid, Tracked { watcher, object, pid });
1928 } else {
1929 oldObj = it->object;
1930 it->object = object;
1931 }
1932
1933 if (hint == DirtyPropertyHint::Reference) {
1934 const auto &sm = QQuick3DObjectPrivate::get(m_owner)->sceneManager;
1935
1936 // First check if the object changed
1937 const bool changed = (oldObj != object);
1938
1939 // Deref old object
1940 if (changed && oldObj) {
1941 QQuick3DObjectPrivate::get(oldObj)->derefSceneManager();
1942 QObject::disconnect(oldObj, &QObject::destroyed, m_owner, nullptr);
1943 }
1944
1945 // Ref new object
1946 if (changed && object) {
1947 QQuick3DObjectPrivate::get(object)->refSceneManager(*sm);
1948 QObject::connect(object, &QObject::destroyed, m_owner, [this, property](QObject *obj) {
1949 Q_UNUSED(obj);
1950 addPropertyWatcher(property, DirtyPropertyHint::Reference, nullptr);
1951 });
1952 }
1953 }
1954
1955 m_dirtyProperties.push_back(property.propertyIndex());
1956 markTrackedPropertyDirty(property, hint);
1957 }
1958}
1959
1960void QQuick3DPropertyChangedTracker::markTrackedPropertyDirty(QMetaProperty property, DirtyPropertyHint hint)
1961{
1962 Q_UNUSED(property);
1963 Q_UNUSED(hint);
1964 QSSG_CHECK_X(false, "QQuick3DPropertyChangedTracker::onPropertyDirty implementation missing");
1965}
1966
1967QQuick3DPropertyWatcher::QQuick3DPropertyWatcher(QQuick3DPropertyChangedTracker *tracker, QMetaProperty property)
1968 : m_tracker(tracker)
1969 , m_property(property)
1970{
1971 Q_ASSERT(tracker != nullptr);
1972 Q_ASSERT(property.isValid() && property.hasNotifySignal());
1973
1974 const bool isPointerType = (property.metaType().flags().testFlag(QMetaType::IsPointer));
1975
1976 if (!isPointerType) {
1977 // Value change notification
1978 const auto idx = staticMetaObject.indexOfSlot("onValuePropertyChanged()");
1979 if (QSSG_GUARD_X(idx != -1, "Method not found!")) {
1980 auto onPropertyChangedMethod = staticMetaObject.method(idx);
1981 connect(m_tracker->m_owner, property.notifySignal(), this, onPropertyChangedMethod);
1982 }
1983 } else {
1984 // Pointer value change notification
1985 const auto idx = staticMetaObject.indexOfSlot("onPointerPropertyChanged()");
1986 if (QSSG_GUARD_X(idx != -1, "Method not found!")) {
1987 auto onPointerPropertyChangedMethod = staticMetaObject.method(idx);
1988 connect(m_tracker->m_owner, property.notifySignal(), this, onPointerPropertyChangedMethod);
1989 }
1990 }
1991}
1992
1993void QQuick3DPropertyWatcher::onValuePropertyChanged()
1994{
1995 m_tracker->m_dirtyProperties.push_back(m_property.propertyIndex());
1996 m_tracker->markTrackedPropertyDirty(m_property, QQuick3DPropertyChangedTracker::DirtyPropertyHint::Value);
1997}
1998
2000{
2001 m_tracker->m_dirtyProperties.push_back(m_property.propertyIndex());
2002 m_tracker->markTrackedPropertyDirty(m_property, QQuick3DPropertyChangedTracker::DirtyPropertyHint::Reference);;
2003}
2004
2005/*!
2006 \qmltype AddDefine
2007 \inherits Command
2008 \inqmlmodule QtQuick3D
2009 \brief Adds a preprocessor define to the shader compilation for a \l {RenderPass}{pass}.
2010 \since 6.11
2011
2012 The AddDefine type is used to inject a \c {#define} into the shader
2013 compilation for a \l RenderPass. This lets you branch shader behavior
2014 at compile time depending on which pass is being executed.
2015
2016 In the shader code, the define can be used with the standard preprocessor
2017 \c {#ifdef} / \c {#if} directives:
2018 \badcode
2019 #ifdef CUSTOM_PASS_MODE
2020 // code compiled only when this define is present
2021 #endif
2022 \endcode
2023
2024 \qml
2025 RenderPass {
2026 commands: [
2027 ColorAttachment { name: "color0"; target: colorTexture },
2028 AddDefine { name: "CUSTOM_PASS_MODE" },
2029 AddDefine { name: "OUTPUT_VERSION"; value: 2 }
2030 ]
2031 }
2032 \endqml
2033*/
2034
2035/*!
2036 \qmlproperty string AddDefine::name
2037 The name of the preprocessor macro to define. This becomes the identifier
2038 used in \c {#ifdef} or \c {#if} checks in the shader code.
2039*/
2040
2041/*!
2042 \qmlproperty int AddDefine::value
2043 The integer value assigned to the preprocessor macro. Defaults to \c 1.
2044 When non-zero, \c {#if NAME} evaluates to \c true in the shader.
2045*/
2046
2047QQuick3DShaderUtilsRenderPassAddDefine::QQuick3DShaderUtilsRenderPassAddDefine()
2048{
2049
2050}
2051
2052QQuick3DShaderUtilsRenderPassAddDefine::~QQuick3DShaderUtilsRenderPassAddDefine() = default;
2053
2054QByteArray QQuick3DShaderUtilsRenderPassAddDefine::name() const
2055{
2056 return command.m_name;
2057}
2058
2059void QQuick3DShaderUtilsRenderPassAddDefine::setName(const QByteArray &newName)
2060{
2061 if (command.m_name != newName) {
2062 command.m_name = newName;
2063 emit nameChanged();
2064 emit changed();
2065 }
2066}
2067
2068int QQuick3DShaderUtilsRenderPassAddDefine::value() const
2069{
2070 return command.m_value;
2071}
2072
2073void QQuick3DShaderUtilsRenderPassAddDefine::setValue(int newValue)
2074{
2075 if (command.m_value != newValue) {
2076 command.m_value = newValue;
2077 emit valueChanged();
2078 emit changed();
2079 }
2080}
2081
2082QSSGCommand *QQuick3DShaderUtilsRenderPassAddDefine::cloneCommand() {
2083 QSSGAddShaderDefine *cmd = new QSSGAddShaderDefine(command);
2084 return cmd;
2085}
2086
2087/*!
2088 \qmltype SubRenderPass
2089 \inherits Command
2090 \inqmlmodule QtQuick3D
2091 \brief Renders a sub-section of work within the same render target as the parent RenderPass.
2092 \since 6.11
2093
2094 SubRenderPass is a \l Command that subdivides a \l RenderPass into
2095 smaller, independently configured pieces while sharing the same render
2096 target. Only the parent \l RenderPass controls which textures are
2097 rendered into (via \l ColorAttachment and \l DepthTextureAttachment)
2098 and when the pass begins (and therefore when the render target is
2099 cleared). A SubRenderPass cannot set its own render target or clear
2100 settings — those are always inherited from the parent pass.
2101
2102 What a SubRenderPass \e can control is \e what gets rendered and
2103 \e how: it has its own \l{RenderPass::commands}{commands}, so it can
2104 carry a \l RenderablesFilter to select a subset of objects, a
2105 \l PipelineStateOverride to change depth/blend state, a different
2106 \l{RenderPass::materialMode}{materialMode}, and so on.
2107
2108 A typical use is a forward-rendering pipeline where a single color
2109 pass uses separate SubRenderPasses for the background, opaque
2110 objects, and transparent objects. All three sub-passes write into the
2111 same color and depth textures, but each has different pipeline state:
2112
2113 \qml
2114 RenderPass {
2115 id: mainColorPass
2116 commands: [
2117 // Render targets are set at the parent RenderPass level only
2118 ColorAttachment { name: "color0"; target: colorTexture },
2119 DepthTextureAttachment { target: depthTexture },
2120
2121 // Sub-pass 1: render the skybox background
2122 SubRenderPass { renderPass: skyboxSubPass },
2123
2124 // Sub-pass 2: opaque objects with depth testing and writing
2125 SubRenderPass { renderPass: opaqueSubPass },
2126
2127 // Sub-pass 3: transparent objects with blending, no depth write
2128 SubRenderPass { renderPass: transparentSubPass }
2129 ]
2130 }
2131
2132 // Sub-passes share mainColorPass's render target
2133 RenderPass {
2134 id: skyboxSubPass
2135 passMode: RenderPass.SkyboxPass
2136 }
2137
2138 RenderPass {
2139 id: opaqueSubPass
2140 commands: [
2141 RenderablesFilter { renderableTypes: RenderablesFilter.Opaque }
2142 ]
2143 }
2144
2145 RenderPass {
2146 id: transparentSubPass
2147 commands: [
2148 RenderablesFilter { renderableTypes: RenderablesFilter.Transparent },
2149 PipelineStateOverride {
2150 blendEnabled: true
2151 depthWriteEnabled: false
2152 }
2153 ]
2154 }
2155 \endqml
2156
2157 \sa RenderPass, RenderablesFilter, PipelineStateOverride
2158*/
2159
2160/*!
2161 \qmlproperty RenderPass SubRenderPass::renderPass
2162 The RenderPass that will be executed as a sub-pass when this command is processed.
2163*/
2164
2165QQuick3DShaderUtilsSubRenderPass::~QQuick3DShaderUtilsSubRenderPass()
2166{
2167
2168}
2169
2170QSSGCommand *QQuick3DShaderUtilsSubRenderPass::cloneCommand()
2171{
2172 QSSGSubRenderPass *cmd = nullptr;
2173
2174 if (!m_renderPass) {
2175 if (!m_hasWarnedAboutInvalidId) {
2176 qCWarning(lcSubRenderPass, "SubRenderPass: No render pass specified. Set the 'renderPass' property.");
2177 m_hasWarnedAboutInvalidId = true;
2178 }
2179 return nullptr;
2180 }
2181
2182 QSSGResourceId userPassId = QQuick3DExtensionHelpers::getResourceId(*m_renderPass);
2183 // Ensure we have a valid resource id before continuing.
2184 if (userPassId != QSSGResourceId::Invalid) {
2185 cmd = new QSSGSubRenderPass();
2186 cmd->setSubPass(userPassId);
2187 // Reset warning flag on success
2188 m_hasWarnedAboutInvalidId = false;
2189 } else {
2190 // Resource ID is not ready yet - this is expected during initialization
2191 qCDebug(lcSubRenderPass, "SubRenderPass: Render pass resource ID not yet available, will retry.");
2192 update();
2193 }
2194
2195 return cmd;
2196}
2197
2198QSSGRenderGraphObject *QQuick3DShaderUtilsSubRenderPass::updateSpatialNode(QSSGRenderGraphObject *node)
2199{
2200 return node;
2201}
2202
2203void QQuick3DShaderUtilsSubRenderPass::itemChange(ItemChange change, const ItemChangeData &value)
2204{
2205 if (change == QQuick3DObject::ItemSceneChange)
2206 updateSceneManager(value.sceneManager);
2207}
2208
2209void QQuick3DShaderUtilsSubRenderPass::updateSceneManager(QQuick3DSceneManager *sceneManager)
2210{
2211 if (sceneManager)
2212 QQuick3DObjectPrivate::refSceneManager(m_renderPass, *sceneManager);
2213 else
2214 QQuick3DObjectPrivate::derefSceneManager(m_renderPass);
2215}
2216
2217QQuick3DRenderPass *QQuick3DShaderUtilsSubRenderPass::renderPass() const
2218{
2219 return m_renderPass;
2220}
2221
2222void QQuick3DShaderUtilsSubRenderPass::setRenderPass(QQuick3DRenderPass *newRenderPass)
2223{
2224 if (m_renderPass == newRenderPass)
2225 return;
2226
2227 QQuick3DObjectPrivate::attachWatcher(this, &QQuick3DShaderUtilsSubRenderPass::setRenderPass, newRenderPass, m_renderPass);
2228
2229 if (newRenderPass)
2230 newRenderPass->update();
2231
2232 m_renderPass = newRenderPass;
2233 m_hasWarnedAboutInvalidId = false; // Reset warning flag when property changes
2234 emit renderPassChanged();
2235 emit changed();
2236}
2237
2238/*!
2239 \qmltype DepthTextureAttachment
2240 \inherits Command
2241 \inqmlmodule QtQuick3D
2242 \brief Defines a depth texture attachment for a \l {RenderPass}{pass}.
2243 \since 6.11
2244
2245 DepthTextureAttachment attaches a \l RenderPassTexture with a depth
2246 format as the depth buffer for a \l RenderPass. Unlike
2247 \l DepthStencilAttachment (which uses an opaque render buffer), the
2248 resulting depth data is stored in a texture that can be read by
2249 subsequent passes via \l RenderOutputProvider.
2250
2251 \qml
2252 RenderPassTexture {
2253 id: depthTexture
2254 format: RenderPassTexture.Depth24
2255 }
2256
2257 RenderPass {
2258 commands: [
2259 ColorAttachment { name: "color0"; target: colorTexture },
2260 DepthTextureAttachment { target: depthTexture }
2261 ]
2262 }
2263
2264 // Expose the depth texture to a material via RenderOutputProvider
2265 Texture {
2266 textureProvider: RenderOutputProvider {
2267 textureSource: RenderOutputProvider.UserPassTexture
2268 renderPass: myRenderPass
2269 }
2270 }
2271 \endqml
2272
2273 \sa RenderPassTexture, DepthStencilAttachment, RenderOutputProvider
2274*/
2275
2276/*!
2277 \qmlproperty RenderPassTexture DepthTextureAttachment::target
2278 The \l RenderPassTexture that will be used as the depth attachment. The texture
2279 must have a depth-compatible format (e.g. \c Depth16, \c Depth24, \c Depth32,
2280 or \c Depth24Stencil8).
2281*/
2282
2283QQuick3DShaderUtilsRenderPassDepthTextureAttachment::~QQuick3DShaderUtilsRenderPassDepthTextureAttachment()
2284{
2285
2286}
2287
2288QSSGCommand *QQuick3DShaderUtilsRenderPassDepthTextureAttachment::cloneCommand()
2289{
2290 if (target) {
2291 QSSGDepthTextureAttachment *cmd = new QSSGDepthTextureAttachment(QByteArrayLiteral("__depth__"));
2292 cmd->m_textureCmd = target->command;
2293 return cmd;
2294 }
2295
2296 return nullptr;
2297}
2298
2299QQuick3DShaderUtilsRenderPassDepthStencilAttachment::~QQuick3DShaderUtilsRenderPassDepthStencilAttachment()
2300{
2301
2302}
2303
2304QSSGCommand *QQuick3DShaderUtilsRenderPassDepthStencilAttachment::cloneCommand()
2305{
2306 QSSGDepthStencilAttachment *cmd = new QSSGDepthStencilAttachment;
2307 return cmd;
2308}
2309
2310QT_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.