Revision control

Copy as Markdown

// Copyright (C) 2020 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
// Qt-Security score:significant reason:default
#include "qquick3dshaderutils_p.h"
#include <QtCore/qfile.h>
#include <QtQml/qqmlcontext.h>
#include <QtQml/qqmlfile.h>
#include "qquick3dviewport_p.h"
#include "qquick3dcustommaterial_p.h"
#include "qquick3deffect_p.h"
#include "qquick3dtextureproviderextension.h"
#include <ssg/qssgrenderextensions.h>
#include <ssg/qquick3dextensionhelpers.h>
#include <QtQuick3DRuntimeRender/private/qssgrenderimage_p.h>
#include <QtCore/QLoggingCategory>
QT_BEGIN_NAMESPACE
Q_LOGGING_CATEGORY(lcSubRenderPass, "qt.quick3d.subrenderpass")
/*!
\qmltype Shader
\inherits QtObject
\inqmlmodule QtQuick3D
\brief Container component for defining shader code used by post-processing effects.
The Shader type is used for populating the \l{Pass::shaders}{shaders} list in the
render \l{Pass}{pass} of an \l Effect.
A shader is code which is executed directly on the graphic hardware at a particular
\l{Shader::stage}{stage} of the rendering pipeline.
\sa Effect
*/
/*!
\qmlproperty url Shader::shader
Specifies the name of the shader source file. For details on how to write shader code,
see the \l Effect documentation.
\warning Shader snippets are assumed to be trusted content. Application
developers are advised to carefully consider the potential implications
before allowing the loading of user-provided content that is not part of the
application.
*/
/*!
\qmlproperty enumeration Shader::stage
Specifies the stage of the rendering pipeline when the shader code will be executed.
The default is \c Shader.Fragment
\value Shader.Vertex The shader is a vertex shader. This code is run once per vertex
in the input geometry and can be used to modify it before the geometry is rasterized
(scan converted). In the case of effects, the input geometry is always a quad (four
vertexes representing the corners of the render target).
\value Shader.Fragment The shader is a fragment shader. After vertex processing,
the modified geometry is turned into fragments (rasterization). Then a fragment shader
is executed for each fragment, assigning a color to it. Fragments are a related concept
to pixels, but with additional information attached. Also, as a result of some
anti-aliasing strategies, there may be more than one fragment for each pixel in the
output.
*/
/*!
\qmltype TextureInput
\inherits QtObject
\inqmlmodule QtQuick3D
\brief Specifies a texture exposed to the shaders of a CustomMaterial or Effect.
This is a type which can be used for exposing a \l Texture to a shader, either
in the \l{Pass}{render pass} of an \l Effect, or in a \l CustomMaterial. It exists
primarily to assign a local name to the \l Texture that can be referenced from
shaders.
When a TextureInput property is declared in an \l Effect or a \l CustomMaterial,
it will automatically be available as a sampler in all shaders by its property
name.
*/
/*!
\qmlproperty Texture TextureInput::texture
The texture for which this TextureInput serves as an indirection.
*/
/*!
\qmlproperty bool TextureInput::enabled
The property determines if this TextureInput is enabled. The default value
is true. When disabled, the shaders of the effect sample a dummy, opaque
black texture instead of the one specified by \l texture.
*/
/*!
\qmltype Pass
\inherits QtObject
\inqmlmodule QtQuick3D
\brief Defines a render pass in an Effect.
An \l Effect may consist of multiple render passes. Each render pass has a
setup phase where the list of \l{Pass::commands}{render commands} are executed,
a \l{Pass::output}{output buffer} and a list of \l{Pass::shaders}{shaders} to
use for rendering the effect.
See the documentation for \l Effect for more details on how to set up multiple
rendering passes.
*/
/*!
\qmlproperty Buffer Pass::output
Specifies the output \l {Buffer}{buffer} of the pass.
*/
/*!
\qmlproperty list Pass::commands
Specifies the list of render \l {Command}{commands} of the pass.
*/
/*!
\qmlproperty list Pass::shaders
Specifies the list of \l {Shader}{shaders} of the pass.
*/
/*!
\qmltype Command
\inherits QtObject
\inqmlmodule QtQuick3D
\brief Supertype of commands to be performed as part of a pass in an Effect.
The Command type should not be instantiated by itself, but only exists as a
polymorphic supertype for the different actions that can be performed as part
of a \l{Pass}{render pass}.
\sa BufferInput, SetUniformValue, Effect
*/
/*!
\qmltype BufferInput
\inherits Command
\inqmlmodule QtQuick3D
\brief Defines an input buffer to be used as input for a pass of an Effect.
BufferInput is a \l Command which can be added to the list of commands in the \l Pass of
an \l Effect. When executed, it will expose the buffer as a sample to the shaders
in the render pass. The shaders must declare a sampler with the name given in the
BufferInput's \c sampler property.
This can be used for sharing intermediate results between the different passes of an
effect.
\sa TextureInput
*/
/*!
\qmlproperty Buffer BufferInput::buffer
Specifies the \l {Buffer}{buffer} which should be exposed to the shader.
*/
/*!
\qmlproperty string BufferInput::sampler
Specifies the name under which the buffer is exposed to the shader.
When this property is not set, the buffer is exposed with the built-in name \c INPUT.
*/
/*!
\qmltype Buffer
\inherits QtObject
\inqmlmodule QtQuick3D
\brief Creates or references a color buffer to be used for a pass of an Effect.
A Buffer can be used to create intermediate buffers to share data between
\l{Pass}{render passes} in an \l Effect.
\note If the \l name property of the Buffer is empty, it will reference the
default output texture of the render pass.
*/
/*!
\qmlproperty enumeration Buffer::format
Specifies the texture format. The default value is Buffer.RGBA8.
\value Buffer.RGBA8
\value Buffer.RGBA16F
\value Buffer.RGBA32F
\value Buffer.R8
\value Buffer.R16
\value Buffer.R16F
\value Buffer.R32F
*/
/*!
\qmlproperty enumeration Buffer::textureFilterOperation
Specifies the texture filtering mode when sampling the contents of the
Buffer. The default value is Buffer.Linear.
\value Buffer.Nearest Use nearest-neighbor filtering.
\value Buffer.Linear Use linear filtering.
*/
/*!
\qmlproperty enumeration Buffer::textureCoordOperation
Specifies the behavior for texture coordinates when sampling outside the [0, 1] range.
The default is Buffer.ClampToEdge.
\value Buffer.ClampToEdge Clamp coordinates to the edges.
\value Buffer.Repeat Wrap the coordinates at the edges to tile the texture.
\value Buffer.MirroredRepeat Wrap the coordinate at the edges, but mirror the texture
when tiling it.
*/
/*!
\qmlproperty real Buffer::sizeMultiplier
Specifies the size multiplier of the buffer. For instance, a value of \c 1.0 creates
a buffer with the same size as the effect's input texture while \c 0.5 creates buffer
where both width and height is half as big. The default value is 1.0.
*/
/*!
\qmlproperty enumeration Buffer::bufferFlags
Specifies the buffer allocation flags. The default is Buffer.None.
\value Buffer.None No special behavior.
\value Buffer.SceneLifetime The buffer is allocated for the whole lifetime of the scene.
*/
/*!
\qmlproperty string Buffer::name
Specifies the name of the buffer.
\note When this property is empty, the Buffer will refer to the default output texture
of the \l{Pass}{render pass} instead of allocating a buffer. This can be useful to
override certain settings of the output, such as the texture format, without introducing
a new, separate intermediate texture.
*/
/*!
\qmltype SetUniformValue
\inherits Command
\inqmlmodule QtQuick3D
\brief Defines a value to be set during a single \l {Pass}{pass}.
\since 5.15
SetUniformValue is a \l Command which can be added to the list of commands in a \l Pass. When
executed, it will set the uniform given by the \l{SetUniformValue::target}{target} property
to \l{SetUniformValue::value}{value}.
\note The value set by this command is will only be set during the \l {Pass}{pass} it occurs in.
For consecutive passes the value will be revert to the initial value of the uniform as it
was defined in the \l Effect item.
\sa BufferInput
*/
/*!
\qmlproperty string SetUniformValue::target
Specifies the name of the uniform that will have its value changed during the \l {Pass}{pass}.
This must match the name of an existing property in the \l Effect.
*/
/*!
\qmlproperty Variant SetUniformValue::value
Specifies the value that will be set on the \c target uniform.
*/
/*!
\qmlvaluetype renderTargetBlend
\inqmlmodule QtQuick3D
\brief Defines blending parameters for a single color attachment of a render pass.
\since 6.11
The renderTargetBlend type is used to specify blending parameters for a single
color attachment of a \l RenderPass. An instance of renderTargetBlend can be
assigned to one of the \l PipelineStateOverride \c targetBlend0 through \c targetBlend7
properties, where the index corresponds to the color attachment slot.
The color blend equation is:
\badcode
result.rgb = srcColor * src.rgb opColor dstColor * dst.rgb
result.a = srcAlpha * src.a opAlpha dstAlpha * dst.a
\endcode
*/
/*!
\qmlproperty bool renderTargetBlend::blendEnabled
If set to \c true, enables blending for the color attachment. If set to \c false,
disables blending and the output color is written directly to the attachment.
The default value is \c false.
*/
/*!
\qmlproperty enumeration renderTargetBlend::colorWrite
A bitmask that controls which color channels are written to the color attachment.
Multiple values can be combined with the \c | operator.
The default value is \c {R | G | B | A}.
\value RenderTargetBlend.R Write to the red channel.
\value RenderTargetBlend.G Write to the green channel.
\value RenderTargetBlend.B Write to the blue channel.
\value RenderTargetBlend.A Write to the alpha channel.
*/
/*!
\qmlproperty enumeration renderTargetBlend::srcColor
Sets the source color blend factor. This value scales the RGB components of the
fragment shader output before the blend operation. The default value is \c One.
\value RenderTargetBlend.Zero Factor is (0, 0, 0).
\value RenderTargetBlend.One Factor is (1, 1, 1).
\value RenderTargetBlend.SrcColor Factor is the source color (r, g, b).
\value RenderTargetBlend.OneMinusSrcColor Factor is (1-r, 1-g, 1-b) of the source.
\value RenderTargetBlend.DstColor Factor is the destination color (r, g, b).
\value RenderTargetBlend.OneMinusDstColor Factor is (1-r, 1-g, 1-b) of the destination.
\value RenderTargetBlend.SrcAlpha Factor is the source alpha (a, a, a).
\value RenderTargetBlend.OneMinusSrcAlpha Factor is (1-a, 1-a, 1-a) of the source.
\value RenderTargetBlend.DstAlpha Factor is the destination alpha (a, a, a).
\value RenderTargetBlend.OneMinusDstAlpha Factor is (1-a, 1-a, 1-a) of the destination.
\value RenderTargetBlend.ConstantColor Factor is the constant color set on the pipeline.
\value RenderTargetBlend.OneMinusConstantColor Factor is one minus the constant color.
\value RenderTargetBlend.ConstantAlpha Factor is the constant alpha.
\value RenderTargetBlend.OneMinusConstantAlpha Factor is one minus the constant alpha.
\value RenderTargetBlend.SrcAlphaSaturate Factor is min(src.a, 1-dst.a) for RGB, 1 for A.
\value RenderTargetBlend.Src1Color Factor is from the second color output of the fragment shader.
\value RenderTargetBlend.OneMinusSrc1Color Factor is one minus the second color output.
\value RenderTargetBlend.Src1Alpha Factor is the alpha of the second color output.
\value RenderTargetBlend.OneMinusSrc1Alpha Factor is one minus the alpha of the second output.
*/
/*!
\qmlproperty enumeration renderTargetBlend::dstColor
Sets the destination color blend factor. This value scales the RGB components of the
current value in the color attachment before the blend operation.
The default value is \c OneMinusSrcAlpha.
See \c srcColor for the list of available values.
*/
/*!
\qmlproperty enumeration renderTargetBlend::opColor
Sets the arithmetic operation used to combine the scaled source and destination
color components. The default value is \c Add.
\value RenderTargetBlend.Add Result = src + dst.
\value RenderTargetBlend.Subtract Result = src - dst.
\value RenderTargetBlend.ReverseSubtract Result = dst - src.
\value RenderTargetBlend.Min Result = min(src, dst).
\value RenderTargetBlend.Max Result = max(src, dst).
*/
/*!
\qmlproperty enumeration renderTargetBlend::srcAlpha
Sets the source alpha blend factor. This value scales the alpha component of the
fragment shader output before the blend operation. The default value is \c One.
See \c srcColor for the list of available values.
*/
/*!
\qmlproperty enumeration renderTargetBlend::dstAlpha
Sets the destination alpha blend factor. This value scales the alpha component of the
current value in the color attachment before the blend operation.
The default value is \c OneMinusSrcAlpha.
See \c srcColor for the list of available values.
*/
/*!
\qmlproperty enumeration renderTargetBlend::opAlpha
Sets the arithmetic operation used to combine the scaled source and destination
alpha components. The default value is \c Add.
See \c opColor for the list of available values.
*/
/*!
\qmltype PipelineStateOverride
\inherits Command
\inqmlmodule QtQuick3D
\brief Defines pipeline state overrides for a single \l {RenderPass}{pass}.
\since 6.11
PipelineStateOverride is a \l Command which can be added to the list of commands in a \l RenderPass.
When executed, it will override the pipeline state in the render pass according to the properties set on
the PipelineStateOverride. Only values that are set will override the existing pipeline state's values.
If you want to reset a value that has been overridden to the default, then set the property
to \c undefined.
\sa renderTargetBlend
*/
/*!
\qmltype DepthStencilAttachment
\inherits Command
\inqmlmodule QtQuick3D
\brief Defines a depth-stencil attachment for a RenderPass.
\since 6.11
DepthStencilAttachment adds an implicit depth/stencil render buffer to a
\l RenderPass. The render buffer is automatically created and managed by the
rendering system. Because it is backed by an opaque render buffer rather
than a texture, its contents cannot be sampled in shaders. If you need to
read back depth values in a later pass, use \l DepthTextureAttachment instead.
Use DepthStencilAttachment when you need depth testing and/or stencil
operations in a pass but do not need to access the depth data afterwards.
\qml
RenderPass {
commands: [
ColorAttachment { name: "color0"; target: colorTexture },
// Add a depth buffer so depth testing works in this pass
DepthStencilAttachment {}
]
}
\endqml
\sa DepthTextureAttachment, RenderPassTexture
*/
namespace QSSGShaderUtils {
ResolveFunction resolveShaderOverride = nullptr;
void setResolveFunction(ResolveFunction fn)
{
resolveShaderOverride = fn;
}
QByteArray resolveShader(const QUrl &fileUrl, const QQmlContext *context, QByteArray &shaderPathKey)
{
if (resolveShaderOverride) {
QByteArray shaderData;
if (resolveShaderOverride(fileUrl, context, shaderData, shaderPathKey))
return shaderData;
}
if (!shaderPathKey.isEmpty())
shaderPathKey.append('>');
const QUrl loadUrl = context ? context->resolvedUrl(fileUrl) : fileUrl;
const QString filePath = QQmlFile::urlToLocalFileOrQrc(loadUrl);
QFile f(filePath);
if (f.open(QIODevice::ReadOnly | QIODevice::Text)) {
shaderPathKey += loadUrl.fileName().toUtf8();
return f.readAll();
} else {
qWarning("Failed to read shader code from %s", qPrintable(filePath));
}
return QByteArray();
}
// These are the QMetaTypes that we convert into uniforms.
static constexpr QMetaType::Type qssg_metatype_list[] {
QMetaType::Double,
QMetaType::Bool,
QMetaType::QVector2D,
QMetaType::QVector3D,
QMetaType::QVector4D,
QMetaType::Int,
QMetaType::QColor,
QMetaType::QSize,
QMetaType::QSizeF,
QMetaType::QPoint,
QMetaType::QPointF,
QMetaType::QRect,
QMetaType::QRectF,
QMetaType::QQuaternion,
QMetaType::QMatrix4x4
};
template<>
struct ShaderType<QMetaType::Double>
{
static constexpr QSSGRenderShaderValue::Type type() { return QSSGRenderShaderValue::Float; }
static QByteArray name() { return QByteArrayLiteral("float"); }
};
template<>
struct ShaderType<QMetaType::Bool>
{
static constexpr QSSGRenderShaderValue::Type type() { return QSSGRenderShaderValue::Boolean; }
static QByteArray name() { return QByteArrayLiteral("bool"); }
};
template<>
struct ShaderType<QMetaType::Int>
{
static constexpr QSSGRenderShaderValue::Type type() { return QSSGRenderShaderValue::Integer; }
static QByteArray name() { return QByteArrayLiteral("int"); }
};
template<>
struct ShaderType<QMetaType::QVector2D>
{
static constexpr QSSGRenderShaderValue::Type type() { return QSSGRenderShaderValue::Vec2; }
static QByteArray name() { return QByteArrayLiteral("vec2"); }
};
template<>
struct ShaderType<QMetaType::QVector3D>
{
static constexpr QSSGRenderShaderValue::Type type() { return QSSGRenderShaderValue::Vec3; }
static QByteArray name() { return QByteArrayLiteral("vec3"); }
};
template<>
struct ShaderType<QMetaType::QVector4D>
{
static constexpr QSSGRenderShaderValue::Type type() { return QSSGRenderShaderValue::Vec4; }
static QByteArray name() { return QByteArrayLiteral("vec4"); }
};
template<>
struct ShaderType<QMetaType::QColor>
{
static constexpr QSSGRenderShaderValue::Type type() { return QSSGRenderShaderValue::Rgba; }
static QByteArray name() { return QByteArrayLiteral("vec4"); }
};
template<>
struct ShaderType<QMetaType::QSize>
{
static constexpr QSSGRenderShaderValue::Type type() { return QSSGRenderShaderValue::Size; }
static QByteArray name() { return QByteArrayLiteral("vec2"); }
};
template<>
struct ShaderType<QMetaType::QSizeF>
{
static constexpr QSSGRenderShaderValue::Type type() { return QSSGRenderShaderValue::SizeF; }
static QByteArray name() { return QByteArrayLiteral("vec2"); }
};
template<>
struct ShaderType<QMetaType::QPoint>
{
static constexpr QSSGRenderShaderValue::Type type() { return QSSGRenderShaderValue::Point; }
static QByteArray name() { return QByteArrayLiteral("vec2"); }
};
template<>
struct ShaderType<QMetaType::QPointF>
{
static constexpr QSSGRenderShaderValue::Type type() { return QSSGRenderShaderValue::PointF; }
static QByteArray name() { return QByteArrayLiteral("vec2"); }
};
template<>
struct ShaderType<QMetaType::QRect>
{
static constexpr QSSGRenderShaderValue::Type type() { return QSSGRenderShaderValue::Rect; }
static QByteArray name() { return QByteArrayLiteral("vec4"); }
};
template<>
struct ShaderType<QMetaType::QRectF>
{
static constexpr QSSGRenderShaderValue::Type type() { return QSSGRenderShaderValue::RectF; }
static QByteArray name() { return QByteArrayLiteral("vec4"); }
};
template<>
struct ShaderType<QMetaType::QQuaternion>
{
static constexpr QSSGRenderShaderValue::Type type() { return QSSGRenderShaderValue::Quaternion; }
static QByteArray name() { return QByteArrayLiteral("vec4"); }
};
template<>
struct ShaderType<QMetaType::QMatrix4x4>
{
static constexpr QSSGRenderShaderValue::Type type() { return QSSGRenderShaderValue::Matrix4x4; }
static QByteArray name() { return QByteArrayLiteral("mat4"); }
};
QByteArray uniformTypeName(QMetaType type)
{
switch (type.id()) {
case QMetaType::Double:
case QMetaType::Float:
return ShaderType<QMetaType::Double>::name();
case QMetaType::Bool:
return ShaderType<QMetaType::Bool>::name();
case QMetaType::QVector2D:
return ShaderType<QMetaType::QVector2D>::name();
case QMetaType::QVector3D:
return ShaderType<QMetaType::QVector3D>::name();
case QMetaType::QVector4D:
return ShaderType<QMetaType::QVector4D>::name();
case QMetaType::Int:
return ShaderType<QMetaType::Int>::name();
case QMetaType::QColor:
return ShaderType<QMetaType::QColor>::name();
case QMetaType::QSize:
return ShaderType<QMetaType::QSize>::name();
case QMetaType::QSizeF:
return ShaderType<QMetaType::QSizeF>::name();
case QMetaType::QPoint:
return ShaderType<QMetaType::QPoint>::name();
case QMetaType::QPointF:
return ShaderType<QMetaType::QPointF>::name();
case QMetaType::QRect:
return ShaderType<QMetaType::QRect>::name();
case QMetaType::QRectF:
return ShaderType<QMetaType::QRectF>::name();
case QMetaType::QQuaternion:
return ShaderType<QMetaType::QQuaternion>::name();
case QMetaType::QMatrix4x4:
return ShaderType<QMetaType::QMatrix4x4>::name();
default:
return QByteArray();
}
}
QByteArray uniformTypeName(QSSGRenderShaderValue::Type type)
{
switch (type) {
case QSSGRenderShaderValue::Float:
return ShaderType<QMetaType::Double>::name();
case QSSGRenderShaderValue::Boolean:
return ShaderType<QMetaType::Bool>::name();
case QSSGRenderShaderValue::Integer:
return ShaderType<QMetaType::Int>::name();
case QSSGRenderShaderValue::Vec2:
return ShaderType<QMetaType::QVector2D>::name();
case QSSGRenderShaderValue::Vec3:
return ShaderType<QMetaType::QVector3D>::name();
case QSSGRenderShaderValue::Vec4:
return ShaderType<QMetaType::QVector4D>::name();
case QSSGRenderShaderValue::Rgba:
return ShaderType<QMetaType::QColor>::name();
case QSSGRenderShaderValue::Size:
return ShaderType<QMetaType::QSize>::name();
case QSSGRenderShaderValue::SizeF:
return ShaderType<QMetaType::QSizeF>::name();
case QSSGRenderShaderValue::Point:
return ShaderType<QMetaType::QPoint>::name();
case QSSGRenderShaderValue::PointF:
return ShaderType<QMetaType::QPointF>::name();
case QSSGRenderShaderValue::Rect:
return ShaderType<QMetaType::QRect>::name();
case QSSGRenderShaderValue::RectF:
return ShaderType<QMetaType::QRectF>::name();
case QSSGRenderShaderValue::Quaternion:
return ShaderType<QMetaType::QQuaternion>::name();
case QSSGRenderShaderValue::Matrix4x4:
return ShaderType<QMetaType::QMatrix4x4>::name();
default:
return QByteArray();
}
}
QSSGRenderShaderValue::Type uniformType(QMetaType type)
{
switch (type.id()) {
case QMetaType::Double:
case QMetaType::Float:
return ShaderType<QMetaType::Double>::type();
case QMetaType::Bool:
return ShaderType<QMetaType::Bool>::type();
case QMetaType::QVector2D:
return ShaderType<QMetaType::QVector2D>::type();
case QMetaType::QVector3D:
return ShaderType<QMetaType::QVector3D>::type();
case QMetaType::QVector4D:
return ShaderType<QMetaType::QVector4D>::type();
case QMetaType::Int:
return ShaderType<QMetaType::Int>::type();
case QMetaType::QColor:
return ShaderType<QMetaType::QColor>::type();
case QMetaType::QSize:
return ShaderType<QMetaType::QSize>::type();
case QMetaType::QSizeF:
return ShaderType<QMetaType::QSizeF>::type();
case QMetaType::QPoint:
return ShaderType<QMetaType::QPoint>::type();
case QMetaType::QPointF:
return ShaderType<QMetaType::QPointF>::type();
case QMetaType::QRect:
return ShaderType<QMetaType::QRect>::type();
case QMetaType::QRectF:
return ShaderType<QMetaType::QRectF>::type();
case QMetaType::QQuaternion:
return ShaderType<QMetaType::QQuaternion>::type();
case QMetaType::QMatrix4x4:
return ShaderType<QMetaType::QMatrix4x4>::type();
default:
return QSSGRenderShaderValue::Unknown;
}
}
MetaTypeList supportedMetatypes()
{
return {std::begin(qssg_metatype_list), std::end(qssg_metatype_list)};
}
}
QQuick3DShaderUtilsBuffer::TextureFormat QQuick3DShaderUtilsBuffer::mapRenderTextureFormat(QSSGRenderTextureFormat::Format fmt)
{
using TextureFormat = QQuick3DShaderUtilsBuffer::TextureFormat;
switch (fmt) {
case QSSGRenderTextureFormat::RGBA8: return TextureFormat::RGBA8;
case QSSGRenderTextureFormat::RGBA16F: return TextureFormat::RGBA16F;
case QSSGRenderTextureFormat::RGBA32F: return TextureFormat::RGBA32F;
case QSSGRenderTextureFormat::R8: return TextureFormat::R8;
case QSSGRenderTextureFormat::R16: return TextureFormat::R16;
case QSSGRenderTextureFormat::R16F: return TextureFormat::R16F;
case QSSGRenderTextureFormat::R32F: return TextureFormat::R32F;
default:
break;
}
return TextureFormat::Unknown;
}
QSSGRenderTextureFormat::Format QQuick3DShaderUtilsBuffer::mapTextureFormat(QQuick3DShaderUtilsBuffer::TextureFormat fmt)
{
using TextureFormat = QQuick3DShaderUtilsBuffer::TextureFormat;
switch (fmt) {
case TextureFormat::RGBA8: return QSSGRenderTextureFormat::RGBA8;
case TextureFormat::RGBA16F: return QSSGRenderTextureFormat::RGBA16F;
case TextureFormat::RGBA32F: return QSSGRenderTextureFormat::RGBA32F;
case TextureFormat::R8: return QSSGRenderTextureFormat::R8;
case TextureFormat::R16: return QSSGRenderTextureFormat::R16;
case TextureFormat::R16F: return QSSGRenderTextureFormat::R16F;
case TextureFormat::R32F: return QSSGRenderTextureFormat::R32F;
default:
break;
}
return QSSGRenderTextureFormat::Unknown;
}
QQuick3DShaderUtilsBuffer::TextureFormat QQuick3DShaderUtilsBuffer::format() const
{
return mapRenderTextureFormat(command.m_format.format);
}
void QQuick3DShaderUtilsBuffer::setFormat(TextureFormat format)
{
QSSGRenderTextureFormat::Format mappedTextureFormat = mapTextureFormat(format);
if (command.m_format == mappedTextureFormat)
return;
command.m_format = mappedTextureFormat;
emit changed();
}
void QQuick3DShaderUtilsBuffer::setTextureFilterOperation(TextureFilterOperation op)
{
if (command.m_filterOp == QSSGRenderTextureFilterOp(op))
return;
command.m_filterOp = QSSGRenderTextureFilterOp(op);
emit changed();
}
void QQuick3DShaderUtilsBuffer::setTextureCoordOperation(TextureCoordOperation texCoordOp)
{
if (command.m_texCoordOp == QSSGRenderTextureCoordOp(texCoordOp))
return;
command.m_texCoordOp = QSSGRenderTextureCoordOp(texCoordOp);
emit changed();
}
void QQuick3DShaderUtilsBuffer::setBufferFlags(AllocateBufferFlagValues flag)
{
if (quint32(command.m_bufferFlags) == quint32(flag))
return;
command.m_bufferFlags = quint32(flag);
emit changed();
}
QQuick3DShaderUtilsRenderPass::~QQuick3DShaderUtilsRenderPass()
{
}
void QQuick3DShaderUtilsRenderPass::qmlAppendCommand(QQmlListProperty<QQuick3DShaderUtilsRenderCommand> *list,
QQuick3DShaderUtilsRenderCommand *command)
{
if (!command)
return;
QQuick3DShaderUtilsRenderPass *that = qobject_cast<QQuick3DShaderUtilsRenderPass *>(list->object);
that->m_commands.push_back(command);
emit that->changed();
}
QQuick3DShaderUtilsRenderCommand *QQuick3DShaderUtilsRenderPass::qmlCommandAt(QQmlListProperty<QQuick3DShaderUtilsRenderCommand> *list,
qsizetype index)
{
QQuick3DShaderUtilsRenderPass *that = qobject_cast<QQuick3DShaderUtilsRenderPass *>(list->object);
return that->m_commands.at(index);
}
qsizetype QQuick3DShaderUtilsRenderPass::qmlCommandCount(QQmlListProperty<QQuick3DShaderUtilsRenderCommand> *list)
{
QQuick3DShaderUtilsRenderPass *that = qobject_cast<QQuick3DShaderUtilsRenderPass *>(list->object);
return that->m_commands.size();
}
void QQuick3DShaderUtilsRenderPass::qmlCommandClear(QQmlListProperty<QQuick3DShaderUtilsRenderCommand> *list)
{
QQuick3DShaderUtilsRenderPass *that = qobject_cast<QQuick3DShaderUtilsRenderPass *>(list->object);
that->m_commands.clear();
emit that->changed();
}
QQmlListProperty<QQuick3DShaderUtilsRenderCommand> QQuick3DShaderUtilsRenderPass::commands()
{
return QQmlListProperty<QQuick3DShaderUtilsRenderCommand>(this,
nullptr,
QQuick3DShaderUtilsRenderPass::qmlAppendCommand,
QQuick3DShaderUtilsRenderPass::qmlCommandCount,
QQuick3DShaderUtilsRenderPass::qmlCommandAt,
QQuick3DShaderUtilsRenderPass::qmlCommandClear);
}
void QQuick3DShaderUtilsRenderPass::qmlAppendShader(QQmlListProperty<QQuick3DShaderUtilsShader> *list,
QQuick3DShaderUtilsShader *shader)
{
if (!shader)
return;
QQuick3DShaderUtilsRenderPass *that = qobject_cast<QQuick3DShaderUtilsRenderPass *>(list->object);
// An append implementation CANNOT rely on the object (shader in this case)
// being complete. When the list references a Shader object living under
// another Effect, its properties may not be set at the point of this
// function being called, so accessing shader->stage is not allowed since
// it may still have its default value, not what is set from QML...
// the only thing we can do is to append to our list, do not try to be clever
that->m_shaders.append(shader);
connect(shader, &QQuick3DShaderUtilsShader::shaderChanged, that, &QQuick3DShaderUtilsRenderPass::changed);
connect(shader, &QQuick3DShaderUtilsShader::stageChanged, that, &QQuick3DShaderUtilsRenderPass::changed);
emit that->changed();
}
QQuick3DShaderUtilsShader *QQuick3DShaderUtilsRenderPass::qmlShaderAt(QQmlListProperty<QQuick3DShaderUtilsShader> *list,
qsizetype index)
{
QQuick3DShaderUtilsRenderPass *that = qobject_cast<QQuick3DShaderUtilsRenderPass *>(list->object);
return that->m_shaders.at(index);
}
qsizetype QQuick3DShaderUtilsRenderPass::qmlShaderCount(QQmlListProperty<QQuick3DShaderUtilsShader> *list)
{
QQuick3DShaderUtilsRenderPass *that = qobject_cast<QQuick3DShaderUtilsRenderPass *>(list->object);
return that->m_shaders.size();
}
void QQuick3DShaderUtilsRenderPass::qmlShaderClear(QQmlListProperty<QQuick3DShaderUtilsShader> *list)
{
QQuick3DShaderUtilsRenderPass *that = qobject_cast<QQuick3DShaderUtilsRenderPass *>(list->object);
for (QQuick3DShaderUtilsShader *shader : that->m_shaders)
shader->disconnect(that);
that->m_shaders.clear();
emit that->changed();
}
QQmlListProperty<QQuick3DShaderUtilsShader> QQuick3DShaderUtilsRenderPass::shaders()
{
return QQmlListProperty<QQuick3DShaderUtilsShader>(this,
nullptr,
QQuick3DShaderUtilsRenderPass::qmlAppendShader,
QQuick3DShaderUtilsRenderPass::qmlShaderCount,
QQuick3DShaderUtilsRenderPass::qmlShaderAt,
QQuick3DShaderUtilsRenderPass::qmlShaderClear);
}
QQuick3DShaderUtilsTextureInput::QQuick3DShaderUtilsTextureInput(QQuick3DObject *p) : QQuick3DObject(p) {}
QQuick3DShaderUtilsTextureInput::~QQuick3DShaderUtilsTextureInput()
{
}
void QQuick3DShaderUtilsTextureInput::setTexture(QQuick3DTexture *texture)
{
if (m_texture == texture)
return;
QObject *p = parent();
while (p != nullptr) {
if (QQuick3DCustomMaterial *mat = qobject_cast<QQuick3DCustomMaterial *>(p)) {
mat->setDynamicTextureMap(this);
QQuick3DObjectPrivate::attachWatcherPriv(mat, this, &QQuick3DShaderUtilsTextureInput::setTexture, texture, m_texture);
break;
} else if (QQuick3DEffect *efx = qobject_cast<QQuick3DEffect *>(p)) {
efx->setDynamicTextureMap(this);
QQuick3DObjectPrivate::attachWatcherPriv(efx, this, &QQuick3DShaderUtilsTextureInput::setTexture, texture, m_texture);
break;
}
p = p->parent();
}
if (p == nullptr) {
qWarning("A TextureInput was defined without a CustomMaterial or Effect ancestor. This should be avoided.");
}
m_texture = texture;
Q_EMIT textureChanged();
}
/*!
\qmltype RenderablesFilter
\inherits Command
\inqmlmodule QtQuick3D
\brief Defines a filter for selecting which renderables to affect in a \l {RenderPass}{pass}.
\since 6.11
The RenderablesFilter type is used to specify which renderables in the scene
should be affected by a \l RenderPass. By setting the \c renderableTypes property,
you can control whether the pass affects opaque objects, transparent objects, or
no objects at all.
Setting \c renderableTypes to \c None is useful when a RenderPass acts as a container
for \l SubRenderPass commands and should not render any scene objects itself.
In addition to filtering by renderable types, you can use the \l {RenderablesFilter::layerMask}{layerMask}
to further refine which renderables are affected based on their assigned \l{Node::layers}{layers}.
\qml
// A pass that renders only opaque scene objects
RenderPass {
id: opaquePass
commands: [
ColorAttachment { name: "color0"; target: opaqueColorTexture },
DepthTextureAttachment { target: depthTexture },
RenderablesFilter {
renderableTypes: RenderablesFilter.Opaque
}
]
}
// A pass that renders only transparent objects on top
RenderPass {
id: transparentPass
commands: [
ColorAttachment { name: "color0"; target: transparentColorTexture },
RenderablesFilter {
renderableTypes: RenderablesFilter.Transparent
}
]
}
\endqml
*/
/*!
\qmlproperty int RenderablesFilter::layerMask
Sets the layer mask for the filter. Only renderables on the specified layers will be affected by the filter.
\sa Node::layers
*/
/*!
\qmlproperty enumeration RenderablesFilter::renderableTypes
Sets the types of renderables that the filter will affect.
\value RenderablesFilter.None No renderables will be rendered. Useful for container passes that only have SubRenderPasses.
\value RenderablesFilter.Opaque Only opaque renderables will be rendered.
\value RenderablesFilter.Transparent Only transparent renderables will be rendered.
\note Multiple values can be combined using the | operator.
\default RenderablesFilter.Opaque | RenderablesFilter.Transparent
*/
QQuick3DShaderUtilsRenderablesFilter::RenderableTypes QQuick3DShaderUtilsRenderablesFilter::renderableTypes() const
{
return static_cast<QQuick3DShaderUtilsRenderablesFilter::RenderableTypes>(command.renderableTypes);
}
QQuick3DShaderUtilsRenderablesFilter::~QQuick3DShaderUtilsRenderablesFilter()
{
}
void QQuick3DShaderUtilsRenderablesFilter::setRenderableTypes(RenderableTypes types)
{
command.renderableTypes = static_cast<QSSGRenderablesFilterCommand::RenderableTypeT>(types.toInt());
}
QQuick3DShaderUtilsPipelineStateOverride::QQuick3DShaderUtilsPipelineStateOverride()
{
// Forward all our own signals to the base class changed() signal so that
// QQuick3DRenderPass can connect once and be notified of any change.
const QMetaObject *mo = metaObject();
const QMetaMethod changedSignal = QMetaMethod::fromSignal(&QQuick3DShaderUtilsRenderCommand::changed);
for (int i = QQuick3DShaderUtilsRenderCommand::staticMetaObject.methodCount(); i < mo->methodCount(); ++i) {
const QMetaMethod mm = mo->method(i);
if (mm.methodType() == QMetaMethod::Signal)
connect(this, mm, this, changedSignal);
}
}
QQuick3DShaderUtilsPipelineStateOverride::~QQuick3DShaderUtilsPipelineStateOverride()
{
}
/*!
\qmlproperty bool PipelineStateOverride::depthTestEnabled
If set to \c true, enables depth testing for the render pass. If set to \c false, disables depth testing.
Setting this property to \c true requires a depth attachment for the render pass.
*/
bool QQuick3DShaderUtilsPipelineStateOverride::depthTestEnabled() const
{
if (command.m_depthTestEnabled)
return *command.m_depthTestEnabled;
return false;
}
void QQuick3DShaderUtilsPipelineStateOverride::setDepthTestEnabled(bool newDepthTestEnabled)
{
if (command.m_depthTestEnabled && *command.m_depthTestEnabled == newDepthTestEnabled)
return;
command.m_depthTestEnabled = newDepthTestEnabled;
emit depthTestEnabledChanged();
}
void QQuick3DShaderUtilsPipelineStateOverride::resetDepthTestEnabled()
{
command.m_depthTestEnabled.reset();
emit depthTestEnabledChanged();
}
/*!
\qmlproperty bool PipelineStateOverride::depthWriteEnabled
If set to \c true, enables depth writing for the render pass. If set to \c false, disables depth writing.
Setting this property to \c true requires a depth attachment for the render pass.
*/
bool QQuick3DShaderUtilsPipelineStateOverride::depthWriteEnabled() const
{
if (command.m_depthWriteEnabled)
return *command.m_depthWriteEnabled;
return false;
}
void QQuick3DShaderUtilsPipelineStateOverride::setDepthWriteEnabled(bool newDepthWriteEnabled)
{
if (command.m_depthWriteEnabled && *command.m_depthWriteEnabled == newDepthWriteEnabled)
return;
command.m_depthWriteEnabled = newDepthWriteEnabled;
emit depthWriteEnabledChanged();
}
void QQuick3DShaderUtilsPipelineStateOverride::resetDepthWriteEnabled()
{
command.m_depthWriteEnabled.reset();
emit depthWriteEnabledChanged();
}
/*!
\qmlproperty bool PipelineStateOverride::blendEnabled
If set to \c true, enables blending for the render pass. If set to \c false, disables blending.
For per-attachment blend settings, use the \c targetBlend0 through \c targetBlend7 properties.
*/
bool QQuick3DShaderUtilsPipelineStateOverride::blendEnabled() const
{
if (command.m_blendEnabled)
return *command.m_blendEnabled;
return false;
}
void QQuick3DShaderUtilsPipelineStateOverride::setBlendEnabled(bool newBlendEnabled)
{
if (command.m_blendEnabled && *command.m_blendEnabled == newBlendEnabled)
return;
command.m_blendEnabled = newBlendEnabled;
emit blendEnabledChanged();
}
void QQuick3DShaderUtilsPipelineStateOverride::resetBlendEnabled()
{
command.m_blendEnabled.reset();
emit blendEnabledChanged();
}
/*!
\qmlproperty bool PipelineStateOverride::usesStencilReference
If set to \c true, enables the use of the stencil reference value for the render pass. If set to \c false,
disables the use of the stencil reference value.
*/
bool QQuick3DShaderUtilsPipelineStateOverride::usesStencilReference() const
{
if (command.m_usesStencilReference)
return *command.m_usesStencilReference;
return false;
}
void QQuick3DShaderUtilsPipelineStateOverride::setUsesStencilReference(bool newUsesStencilReference)
{
if (command.m_usesStencilReference && *command.m_usesStencilReference == newUsesStencilReference)
return;
command.m_usesStencilReference = newUsesStencilReference;
emit usesStencilReferenceChanged();
}
void QQuick3DShaderUtilsPipelineStateOverride::resetUsesStencilReference()
{
command.m_usesStencilReference.reset();
emit usesStencilReferenceChanged();
}
/*!
\qmlproperty bool PipelineStateOverride::usesScissor
If set to \c true, enables scissor testing for the render pass. If set to \c false,
disables the scissor test. Use the \c scissor property to set the scissor rectangle.
*/
bool QQuick3DShaderUtilsPipelineStateOverride::usesScissor() const
{
if (command.m_usesScissor)
return *command.m_usesScissor;
return false;
}
void QQuick3DShaderUtilsPipelineStateOverride::setUsesScissor(bool newUsesScissor)
{
if (command.m_usesScissor && *command.m_usesScissor == newUsesScissor)
return;
command.m_usesScissor = newUsesScissor;
emit usesScissorChanged();
}
void QQuick3DShaderUtilsPipelineStateOverride::resetUsesScissor()
{
command.m_usesScissor.reset();
emit usesScissorChanged();
}
/*!
\qmlproperty enumeration PipelineStateOverride::depthFunction
Sets the depth comparison function for the render pass.
\value PipelineStateOverride.Never The depth test never passes.
\value PipelineStateOverride.Less The depth test passes when the incoming depth is less than the stored depth.
\value PipelineStateOverride.Equal The depth test passes when the incoming depth equals the stored depth.
\value PipelineStateOverride.LessOrEqual The depth test passes when the incoming depth is less than or equal to the stored depth.
\value PipelineStateOverride.Greater The depth test passes when the incoming depth is greater than the stored depth.
\value PipelineStateOverride.NotEqual The depth test passes when the incoming depth does not equal the stored depth.
\value PipelineStateOverride.GreaterOrEqual The depth test passes when the incoming depth is greater than or equal to the stored depth.
\value PipelineStateOverride.Always The depth test always passes.
*/
QQuick3DShaderUtilsPipelineStateOverride::CompareOperation QQuick3DShaderUtilsPipelineStateOverride::depthFunction() const
{
if (command.m_depthFunction)
return CompareOperation(*command.m_depthFunction);
return CompareOperation::Less;
}
void QQuick3DShaderUtilsPipelineStateOverride::setDepthFunction(CompareOperation newDepthFunction)
{
if (command.m_depthFunction && *command.m_depthFunction == QRhiGraphicsPipeline::CompareOp(newDepthFunction))
return;
command.m_depthFunction = QRhiGraphicsPipeline::CompareOp(newDepthFunction);
emit depthFunctionChanged();
}
void QQuick3DShaderUtilsPipelineStateOverride::resetDepthFunction()
{
command.m_depthFunction.reset();
emit depthFunctionChanged();
}
/*!
\qmlproperty enumeration PipelineStateOverride::cullMode
Sets the face culling mode for the render pass.
\value PipelineStateOverride.None No face culling.
\value PipelineStateOverride.Front Front-facing polygons are culled.
\value PipelineStateOverride.Back Back-facing polygons are culled.
*/
QQuick3DShaderUtilsPipelineStateOverride::CullMode QQuick3DShaderUtilsPipelineStateOverride::cullMode() const
{
if (command.m_cullMode)
return CullMode(*command.m_cullMode);
return CullMode::Back;
}
void QQuick3DShaderUtilsPipelineStateOverride::setCullMode(CullMode newCullMode)
{
if (command.m_cullMode && *command.m_cullMode == QRhiGraphicsPipeline::CullMode(newCullMode))
return;
command.m_cullMode = QRhiGraphicsPipeline::CullMode(newCullMode);
emit cullModeChanged();
}
void QQuick3DShaderUtilsPipelineStateOverride::resetCullMode()
{
command.m_cullMode.reset();
emit cullModeChanged();
}
/*!
\qmlproperty enumeration PipelineStateOverride::polygonMode
Sets the polygon rasterization mode for the render pass.
\value PipelineStateOverride.Fill Polygons are filled (default).
\value PipelineStateOverride.Line Polygon edges are drawn as lines (wireframe).
*/
QQuick3DShaderUtilsPipelineStateOverride::PolygonMode QQuick3DShaderUtilsPipelineStateOverride::polygonMode() const
{
if (command.m_polygonMode)
return PolygonMode(*command.m_polygonMode);
return PolygonMode::Fill;
}
void QQuick3DShaderUtilsPipelineStateOverride::setPolygonMode(PolygonMode newPolygonMode)
{
if (command.m_polygonMode && *command.m_polygonMode == QRhiGraphicsPipeline::PolygonMode(newPolygonMode))
return;
command.m_polygonMode = QRhiGraphicsPipeline::PolygonMode(newPolygonMode);
emit polygonModeChanged();
}
void QQuick3DShaderUtilsPipelineStateOverride::resetPolygonMode()
{
command.m_polygonMode.reset();
emit polygonModeChanged();
}
/*!
\qmlproperty uint PipelineStateOverride::stencilWriteMask
Sets the stencil write mask for the render pass. Each bit controls whether the corresponding
bit in the stencil buffer can be written.
*/
quint32 QQuick3DShaderUtilsPipelineStateOverride::stencilWriteMask() const
{
if (command.m_stencilWriteMask)
return *command.m_stencilWriteMask;
return 0;
}
void QQuick3DShaderUtilsPipelineStateOverride::setStencilWriteMask(quint32 newStencilWriteMask)
{
if (command.m_stencilWriteMask && *command.m_stencilWriteMask == newStencilWriteMask)
return;
command.m_stencilWriteMask = newStencilWriteMask;
emit stencilWriteMaskChanged();
}
void QQuick3DShaderUtilsPipelineStateOverride::resetStencilWriteMask()
{
command.m_stencilWriteMask.reset();
emit stencilWriteMaskChanged();
}
/*!
\qmlproperty uint PipelineStateOverride::stencilReference
Sets the stencil reference value for the render pass. This value is used in stencil
comparison operations when \c usesStencilReference is \c true.
*/
quint32 QQuick3DShaderUtilsPipelineStateOverride::stencilReference() const
{
if (command.m_stencilReference)
return *command.m_stencilReference;
return 0;
}
void QQuick3DShaderUtilsPipelineStateOverride::setStencilReference(quint32 newStencilReference)
{
if (command.m_stencilReference && *command.m_stencilReference == newStencilReference)
return;
command.m_stencilReference = newStencilReference;
emit stencilReferenceChanged();
}
void QQuick3DShaderUtilsPipelineStateOverride::resetStencilReference()
{
command.m_stencilReference.reset();
emit stencilReferenceChanged();
}
/*!
\qmlproperty rect PipelineStateOverride::viewport
Sets the viewport rectangle for the render pass. The rectangle specifies the region
of the render target to draw into, in pixels (x, y, width, height).
If not set, the full render target dimensions are used.
*/
QRectF QQuick3DShaderUtilsPipelineStateOverride::viewport() const
{
if (command.m_viewport) {
const QRhiViewport &vp = *command.m_viewport;
return QRectF(vp.viewport()[0], vp.viewport()[1], vp.viewport()[2], vp.viewport()[3]);
}
return QRectF();
}
void QQuick3DShaderUtilsPipelineStateOverride::setViewport(const QRectF &newViewport)
{
if (command.m_viewport) {
const QRhiViewport &vp = *command.m_viewport;
if (vp.viewport()[0] == newViewport.x() &&
vp.viewport()[1] == newViewport.y() &&
vp.viewport()[2] == newViewport.width() &&
vp.viewport()[3] == newViewport.height())
return;
}
command.m_viewport = QRhiViewport(newViewport.x(), newViewport.y(), newViewport.width(), newViewport.height());
emit viewportChanged();
}
void QQuick3DShaderUtilsPipelineStateOverride::resetViewport()
{
command.m_viewport.reset();
emit viewportChanged();
}
/*!
\qmlproperty rect PipelineStateOverride::scissor
Sets the scissor rectangle for the render pass. Fragments outside this rectangle are
discarded. Requires \c usesScissor to be \c true.
*/
QRect QQuick3DShaderUtilsPipelineStateOverride::scissor() const
{
if (command.m_scissor) {
const QRhiScissor &sc = *command.m_scissor;
return QRect(sc.scissor()[0], sc.scissor()[1], sc.scissor()[2], sc.scissor()[3]);
}
return QRect();
}
void QQuick3DShaderUtilsPipelineStateOverride::setScissor(const QRect &newScissor)
{
if (command.m_viewport) {
const QRhiScissor &sc = *command.m_scissor;
if (sc.scissor()[0] == newScissor.x() &&
sc.scissor()[1] == newScissor.y() &&
sc.scissor()[2] == newScissor.width() &&
sc.scissor()[3] == newScissor.height())
return;
}
command.m_scissor = QRhiScissor(newScissor.x(), newScissor.y(), newScissor.width(), newScissor.height());
emit scissorChanged();
}
void QQuick3DShaderUtilsPipelineStateOverride::resetScissor()
{
command.m_scissor.reset();
}
/*!
\qmlproperty renderTargetBlend PipelineStateOverride::targetBlend0
Sets the blending parameters for color attachment 0 of the render pass.
\sa renderTargetBlend
*/
QQuick3DRenderPassTargetBlend QQuick3DShaderUtilsPipelineStateOverride::targetBlend0() const
{
if (command.m_targetBlend0)
return *command.m_targetBlend0;
return {};
}
void QQuick3DShaderUtilsPipelineStateOverride::setTargetBlend0(const QQuick3DRenderPassTargetBlend &newTargetBlend0)
{
if (command.m_targetBlend0 && QQuick3DRenderPassTargetBlend(*command.m_targetBlend0) == newTargetBlend0)
return;
command.m_targetBlend0 = newTargetBlend0.toRhiTargetBlend();;
emit targetBlend0Changed();
}
void QQuick3DShaderUtilsPipelineStateOverride::resetTargetBlend0()
{
command.m_targetBlend0.reset();
emit targetBlend0Changed();
}
/*!
\qmlproperty renderTargetBlend PipelineStateOverride::targetBlend1
Sets the blending parameters for color attachment 1 of the render pass.
\sa renderTargetBlend
*/
QQuick3DRenderPassTargetBlend QQuick3DShaderUtilsPipelineStateOverride::targetBlend1() const
{
if (command.m_targetBlend1)
return *command.m_targetBlend1;
return {};
}
void QQuick3DShaderUtilsPipelineStateOverride::setTargetBlend1(const QQuick3DRenderPassTargetBlend &newTargetBlend1)
{
if (command.m_targetBlend1 && QQuick3DRenderPassTargetBlend(*command.m_targetBlend1) == newTargetBlend1)
return;
command.m_targetBlend1 = newTargetBlend1.toRhiTargetBlend();
emit targetBlend1Changed();
}
void QQuick3DShaderUtilsPipelineStateOverride::resetTargetBlend1()
{
command.m_targetBlend1.reset();
emit targetBlend1Changed();
}
/*!
\qmlproperty renderTargetBlend PipelineStateOverride::targetBlend2
Sets the blending parameters for color attachment 2 of the render pass.
\sa renderTargetBlend
*/
QQuick3DRenderPassTargetBlend QQuick3DShaderUtilsPipelineStateOverride::targetBlend2() const
{
if (command.m_targetBlend2)
return *command.m_targetBlend2;
return {};
}
void QQuick3DShaderUtilsPipelineStateOverride::setTargetBlend2(const QQuick3DRenderPassTargetBlend &newTargetBlend2)
{
if (command.m_targetBlend2 && QQuick3DRenderPassTargetBlend(*command.m_targetBlend2) == newTargetBlend2)
return;
command.m_targetBlend2 = newTargetBlend2.toRhiTargetBlend();
emit targetBlend2Changed();
}
void QQuick3DShaderUtilsPipelineStateOverride::resetTargetBlend2()
{
command.m_targetBlend2.reset();
emit targetBlend2Changed();
}
/*!
\qmlproperty renderTargetBlend PipelineStateOverride::targetBlend3
Sets the blending parameters for color attachment 3 of the render pass.
\sa renderTargetBlend
*/
QQuick3DRenderPassTargetBlend QQuick3DShaderUtilsPipelineStateOverride::targetBlend3() const
{
if (command.m_targetBlend3)
return *command.m_targetBlend3;
return {};
}
void QQuick3DShaderUtilsPipelineStateOverride::setTargetBlend3(const QQuick3DRenderPassTargetBlend &newTargetBlend3)
{
if (command.m_targetBlend3 && QQuick3DRenderPassTargetBlend(*command.m_targetBlend3) == newTargetBlend3)
return;
command.m_targetBlend3 = newTargetBlend3.toRhiTargetBlend();
emit targetBlend3Changed();
}
void QQuick3DShaderUtilsPipelineStateOverride::resetTargetBlend3()
{
command.m_targetBlend3.reset();
emit targetBlend3Changed();
}
/*!
\qmlproperty renderTargetBlend PipelineStateOverride::targetBlend4
Sets the blending parameters for color attachment 4 of the render pass.
\sa renderTargetBlend
*/
QQuick3DRenderPassTargetBlend QQuick3DShaderUtilsPipelineStateOverride::targetBlend4() const
{
if (command.m_targetBlend4)
return *command.m_targetBlend4;
return {};
}
void QQuick3DShaderUtilsPipelineStateOverride::setTargetBlend4(const QQuick3DRenderPassTargetBlend &newTargetBlend4)
{
if (command.m_targetBlend4 && QQuick3DRenderPassTargetBlend(*command.m_targetBlend4) == newTargetBlend4)
return;
command.m_targetBlend4 = newTargetBlend4.toRhiTargetBlend();
emit targetBlend4Changed();
}
void QQuick3DShaderUtilsPipelineStateOverride::resetTargetBlend4()
{
command.m_targetBlend4.reset();
emit targetBlend4Changed();
}
/*!
\qmlproperty renderTargetBlend PipelineStateOverride::targetBlend5
Sets the blending parameters for color attachment 5 of the render pass.
\sa renderTargetBlend
*/
QQuick3DRenderPassTargetBlend QQuick3DShaderUtilsPipelineStateOverride::targetBlend5() const
{
if (command.m_targetBlend5)
return *command.m_targetBlend5;
return {};
}
void QQuick3DShaderUtilsPipelineStateOverride::setTargetBlend5(const QQuick3DRenderPassTargetBlend &newTargetBlend5)
{
if (command.m_targetBlend5 && QQuick3DRenderPassTargetBlend(*command.m_targetBlend5) == newTargetBlend5)
return;
command.m_targetBlend5 = newTargetBlend5.toRhiTargetBlend();
emit targetBlend5Changed();
}
void QQuick3DShaderUtilsPipelineStateOverride::resetTargetBlend5()
{
command.m_targetBlend5.reset();
emit targetBlend5Changed();
}
/*!
\qmlproperty renderTargetBlend PipelineStateOverride::targetBlend6
Sets the blending parameters for color attachment 6 of the render pass.
\sa renderTargetBlend
*/
QQuick3DRenderPassTargetBlend QQuick3DShaderUtilsPipelineStateOverride::targetBlend6() const
{
if (command.m_targetBlend6)
return *command.m_targetBlend6;
return {};
}
void QQuick3DShaderUtilsPipelineStateOverride::setTargetBlend6(const QQuick3DRenderPassTargetBlend &newTargetBlend6)
{
if (command.m_targetBlend6 && QQuick3DRenderPassTargetBlend(*command.m_targetBlend6) == newTargetBlend6)
return;
command.m_targetBlend6 = newTargetBlend6.toRhiTargetBlend();
emit targetBlend6Changed();
}
void QQuick3DShaderUtilsPipelineStateOverride::resetTargetBlend6()
{
command.m_targetBlend6.reset();
emit targetBlend6Changed();
}
/*!
\qmlproperty renderTargetBlend PipelineStateOverride::targetBlend7
Sets the blending parameters for color attachment 7 of the render pass.
\sa renderTargetBlend
*/
QQuick3DRenderPassTargetBlend QQuick3DShaderUtilsPipelineStateOverride::targetBlend7() const
{
if (command.m_targetBlend7)
return *command.m_targetBlend7;
return {};
}
void QQuick3DShaderUtilsPipelineStateOverride::setTargetBlend7(const QQuick3DRenderPassTargetBlend &newTargetBlend7)
{
if (command.m_targetBlend7 && QQuick3DRenderPassTargetBlend(*command.m_targetBlend7) == newTargetBlend7)
return;
command.m_targetBlend7 = newTargetBlend7.toRhiTargetBlend();
emit targetBlend7Changed();
}
void QQuick3DShaderUtilsPipelineStateOverride::resetTargetBlend7()
{
command.m_targetBlend7.reset();
emit targetBlend7Changed();
}
/*!
\qmltype RenderPassTexture
\inherits Object3D
\inqmlmodule QtQuick3D
\brief Defines a texture to be used as a render target in a \l {RenderPass}{pass}.
\since 6.11
RenderPassTexture declares an off-screen texture that a \l RenderPass can
render into. The texture is automatically created and managed by the
rendering system. Its size tracks the \l View3D viewport by default.
Once declared, attach the texture to a pass via \l ColorAttachment (for
color output) or \l DepthTextureAttachment (for depth output). The
resulting texture can then be accessed from a subsequent pass or a
\l Texture component using \l RenderOutputProvider.
\qml
// Declare a float color buffer
RenderPassTexture {
id: hdrColorTexture
format: RenderPassTexture.RGBA16F
}
RenderPass {
commands: [
ColorAttachment {
name: "color0"
target: hdrColorTexture
}
]
}
\endqml
\sa ColorAttachment, DepthTextureAttachment, RenderOutputProvider
*/
/*!
\qmlproperty enumeration RenderPassTexture::format
Sets the format of the render target texture.
Available formats are:
\value RenderPassTexture.Unknown
\value RenderPassTexture.RGBA8
\value RenderPassTexture.RGBA16F
\value RenderPassTexture.RGBA32F
\value RenderPassTexture.R8
\value RenderPassTexture.R16
\value RenderPassTexture.R16F
\value RenderPassTexture.R32F
\value RenderPassTexture.Depth16
\value RenderPassTexture.Depth24
\value RenderPassTexture.Depth32
\value RenderPassTexture.Depth24Stencil8
*/
QQuick3DShaderUtilsRenderPassTexture::TextureFormat QQuick3DShaderUtilsRenderPassTexture::format() const
{
return fromRenderTextureFormat(command->format());
}
QQuick3DShaderUtilsRenderPassTexture::~QQuick3DShaderUtilsRenderPassTexture()
{
}
void QQuick3DShaderUtilsRenderPassTexture::setFormat(TextureFormat newFormat)
{
if (!command)
command = std::make_shared<QSSGAllocateTexture>();
command->setFormat(asRenderTextureFormat(newFormat));
}
QSSGRenderTextureFormat QQuick3DShaderUtilsRenderPassTexture::asRenderTextureFormat(TextureFormat fmt)
{
switch (fmt) {
case TextureFormat::Unknown: return QSSGRenderTextureFormat::Unknown;
case TextureFormat::RGBA8: return QSSGRenderTextureFormat::RGBA8;
case TextureFormat::RGBA16F: return QSSGRenderTextureFormat::RGBA16F;
case TextureFormat::RGBA32F: return QSSGRenderTextureFormat::RGBA32F;
case TextureFormat::R8: return QSSGRenderTextureFormat::R8;
case TextureFormat::R16: return QSSGRenderTextureFormat::R16;
case TextureFormat::R16F: return QSSGRenderTextureFormat::R16F;
case TextureFormat::R32F: return QSSGRenderTextureFormat::R32F;
case TextureFormat::Depth16: return QSSGRenderTextureFormat::Depth16;
case TextureFormat::Depth24: return QSSGRenderTextureFormat::Depth24;
case TextureFormat::Depth32: return QSSGRenderTextureFormat::Depth32;
case TextureFormat::Depth24Stencil8: return QSSGRenderTextureFormat::Depth24Stencil8;
default:
break;
}
return QSSGRenderTextureFormat::Unknown;
}
QQuick3DShaderUtilsRenderPassTexture::TextureFormat QQuick3DShaderUtilsRenderPassTexture::fromRenderTextureFormat(QSSGRenderTextureFormat fmt)
{
switch (fmt.format) {
case QSSGRenderTextureFormat::Unknown: return TextureFormat::Unknown;
case QSSGRenderTextureFormat::RGBA8: return TextureFormat::RGBA8;
case QSSGRenderTextureFormat::RGBA16F: return TextureFormat::RGBA16F;
case QSSGRenderTextureFormat::RGBA32F: return TextureFormat::RGBA32F;
case QSSGRenderTextureFormat::R8: return TextureFormat::R8;
case QSSGRenderTextureFormat::R16: return TextureFormat::R16;
case QSSGRenderTextureFormat::R16F: return TextureFormat::R16F;
case QSSGRenderTextureFormat::R32F: return TextureFormat::R32F;
case QSSGRenderTextureFormat::Depth16: return TextureFormat::Depth16;
case QSSGRenderTextureFormat::Depth24: return TextureFormat::Depth24;
case QSSGRenderTextureFormat::Depth32: return TextureFormat::Depth32;
case QSSGRenderTextureFormat::Depth24Stencil8: return TextureFormat::Depth24Stencil8;
default:
break;
}
return TextureFormat::Unknown;
}
/*!
\qmltype ColorAttachment
\inherits Command
\inqmlmodule QtQuick3D
\brief Defines a color attachment for a render pass.
\since 6.11
The ColorAttachment type is used to specify a color attachment for a \l RenderPass.
The \l name property is used to identify the attachment within the render pass.
If the \l {RenderPass::materialMode}{RenderPass.AugmentMaterial} mode is used,
the name will be exposed as an output with \c name in the fragment shader.
\sa RenderPassTexture
\qml
RenderPass {
// Define a render target texture
RenderPassTexture {
id: colorTexture
format: RenderPassTexture.RGBA16F
}
commands: [
// Define a color attachment using the texture
ColorAttachment {
name: "color0"
target: colorTexture
}
]
}
\endqml
*/
/*!
\qmlproperty RenderPassTexture ColorAttachment::target
The \l RenderPassTexture that will be used as the color attachment. The texture
must have a color-compatible format (e.g. \c RGBA8, \c RGBA16F).
*/
/*!
\qmlproperty string ColorAttachment::name
The name used to identify this color attachment within the render pass. When
\l {RenderPass::materialMode}{materialMode} is set to \c AugmentMaterial, this name
is exposed as a fragment shader output variable, allowing custom shader code to
write to this attachment.
*/
QQuick3DShaderUtilsRenderPassColorAttachment::~QQuick3DShaderUtilsRenderPassColorAttachment()
{
}
QByteArray QQuick3DShaderUtilsRenderPassColorAttachment::name() const
{
return m_name;
}
void QQuick3DShaderUtilsRenderPassColorAttachment::setName(const QByteArray &newName)
{
m_name = newName;
}
QSSGCommand *QQuick3DShaderUtilsRenderPassColorAttachment::cloneCommand()
{
if (target) {
QSSGColorAttachment *cmd = new QSSGColorAttachment(m_name);
cmd->m_textureCmd = target->command;
return cmd;
}
return nullptr;
}
QQuick3DPropertyChangedTracker::~QQuick3DPropertyChangedTracker()
{
}
std::optional<QQuick3DPropertyChangedTracker::UniformProperty> QQuick3DPropertyChangedTracker::createOrUpdateTrackedProperty(const QMetaProperty property,
bool addWatchers)
{
Q_ASSERT(property.isValid());
std::optional<QQuick3DPropertyChangedTracker::UniformProperty> result;
const int propertyIndex = property.propertyIndex();
static auto getSamplerHint = [](const QQuick3DTexture &texture) {
if (auto *po = QQuick3DObjectPrivate::get(&texture)) {
if (po->type == QQuick3DObjectPrivate::Type::TextureProvider) {
auto textureProvider = static_cast<QQuick3DTextureProviderExtension *>(texture.textureProvider());
switch (textureProvider->samplerHint()) {
case QQuick3DTextureProviderExtension::SamplerHint::Sampler2D:
return QSSGRenderSamplerType::Sampler2D;
case QQuick3DTextureProviderExtension::SamplerHint::Sampler2DArray:
return QSSGRenderSamplerType::Sampler2DArray;
case QQuick3DTextureProviderExtension::SamplerHint::Sampler3D:
return QSSGRenderSamplerType::Sampler3D;
case QQuick3DTextureProviderExtension::SamplerHint::SamplerCube:
return QSSGRenderSamplerType::SamplerCube;
case QQuick3DTextureProviderExtension::SamplerHint::SamplerCubeArray:
return QSSGRenderSamplerType::SamplerCubeArray;
case QQuick3DTextureProviderExtension::SamplerHint::SamplerBuffer:
return QSSGRenderSamplerType::SamplerBuffer;
}
} else if (po->type == QQuick3DObjectPrivate::Type::ImageCube) {
return QSSGRenderSamplerType::SamplerCube;
} else if (texture.textureData() && texture.textureData()->depth() > 0) {
return QSSGRenderSamplerType::Sampler3D;
}
}
return QSSGRenderSamplerType::Sampler2D;
};
const auto addTextureToUniforms =
[&](const char *name, QQuick3DTexture *texture, int propertyIndex) -> QQuick3DPropertyChangedTracker::UniformProperty {
QSSGRenderImage *ri = static_cast<QSSGRenderImage *>(QQuick3DObjectPrivate::get(texture)->spatialNode);
auto samplerName = QSSGBaseTypeHelpers::toString(getSamplerHint(*texture));
if (addWatchers) {
if (auto textureProvider = qobject_cast<QQuick3DTextureProviderExtension *>(texture->textureProvider())) {
// FIXME? We don't have a way of adding watchers after the initial extraction so switching
// textureProvider during runtime will not give a callback.
QObject::connect(textureProvider, &QQuick3DTextureProviderExtension::surfaceChanged, m_owner, [this, property, texture]() {
addPropertyWatcher(property, DirtyPropertyHint::Reference, texture);
});
}
QObject::connect(texture, &QQuick3DTexture::textureProviderChanged, m_owner, [this, property, texture]() {
addPropertyWatcher(property, DirtyPropertyHint::Reference, texture);
});
// Re-register watcher when the texture backend node is (re)created
QObject::connect(texture, &QQuick3DTexture::textureInternalChange, m_owner,
[this, property, texture](QQuick3DTextureInternalChange change) {
if (change.type == QQuick3DTextureInternalChange::Type::BackendNode)
addPropertyWatcher(property, DirtyPropertyHint::Reference, texture);
});
}
return UniformProperty(name, samplerName, QVariant::fromValue(ri), QSSGRenderShaderValue::Texture, propertyIndex);
};
// The TextureInput type needs extra watchers for its properties...
const auto addTextureInputWatchers = [&](QMetaProperty property, QQuick3DShaderUtilsTextureInput *textureInput) {
QObject::connect(textureInput, &QQuick3DShaderUtilsTextureInput::enabledChanged, m_owner, [this, property, textureInput]() {
addPropertyWatcher(property, DirtyPropertyHint::Reference, textureInput);
});
QObject::connect(textureInput, &QQuick3DShaderUtilsTextureInput::textureChanged, m_owner, [this, property, textureInput]() {
addPropertyWatcher(property, DirtyPropertyHint::Reference, textureInput);
});
};
const char *name = property.name();
QMetaType propType = property.metaType();
QVariant propValue = property.read(m_owner);
if (propType == QMetaType(QMetaType::QVariant))
propType = propValue.metaType();
const auto type = QSSGShaderUtils::uniformType(propType);
if (type != QSSGRenderShaderValue::Unknown) {
result = UniformProperty(name, QSSGShaderUtils::uniformTypeName(propType), propValue, QSSGShaderUtils::uniformType(propType), propertyIndex);
if (addWatchers)
addPropertyWatcher(property, DirtyPropertyHint::Value);
} else {
if (propType.id() >= QMetaType::User) {
if (propType.id() == qMetaTypeId<QQuick3DTexture *>()) {
if (QQuick3DTexture *texture = property.read(m_owner).value<QQuick3DTexture *>()) {
result = addTextureToUniforms(name, texture, propertyIndex);
if (addWatchers)
addPropertyWatcher(property, DirtyPropertyHint::Reference, texture);
}
} else if (propType.id() == qMetaTypeId<QQuick3DShaderUtilsTextureInput *>()) { // For compatibility, also check for texture input types
if (QQuick3DShaderUtilsTextureInput *textureInput = property.read(m_owner).value<QQuick3DShaderUtilsTextureInput *>();
textureInput && textureInput->texture()) {
result = addTextureToUniforms(property.name(), textureInput->texture(), propertyIndex);
if (addWatchers) {
addTextureInputWatchers(property, textureInput);
addPropertyWatcher(property, DirtyPropertyHint::Reference, textureInput);
}
}
}
} else if (propType == QMetaType(QMetaType::QObjectStar)) {
if (QQuick3DTexture *texture = qobject_cast<QQuick3DTexture *>(propValue.value<QObject *>())) {
result = addTextureToUniforms(name, texture, propertyIndex);
if (addWatchers)
addPropertyWatcher(property, DirtyPropertyHint::Reference, texture);
} else if (QQuick3DShaderUtilsTextureInput *textureInput = qobject_cast<QQuick3DShaderUtilsTextureInput *>(
propValue.value<QObject *>());
textureInput && textureInput->texture()) {
result = addTextureToUniforms(property.name(), textureInput->texture(), propertyIndex);
if (addWatchers) {
addTextureInputWatchers(property, textureInput);
addPropertyWatcher(property, DirtyPropertyHint::Reference, textureInput);
}
}
}
}
return result;
}
QList<QQuick3DPropertyChangedTracker::UniformProperty> QQuick3DPropertyChangedTracker::extractProperties()
{
if (!m_dirtyProperties.empty()) {
for (int propertyIndex : std::as_const(m_dirtyProperties)) {
auto it = std::lower_bound(m_propertyList.begin(),
m_propertyList.end(),
propertyIndex,
[](const UniformProperty &p, int index) { return p.pid < index; });
if (it == m_propertyList.end()) {
Q_ASSERT(false);
continue;
}
QMetaProperty property = m_owner->metaObject()->property(propertyIndex);
if (Q_UNLIKELY(!property.isValid())) {
m_propertyList.erase(it);
continue;
}
if (auto trackedProperty = createOrUpdateTrackedProperty(property, false); trackedProperty.has_value())
*it = *trackedProperty;
else
m_propertyList.erase(it);
}
m_dirtyProperties.clear();
}
if (m_extracted)
return m_propertyList;
auto metaObject = m_owner->metaObject();
// Properties -> uniforms
const int propCount = metaObject->propertyCount();
int propOffset = metaObject->propertyOffset();
m_propertyList.reserve(propCount);
// Classes can have multilayered inheritance structure, so find the actual propOffset by
// walking up the inheritance chain.
const QMetaObject *superClass = metaObject->superClass();
while (superClass && qstrcmp(superClass->className(), m_superClassName) != 0) {
propOffset = superClass->propertyOffset();
superClass = superClass->superClass();
}
for (int i = propOffset; i != propCount; ++i) {
const QMetaProperty property = metaObject->property(i);
if (Q_UNLIKELY(!property.isValid()))
continue;
if (auto uniformProperty = createOrUpdateTrackedProperty(property, true); uniformProperty.has_value()) {
m_propertyList.push_back(*uniformProperty);
}
}
m_dirtyProperties.reserve(m_propertyList.size());
m_extracted = true;
return m_propertyList;
}
void QQuick3DPropertyChangedTracker::addPropertyWatcher(QMetaProperty property, DirtyPropertyHint hint, QQuick3DObject *object)
{
if (property.isValid() && property.hasNotifySignal()) {
// Check if we're already watching this property.
const auto pid = property.propertyIndex();
Q_ASSERT(pid != -1);
auto it = m_trackedProperties.find(pid);
const bool found = (it != m_trackedProperties.end());
QQuick3DObject *oldObj = nullptr;
if (!found) {
QQuick3DPropertyWatcher *watcher = new QQuick3DPropertyWatcher(this, property);
it = m_trackedProperties.insert(pid, Tracked { watcher, object, pid });
} else {
oldObj = it->object;
it->object = object;
}
if (hint == DirtyPropertyHint::Reference) {
const auto &sm = QQuick3DObjectPrivate::get(m_owner)->sceneManager;
// First check if the object changed
const bool changed = (oldObj != object);
// Deref old object
if (changed && oldObj) {
QQuick3DObjectPrivate::get(oldObj)->derefSceneManager();
QObject::disconnect(oldObj, &QObject::destroyed, m_owner, nullptr);
}
// Ref new object
if (changed && object) {
QQuick3DObjectPrivate::get(object)->refSceneManager(*sm);
QObject::connect(object, &QObject::destroyed, m_owner, [this, property](QObject *obj) {
Q_UNUSED(obj);
addPropertyWatcher(property, DirtyPropertyHint::Reference, nullptr);
});
}
}
m_dirtyProperties.push_back(property.propertyIndex());
markTrackedPropertyDirty(property, hint);
}
}
void QQuick3DPropertyChangedTracker::markTrackedPropertyDirty(QMetaProperty property, DirtyPropertyHint hint)
{
Q_UNUSED(property);
Q_UNUSED(hint);
QSSG_CHECK_X(false, "QQuick3DPropertyChangedTracker::onPropertyDirty implementation missing");
}
QQuick3DPropertyWatcher::QQuick3DPropertyWatcher(QQuick3DPropertyChangedTracker *tracker, QMetaProperty property)
: m_tracker(tracker)
, m_property(property)
{
Q_ASSERT(tracker != nullptr);
Q_ASSERT(property.isValid() && property.hasNotifySignal());
// Ensure that the watcher is on the watched object's thread if it's not already there.
// If not, the watcher will be created on the render thread and the notify connections
// will be queued to the render thread, which is not what we want
// (as can end-up concurrently mutating the dirty lists).
if (thread() != tracker->m_owner->thread())
moveToThread(tracker->m_owner->thread());
const bool isPointerType = (property.metaType().flags().testFlag(QMetaType::IsPointer));
if (!isPointerType) {
// Value change notification
const auto idx = staticMetaObject.indexOfSlot("onValuePropertyChanged()");
if (QSSG_GUARD_X(idx != -1, "Method not found!")) {
auto onPropertyChangedMethod = staticMetaObject.method(idx);
connect(m_tracker->m_owner, property.notifySignal(), this, onPropertyChangedMethod);
}
} else {
// Pointer value change notification
const auto idx = staticMetaObject.indexOfSlot("onPointerPropertyChanged()");
if (QSSG_GUARD_X(idx != -1, "Method not found!")) {
auto onPointerPropertyChangedMethod = staticMetaObject.method(idx);
connect(m_tracker->m_owner, property.notifySignal(), this, onPointerPropertyChangedMethod);
}
}
}
void QQuick3DPropertyWatcher::onValuePropertyChanged()
{
m_tracker->m_dirtyProperties.push_back(m_property.propertyIndex());
m_tracker->markTrackedPropertyDirty(m_property, QQuick3DPropertyChangedTracker::DirtyPropertyHint::Value);
}
void QQuick3DPropertyWatcher::onPointerPropertyChanged()
{
m_tracker->m_dirtyProperties.push_back(m_property.propertyIndex());
m_tracker->markTrackedPropertyDirty(m_property, QQuick3DPropertyChangedTracker::DirtyPropertyHint::Reference);;
}
/*!
\qmltype AddDefine
\inherits Command
\inqmlmodule QtQuick3D
\brief Adds a preprocessor define to the shader compilation for a \l {RenderPass}{pass}.
\since 6.11
The AddDefine type is used to inject a \c {#define} into the shader
compilation for a \l RenderPass. This lets you branch shader behavior
at compile time depending on which pass is being executed.
In the shader code, the define can be used with the standard preprocessor
\c {#ifdef} / \c {#if} directives:
\badcode
#ifdef CUSTOM_PASS_MODE
// code compiled only when this define is present
#endif
\endcode
\qml
RenderPass {
commands: [
ColorAttachment { name: "color0"; target: colorTexture },
AddDefine { name: "CUSTOM_PASS_MODE" },
AddDefine { name: "OUTPUT_VERSION"; value: 2 }
]
}
\endqml
*/
/*!
\qmlproperty string AddDefine::name
The name of the preprocessor macro to define. This becomes the identifier
used in \c {#ifdef} or \c {#if} checks in the shader code.
*/
/*!
\qmlproperty int AddDefine::value
The integer value assigned to the preprocessor macro. Defaults to \c 1.
When non-zero, \c {#if NAME} evaluates to \c true in the shader.
*/
QQuick3DShaderUtilsRenderPassAddDefine::QQuick3DShaderUtilsRenderPassAddDefine()
{
}
QQuick3DShaderUtilsRenderPassAddDefine::~QQuick3DShaderUtilsRenderPassAddDefine() = default;
QByteArray QQuick3DShaderUtilsRenderPassAddDefine::name() const
{
return command.m_name;
}
void QQuick3DShaderUtilsRenderPassAddDefine::setName(const QByteArray &newName)
{
if (command.m_name != newName) {
command.m_name = newName;
emit nameChanged();
emit changed();
}
}
int QQuick3DShaderUtilsRenderPassAddDefine::value() const
{
return command.m_value;
}
void QQuick3DShaderUtilsRenderPassAddDefine::setValue(int newValue)
{
if (command.m_value != newValue) {
command.m_value = newValue;
emit valueChanged();
emit changed();
}
}
QSSGCommand *QQuick3DShaderUtilsRenderPassAddDefine::cloneCommand() {
QSSGAddShaderDefine *cmd = new QSSGAddShaderDefine(command);
return cmd;
}
/*!
\qmltype SubRenderPass
\inherits Command
\inqmlmodule QtQuick3D
\brief Renders a sub-section of work within the same render target as the parent RenderPass.
\since 6.11
SubRenderPass is a \l Command that subdivides a \l RenderPass into
smaller, independently configured pieces while sharing the same render
target. Only the parent \l RenderPass controls which textures are
rendered into (via \l ColorAttachment and \l DepthTextureAttachment)
and when the pass begins (and therefore when the render target is
cleared). A SubRenderPass cannot set its own render target or clear
settings — those are always inherited from the parent pass.
What a SubRenderPass \e can control is \e what gets rendered and
\e how: it has its own \l{RenderPass::commands}{commands}, so it can
carry a \l RenderablesFilter to select a subset of objects, a
\l PipelineStateOverride to change depth/blend state, a different
\l{RenderPass::materialMode}{materialMode}, and so on.
A typical use is a forward-rendering pipeline where a single color
pass uses separate SubRenderPasses for the background, opaque
objects, and transparent objects. All three sub-passes write into the
same color and depth textures, but each has different pipeline state:
\qml
RenderPass {
id: mainColorPass
commands: [
// Render targets are set at the parent RenderPass level only
ColorAttachment { name: "color0"; target: colorTexture },
DepthTextureAttachment { target: depthTexture },
// Sub-pass 1: render the skybox background
SubRenderPass { renderPass: skyboxSubPass },
// Sub-pass 2: opaque objects with depth testing and writing
SubRenderPass { renderPass: opaqueSubPass },
// Sub-pass 3: transparent objects with blending, no depth write
SubRenderPass { renderPass: transparentSubPass }
]
}
// Sub-passes share mainColorPass's render target
RenderPass {
id: skyboxSubPass
passMode: RenderPass.SkyboxPass
}
RenderPass {
id: opaqueSubPass
commands: [
RenderablesFilter { renderableTypes: RenderablesFilter.Opaque }
]
}
RenderPass {
id: transparentSubPass
commands: [
RenderablesFilter { renderableTypes: RenderablesFilter.Transparent },
PipelineStateOverride {
blendEnabled: true
depthWriteEnabled: false
}
]
}
\endqml
\sa RenderPass, RenderablesFilter, PipelineStateOverride
*/
/*!
\qmlproperty RenderPass SubRenderPass::renderPass
The RenderPass that will be executed as a sub-pass when this command is processed.
*/
QQuick3DShaderUtilsSubRenderPass::~QQuick3DShaderUtilsSubRenderPass()
{
if (m_renderPass)
QQuick3DRenderPassPrivateHelper::subPassDeref(m_renderPass);
}
QSSGCommand *QQuick3DShaderUtilsSubRenderPass::cloneCommand()
{
QSSGSubRenderPass *cmd = nullptr;
if (!m_renderPass) {
if (!m_hasWarnedAboutInvalidId) {
qCWarning(lcSubRenderPass, "SubRenderPass: No render pass specified. Set the 'renderPass' property.");
m_hasWarnedAboutInvalidId = true;
}
return nullptr;
}
QSSGResourceId userPassId = QQuick3DExtensionHelpers::getResourceId(*m_renderPass);
// Ensure we have a valid resource id before continuing.
if (userPassId != QSSGResourceId::Invalid) {
cmd = new QSSGSubRenderPass();
cmd->setSubPass(userPassId);
// Reset warning flag on success
m_hasWarnedAboutInvalidId = false;
} else {
// Resource ID is not ready yet - this is expected during initialization
qCDebug(lcSubRenderPass, "SubRenderPass: Render pass resource ID not yet available, will retry.");
update();
}
return cmd;
}
QSSGRenderGraphObject *QQuick3DShaderUtilsSubRenderPass::updateSpatialNode(QSSGRenderGraphObject *node)
{
return node;
}
void QQuick3DShaderUtilsSubRenderPass::itemChange(ItemChange change, const ItemChangeData &value)
{
if (change == QQuick3DObject::ItemSceneChange)
updateSceneManager(value.sceneManager);
}
void QQuick3DShaderUtilsSubRenderPass::updateSceneManager(QQuick3DSceneManager *sceneManager)
{
if (sceneManager)
QQuick3DObjectPrivate::refSceneManager(m_renderPass, *sceneManager);
else
QQuick3DObjectPrivate::derefSceneManager(m_renderPass);
}
QQuick3DRenderPass *QQuick3DShaderUtilsSubRenderPass::renderPass() const
{
return m_renderPass;
}
void QQuick3DShaderUtilsSubRenderPass::setRenderPass(QQuick3DRenderPass *newRenderPass)
{
if (m_renderPass == newRenderPass)
return;
QQuick3DObjectPrivate::attachWatcher(this, &QQuick3DShaderUtilsSubRenderPass::setRenderPass, newRenderPass, m_renderPass.data());
if (newRenderPass)
QQuick3DRenderPassPrivateHelper::subPassRef(newRenderPass);
// NOTE: When the watcher calls us because the referenced pass is being
// destroyed, the QPointer is already null, so a dying pass is never deref'd.
if (m_renderPass)
QQuick3DRenderPassPrivateHelper::subPassDeref(m_renderPass);
m_renderPass = newRenderPass;
m_hasWarnedAboutInvalidId = false; // Reset warning flag when property changes
emit renderPassChanged();
emit changed();
}
/*!
\qmltype DepthTextureAttachment
\inherits Command
\inqmlmodule QtQuick3D
\brief Defines a depth texture attachment for a \l {RenderPass}{pass}.
\since 6.11
DepthTextureAttachment attaches a \l RenderPassTexture with a depth
format as the depth buffer for a \l RenderPass. Unlike
\l DepthStencilAttachment (which uses an opaque render buffer), the
resulting depth data is stored in a texture that can be read by
subsequent passes via \l RenderOutputProvider.
\qml
RenderPassTexture {
id: depthTexture
format: RenderPassTexture.Depth24
}
RenderPass {
commands: [
ColorAttachment { name: "color0"; target: colorTexture },
DepthTextureAttachment { target: depthTexture }
]
}
// Expose the depth texture to a material via RenderOutputProvider
Texture {
textureProvider: RenderOutputProvider {
textureSource: RenderOutputProvider.UserPassTexture
renderPass: myRenderPass
}
}
\endqml
\sa RenderPassTexture, DepthStencilAttachment, RenderOutputProvider
*/
/*!
\qmlproperty RenderPassTexture DepthTextureAttachment::target
The \l RenderPassTexture that will be used as the depth attachment. The texture
must have a depth-compatible format (e.g. \c Depth16, \c Depth24, \c Depth32,
or \c Depth24Stencil8).
*/
QQuick3DShaderUtilsRenderPassDepthTextureAttachment::~QQuick3DShaderUtilsRenderPassDepthTextureAttachment()
{
}
QSSGCommand *QQuick3DShaderUtilsRenderPassDepthTextureAttachment::cloneCommand()
{
if (target) {
QSSGDepthTextureAttachment *cmd = new QSSGDepthTextureAttachment(QByteArrayLiteral("__depth__"));
cmd->m_textureCmd = target->command;
return cmd;
}
return nullptr;
}
QQuick3DShaderUtilsRenderPassDepthStencilAttachment::~QQuick3DShaderUtilsRenderPassDepthStencilAttachment()
{
}
QSSGCommand *QQuick3DShaderUtilsRenderPassDepthStencilAttachment::cloneCommand()
{
QSSGDepthStencilAttachment *cmd = new QSSGDepthStencilAttachment;
return cmd;
}
QT_END_NAMESPACE