diff options
author | Laszlo Agocs <laszlo.agocs@qt.io> | 2023-12-07 20:20:06 +0100 |
---|---|---|
committer | Laszlo Agocs <laszlo.agocs@qt.io> | 2024-01-15 21:25:12 +0100 |
commit | 7295ece36399f25e3a3226b3222261e803140c94 (patch) | |
tree | 2342c8cbf21af204a016f343fcec83a81bded297 | |
parent | b5a05e4e7e97f8ef5c4cc65fee0cfcfd9e4c5cc4 (diff) |
Allow QQuickRt adopt a native texture array for multiview rendering
Limiting multiview to fromRhiRenderTarget() is quite limiting, since
in practice it often involves duplicating the convenience logic that
is built into Qt Quick for fromVulkanImage() and similar.
We should also support passing in a texture array object to from
fromOpenGLTexture, fromVulkanImage, etc. The array size then
implies the view count.
We are also required to use a texture array for depth/stencil as
well in this case.
Add the necessary plumbing so that clients, such as an OpenXR
integration, can continue to use fromXxxxx(), just the
*MultiView variants, instead of forcing them to create a
QRhiTextureRenderTarget themselves upfront.
Task-number: QTBUG-119850
Task-number: QTBUG-114871
Change-Id: I79978e5ff5f154c8914afbe2478d002291219aaf
Reviewed-by: Andy Nichols <andy.nichols@qt.io>
-rw-r--r-- | src/quick/items/qquickrendertarget.cpp | 338 | ||||
-rw-r--r-- | src/quick/items/qquickrendertarget.h | 4 | ||||
-rw-r--r-- | src/quick/items/qquickrendertarget_p.h | 9 | ||||
-rw-r--r-- | src/quick/items/qquickwindow.cpp | 2 | ||||
-rw-r--r-- | src/quick/items/qquickwindow_p.h | 1 | ||||
-rw-r--r-- | src/quick/scenegraph/coreapi/qsgmaterial.cpp | 13 |
6 files changed, 361 insertions, 6 deletions
diff --git a/src/quick/items/qquickrendertarget.cpp b/src/quick/items/qquickrendertarget.cpp index 60454455d6..9c4f02b5ae 100644 --- a/src/quick/items/qquickrendertarget.cpp +++ b/src/quick/items/qquickrendertarget.cpp @@ -221,6 +221,71 @@ QQuickRenderTarget QQuickRenderTarget::fromOpenGLTexture(uint textureId, uint fo /*! \overload + \return a new QQuickRenderTarget referencing a 2D texture array with the + specified \a arraySize and OpenGL \a textureId. + + \note This implies multiview rendering (GL_OVR_multiview etc.), which can be + relevant with VR/AR especially. \a arraySize is the number of views, + typically \c 2. This overload should not be used other cases. + See \l QSGMaterial::viewCount() for details on enabling multiview rendering + within the Qt Quick scenegraph. + + \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. + + \a sampleCount specific 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 texture array with a matching + number of layers, sample count, and a format of \c D24S8 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.8 + + \sa QQuickWindow::setRenderTarget(), QQuickRenderControl + */ +QQuickRenderTarget QQuickRenderTarget::fromOpenGLTextureMultiView(uint textureId, uint format, const QSize &pixelSize, int sampleCount, int arraySize) +{ + 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; + } + + if (arraySize < 1) { + qWarning("QQuickRenderTarget: Texture array must have at least one element"); + return rt; + } + + d->type = QQuickRenderTargetPrivate::Type::NativeTextureArray; + d->pixelSize = pixelSize; + d->sampleCount = qMax(1, sampleCount); + + auto rhiFormat = QSGRhiSupport::toRhiTextureFormatFromGL(format); + d->u.nativeTextureArray = { textureId, 0, arraySize, uint(rhiFormat), 0 }; + + 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). @@ -445,6 +510,82 @@ QQuickRenderTarget QQuickRenderTarget::fromD3D12Texture(void *texture, return rt; } + +/*! + \overload + + \return a new QQuickRenderTarget referencing a D3D12 texture array object + specified by \a texture. The number of array elements (layers) is given in + \a arraySize. + + \note This implies multiview rendering (GL_OVR_multiview etc.), which can be + relevant with VR/AR especially. \a arraySize is the number of views, + typically \c 2. This overload should not be used other cases. + See \l QSGMaterial::viewCount() for details on enabling multiview rendering + within the Qt Quick scenegraph. + + \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 specific 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 texture array with a matching + number of layers, sample count, and a format of \l{QRhiTexture::}{D24S8} 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.8 + + \sa QQuickWindow::setRenderTarget(), QQuickRenderControl + */ +QQuickRenderTarget QQuickRenderTarget::fromD3D12TextureMultiView(void *texture, + int resourceState, + uint format, + const QSize &pixelSize, + int sampleCount, + int arraySize) +{ + 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; + } + + if (arraySize < 1) { + qWarning("QQuickRenderTarget: Texture array must have at least one element"); + return rt; + } + + d->type = QQuickRenderTargetPrivate::Type::NativeTextureArray; + d->pixelSize = pixelSize; + d->sampleCount = qMax(1, sampleCount); + + QRhiTexture::Flags flags; + auto rhiFormat = QSGRhiSupport::toRhiTextureFormatFromDXGI(format, &flags); + d->u.nativeTextureArray = { quint64(texture), resourceState, arraySize, uint(rhiFormat), uint(flags) }; + + return rt; +} #endif /*! @@ -505,6 +646,74 @@ QQuickRenderTarget QQuickRenderTarget::fromMetalTexture(MTLTexture *texture, uin /*! \overload + \return a new QQuickRenderTarget referencing a Metal texture array object + with \a arraySize elements specified by \a texture. + + \note This implies multiview rendering (GL_OVR_multiview etc.), which can be + relevant with VR/AR especially. \a arraySize is the number of views, + typically \c 2. This overload should not be used other cases. + See \l QSGMaterial::viewCount() for details on enabling multiview rendering + within the Qt Quick scenegraph. + + \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 specific 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 texture array with a matching + number of layers, sample count, and a format of \c D24S8 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.8 + + \sa QQuickWindow::setRenderTarget(), QQuickRenderControl + */ +QQuickRenderTarget QQuickRenderTarget::fromMetalTextureMultiView(MTLTexture *texture, uint format, + const QSize &pixelSize, int sampleCount, int arraySize) +{ + 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; + } + + if (arraySize < 1) { + qWarning("QQuickRenderTarget: Texture array must have at least one element"); + return rt; + } + + d->type = QQuickRenderTargetPrivate::Type::NativeTextureArray; + d->pixelSize = pixelSize; + d->sampleCount = qMax(1, sampleCount); + + QRhiTexture::Flags flags; + auto rhiFormat = QSGRhiSupport::toRhiTextureFormatFromMetal(format, &flags); + d->u.nativeTextureArray = { quint64(texture), 0, arraySize, uint(rhiFormat), uint(flags) }; + + 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. @@ -592,6 +801,75 @@ QQuickRenderTarget QQuickRenderTarget::fromVulkanImage(VkImage image, VkImageLay /*! \overload + \return a new QQuickRenderTarget referencing a Vulkan image object with + \a arraySize layers specified by \a image. The current \a layout of the image + must be provided as well. + + \note This implies multiview rendering (GL_OVR_multiview etc.), which can be + relevant with VR/AR especially. \a arraySize is the number of views, + typically \c 2. This overload should not be used other cases. + See \l QSGMaterial::viewCount() for details on enabling multiview rendering + within the Qt Quick scenegraph. + + \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 specific 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 texture array with a matching + number of layers, sample count, and a format of \c D24S8 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.8 + + \sa QQuickWindow::setRenderTarget(), QQuickRenderControl + */ +QQuickRenderTarget QQuickRenderTarget::fromVulkanImageMultiView(VkImage image, VkImageLayout layout, VkFormat format, + const QSize &pixelSize, int sampleCount, int arraySize) +{ + 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; + } + + if (arraySize < 1) { + qWarning("QQuickRenderTarget: Texture array must have at least one element"); + return rt; + } + + d->type = QQuickRenderTargetPrivate::Type::NativeTextureArray; + d->pixelSize = pixelSize; + d->sampleCount = qMax(1, sampleCount); + + QRhiTexture::Flags flags; + auto rhiFormat = QSGRhiSupport::toRhiTextureFormatFromVulkan(format, &flags); + d->u.nativeTextureArray = { quint64(image), layout, arraySize, uint(rhiFormat), uint(flags) }; + + 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. @@ -716,6 +994,14 @@ bool QQuickRenderTarget::isEqual(const QQuickRenderTarget &other) const noexcept || d->u.nativeTexture.rhiFlags != other.d->u.nativeTexture.rhiFlags) 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.rhiFlags != other.d->u.nativeTextureArray.rhiFlags) + return false; + break; case QQuickRenderTargetPrivate::Type::NativeRenderbuffer: if (d->u.nativeRenderbufferObject != other.d->u.nativeRenderbufferObject) return false; @@ -766,6 +1052,39 @@ static bool createRhiRenderTarget(const QRhiColorAttachment &colorAttachment, return true; } +static bool createRhiRenderTargetMultiView(const QRhiColorAttachment &colorAttachment, + const QSize &pixelSize, + int arraySize, + int sampleCount, + QRhi *rhi, + QQuickWindowRenderTarget *dst) +{ + std::unique_ptr<QRhiTexture> depthStencil(rhi->newTextureArray(QRhiTexture::D24S8, arraySize, pixelSize, sampleCount, QRhiTexture::RenderTarget)); + if (!depthStencil->create()) { + qWarning("Failed to build depth-stencil texture array for QQuickRenderTarget"); + return false; + } + + QRhiTextureRenderTargetDescription rtDesc(colorAttachment); + rtDesc.setDepthTexture(depthStencil.get()); + std::unique_ptr<QRhiTextureRenderTarget> rt(rhi->newTextureRenderTarget(rtDesc)); + 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->renderTarget = rt.release(); + dst->rpDesc = rp.release(); + dst->depthStencilTexture = depthStencil.release(); + dst->multiViewCount = arraySize; + dst->owns = true; // ownership of the native resource itself is not transferred but the QRhi objects are on us now + + return true; +} + bool QQuickRenderTargetPrivate::resolve(QRhi *rhi, QQuickWindowRenderTarget *dst) { switch (type) { @@ -792,6 +1111,25 @@ bool QQuickRenderTargetPrivate::resolve(QRhi *rhi, QQuickWindowRenderTarget *dst } return true; + case Type::NativeTextureArray: + { + const auto format = u.nativeTextureArray.rhiFormat == QRhiTexture::UnknownFormat ? QRhiTexture::RGBA8 + : QRhiTexture::Format(u.nativeTextureArray.rhiFormat); + const auto flags = QRhiTexture::RenderTarget | QRhiTexture::Flags(u.nativeTextureArray.rhiFlags); + const int arraySize = u.nativeTextureArray.arraySize; + std::unique_ptr<QRhiTexture> texture(rhi->newTextureArray(format, arraySize, pixelSize, sampleCount, flags)); + if (!texture->createFrom({ u.nativeTextureArray.object, u.nativeTextureArray.layoutOrState })) { + qWarning("Failed to build wrapper texture array for QQuickRenderTarget"); + return false; + } + QRhiColorAttachment att(texture.get()); + att.setMultiViewCount(arraySize); + if (!createRhiRenderTargetMultiView(att, pixelSize, arraySize, sampleCount, rhi, dst)) + return false; + dst->texture = texture.release(); + } + return true; + case Type::NativeRenderbuffer: { std::unique_ptr<QRhiRenderBuffer> renderbuffer(rhi->newRenderBuffer(QRhiRenderBuffer::Color, pixelSize, sampleCount)); diff --git a/src/quick/items/qquickrendertarget.h b/src/quick/items/qquickrendertarget.h index 646bdd9813..1a181185ca 100644 --- a/src/quick/items/qquickrendertarget.h +++ b/src/quick/items/qquickrendertarget.h @@ -40,6 +40,7 @@ public: #if QT_CONFIG(opengl) || defined(Q_QDOC) static QQuickRenderTarget fromOpenGLTexture(uint textureId, uint format, const QSize &pixelSize, int sampleCount = 1); static QQuickRenderTarget fromOpenGLTexture(uint textureId, const QSize &pixelSize, int sampleCount = 1); + static QQuickRenderTarget fromOpenGLTextureMultiView(uint textureId, uint format, const QSize &pixelSize, int sampleCount, int arraySize); static QQuickRenderTarget fromOpenGLRenderBuffer(uint renderbufferId, const QSize &pixelSize, int sampleCount = 1); #endif @@ -47,16 +48,19 @@ public: static QQuickRenderTarget fromD3D11Texture(void *texture, uint format, const QSize &pixelSize, int sampleCount = 1); static QQuickRenderTarget fromD3D11Texture(void *texture, const QSize &pixelSize, int sampleCount = 1); static QQuickRenderTarget fromD3D12Texture(void *texture, int resourceState, uint format, const QSize &pixelSize, int sampleCount = 1); + static QQuickRenderTarget fromD3D12TextureMultiView(void *texture, int resourceState, uint format, const QSize &pixelSize, int sampleCount, int arraySize); #endif #if defined(Q_OS_MACOS) || defined(Q_OS_IOS) || defined(Q_QDOC) static QQuickRenderTarget fromMetalTexture(MTLTexture *texture, uint format, const QSize &pixelSize, int sampleCount = 1); static QQuickRenderTarget fromMetalTexture(MTLTexture *texture, const QSize &pixelSize, int sampleCount = 1); + static QQuickRenderTarget fromMetalTextureMultiView(MTLTexture *texture, uint format, const QSize &pixelSize, int sampleCount, int arraySize); #endif #if QT_CONFIG(vulkan) || defined(Q_QDOC) static QQuickRenderTarget fromVulkanImage(VkImage image, VkImageLayout layout, VkFormat format, const QSize &pixelSize, int sampleCount = 1); static QQuickRenderTarget fromVulkanImage(VkImage image, VkImageLayout layout, const QSize &pixelSize, int sampleCount = 1); + static QQuickRenderTarget fromVulkanImageMultiView(VkImage image, VkImageLayout layout, VkFormat format, const QSize &pixelSize, int sampleCount, int arraySize); #endif static QQuickRenderTarget fromRhiRenderTarget(QRhiRenderTarget *renderTarget); diff --git a/src/quick/items/qquickrendertarget_p.h b/src/quick/items/qquickrendertarget_p.h index fd1fb2afd8..7f3190947e 100644 --- a/src/quick/items/qquickrendertarget_p.h +++ b/src/quick/items/qquickrendertarget_p.h @@ -36,6 +36,7 @@ public: enum class Type { Null, NativeTexture, + NativeTextureArray, NativeRenderbuffer, RhiRenderTarget, PaintDevice @@ -52,8 +53,16 @@ public: uint rhiFormat; uint rhiFlags; }; + struct NativeTextureArray { + quint64 object; + int layoutOrState; + int arraySize; + uint rhiFormat; + uint rhiFlags; + }; union { NativeTexture nativeTexture; + NativeTextureArray nativeTextureArray; quint64 nativeRenderbufferObject; QRhiRenderTarget *rhiRt; QPaintDevice *paintDevice; diff --git a/src/quick/items/qquickwindow.cpp b/src/quick/items/qquickwindow.cpp index 1893c269cb..36bfc52aeb 100644 --- a/src/quick/items/qquickwindow.cpp +++ b/src/quick/items/qquickwindow.cpp @@ -484,6 +484,7 @@ void QQuickWindowRenderTarget::reset(QRhi *rhi) delete texture; delete renderBuffer; delete depthStencil; + delete depthStencilTexture; } delete paintDevice; @@ -494,6 +495,7 @@ void QQuickWindowRenderTarget::reset(QRhi *rhi) texture = nullptr; renderBuffer = nullptr; depthStencil = nullptr; + depthStencilTexture = nullptr; paintDevice = nullptr; owns = false; multiViewCount = 1; diff --git a/src/quick/items/qquickwindow_p.h b/src/quick/items/qquickwindow_p.h index cfec6991da..9c45ccb9ca 100644 --- a/src/quick/items/qquickwindow_p.h +++ b/src/quick/items/qquickwindow_p.h @@ -81,6 +81,7 @@ public: QRhiTexture *texture = nullptr; QRhiRenderBuffer *renderBuffer = nullptr; QRhiRenderBuffer *depthStencil = nullptr; + QRhiTexture *depthStencilTexture = nullptr; // for multiview QPaintDevice *paintDevice = nullptr; bool owns = false; int multiViewCount = 1; diff --git a/src/quick/scenegraph/coreapi/qsgmaterial.cpp b/src/quick/scenegraph/coreapi/qsgmaterial.cpp index bdbc6ee73c..99c70a6c38 100644 --- a/src/quick/scenegraph/coreapi/qsgmaterial.cpp +++ b/src/quick/scenegraph/coreapi/qsgmaterial.cpp @@ -407,12 +407,13 @@ int QSGMaterial::compare(const QSGMaterial *other) const ) \endcode - See \l QRhi::MultiView, \l QRhiColorAttachment::setMultiViewCount(), and \l - QRhiGraphicsPipeline::setMultiViewCount() for further, lower-level details - on multiview support in Qt. The Qt Quick scene graph renderer is prepared - to recognize multiview render targets, when specified via - \l QQuickRenderTarget::fromRhiRenderTarget(), and propagate the view count to - graphics pipelines and the materials. + See \l QRhi::MultiView, \l QRhiColorAttachment::setMultiViewCount(), and + \l QRhiGraphicsPipeline::setMultiViewCount() for further, lower-level details + on multiview support in Qt. The Qt Quick scene graph renderer is prepared to + recognize multiview render targets, when specified via + \l QQuickRenderTarget::fromRhiRenderTarget() or the \c MultiView + functions such as \l{QQuickRenderTarget::}{fromVulkanImageMultiView()}, and + propagate the view count to graphics pipelines and the materials. \since 6.8 */ |