Source code
Revision control
Copy as Markdown
// Copyright (C) 2020 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
// Qt-Security score:significant reason:default
#include "qquickrendertarget_p.h"
#include <rhi/qrhi.h>
#include <QtQuick/private/qquickitem_p.h>
#include <QtQuick/private/qquickwindow_p.h>
#include <QtQuick/private/qsgrhisupport_p.h>
QT_BEGIN_NAMESPACE
/*!
\class QQuickRenderTarget
\since 6.0
\inmodule QtQuick
\brief The QQuickRenderTarget class provides an opaque container for native
graphics resources specifying a render target, and associated metadata.
\sa QQuickWindow::setRenderTarget(), QQuickGraphicsDevice
*/
QQuickRenderTargetPrivate::QQuickRenderTargetPrivate()
: ref(1)
{
}
QQuickRenderTargetPrivate::QQuickRenderTargetPrivate(const QQuickRenderTargetPrivate &other)
: ref(1),
type(other.type),
pixelSize(other.pixelSize),
devicePixelRatio(other.devicePixelRatio),
sampleCount(other.sampleCount),
u(other.u),
customDepthTexture(other.customDepthTexture),
mirrorVertically(other.mirrorVertically),
multisampleResolve(other.multisampleResolve)
{
}
/*!
Constructs a default QQuickRenderTarget that does not reference any native
objects.
*/
QQuickRenderTarget::QQuickRenderTarget()
: d(new QQuickRenderTargetPrivate)
{
}
/*!
\internal
*/
void QQuickRenderTarget::detach()
{
qAtomicDetach(d);
}
/*!
\internal
*/
QQuickRenderTarget::QQuickRenderTarget(const QQuickRenderTarget &other)
: d(other.d)
{
d->ref.ref();
}
/*!
\internal
*/
QQuickRenderTarget &QQuickRenderTarget::operator=(const QQuickRenderTarget &other)
{
qAtomicAssign(d, other.d);
return *this;
}
/*!
Destructor.
*/
QQuickRenderTarget::~QQuickRenderTarget()
{
if (!d->ref.deref())
delete d;
}
/*!
\return true if this QQuickRenderTarget is default constructed, referencing
no native objects.
*/
bool QQuickRenderTarget::isNull() const
{
return d->type == QQuickRenderTargetPrivate::Type::Null;
}
/*!
\return the device pixel ratio for the render target. This is the ratio
between \e{device pixels} and \e{device independent pixels}.
The default device pixel ratio is 1.0.
\since 6.3
\sa setDevicePixelRatio()
*/
qreal QQuickRenderTarget::devicePixelRatio() const
{
return d->devicePixelRatio;
}
/*!
Sets the device pixel ratio for this render target to \a ratio. This is
the ratio between \e{device pixels} and \e{device independent pixels}.
Note that the specified device pixel ratio value will be ignored if
QQuickRenderControl::renderWindow() is re-implemented to return a valid
QWindow.
\since 6.3
\sa devicePixelRatio()
*/
void QQuickRenderTarget::setDevicePixelRatio(qreal ratio)
{
if (d->devicePixelRatio == ratio)
return;
detach();
d->devicePixelRatio = ratio;
}
/*!
\return Returns whether the render target is mirrored vertically.
The default value is \c {false}.
\since 6.4
\sa setMirrorVertically()
*/
bool QQuickRenderTarget::mirrorVertically() const
{
return d->mirrorVertically;
}
/*!
Sets the size of the render target contents should be mirrored vertically to
\a enable when drawing. This allows easy integration of third-party rendering
code that does not follow the standard expectations.
\note This function should not be used when using the \c software backend.
\since 6.4
\sa mirrorVertically()
*/
void QQuickRenderTarget::setMirrorVertically(bool enable)
{
if (d->mirrorVertically == enable)
return;
detach();
d->mirrorVertically = 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
\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 that 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.
\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 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.
The OpenGL object name \a textureId must be a valid name in the rendering
context used by the Qt Quick scenegraph.
\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.4
\sa QQuickWindow::setRenderTarget(), QQuickRenderControl
*/
#if QT_CONFIG(opengl) || defined(Q_QDOC)
QQuickRenderTarget QQuickRenderTarget::fromOpenGLTexture(uint textureId, uint format,
const QSize &pixelSize, int sampleCount)
{
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;
}
d->type = QQuickRenderTargetPrivate::Type::NativeTexture;
d->pixelSize = pixelSize;
d->sampleCount = qMax(1, sampleCount);
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;
}
/*!
\overload
\return a new QQuickRenderTarget referencing an OpenGL texture
object specified by \a textureId. The texture is assumed to have a
format of GL_RGBA (GL_RGBA8).
\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.
The OpenGL object name \a textureId must be a valid name in the rendering
context used by the Qt Quick scenegraph.
\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.
\sa QQuickWindow::setRenderTarget(), QQuickRenderControl
*/
QQuickRenderTarget QQuickRenderTarget::fromOpenGLTexture(uint textureId, const QSize &pixelSize, int sampleCount)
{
return fromOpenGLTexture(textureId, 0, pixelSize, sampleCount);
}
/*!
\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 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, 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.
The renderbuffer will be used as the color attachment for the internal
framebuffer object. This function is provided to allow targeting
renderbuffers that are created by the application with some external buffer
underneath, such as an EGLImageKHR. Once the application has called
\l{https://www.khronos.org/registry/OpenGL/extensions/OES/OES_EGL_image.txt}{glEGLImageTargetRenderbufferStorageOES},
the renderbuffer can be passed to this function.
\a pixelSize specifies the size of the image, in pixels.
\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.
\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.2
\sa QQuickWindow::setRenderTarget(), QQuickRenderControl
*/
QQuickRenderTarget QQuickRenderTarget::fromOpenGLRenderBuffer(uint renderbufferId, const QSize &pixelSize, int sampleCount)
{
QQuickRenderTarget rt;
QQuickRenderTargetPrivate *d = QQuickRenderTargetPrivate::get(&rt);
if (!renderbufferId) {
qWarning("QQuickRenderTarget: renderbufferId is invalid");
return rt;
}
if (pixelSize.isEmpty()) {
qWarning("QQuickRenderTarget: Cannot create with empty size");
return rt;
}
d->type = QQuickRenderTargetPrivate::Type::NativeRenderbuffer;
d->pixelSize = pixelSize;
d->sampleCount = qMax(1, sampleCount);
d->u.nativeRenderbufferObject = renderbufferId;
return rt;
}
#endif
/*!
\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.
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.4
\sa QQuickWindow::setRenderTarget(), QQuickRenderControl
*/
#if defined(Q_OS_WIN) || defined(Q_QDOC)
QQuickRenderTarget QQuickRenderTarget::fromD3D11Texture(void *texture, 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), 0, uint(rhiFormat), uint(formatFlags), uint(rhiFormat), uint(formatFlags) };
return rt;
}
/*!
\overload
\return a new QQuickRenderTarget referencing a D3D11 texture
object specified by \a texture. The texture is assumed to have a
format of DXGI_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.
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.
\sa QQuickWindow::setRenderTarget(), QQuickRenderControl
*/
QQuickRenderTarget QQuickRenderTarget::fromD3D11Texture(void *texture, const QSize &pixelSize, int sampleCount)
{
return fromD3D11Texture(texture, 0 /* DXGI_FORMAT_UNKNOWN */, pixelSize, sampleCount);
}
/*!
\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, 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
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
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,
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
specified by \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 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.4
\sa QQuickWindow::setRenderTarget(), QQuickRenderControl
*/
#if QT_CONFIG(metal) || defined(Q_QDOC)
QQuickRenderTarget QQuickRenderTarget::fromMetalTexture(MTLTexture *texture, 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::toRhiTextureFormatFromMetal(format, &formatFlags);
d->u.nativeTexture = { quint64(texture), 0, uint(rhiFormat), uint(formatFlags), uint(rhiFormat), uint(formatFlags) };
return rt;
}
/*!
\overload
\return a new QQuickRenderTarget referencing a Metal texture object
specified by \a texture. The texture is assumed to have a format of
MTLPixelFormatRGBA8Unorm.
\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.
\sa QQuickWindow::setRenderTarget(), QQuickRenderControl
*/
QQuickRenderTarget QQuickRenderTarget::fromMetalTexture(MTLTexture *texture, const QSize &pixelSize, int sampleCount)
{
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,
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
/*!
\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.
\a format specifies the VkFormat of the image. Only image 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 image 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.4
\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 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;
}
d->type = QQuickRenderTargetPrivate::Type::NativeTexture;
d->pixelSize = pixelSize;
d->sampleCount = qMax(1, sampleCount);
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;
}
/*!
\overload
\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 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.
\sa QQuickWindow::setRenderTarget(), QQuickRenderControl
*/
QQuickRenderTarget QQuickRenderTarget::fromVulkanImage(VkImage image, VkImageLayout layout, const QSize &pixelSize, int sampleCount)
{
return fromVulkanImage(image, layout, VK_FORMAT_UNDEFINED, pixelSize, sampleCount);
}
/*!
\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,
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;
QQuickRenderTargetPrivate *d = QQuickRenderTargetPrivate::get(&rt);
if (!renderTarget) {
qWarning("QQuickRenderTarget: Needs a valid QRhiRenderTarget");
return rt;
}
d->type = QQuickRenderTargetPrivate::Type::RhiRenderTarget;
d->pixelSize = renderTarget->pixelSize();
d->sampleCount = renderTarget->sampleCount();
d->u.rhiRt = renderTarget;
return rt;
}
/*!
\return a new QQuickRenderTarget referencing a paint device object
specified by \a device.
This option of redirecting rendering to a QPaintDevice is available only
when running with the \c software backend of Qt Quick.
\note The QQuickRenderTarget does not take ownship of \a device, it is the
caller's responsibility to ensure the object exists as long as necessary.
\since 6.4
\sa QQuickWindow::setRenderTarget(), QQuickRenderControl
*/
QQuickRenderTarget QQuickRenderTarget::fromPaintDevice(QPaintDevice *device)
{
QQuickRenderTarget rt;
QQuickRenderTargetPrivate *d = QQuickRenderTargetPrivate::get(&rt);
d->type = QQuickRenderTargetPrivate::Type::PaintDevice;
d->pixelSize = QSize(device->width(), device->height());
d->u.paintDevice = device;
return rt;
}
/*!
\fn bool QQuickRenderTarget::operator==(const QQuickRenderTarget &a, const QQuickRenderTarget &b) noexcept
\return true if \a a and \a b refer to the same set of native objects and
have matching associated data (size, sample count).
*/
/*!
\fn bool QQuickRenderTarget::operator!=(const QQuickRenderTarget &a, const QQuickRenderTarget &b) noexcept
\return true if \a a and \a b refer to a different set of native objects,
or the associated data (size, sample count) does not match.
*/
/*!
\internal
*/
bool QQuickRenderTarget::isEqual(const QQuickRenderTarget &other) const noexcept
{
if (d->type != other.d->type
|| d->pixelSize != other.d->pixelSize
|| d->devicePixelRatio != other.d->devicePixelRatio
|| d->sampleCount != other.d->sampleCount
|| d->mirrorVertically != other.d->mirrorVertically
|| d->multisampleResolve != other.d->multisampleResolve)
{
return false;
}
switch (d->type) {
case QQuickRenderTargetPrivate::Type::Null:
break;
case QQuickRenderTargetPrivate::Type::NativeTexture:
if (d->u.nativeTexture.object != other.d->u.nativeTexture.object
|| d->u.nativeTexture.layoutOrState != other.d->u.nativeTexture.layoutOrState
|| d->u.nativeTexture.rhiFormat != other.d->u.nativeTexture.rhiFormat
|| 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:
if (d->u.nativeRenderbufferObject != other.d->u.nativeRenderbufferObject)
return false;
break;
case QQuickRenderTargetPrivate::Type::RhiRenderTarget:
if (d->u.rhiRt != other.d->u.rhiRt)
return false;
break;
case QQuickRenderTargetPrivate::Type::PaintDevice:
if (d->u.paintDevice != other.d->u.paintDevice)
return false;
break;
default:
break;
}
return true;
}
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,
QSGRhiSupport::depthStencilBufferFlags()));
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)
{
// 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,
QSGRhiSupport::depthStencilBufferFlags()));
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) {
QRhiTexture::Format multisampleDepthTextureFormat = depthTexture->format();
depthStencil.reset(rhi->newTexture(multisampleDepthTextureFormat, 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());
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();
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) {
QRhiTexture::Format format;
if (maybeCustomDepthTexture)
format = maybeCustomDepthTexture->format();
else
format = rhi->isTextureFormatSupported(QRhiTexture::D24S8) ? QRhiTexture::D24S8 : QRhiTexture::D32FS8;
depthStencil.reset(rhi->newTextureArray(format, 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->implicitBuffers.reset(rhi);
return true;
case Type::NativeTexture:
{
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;
}
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;
}
if (!createRhiRenderTargetMultiView(texture.get(), customDepthTexture, pixelSize, arraySize, sampleCount, multisampleResolve, rhi, dst))
return false;
dst->res.texture = texture.release();
}
return true;
case Type::NativeRenderbuffer:
{
std::unique_ptr<QRhiRenderBuffer> renderbuffer(rhi->newRenderBuffer(QRhiRenderBuffer::Color, pixelSize, sampleCount));
if (!renderbuffer->createFrom({ u.nativeRenderbufferObject })) {
qWarning("Failed to build wrapper renderbuffer for QQuickRenderTarget");
return false;
}
if (customDepthTexture)
qWarning("Custom depth texture is not supported with renderbuffers in QQuickRenderTarget");
if (!createRhiRenderTargetWithRenderBuffer(renderbuffer.get(), pixelSize, sampleCount, rhi, dst))
return false;
dst->res.renderBuffer = renderbuffer.release();
}
return true;
case Type::RhiRenderTarget:
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->implicitBuffers.reset(rhi);
dst->sw.paintDevice = u.paintDevice;
dst->sw.owns = false;
return true;
}
Q_UNREACHABLE_RETURN(false);
}
QT_END_NAMESPACE