aboutsummaryrefslogtreecommitdiffstats
path: root/src/quick/items/qquickrendertarget.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/quick/items/qquickrendertarget.cpp')
-rw-r--r--src/quick/items/qquickrendertarget.cpp1164
1 files changed, 1094 insertions, 70 deletions
diff --git a/src/quick/items/qquickrendertarget.cpp b/src/quick/items/qquickrendertarget.cpp
index 7242a55d88..bce5b358ff 100644
--- a/src/quick/items/qquickrendertarget.cpp
+++ b/src/quick/items/qquickrendertarget.cpp
@@ -2,7 +2,7 @@
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#include "qquickrendertarget_p.h"
-#include <QtGui/private/qrhi_p.h>
+#include <rhi/qrhi.h>
#include <QtQuick/private/qquickitem_p.h>
#include <QtQuick/private/qquickwindow_p.h>
#include <QtQuick/private/qsgrhisupport_p.h>
@@ -25,14 +25,16 @@ QQuickRenderTargetPrivate::QQuickRenderTargetPrivate()
{
}
-QQuickRenderTargetPrivate::QQuickRenderTargetPrivate(const QQuickRenderTargetPrivate *other)
+QQuickRenderTargetPrivate::QQuickRenderTargetPrivate(const QQuickRenderTargetPrivate &other)
: ref(1),
- type(other->type),
- pixelSize(other->pixelSize),
- devicePixelRatio(other->devicePixelRatio),
- sampleCount(other->sampleCount),
- u(other->u),
- mirrorVertically(other->mirrorVertically)
+ type(other.type),
+ pixelSize(other.pixelSize),
+ devicePixelRatio(other.devicePixelRatio),
+ sampleCount(other.sampleCount),
+ u(other.u),
+ customDepthTexture(other.customDepthTexture),
+ mirrorVertically(other.mirrorVertically),
+ multisampleResolve(other.multisampleResolve)
{
}
@@ -161,6 +163,84 @@ void QQuickRenderTarget::setMirrorVertically(bool enable)
}
/*!
+ \return the currently set depth texture or, in most cases, \nullptr.
+
+ The value is only non-null when setDepthTexture() was called.
+
+ \since 6.8
+ */
+QRhiTexture *QQuickRenderTarget::depthTexture() const
+{
+ return d->customDepthTexture;
+}
+
+/*!
+ Requests using the given \a texture as the depth or depth-stencil buffer.
+ Ownership of \a texture is not taken.
+
+ The request is only taken into account when relevant. For example, calling
+ this function has no effect with fromRhiRenderTarget(), fromPaintDevice(),
+ or fromOpenGLRenderBuffer().
+
+ Normally a depth-stencil buffer is created automatically, transparently to
+ the user of QQuickRenderTarget. Therefore, there is no need to call this
+ function in most cases when working with QQuickRenderTarget. In special
+ circumstances, it can however become essential to be able to provide a
+ texture to render depth (or depth and stencil) data into, instead of letting
+ Qt Quick create its own intermediate textures or buffers. An example of this
+ is \l{https://www.khronos.org/openxr/}{OpenXR} and its extensions such as
+ \l{https://registry.khronos.org/OpenXR/specs/1.0/html/xrspec.html#XR_KHR_composition_layer_depth}{XR_KHR_composition_layer_depth}.
+ In order to "submit the depth buffer" to the XR compositor, one has to, in
+ practice, retrieve an already created depth (depth-stencil) texture from
+ OpenXR (from the XrSwapchain) and use that texture as the render target for
+ depth data. That would not be possible without this function.
+
+ \note The \a texture is always expected to be a non-multisample 2D texture
+ or texture array (for multiview). If MSAA is involved, the samples are
+ resolved into \a texture at the end of the render pass, regardless of having
+ the MultisampleResolve flag set or not. MSAA is only supported for depth
+ (depth-stencil) textures when the underlying 3D API supports this, and this
+ support is not universally available. See \l{QRhi::ResolveDepthStencil}{the
+ relevant QRhi feature flag} for details. When this is not supported and
+ multisampling is requested in combination with a custom depth texture, \a
+ texture is not going to be touched during rendering and a warning is
+ printed.
+
+ \since 6.8
+
+ \note When it comes to OpenGL and OpenGL ES, using depth textures is not
+ functional on OpenGL ES 2.0 and requires at least OpenGL ES 3.0. Multisample
+ (MSAA) support is not available without at least OpenGL ES 3.1, or OpenGL
+ 3.0 on desktop.
+ */
+void QQuickRenderTarget::setDepthTexture(QRhiTexture *texture)
+{
+ if (d->customDepthTexture == texture)
+ return;
+
+ detach();
+ d->customDepthTexture = texture;
+}
+
+/*!
+ \enum QQuickRenderTarget::Flag
+ Flags for the static QQuickRenderTarget constructor functions.
+
+ \value MultisampleResolve Indicates the the \c sampleCount argument is not
+ the number of samples for the provided texture (and that the texture is
+ still a non-multisample texture), but rather the desired samples for
+ multisample antialiasing. Triggers automatically creating and managing an
+ intermediate multisample texture (or texture array) as the color buffer,
+ transparently to the application. The samples are resolved into the provided
+ texture at the end of the render pass automatically. When this flag is not
+ set, and the \c sampleCount argument is greater than 1, it implies the
+ provided texture is multisample. The flag has no effect is the
+ \c sampleCount is 1 (indicating that multisampling is not involved).
+
+ \since 6.8
+*/
+
+/*!
\return a new QQuickRenderTarget referencing an OpenGL texture object
specified by \a textureId.
@@ -171,7 +251,7 @@ void QQuickRenderTarget::setMirrorVertically(bool enable)
\a pixelSize specifies the size of the image, in pixels. Currently only 2D
textures are supported.
- \a sampleCount specific the number of samples. 0 or 1 means no
+ \a sampleCount specifies the number of samples. 0 or 1 means no
multisampling, while a value like 4 or 8 states that the native object is a
multisample texture.
@@ -212,8 +292,9 @@ QQuickRenderTarget QQuickRenderTarget::fromOpenGLTexture(uint textureId, uint fo
d->pixelSize = pixelSize;
d->sampleCount = qMax(1, sampleCount);
- auto rhiFormat = QSGRhiSupport::toRhiTextureFormatFromGL(format);
- d->u.nativeTexture = { textureId, 0, uint(rhiFormat), 0 };
+ QRhiTexture::Flags formatFlags;
+ QRhiTexture::Format rhiFormat = QSGRhiSupport::toRhiTextureFormatFromGL(format, &formatFlags);
+ d->u.nativeTexture = { textureId, 0, uint(rhiFormat), uint(formatFlags), uint(rhiFormat), uint(formatFlags) };
return rt;
}
@@ -228,7 +309,7 @@ QQuickRenderTarget QQuickRenderTarget::fromOpenGLTexture(uint textureId, uint fo
\a pixelSize specifies the size of the image, in pixels. Currently
only 2D textures are supported.
- \a sampleCount specific the number of samples. 0 or 1 means no
+ \a sampleCount specifies the number of samples. 0 or 1 means no
multisampling, while a value like 4 or 8 states that the native
object is a multisample texture.
@@ -252,6 +333,90 @@ QQuickRenderTarget QQuickRenderTarget::fromOpenGLTexture(uint textureId, const Q
}
/*!
+ \overload
+
+ \return a new QQuickRenderTarget referencing an OpenGL 2D texture or texture
+ array object specified by \a textureId.
+
+ \a format specifies the native internal format of the texture. Only texture
+ formats that are supported by Qt's rendering infrastructure should be used.
+
+ \a pixelSize specifies the size of the image, in pixels. Currently only 2D
+ textures and 2D texture arrays are supported.
+
+ \a sampleCount specifies the number of samples. 0 or 1 means no
+ multisampling, while a value like 4 or 8 states that the native object is a
+ multisample texture, except when \a flags contains \l MultisampleResolve. In
+ that case, \a textureId is assumed to be a non-multisample 2D texture or 2D
+ texture array, and \a sampleCount defines the number of samples desired. The
+ resulting QQuickRenderTarget will use an intermediate, automatically created
+ multisample texture (or texture array) as its color attachment, and will
+ resolve the samples into \a textureId. This is the recommended approach to
+ perform MSAA when the native OpenGL texture is not already multisample.
+
+ When \a arraySize is greater than 1, it implies multiview rendering
+ (\l{https://registry.khronos.org/OpenGL/extensions/OVR/OVR_multiview.txt}{GL_OVR_multiview},
+ \l QRhiColorAttachment::setMultiViewCount()), which can be relevant with
+ VR/AR especially. In this case \a arraySize is the number of views,
+ typically \c 2. See \l QSGMaterial::viewCount() for details on enabling
+ multiview rendering within the Qt Quick scenegraph.
+
+ A depth-stencil buffer, if applicable, is created and used automatically.
+ When the color buffer is multisample, the depth-stencil buffer will
+ automatically be multisample too. For multiview rendering, the depth-stencil
+ texture will automatically be made an array with a matching \a arraySize.
+
+ The OpenGL object name \a textureId must be a valid 2D texture name in the
+ rendering context used by the Qt Quick scenegraph. When \a arraySize is
+ greater than 1, \a textureId must be a valid 2D texture array name.
+
+ \note the resulting QQuickRenderTarget does not own any native resources, it
+ merely contains references and the associated metadata of the size and
+ sample count. It is the caller's responsibility to ensure that the native
+ resource exists as long as necessary.
+
+ \since 6.8
+
+ \note The implementation of this overload is not compatible with OpenGL ES
+ 2.0 or 3.0, and requires OpenGL ES 3.1 at minimum. (or OpenGL 3.0 on
+ desktop)
+
+ \sa QQuickWindow::setRenderTarget(), QQuickRenderControl, fromOpenGLTexture()
+ */
+QQuickRenderTarget QQuickRenderTarget::fromOpenGLTexture(uint textureId, uint format, const QSize &pixelSize, int sampleCount, int arraySize, Flags flags)
+{
+ QQuickRenderTarget rt;
+ QQuickRenderTargetPrivate *d = QQuickRenderTargetPrivate::get(&rt);
+
+ if (!textureId) {
+ qWarning("QQuickRenderTarget: textureId is invalid");
+ return rt;
+ }
+
+ if (pixelSize.isEmpty()) {
+ qWarning("QQuickRenderTarget: Cannot create with empty size");
+ return rt;
+ }
+
+ QRhiTexture::Flags formatFlags;
+ QRhiTexture::Format rhiFormat = QSGRhiSupport::toRhiTextureFormatFromGL(format, &formatFlags);
+
+ d->pixelSize = pixelSize;
+ d->sampleCount = qMax(1, sampleCount);
+ d->multisampleResolve = flags.testFlag(Flag::MultisampleResolve);
+
+ if (arraySize <= 1) {
+ d->type = QQuickRenderTargetPrivate::Type::NativeTexture;
+ d->u.nativeTexture = { textureId, 0, uint(rhiFormat), uint(formatFlags), uint(rhiFormat), uint(formatFlags) };
+ } else {
+ d->type = QQuickRenderTargetPrivate::Type::NativeTextureArray;
+ d->u.nativeTextureArray = { textureId, 0, arraySize, uint(rhiFormat), uint(formatFlags), uint(rhiFormat), uint(formatFlags) };
+ }
+
+ return rt;
+}
+
+/*!
\return a new QQuickRenderTarget referencing an OpenGL renderbuffer object
specified by \a renderbufferId.
@@ -264,7 +429,7 @@ QQuickRenderTarget QQuickRenderTarget::fromOpenGLTexture(uint textureId, const Q
\a pixelSize specifies the size of the image, in pixels.
- \a sampleCount specific the number of samples. 0 or 1 means no
+ \a sampleCount specifies the number of samples. 0 or 1 means no
multisampling, while a value like 4 or 8 states that the native object is a
multisample renderbuffer.
@@ -311,7 +476,7 @@ QQuickRenderTarget QQuickRenderTarget::fromOpenGLRenderBuffer(uint renderbufferI
\a pixelSize specifies the size of the image, in pixels. Currently only 2D
textures are supported.
- \a sampleCount specific the number of samples. 0 or 1 means no
+ \a sampleCount specifies the number of samples. 0 or 1 means no
multisampling, while a value like 4 or 8 states that the native object is a
multisample texture.
@@ -349,9 +514,9 @@ QQuickRenderTarget QQuickRenderTarget::fromD3D11Texture(void *texture, uint form
d->pixelSize = pixelSize;
d->sampleCount = qMax(1, sampleCount);
- QRhiTexture::Flags flags;
- auto rhiFormat = QSGRhiSupport::toRhiTextureFormatFromD3D11(format, &flags);
- d->u.nativeTexture = { quint64(texture), 0, uint(rhiFormat), uint(flags) };
+ QRhiTexture::Flags formatFlags;
+ QRhiTexture::Format rhiFormat = QSGRhiSupport::toRhiTextureFormatFromDXGI(format, &formatFlags);
+ d->u.nativeTexture = { quint64(texture), 0, uint(rhiFormat), uint(formatFlags), uint(rhiFormat), uint(formatFlags) };
return rt;
}
@@ -366,7 +531,7 @@ QQuickRenderTarget QQuickRenderTarget::fromD3D11Texture(void *texture, uint form
\a pixelSize specifies the size of the image, in pixels. Currently only 2D
textures are supported.
- \a sampleCount specific the number of samples. 0 or 1 means no
+ \a sampleCount specifies the number of samples. 0 or 1 means no
multisampling, while a value like 4 or 8 states that the native object is a
multisample texture.
@@ -385,7 +550,208 @@ QQuickRenderTarget QQuickRenderTarget::fromD3D11Texture(void *texture, const QSi
{
return fromD3D11Texture(texture, 0 /* DXGI_FORMAT_UNKNOWN */, pixelSize, sampleCount);
}
-#endif
+
+/*!
+ \overload
+
+ \return a new QQuickRenderTarget referencing a D3D11 texture object
+ specified by \a texture.
+
+ \a format specifies the DXGI_FORMAT of the texture. Only texture formats
+ that are supported by Qt's rendering infrastructure should be used.
+
+ \a pixelSize specifies the size of the image, in pixels. Currently only 2D
+ textures are supported.
+
+ \a sampleCount specifies the number of samples. 0 or 1 means no
+ multisampling, while a value like 4 or 8 states that the native object is a
+ multisample texture, except when \a flags contains \l MultisampleResolve. In
+ that case, \a texture is assumed to be a non-multisample 2D texture and \a
+ sampleCount defines the number of samples desired. The resulting
+ QQuickRenderTarget will use an intermediate, automatically created
+ multisample texture as its color attachment, and will resolve the samples
+ into \a texture. This is the recommended approach to perform MSAA when the
+ native texture is not already multisample.
+
+ The texture is used as the first color attachment of the render target used
+ by the Qt Quick scenegraph. A depth-stencil buffer, if applicable, is
+ created and used automatically. When the color buffer is multisample, the
+ depth-stencil buffer will automatically be multisample too.
+
+ \note the resulting QQuickRenderTarget does not own any native resources, it
+ merely contains references and the associated metadata of the size and
+ sample count. It is the caller's responsibility to ensure that the native
+ resource exists as long as necessary.
+
+ \since 6.8
+
+ \sa QQuickWindow::setRenderTarget(), QQuickRenderControl, fromD3D11Texture()
+ */
+QQuickRenderTarget QQuickRenderTarget::fromD3D11Texture(void *texture, uint format, const QSize &pixelSize, int sampleCount, Flags flags)
+{
+ QQuickRenderTarget rt = fromD3D11Texture(texture, format, pixelSize, sampleCount);
+ QQuickRenderTargetPrivate::get(&rt)->multisampleResolve = flags.testFlag(Flag::MultisampleResolve);
+ return rt;
+}
+
+/*!
+ \return a new QQuickRenderTarget referencing a D3D12 texture object
+ specified by \a texture.
+
+ \a resourceState must a valid bitmask with bits from D3D12_RESOURCE_STATES,
+ specifying the resource's current state.
+
+ \a format specifies the DXGI_FORMAT of the texture. Only texture formats
+ that are supported by Qt's rendering infrastructure should be used.
+
+ \a pixelSize specifies the size of the image, in pixels. Currently only 2D
+ textures are supported.
+
+ \a sampleCount specifies the number of samples. 0 or 1 means no
+ multisampling, while a value like 4 or 8 states that the native object is a
+ multisample texture.
+
+ The texture is used as the first color attachment of the render target used
+ by the Qt Quick scenegraph. A depth-stencil buffer, if applicable, is
+ created and used automatically.
+
+ \note the resulting QQuickRenderTarget does not own any native resources,
+ it merely contains references and the associated metadata of the size and
+ sample count. It is the caller's responsibility to ensure that the native
+ resource exists as long as necessary.
+
+ \since 6.6
+
+ \sa QQuickWindow::setRenderTarget(), QQuickRenderControl
+ */
+QQuickRenderTarget QQuickRenderTarget::fromD3D12Texture(void *texture,
+ int resourceState,
+ uint format,
+ const QSize &pixelSize,
+ int sampleCount)
+{
+ QQuickRenderTarget rt;
+ QQuickRenderTargetPrivate *d = QQuickRenderTargetPrivate::get(&rt);
+
+ if (!texture) {
+ qWarning("QQuickRenderTarget: texture is null");
+ return rt;
+ }
+
+ if (pixelSize.isEmpty()) {
+ qWarning("QQuickRenderTarget: Cannot create with empty size");
+ return rt;
+ }
+
+ d->type = QQuickRenderTargetPrivate::Type::NativeTexture;
+ d->pixelSize = pixelSize;
+ d->sampleCount = qMax(1, sampleCount);
+
+ QRhiTexture::Flags formatFlags;
+ QRhiTexture::Format rhiFormat = QSGRhiSupport::toRhiTextureFormatFromDXGI(format, &formatFlags);
+ d->u.nativeTexture = { quint64(texture), resourceState, uint(rhiFormat), uint(formatFlags), uint(rhiFormat), uint(formatFlags) };
+
+ return rt;
+}
+
+/*!
+ \overload
+
+ \return a new QQuickRenderTarget referencing a D3D12 2D texture or 2D
+ texture array object specified by \a texture.
+
+ \a resourceState must a valid bitmask with bits from D3D12_RESOURCE_STATES,
+ specifying the resource's current state.
+
+ \a format specifies the DXGI_FORMAT of the texture. Only texture formats
+ that are supported by Qt's rendering infrastructure should be used.
+
+ \a viewFormat is the DXGI_FORMAT used for the render target view (RTV).
+ Often the same as \a format. Functional only when
+ \l{https://microsoft.github.io/DirectX-Specs/d3d/RelaxedCasting.html}{relaxed
+ format casting} is supported by the driver, the argument is ignored otherwise.
+ In practice support is expected to be always available on Windows 10 1703
+ and newer.
+
+ \a pixelSize specifies the size of the image, in pixels. Currently only 2D
+ textures and 2D texture arrays are supported.
+
+ \a sampleCount specifies the number of samples. 0 or 1 means no
+ multisampling, while a value like 4 or 8 states that the native object is a
+ multisample texture, except when \a flags contains \l MultisampleResolve. In
+ that case, \a texture is assumed to be a non-multisample 2D texture or 2D
+ texture array, and \a sampleCount defines the number of samples desired. The
+ resulting QQuickRenderTarget will use an intermediate, automatically created
+ multisample texture (or texture array) as its color attachment, and will
+ resolve the samples into \a texture. This is the recommended approach to
+ perform MSAA when the native D3D12 texture is not already multisample.
+
+ The number of array elements (layers) is given in \a arraySize. When greater
+ than 1, it implies multiview rendering
+ (\l{https://microsoft.github.io/DirectX-Specs/d3d/ViewInstancing.html}{view
+ instancing}), which can be relevant with VR/AR especially. \a arraySize is
+ the number of views, typically \c 2. See \l QSGMaterial::viewCount() for
+ details on enabling multiview rendering within the Qt Quick scenegraph.
+
+ The texture is used as the first color attachment of the render target used
+ by the Qt Quick scenegraph. A depth-stencil buffer, if applicable, is
+ created and used automatically. When the color buffer is multisample, the
+ depth-stencil buffer will automatically be multisample too. For multiview
+ rendering, the depth-stencil texture will automatically be made an array
+ with a matching \a arraySize.
+
+ \note the resulting QQuickRenderTarget does not own any native resources, it
+ merely contains references and the associated metadata of the size and
+ sample count. It is the caller's responsibility to ensure that the native
+ resource exists as long as necessary.
+
+ \since 6.8
+
+ \sa QQuickWindow::setRenderTarget(), QQuickRenderControl
+ */
+QQuickRenderTarget QQuickRenderTarget::fromD3D12Texture(void *texture,
+ int resourceState,
+ uint format,
+ uint viewFormat,
+ const QSize &pixelSize,
+ int sampleCount,
+ int arraySize,
+ Flags flags)
+{
+ QQuickRenderTarget rt;
+ QQuickRenderTargetPrivate *d = QQuickRenderTargetPrivate::get(&rt);
+
+ if (!texture) {
+ qWarning("QQuickRenderTarget: texture is null");
+ return rt;
+ }
+
+ if (pixelSize.isEmpty()) {
+ qWarning("QQuickRenderTarget: Cannot create with empty size");
+ return rt;
+ }
+
+ QRhiTexture::Flags formatFlags;
+ QRhiTexture::Format rhiFormat = QSGRhiSupport::toRhiTextureFormatFromDXGI(format, &formatFlags);
+ QRhiTexture::Flags viewFormatFlags;
+ QRhiTexture::Format rhiViewFormat = QSGRhiSupport::toRhiTextureFormatFromDXGI(viewFormat, &viewFormatFlags);
+
+ d->pixelSize = pixelSize;
+ d->sampleCount = qMax(1, sampleCount);
+ d->multisampleResolve = flags.testFlag(Flag::MultisampleResolve);
+
+ if (arraySize <= 1) {
+ d->type = QQuickRenderTargetPrivate::Type::NativeTexture;
+ d->u.nativeTexture = { quint64(texture), resourceState, uint(rhiFormat), uint(formatFlags), uint(rhiViewFormat), uint(viewFormatFlags) };
+ } else {
+ d->type = QQuickRenderTargetPrivate::Type::NativeTextureArray;
+ d->u.nativeTextureArray = { quint64(texture), resourceState, arraySize, uint(rhiFormat), uint(formatFlags), uint(rhiViewFormat), uint(viewFormatFlags) };
+ }
+
+ return rt;
+}
+
+#endif // Q_OS_WIN
/*!
\return a new QQuickRenderTarget referencing a Metal texture object
@@ -397,7 +763,7 @@ QQuickRenderTarget QQuickRenderTarget::fromD3D11Texture(void *texture, const QSi
\a pixelSize specifies the size of the image, in pixels. Currently only 2D
textures are supported.
- \a sampleCount specific the number of samples. 0 or 1 means no
+ \a sampleCount specifies the number of samples. 0 or 1 means no
multisampling, while a value like 4 or 8 states that the native object is a
multisample texture.
@@ -414,7 +780,7 @@ QQuickRenderTarget QQuickRenderTarget::fromD3D11Texture(void *texture, const QSi
\sa QQuickWindow::setRenderTarget(), QQuickRenderControl
*/
-#if defined(Q_OS_MACOS) || defined(Q_OS_IOS) || defined(Q_QDOC)
+#if QT_CONFIG(metal) || defined(Q_QDOC)
QQuickRenderTarget QQuickRenderTarget::fromMetalTexture(MTLTexture *texture, uint format,
const QSize &pixelSize, int sampleCount)
{
@@ -435,9 +801,9 @@ QQuickRenderTarget QQuickRenderTarget::fromMetalTexture(MTLTexture *texture, uin
d->pixelSize = pixelSize;
d->sampleCount = qMax(1, sampleCount);
- QRhiTexture::Flags flags;
- auto rhiFormat = QSGRhiSupport::toRhiTextureFormatFromMetal(format, &flags);
- d->u.nativeTexture = { quint64(texture), 0, uint(rhiFormat), uint(flags) };
+ QRhiTexture::Flags formatFlags;
+ QRhiTexture::Format rhiFormat = QSGRhiSupport::toRhiTextureFormatFromMetal(format, &formatFlags);
+ d->u.nativeTexture = { quint64(texture), 0, uint(rhiFormat), uint(formatFlags), uint(rhiFormat), uint(formatFlags) };
return rt;
}
@@ -452,7 +818,7 @@ QQuickRenderTarget QQuickRenderTarget::fromMetalTexture(MTLTexture *texture, uin
\a pixelSize specifies the size of the image, in pixels. Currently only 2D
textures are supported.
- \a sampleCount specific the number of samples. 0 or 1 means no
+ \a sampleCount specifies the number of samples. 0 or 1 means no
multisampling, while a value like 4 or 8 states that the native object is a
multisample texture.
@@ -471,6 +837,94 @@ QQuickRenderTarget QQuickRenderTarget::fromMetalTexture(MTLTexture *texture, con
{
return fromMetalTexture(texture, 0 /* MTLPixelFormatInvalid */, pixelSize, sampleCount);
}
+
+/*!
+ \overload
+
+ \return a new QQuickRenderTarget referencing a Metal 2D texture or 2D
+ texture array given in \a texture.
+
+ \a format specifies the MTLPixelFormat of the texture. Only texture formats
+ that are supported by Qt's rendering infrastructure should be used.
+
+ \a viewFormat is usually set to the same value as \a format. In some cases,
+ such as when rendering into a texture with a \c{_SRGB} format and the
+ implicit linear->sRGB conversion on shader writes is not wanted, the value
+ can be different. Note however that the value may be ignored by Qt, when at
+ run time QRhi reports that the \l{QRhi::TextureViewFormat} feature is
+ unsupported.
+
+ \a pixelSize specifies the size of the image, in pixels. Currently only 2D
+ textures and 2D texture arrays are supported.
+
+ \a sampleCount specifies the number of samples. 0 or 1 means no
+ multisampling, while a value like 4 or 8 states that the native object is a
+ multisample texture, except when \a flags contains \l MultisampleResolve. In
+ that case, \a texture is assumed to be a non-multisample 2D texture or 2D
+ texture array, and \a sampleCount defines the number of samples desired. The
+ resulting QQuickRenderTarget will use an intermediate, automatically created
+ multisample texture (or texture array) as its color attachment, and will
+ resolve the samples into \a texture. This is the recommended approach to
+ perform MSAA when the native Metal texture is not already multisample.
+
+ The number of array elements (layers) is given in \a arraySize. When greater
+ than 1, it implies multiview rendering, which can be relevant with VR/AR
+ especially. \a arraySize is the number of views, typically \c 2. See
+ \l QSGMaterial::viewCount() for details on enabling multiview rendering within
+ the Qt Quick scenegraph.
+
+ The texture is used as the first color attachment of the render target used
+ by the Qt Quick scenegraph. A depth-stencil buffer, if applicable, is
+ created and used automatically. When the color buffer is multisample, the
+ depth-stencil buffer will automatically be multisample too. For multiview
+ rendering, the depth-stencil texture will automatically be made an array
+ with a matching \a arraySize.
+
+ \note the resulting QQuickRenderTarget does not own any native resources, it
+ merely contains references and the associated metadata of the size and
+ sample count. It is the caller's responsibility to ensure that the native
+ resource exists as long as necessary.
+
+ \since 6.8
+
+ \sa QQuickWindow::setRenderTarget(), QQuickRenderControl
+ */
+QQuickRenderTarget QQuickRenderTarget::fromMetalTexture(MTLTexture *texture, uint format, uint viewFormat,
+ const QSize &pixelSize, int sampleCount, int arraySize, Flags flags)
+{
+ QQuickRenderTarget rt;
+ QQuickRenderTargetPrivate *d = QQuickRenderTargetPrivate::get(&rt);
+
+ if (!texture) {
+ qWarning("QQuickRenderTarget: texture is null");
+ return rt;
+ }
+
+ if (pixelSize.isEmpty()) {
+ qWarning("QQuickRenderTarget: Cannot create with empty size");
+ return rt;
+ }
+
+ QRhiTexture::Flags formatFlags;
+ QRhiTexture::Format rhiFormat = QSGRhiSupport::toRhiTextureFormatFromMetal(format, &formatFlags);
+ QRhiTexture::Flags viewFormatFlags;
+ QRhiTexture::Format rhiViewFormat = QSGRhiSupport::toRhiTextureFormatFromMetal(viewFormat, &viewFormatFlags);
+
+ d->pixelSize = pixelSize;
+ d->sampleCount = qMax(1, sampleCount);
+ d->multisampleResolve = flags.testFlag(Flag::MultisampleResolve);
+
+ if (arraySize <= 1) {
+ d->type = QQuickRenderTargetPrivate::Type::NativeTexture;
+ d->u.nativeTexture = { quint64(texture), 0, uint(rhiFormat), uint(formatFlags), uint(rhiViewFormat), uint(viewFormatFlags) };
+ } else {
+ d->type = QQuickRenderTargetPrivate::Type::NativeTextureArray;
+ d->u.nativeTextureArray = { quint64(texture), 0, arraySize, uint(rhiFormat), uint(formatFlags), uint(rhiViewFormat), uint(viewFormatFlags) };
+ }
+
+ return rt;
+}
+
#endif
/*!
@@ -484,7 +938,7 @@ QQuickRenderTarget QQuickRenderTarget::fromMetalTexture(MTLTexture *texture, con
\a pixelSize specifies the size of the image, in pixels. Currently only 2D
textures are supported.
- \a sampleCount specific the number of samples. 0 or 1 means no
+ \a sampleCount specifies the number of samples. 0 or 1 means no
multisampling, while a value like 4 or 8 states that the native object is a
multisample texture.
@@ -502,8 +956,7 @@ QQuickRenderTarget QQuickRenderTarget::fromMetalTexture(MTLTexture *texture, con
\sa QQuickWindow::setRenderTarget(), QQuickRenderControl
*/
#if QT_CONFIG(vulkan) || defined(Q_QDOC)
-QQuickRenderTarget QQuickRenderTarget::fromVulkanImage(VkImage image, VkImageLayout layout, VkFormat format,
- const QSize &pixelSize, int sampleCount)
+QQuickRenderTarget QQuickRenderTarget::fromVulkanImage(VkImage image, VkImageLayout layout, VkFormat format, const QSize &pixelSize, int sampleCount)
{
QQuickRenderTarget rt;
QQuickRenderTargetPrivate *d = QQuickRenderTargetPrivate::get(&rt);
@@ -522,9 +975,9 @@ QQuickRenderTarget QQuickRenderTarget::fromVulkanImage(VkImage image, VkImageLay
d->pixelSize = pixelSize;
d->sampleCount = qMax(1, sampleCount);
- QRhiTexture::Flags flags;
- auto rhiFormat = QSGRhiSupport::toRhiTextureFormatFromVulkan(format, &flags);
- d->u.nativeTexture = { quint64(image), layout, uint(rhiFormat), uint(flags) };
+ QRhiTexture::Flags formatFlags;
+ QRhiTexture::Format rhiFormat = QSGRhiSupport::toRhiTextureFormatFromVulkan(format, &formatFlags);
+ d->u.nativeTexture = { quint64(image), layout, uint(rhiFormat), uint(formatFlags), uint(rhiFormat), uint(formatFlags) };
return rt;
}
@@ -532,14 +985,14 @@ QQuickRenderTarget QQuickRenderTarget::fromVulkanImage(VkImage image, VkImageLay
/*!
\overload
- \return a new QQuickRenderTarget referencing n Vulkan image object specified
+ \return a new QQuickRenderTarget referencing a Vulkan image object specified
by \a image. The image is assumed to have a format of
VK_FORMAT_R8G8B8A8_UNORM.
\a pixelSize specifies the size of the image, in pixels. Currently only 2D
textures are supported.
- \a sampleCount specific the number of samples. 0 or 1 means no
+ \a sampleCount specifies the number of samples. 0 or 1 means no
multisampling, while a value like 4 or 8 states that the native object is a
multisample texture.
@@ -558,11 +1011,113 @@ QQuickRenderTarget QQuickRenderTarget::fromVulkanImage(VkImage image, VkImageLay
{
return fromVulkanImage(image, layout, VK_FORMAT_UNDEFINED, pixelSize, sampleCount);
}
-#endif
/*!
- \internal
+ \overload
+
+ \return a new QQuickRenderTarget referencing a Vulkan image object
+ specified by \a image. The current \a layout of the image must be provided
+ as well. The image must be either a 2D texture or 2D texture array.
+
+ \a format specifies the VkFormat of the image. Only image formats that are
+ supported by Qt's rendering infrastructure should be used.
+
+ \a viewFormat is usually set to the same value as \a format. In some cases,
+ such as when rendering into a texture with a \c{_SRGB} format and the
+ implicit linear->sRGB conversion on shader writes is not wanted, the value
+ can be different. (for example, a \a format of \c VK_FORMAT_R8G8B8A8_SRGB
+ and \a viewFormat of \c VK_FORMAT_R8G8B8A8_UNORM).
+
+ \a pixelSize specifies the size of the image, in pixels. Currently only 2D
+ textures are supported.
+
+ \a sampleCount specifies the number of samples. 0 or 1 means no
+ multisampling, while a value like 4 or 8 states that the native object is a
+ multisample texture, except when \a flags contains \l MultisampleResolve. In
+ that case, \a image is assumed to be a non-multisample 2D texture or 2D
+ texture array, and \a sampleCount defines the number of samples desired. The
+ resulting QQuickRenderTarget will use an intermediate, automatically created
+ multisample texture (or texture array) as its color attachment, and will
+ resolve the samples into \a image. This is the recommended approach to
+ perform MSAA when the native Vulkan image is not already multisample.
+
+ The number of array elements (layers) is given in \a arraySize. When greater
+ than 1, it implies multiview rendering
+ (\l{https://registry.khronos.org/vulkan/specs/1.3-extensions/man/html/VK_KHR_multiview.html}{VK_KHR_multiview}),
+ which can be relevant with VR/AR especially. \a arraySize is the number of
+ views, typically \c 2. See \l QSGMaterial::viewCount() for details on
+ enabling multiview rendering within the Qt Quick scenegraph.
+
+ The texture is used as the first color attachment of the render target used
+ by the Qt Quick scenegraph. A depth-stencil buffer, if applicable, is
+ created and used automatically. When the color buffer is multisample, the
+ depth-stencil buffer will automatically be multisample too. For multiview
+ rendering, the depth-stencil texture will automatically be made an array
+ with a matching \a arraySize.
+
+ \note the resulting QQuickRenderTarget does not own any native resources, it
+ merely contains references and the associated metadata of the size and
+ sample count. It is the caller's responsibility to ensure that the native
+ resource exists as long as necessary.
+
+ \since 6.8
+
+ \sa QQuickWindow::setRenderTarget(), QQuickRenderControl
*/
+QQuickRenderTarget QQuickRenderTarget::fromVulkanImage(VkImage image, VkImageLayout layout, VkFormat format, VkFormat viewFormat,
+ const QSize &pixelSize, int sampleCount, int arraySize, Flags flags)
+{
+ QQuickRenderTarget rt;
+ QQuickRenderTargetPrivate *d = QQuickRenderTargetPrivate::get(&rt);
+
+ if (image == VK_NULL_HANDLE) {
+ qWarning("QQuickRenderTarget: image is invalid");
+ return rt;
+ }
+
+ if (pixelSize.isEmpty()) {
+ qWarning("QQuickRenderTarget: Cannot create with empty size");
+ return rt;
+ }
+
+ QRhiTexture::Flags formatFlags;
+ QRhiTexture::Format rhiFormat = QSGRhiSupport::toRhiTextureFormatFromVulkan(format, &formatFlags);
+ QRhiTexture::Flags viewFormatFlags;
+ QRhiTexture::Format rhiViewFormat = QSGRhiSupport::toRhiTextureFormatFromVulkan(viewFormat, &viewFormatFlags);
+
+ d->pixelSize = pixelSize;
+ d->sampleCount = qMax(1, sampleCount);
+ d->multisampleResolve = flags.testFlag(Flag::MultisampleResolve);
+
+ if (arraySize <= 1) {
+ d->type = QQuickRenderTargetPrivate::Type::NativeTexture;
+ d->u.nativeTexture = { quint64(image), layout, uint(rhiFormat), uint(formatFlags), uint(rhiViewFormat), uint(viewFormatFlags) };
+ } else {
+ d->type = QQuickRenderTargetPrivate::Type::NativeTextureArray;
+ d->u.nativeTextureArray = { quint64(image), layout, arraySize, uint(rhiFormat), uint(formatFlags), uint(rhiViewFormat), uint(viewFormatFlags) };
+ }
+
+ return rt;
+}
+
+#endif // Vulkan
+
+/*!
+ \return a new QQuickRenderTarget referencing an existing \a renderTarget.
+
+ \a renderTarget will in most cases be a QRhiTextureRenderTarget, which
+ allows directing the Qt Quick scene's rendering into a QRhiTexture.
+
+ \note the resulting QQuickRenderTarget does not own \a renderTarget and any
+ underlying native resources, it merely contains references and the
+ associated metadata of the size and sample count. It is the caller's
+ responsibility to ensure that the referenced resources exists as long as
+ necessary.
+
+ \since 6.6
+
+ \sa QQuickWindow::setRenderTarget(), QQuickRenderControl
+*/
QQuickRenderTarget QQuickRenderTarget::fromRhiRenderTarget(QRhiRenderTarget *renderTarget)
{
QQuickRenderTarget rt;
@@ -628,7 +1183,8 @@ bool QQuickRenderTarget::isEqual(const QQuickRenderTarget &other) const noexcept
|| d->pixelSize != other.d->pixelSize
|| d->devicePixelRatio != other.d->devicePixelRatio
|| d->sampleCount != other.d->sampleCount
- || d->mirrorVertically != other.d->mirrorVertically)
+ || d->mirrorVertically != other.d->mirrorVertically
+ || d->multisampleResolve != other.d->multisampleResolve)
{
return false;
}
@@ -638,9 +1194,21 @@ bool QQuickRenderTarget::isEqual(const QQuickRenderTarget &other) const noexcept
break;
case QQuickRenderTargetPrivate::Type::NativeTexture:
if (d->u.nativeTexture.object != other.d->u.nativeTexture.object
- || d->u.nativeTexture.layout != other.d->u.nativeTexture.layout
+ || d->u.nativeTexture.layoutOrState != other.d->u.nativeTexture.layoutOrState
|| d->u.nativeTexture.rhiFormat != other.d->u.nativeTexture.rhiFormat
- || d->u.nativeTexture.rhiFlags != other.d->u.nativeTexture.rhiFlags)
+ || d->u.nativeTexture.rhiFormatFlags != other.d->u.nativeTexture.rhiFormatFlags
+ || d->u.nativeTexture.rhiViewFormat != other.d->u.nativeTexture.rhiViewFormat
+ || d->u.nativeTexture.rhiViewFormatFlags != other.d->u.nativeTexture.rhiViewFormatFlags)
+ return false;
+ break;
+ case QQuickRenderTargetPrivate::Type::NativeTextureArray:
+ if (d->u.nativeTextureArray.object != other.d->u.nativeTextureArray.object
+ || d->u.nativeTextureArray.layoutOrState != other.d->u.nativeTextureArray.layoutOrState
+ || d->u.nativeTextureArray.arraySize != other.d->u.nativeTextureArray.arraySize
+ || d->u.nativeTextureArray.rhiFormat != other.d->u.nativeTextureArray.rhiFormat
+ || d->u.nativeTextureArray.rhiFormatFlags != other.d->u.nativeTextureArray.rhiFormatFlags
+ || d->u.nativeTextureArray.rhiViewFormat != other.d->u.nativeTextureArray.rhiViewFormat
+ || d->u.nativeTextureArray.rhiViewFormatFlags != other.d->u.nativeTextureArray.rhiViewFormatFlags)
return false;
break;
case QQuickRenderTargetPrivate::Type::NativeRenderbuffer:
@@ -662,21 +1230,260 @@ bool QQuickRenderTarget::isEqual(const QQuickRenderTarget &other) const noexcept
return true;
}
-static bool createRhiRenderTarget(const QRhiColorAttachment &colorAttachment,
+static bool createRhiRenderTargetWithRenderBuffer(QRhiRenderBuffer *renderBuffer,
+ const QSize &pixelSize,
+ int sampleCount,
+ QRhi *rhi,
+ QQuickWindowRenderTarget *dst)
+{
+ sampleCount = QSGRhiSupport::chooseSampleCount(sampleCount, rhi);
+
+ std::unique_ptr<QRhiRenderBuffer> depthStencil;
+ if (dst->implicitBuffers.depthStencil) {
+ if (dst->implicitBuffers.depthStencil->pixelSize() == pixelSize
+ && dst->implicitBuffers.depthStencil->sampleCount() == sampleCount)
+ {
+ depthStencil.reset(dst->implicitBuffers.depthStencil);
+ dst->implicitBuffers.depthStencil = nullptr;
+ }
+ }
+ dst->implicitBuffers.reset(rhi);
+
+ if (!depthStencil) {
+ depthStencil.reset(rhi->newRenderBuffer(QRhiRenderBuffer::DepthStencil, pixelSize, sampleCount));
+ depthStencil->setName(QByteArrayLiteral("Depth-stencil buffer for QQuickRenderTarget"));
+ if (!depthStencil->create()) {
+ qWarning("Failed to build depth-stencil buffer for QQuickRenderTarget");
+ return false;
+ }
+ }
+
+ QRhiColorAttachment colorAttachment(renderBuffer);
+ QRhiTextureRenderTargetDescription rtDesc(colorAttachment);
+ rtDesc.setDepthStencilBuffer(depthStencil.get());
+ std::unique_ptr<QRhiTextureRenderTarget> rt(rhi->newTextureRenderTarget(rtDesc));
+ rt->setName(QByteArrayLiteral("RT for QQuickRenderTarget with renderbuffer"));
+ std::unique_ptr<QRhiRenderPassDescriptor> rp(rt->newCompatibleRenderPassDescriptor());
+ rt->setRenderPassDescriptor(rp.get());
+
+ if (!rt->create()) {
+ qWarning("Failed to build renderbuffer-based render target for QQuickRenderTarget");
+ return false;
+ }
+
+ dst->rt.renderTarget = rt.release();
+ dst->rt.owns = true;
+ dst->res.rpDesc = rp.release();
+ dst->implicitBuffers.depthStencil = depthStencil.release();
+
+ return true;
+}
+
+static bool createRhiRenderTarget(QRhiTexture *texture,
const QSize &pixelSize,
int sampleCount,
+ bool multisampleResolve,
QRhi *rhi,
QQuickWindowRenderTarget *dst)
{
- std::unique_ptr<QRhiRenderBuffer> depthStencil(rhi->newRenderBuffer(QRhiRenderBuffer::DepthStencil, pixelSize, sampleCount));
- if (!depthStencil->create()) {
- qWarning("Failed to build depth-stencil buffer for QQuickRenderTarget");
- return false;
+ // Simple path: no user-supplied depth texture. So create our own
+ // depth-stencil buffer, using renderbuffers (so this is still GLES 2.0
+ // compatible), with MSAA support being GLES 3.0 compatible.
+
+ sampleCount = QSGRhiSupport::chooseSampleCount(sampleCount, rhi);
+ if (sampleCount <= 1)
+ multisampleResolve = false;
+
+ std::unique_ptr<QRhiRenderBuffer> depthStencil;
+ if (dst->implicitBuffers.depthStencil) {
+ if (dst->implicitBuffers.depthStencil->pixelSize() == pixelSize
+ && dst->implicitBuffers.depthStencil->sampleCount() == sampleCount)
+ {
+ depthStencil.reset(dst->implicitBuffers.depthStencil);
+ dst->implicitBuffers.depthStencil = nullptr;
+ }
+ }
+
+ std::unique_ptr<QRhiTexture> colorBuffer;
+ QRhiTexture::Flags multisampleTextureFlags;
+ QRhiTexture::Format multisampleTextureFormat = texture->format();
+ if (multisampleResolve) {
+ multisampleTextureFlags = QRhiTexture::RenderTarget;
+ if (texture->flags().testFlag(QRhiTexture::sRGB))
+ multisampleTextureFlags |= QRhiTexture::sRGB;
+
+ if (dst->implicitBuffers.multisampleTexture) {
+ if (dst->implicitBuffers.multisampleTexture->pixelSize() == pixelSize
+ && dst->implicitBuffers.multisampleTexture->format() == multisampleTextureFormat
+ && dst->implicitBuffers.multisampleTexture->sampleCount() == sampleCount
+ && dst->implicitBuffers.multisampleTexture->flags().testFlags(multisampleTextureFlags))
+ {
+ colorBuffer.reset(dst->implicitBuffers.multisampleTexture);
+ dst->implicitBuffers.multisampleTexture = nullptr;
+ }
+ }
}
+ dst->implicitBuffers.reset(rhi);
+
+ if (!depthStencil) {
+ depthStencil.reset(rhi->newRenderBuffer(QRhiRenderBuffer::DepthStencil, pixelSize, sampleCount));
+ depthStencil->setName(QByteArrayLiteral("Depth-stencil buffer for QQuickRenderTarget"));
+ if (!depthStencil->create()) {
+ qWarning("Failed to build depth-stencil buffer for QQuickRenderTarget");
+ return false;
+ }
+ }
+
+ if (multisampleResolve && !colorBuffer) {
+ colorBuffer.reset(rhi->newTexture(multisampleTextureFormat, pixelSize, sampleCount, multisampleTextureFlags));
+ colorBuffer->setName(QByteArrayLiteral("Multisample color buffer for QQuickRenderTarget"));
+ colorBuffer->setWriteViewFormat(texture->writeViewFormat());
+ if (!colorBuffer->create()) {
+ qWarning("Failed to build multisample color buffer for QQuickRenderTarget");
+ return false;
+ }
+ }
+
+ QRhiColorAttachment colorAttachment;
+ if (multisampleResolve) {
+ colorAttachment.setTexture(colorBuffer.get());
+ colorAttachment.setResolveTexture(texture);
+ } else {
+ colorAttachment.setTexture(texture);
+ }
QRhiTextureRenderTargetDescription rtDesc(colorAttachment);
rtDesc.setDepthStencilBuffer(depthStencil.get());
std::unique_ptr<QRhiTextureRenderTarget> rt(rhi->newTextureRenderTarget(rtDesc));
+ rt->setName(QByteArrayLiteral("RT for QQuickRenderTarget"));
+ std::unique_ptr<QRhiRenderPassDescriptor> rp(rt->newCompatibleRenderPassDescriptor());
+ rt->setRenderPassDescriptor(rp.get());
+
+ if (!rt->create()) {
+ qWarning("Failed to build texture render target for QQuickRenderTarget");
+ return false;
+ }
+
+ dst->rt.renderTarget = rt.release();
+ dst->rt.owns = true;
+ dst->res.rpDesc = rp.release();
+ dst->implicitBuffers.depthStencil = depthStencil.release();
+ if (multisampleResolve)
+ dst->implicitBuffers.multisampleTexture = colorBuffer.release();
+
+ return true;
+}
+
+static bool createRhiRenderTargetWithDepthTexture(QRhiTexture *texture,
+ QRhiTexture *depthTexture,
+ const QSize &pixelSize,
+ int sampleCount,
+ bool multisampleResolve,
+ QRhi *rhi,
+ QQuickWindowRenderTarget *dst)
+{
+ // This version takes a user-supplied depthTexture. That texture is always
+ // non-multisample. If sample count is > 1, we still need our own
+ // multisample depth-stencil buffer, and the depth(stencil) data is expected
+ // to be resolved (and written out) to depthTexture, _if_ the underlying API
+ // supports it (see QRhi's ResolveDepthStencil feature). The intermediate,
+ // multisample depth-stencil buffer must be a texture here (not
+ // renderbuffer), specifically for OpenGL ES and its related multisample
+ // extensions.
+
+ sampleCount = QSGRhiSupport::chooseSampleCount(sampleCount, rhi);
+ if (sampleCount <= 1)
+ multisampleResolve = false;
+
+ std::unique_ptr<QRhiTexture> depthStencil;
+ if (dst->implicitBuffers.depthStencilTexture) {
+ if (dst->implicitBuffers.depthStencilTexture->pixelSize() == pixelSize
+ && dst->implicitBuffers.depthStencilTexture->sampleCount() == sampleCount)
+ {
+ depthStencil.reset(dst->implicitBuffers.depthStencilTexture);
+ dst->implicitBuffers.depthStencilTexture = nullptr;
+ }
+ }
+
+ std::unique_ptr<QRhiTexture> colorBuffer;
+ QRhiTexture::Flags multisampleTextureFlags;
+ QRhiTexture::Format multisampleTextureFormat = texture->format();
+ if (multisampleResolve) {
+ multisampleTextureFlags = QRhiTexture::RenderTarget;
+ if (texture->flags().testFlag(QRhiTexture::sRGB))
+ multisampleTextureFlags |= QRhiTexture::sRGB;
+
+ if (dst->implicitBuffers.multisampleTexture) {
+ if (dst->implicitBuffers.multisampleTexture->pixelSize() == pixelSize
+ && dst->implicitBuffers.multisampleTexture->format() == multisampleTextureFormat
+ && dst->implicitBuffers.multisampleTexture->sampleCount() == sampleCount
+ && dst->implicitBuffers.multisampleTexture->flags().testFlags(multisampleTextureFlags))
+ {
+ colorBuffer.reset(dst->implicitBuffers.multisampleTexture);
+ dst->implicitBuffers.multisampleTexture = nullptr;
+ }
+ }
+ }
+
+ dst->implicitBuffers.reset(rhi);
+
+ bool needsDepthStencilBuffer = true;
+ if (sampleCount <= 1) {
+ depthStencil.reset();
+ needsDepthStencilBuffer = false;
+ }
+ if (depthTexture->pixelSize() != pixelSize) {
+ qWarning("Custom depth texture size (%dx%d) does not match the QQuickRenderTarget (%dx%d)",
+ depthTexture->pixelSize().width(),
+ depthTexture->pixelSize().height(),
+ pixelSize.width(),
+ pixelSize.height());
+ return false;
+ }
+ if (depthTexture->sampleCount() > 1) {
+ qWarning("Custom depth texture cannot be multisample");
+ return false;
+ }
+ if (needsDepthStencilBuffer && !depthStencil) {
+ depthStencil.reset(rhi->newTexture(QRhiTexture::D24S8, pixelSize, sampleCount, QRhiTexture::RenderTarget));
+ depthStencil->setName(QByteArrayLiteral("Depth-stencil texture for QQuickRenderTarget"));
+ if (!depthStencil->create()) {
+ qWarning("Failed to build depth-stencil buffer for QQuickRenderTarget");
+ return false;
+ }
+ }
+
+ if (multisampleResolve && !colorBuffer) {
+ colorBuffer.reset(rhi->newTexture(multisampleTextureFormat, pixelSize, sampleCount, multisampleTextureFlags));
+ colorBuffer->setName(QByteArrayLiteral("Multisample color buffer for QQuickRenderTarget"));
+ colorBuffer->setWriteViewFormat(texture->writeViewFormat());
+ if (!colorBuffer->create()) {
+ qWarning("Failed to build multisample color buffer for QQuickRenderTarget");
+ return false;
+ }
+ }
+
+ QRhiColorAttachment colorAttachment;
+ if (multisampleResolve) {
+ colorAttachment.setTexture(colorBuffer.get());
+ colorAttachment.setResolveTexture(texture);
+ } else {
+ colorAttachment.setTexture(texture);
+ }
+
+ QRhiTextureRenderTargetDescription rtDesc(colorAttachment);
+ if (sampleCount > 1) {
+ rtDesc.setDepthTexture(depthStencil.get());
+ if (rhi->isFeatureSupported(QRhi::ResolveDepthStencil))
+ rtDesc.setDepthResolveTexture(depthTexture);
+ else
+ qWarning("Depth-stencil resolve is not supported by the underlying 3D API, depth contents will not be resolved");
+ } else {
+ rtDesc.setDepthTexture(depthTexture);
+ }
+
+ std::unique_ptr<QRhiTextureRenderTarget> rt(rhi->newTextureRenderTarget(rtDesc));
+ rt->setName(QByteArrayLiteral("RT for QQuickRenderTarget"));
std::unique_ptr<QRhiRenderPassDescriptor> rp(rt->newCompatibleRenderPassDescriptor());
rt->setRenderPassDescriptor(rp.get());
@@ -685,37 +1492,222 @@ static bool createRhiRenderTarget(const QRhiColorAttachment &colorAttachment,
return false;
}
- dst->renderTarget = rt.release();
- dst->rpDesc = rp.release();
- dst->depthStencil = depthStencil.release();
- dst->owns = true; // ownership of the native resource itself is not transferred but the QRhi objects are on us now
+ dst->rt.renderTarget = rt.release();
+ dst->rt.owns = true;
+ dst->res.rpDesc = rp.release();
+ if (depthStencil)
+ dst->implicitBuffers.depthStencilTexture = depthStencil.release();
+ if (multisampleResolve)
+ dst->implicitBuffers.multisampleTexture = colorBuffer.release();
+
+ return true;
+}
+
+static bool createRhiRenderTargetMultiView(QRhiTexture *texture,
+ QRhiTexture *maybeCustomDepthTexture,
+ const QSize &pixelSize,
+ int arraySize,
+ int sampleCount,
+ bool multisampleResolve,
+ QRhi *rhi,
+ QQuickWindowRenderTarget *dst)
+{
+ // Multiview path, working with texture arrays. Optionally with a
+ // user-supplied, non-multisample depth texture (array). (same semantics
+ // then as with createRhiRenderTargetWithDepthTexture, but everything is a
+ // 2D texture array here)
+
+ sampleCount = QSGRhiSupport::chooseSampleCount(sampleCount, rhi);
+ if (sampleCount <= 1)
+ multisampleResolve = false;
+
+ std::unique_ptr<QRhiTexture> depthStencil;
+ if (dst->implicitBuffers.depthStencilTexture) {
+ if (dst->implicitBuffers.depthStencilTexture->pixelSize() == pixelSize
+ && dst->implicitBuffers.depthStencilTexture->sampleCount() == sampleCount
+ && dst->implicitBuffers.depthStencilTexture->arraySize() == arraySize)
+ {
+ depthStencil.reset(dst->implicitBuffers.depthStencilTexture);
+ dst->implicitBuffers.depthStencilTexture = nullptr;
+ }
+ }
+
+ std::unique_ptr<QRhiTexture> colorBuffer;
+ QRhiTexture::Flags multisampleTextureFlags;
+ QRhiTexture::Format multisampleTextureFormat = texture->format();
+ if (multisampleResolve) {
+ multisampleTextureFlags = QRhiTexture::RenderTarget;
+ if (texture->flags().testFlag(QRhiTexture::sRGB))
+ multisampleTextureFlags |= QRhiTexture::sRGB;
+
+ if (dst->implicitBuffers.multisampleTexture) {
+ if (dst->implicitBuffers.multisampleTexture->pixelSize() == pixelSize
+ && dst->implicitBuffers.multisampleTexture->format() == multisampleTextureFormat
+ && dst->implicitBuffers.multisampleTexture->sampleCount() == sampleCount
+ && dst->implicitBuffers.multisampleTexture->arraySize() == arraySize
+ && dst->implicitBuffers.multisampleTexture->flags().testFlags(multisampleTextureFlags))
+ {
+ colorBuffer.reset(dst->implicitBuffers.multisampleTexture);
+ dst->implicitBuffers.multisampleTexture = nullptr;
+ }
+ }
+ }
+
+ dst->implicitBuffers.reset(rhi);
+
+ bool needsDepthStencilBuffer = true;
+ if (maybeCustomDepthTexture) {
+ if (sampleCount <= 1) {
+ depthStencil.reset();
+ needsDepthStencilBuffer = false;
+ }
+ if (maybeCustomDepthTexture->arraySize() != arraySize) {
+ qWarning("Custom depth texture array size (%d) does not match QQuickRenderTarget (%d)",
+ maybeCustomDepthTexture->arraySize(), arraySize);
+ return false;
+ }
+ if (maybeCustomDepthTexture->pixelSize() != pixelSize) {
+ qWarning("Custom depth texture size (%dx%d) does not match the QQuickRenderTarget (%dx%d)",
+ maybeCustomDepthTexture->pixelSize().width(),
+ maybeCustomDepthTexture->pixelSize().height(),
+ pixelSize.width(),
+ pixelSize.height());
+ return false;
+ }
+ if (maybeCustomDepthTexture->sampleCount() > 1) {
+ qWarning("Custom depth texture cannot be multisample");
+ return false;
+ }
+ }
+ if (needsDepthStencilBuffer && !depthStencil) {
+ depthStencil.reset(rhi->newTextureArray(QRhiTexture::D24S8, arraySize, pixelSize, sampleCount, QRhiTexture::RenderTarget));
+ depthStencil->setName(QByteArrayLiteral("Depth-stencil buffer (multiview) for QQuickRenderTarget"));
+ if (!depthStencil->create()) {
+ qWarning("Failed to build depth-stencil texture array for QQuickRenderTarget");
+ return false;
+ }
+ }
+
+ if (multisampleResolve && !colorBuffer) {
+ colorBuffer.reset(rhi->newTextureArray(multisampleTextureFormat, arraySize, pixelSize, sampleCount, multisampleTextureFlags));
+ colorBuffer->setName(QByteArrayLiteral("Multisample color buffer (multiview) for QQuickRenderTarget"));
+ colorBuffer->setWriteViewFormat(texture->writeViewFormat());
+ if (!colorBuffer->create()) {
+ qWarning("Failed to build multisample texture array for QQuickRenderTarget");
+ return false;
+ }
+ }
+
+ QRhiColorAttachment colorAttachment;
+ colorAttachment.setMultiViewCount(arraySize);
+ if (multisampleResolve) {
+ colorAttachment.setTexture(colorBuffer.get());
+ colorAttachment.setResolveTexture(texture);
+ } else {
+ colorAttachment.setTexture(texture);
+ }
+
+ QRhiTextureRenderTargetDescription rtDesc(colorAttachment);
+ if (sampleCount > 1) {
+ rtDesc.setDepthTexture(depthStencil.get());
+ if (maybeCustomDepthTexture) {
+ if (rhi->isFeatureSupported(QRhi::ResolveDepthStencil))
+ rtDesc.setDepthResolveTexture(maybeCustomDepthTexture);
+ else
+ qWarning("Depth-stencil resolve is not supported by the underlying 3D API, depth contents will not be resolved");
+ }
+ } else {
+ if (depthStencil)
+ rtDesc.setDepthTexture(depthStencil.get());
+ else if (maybeCustomDepthTexture)
+ rtDesc.setDepthTexture(maybeCustomDepthTexture);
+ }
+
+ QRhiTextureRenderTarget::Flags rtFlags;
+ if (!maybeCustomDepthTexture)
+ rtFlags |= QRhiTextureRenderTarget::DoNotStoreDepthStencilContents;
+
+ std::unique_ptr<QRhiTextureRenderTarget> rt(rhi->newTextureRenderTarget(rtDesc, rtFlags));
+ rt->setName(QByteArrayLiteral("RT for multiview QQuickRenderTarget"));
+ std::unique_ptr<QRhiRenderPassDescriptor> rp(rt->newCompatibleRenderPassDescriptor());
+ rt->setRenderPassDescriptor(rp.get());
+
+ if (!rt->create()) {
+ qWarning("Failed to build multiview texture render target for QQuickRenderTarget");
+ return false;
+ }
+
+ dst->rt.renderTarget = rt.release();
+ dst->rt.owns = true;
+ dst->res.rpDesc = rp.release();
+ if (depthStencil)
+ dst->implicitBuffers.depthStencilTexture = depthStencil.release();
+ if (multisampleResolve)
+ dst->implicitBuffers.multisampleTexture = colorBuffer.release();
+
+ dst->rt.multiViewCount = arraySize;
return true;
}
bool QQuickRenderTargetPrivate::resolve(QRhi *rhi, QQuickWindowRenderTarget *dst)
{
+ // dst->implicitBuffers may contain valid objects. If so, and their
+ // properties are suitable, they are expected to be reused. Once taken what
+ // we can reuse, it needs to be reset().
+
switch (type) {
case Type::Null:
- dst->renderTarget = nullptr;
- dst->paintDevice = nullptr;
- dst->owns = false;
+ dst->implicitBuffers.reset(rhi);
return true;
case Type::NativeTexture:
{
- const auto format = u.nativeTexture.rhiFormat == QRhiTexture::UnknownFormat ? QRhiTexture::RGBA8
- : QRhiTexture::Format(u.nativeTexture.rhiFormat);
- const auto flags = QRhiTexture::RenderTarget | QRhiTexture::Flags(u.nativeTexture.rhiFlags);
- std::unique_ptr<QRhiTexture> texture(rhi->newTexture(format, pixelSize, sampleCount, flags));
- if (!texture->createFrom({ u.nativeTexture.object, u.nativeTexture.layout })) {
+ QRhiTexture::Format format = u.nativeTexture.rhiFormat == QRhiTexture::UnknownFormat ? QRhiTexture::RGBA8
+ : QRhiTexture::Format(u.nativeTexture.rhiFormat);
+ QRhiTexture::Format viewFormat = u.nativeTexture.rhiViewFormat == QRhiTexture::UnknownFormat ? QRhiTexture::RGBA8
+ : QRhiTexture::Format(u.nativeTexture.rhiViewFormat);
+ const auto flags = QRhiTexture::RenderTarget | QRhiTexture::Flags(u.nativeTexture.rhiFormatFlags);
+ std::unique_ptr<QRhiTexture> texture(rhi->newTexture(format, pixelSize, multisampleResolve ? 1 : sampleCount, flags));
+ const bool textureIsSrgb = flags.testFlag(QRhiTexture::sRGB);
+ const bool viewIsSrgb = QRhiTexture::Flags(u.nativeTexture.rhiViewFormatFlags).testFlag(QRhiTexture::sRGB);
+ if (viewFormat != format || viewIsSrgb != textureIsSrgb)
+ texture->setWriteViewFormat({ viewFormat, viewIsSrgb });
+ if (!texture->createFrom({ u.nativeTexture.object, u.nativeTexture.layoutOrState })) {
qWarning("Failed to build wrapper texture for QQuickRenderTarget");
return false;
}
- QRhiColorAttachment att(texture.get());
- if (!createRhiRenderTarget(att, pixelSize, sampleCount, rhi, dst))
+ if (customDepthTexture) {
+ if (!createRhiRenderTargetWithDepthTexture(texture.get(), customDepthTexture, pixelSize, sampleCount, multisampleResolve, rhi, dst))
+ return false;
+ } else {
+ if (!createRhiRenderTarget(texture.get(), pixelSize, sampleCount, multisampleResolve, rhi, dst))
+ return false;
+ }
+ dst->res.texture = texture.release();
+ }
+ return true;
+
+ case Type::NativeTextureArray:
+ {
+ QRhiTexture::Format format = u.nativeTextureArray.rhiFormat == QRhiTexture::UnknownFormat ? QRhiTexture::RGBA8
+ : QRhiTexture::Format(u.nativeTextureArray.rhiFormat);
+ QRhiTexture::Format viewFormat = u.nativeTextureArray.rhiViewFormat == QRhiTexture::UnknownFormat ? QRhiTexture::RGBA8
+ : QRhiTexture::Format(u.nativeTextureArray.rhiViewFormat);
+ const auto flags = QRhiTexture::RenderTarget | QRhiTexture::Flags(u.nativeTextureArray.rhiFormatFlags);
+ const int arraySize = u.nativeTextureArray.arraySize;
+ std::unique_ptr<QRhiTexture> texture(rhi->newTextureArray(format, arraySize, pixelSize, multisampleResolve ? 1 : sampleCount, flags));
+ const bool textureIsSrgb = flags.testFlag(QRhiTexture::sRGB);
+ const bool viewIsSrgb = QRhiTexture::Flags(u.nativeTextureArray.rhiViewFormatFlags).testFlag(QRhiTexture::sRGB);
+ if (viewFormat != format || viewIsSrgb != textureIsSrgb)
+ texture->setWriteViewFormat({ viewFormat, viewIsSrgb });
+ if (!texture->createFrom({ u.nativeTextureArray.object, u.nativeTextureArray.layoutOrState })) {
+ qWarning("Failed to build wrapper texture array for QQuickRenderTarget");
return false;
- dst->texture = texture.release();
+ }
+ if (!createRhiRenderTargetMultiView(texture.get(), customDepthTexture, pixelSize, arraySize, sampleCount, multisampleResolve, rhi, dst))
+ return false;
+ dst->res.texture = texture.release();
}
return true;
@@ -726,27 +1718,59 @@ bool QQuickRenderTargetPrivate::resolve(QRhi *rhi, QQuickWindowRenderTarget *dst
qWarning("Failed to build wrapper renderbuffer for QQuickRenderTarget");
return false;
}
- QRhiColorAttachment att(renderbuffer.get());
- if (!createRhiRenderTarget(att, pixelSize, sampleCount, rhi, dst))
+ if (customDepthTexture)
+ qWarning("Custom depth texture is not supported with renderbuffers in QQuickRenderTarget");
+ if (!createRhiRenderTargetWithRenderBuffer(renderbuffer.get(), pixelSize, sampleCount, rhi, dst))
return false;
- dst->renderBuffer = renderbuffer.release();
+ dst->res.renderBuffer = renderbuffer.release();
}
return true;
case Type::RhiRenderTarget:
- dst->renderTarget = u.rhiRt;
- dst->rpDesc = u.rhiRt->renderPassDescriptor(); // just for QQuickWindowRenderTarget::reset()
- dst->owns = false;
+ dst->implicitBuffers.reset(rhi);
+ dst->rt.renderTarget = u.rhiRt;
+ dst->rt.owns = false;
+ if (dst->rt.renderTarget->resourceType() == QRhiResource::TextureRenderTarget) {
+ auto texRt = static_cast<QRhiTextureRenderTarget *>(dst->rt.renderTarget);
+ const QRhiTextureRenderTargetDescription desc = texRt->description();
+ bool first = true;
+ for (auto it = desc.cbeginColorAttachments(), end = desc.cendColorAttachments(); it != end; ++it) {
+ if (it->multiViewCount() <= 1)
+ continue;
+ if (first || dst->rt.multiViewCount == it->multiViewCount()) {
+ first = false;
+ if (it->texture() && it->texture()->flags().testFlag(QRhiTexture::TextureArray)) {
+ if (it->texture()->arraySize() >= it->layer() + it->multiViewCount()) {
+ dst->rt.multiViewCount = it->multiViewCount();
+ } else {
+ qWarning("Invalid QQuickRenderTarget; needs at least %d elements in texture array, got %d",
+ it->layer() + it->multiViewCount(),
+ it->texture()->arraySize());
+ return false;
+ }
+ } else {
+ qWarning("Invalid QQuickRenderTarget; multiview requires a texture array");
+ return false;
+ }
+ } else {
+ qWarning("Inconsistent multiViewCount in QQuickRenderTarget (was %d, now found an attachment with %d)",
+ dst->rt.multiViewCount, it->multiViewCount());
+ return false;
+ }
+ }
+ }
+ if (customDepthTexture)
+ qWarning("Custom depth texture is not supported with QRhiRenderTarget in QQuickRenderTarget");
return true;
+
case Type::PaintDevice:
- dst->paintDevice = u.paintDevice;
- dst->owns = false;
+ dst->implicitBuffers.reset(rhi);
+ dst->sw.paintDevice = u.paintDevice;
+ dst->sw.owns = false;
return true;
-
- default:
- break;
}
- return false;
+
+ Q_UNREACHABLE_RETURN(false);
}
QT_END_NAMESPACE