summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorLaszlo Agocs <laszlo.agocs@qt.io>2024-02-23 16:13:54 +0100
committerLaszlo Agocs <laszlo.agocs@qt.io>2024-03-12 20:27:42 +0100
commitbce2522ad01c427d0f5afb7b15687625d0fbb994 (patch)
tree08b9d01afed680f1f683de5f2ce6aa8e1f38985f
parent173164cd477211e574c0d04abef51aa0f4c3f78d (diff)
rhi: Add basic support for specifying a view format for a texture
We have to be able to strip the _SRGB nonsense from the render target view formats when rendering into textures that are provided from an external engine (e.g. OpenXR) and are forced onto us with formats such as VK_FORMAT_R8G8B8A8_SRGB. This highlights some limitation of the current system, which has very limited handling of sRGB stuff since proper renderers such as Qt Quick 3D have first class support for linearization and converting to sRGB at the end of their shading pipeline, so _SRGB format textures are never used in practice. OpenGL has an issue which is different from everything else, namely that we do not correctly do the glEnable/Disable on GL_FRAMEBUFFER_SRGB. The QOpenGLExtensions flag is not what we need. We need to know if the sRGB-conversion-on-write is supported or not, not that some framebuffer is sRGB-capable. So do our own query based on the desktop and the ES extension (GL_EXT_sRGB_write_control is something we never checked for, but that is the appropriate GLES extension, supported on the Quest 3 for instance) This is now corrected in the gl backend. This means that the colors will no longer be "too bright" with OpenGL ES and multiview on the Quest 3 for example. Unlike OpenGL, Vulkan and D3D automatically convert in shader reads and writes when the shader resource view or the render target view has a _SRGB format. (which we get by default since we pass on the texture format) Getting a second linear->sRGB conversion on the already sRGB data generated by e.g. Qt Quick 3D is just wrong. Allow solving this by a new function that can be optionally called to say we want (RGBA8, srgb=false), i.e. VK_FORMAT_R8G8B8A8_UNORM, for the views. Of course, reality is more complicated. D3D11 for instance does not allow "casting" a fully typed texture, we'd need to use a _TYPELESS format for that (possibly with other consequences), so skip D3D11. For D3D12 this should work from Windows 1703 on. Implementing for Metal is also left as a future exercise - it is neither needed at the moment within Qt, nor is it trivial, because view textures have to be created explicitly in Metal, normally we just work with the MTLTexture as-is, not with views. Task-number: QTBUG-122288 Task-number: QTBUG-122614 Change-Id: I8aea4e892b308d48f0bf18bdef481e460fbc9d47 Reviewed-by: Qt CI Bot <qt_ci_bot@qt-project.org> Reviewed-by: Andy Nichols <andy.nichols@qt.io>
-rw-r--r--src/gui/rhi/qrhi.cpp99
-rw-r--r--src/gui/rhi/qrhi.h14
-rw-r--r--src/gui/rhi/qrhid3d11.cpp2
-rw-r--r--src/gui/rhi/qrhid3d12.cpp41
-rw-r--r--src/gui/rhi/qrhid3d12_p.h3
-rw-r--r--src/gui/rhi/qrhigles2.cpp14
-rw-r--r--src/gui/rhi/qrhigles2_p.h4
-rw-r--r--src/gui/rhi/qrhimetal.mm2
-rw-r--r--src/gui/rhi/qrhivulkan.cpp53
-rw-r--r--src/gui/rhi/qrhivulkan_p.h6
-rw-r--r--tests/auto/gui/rhi/qrhi/tst_qrhi.cpp3
11 files changed, 215 insertions, 26 deletions
diff --git a/src/gui/rhi/qrhi.cpp b/src/gui/rhi/qrhi.cpp
index 8912eb548b..4cba1beec3 100644
--- a/src/gui/rhi/qrhi.cpp
+++ b/src/gui/rhi/qrhi.cpp
@@ -1004,6 +1004,24 @@ Q_LOGGING_CATEGORY(QRHI_LOG_INFO, "qt.rhi.general")
as well. Multiview rendering is not supported in combination with
tessellation or geometry shaders. See QRhiColorAttachment::setMultiViewCount()
for further details on multiview rendering. This enum value has been introduced in Qt 6.7.
+
+ \value TextureViewFormat Indicates that setting a
+ \l{QRhiTexture::setWriteViewFormat()}{view format} on a QRhiTexture is
+ effective. When reported as supported, setting the read (sampling) or write
+ (render target / image load-store) view mode changes the texture's viewing
+ format. When unsupported, setting a view format has no effect. Note that Qt
+ has no knowledge or control over format compatibility or resource view rules
+ in the underlying 3D API and its implementation. Passing in unsuitable,
+ incompatible formats may lead to errors and unspecified behavior. This is
+ provided mainly to allow "casting" rendering into a texture created with an
+ sRGB format to non-sRGB to avoid the unwanted linear->sRGB conversion on
+ shader writes. Other types of casting may or may not be functional,
+ depending on the underlying API. Currently implemented for Vulkan and Direct
+ 3D 12. With D3D12 the feature is available only if
+ \c CastingFullyTypedFormatSupported is supported, see
+ \l{https://microsoft.github.io/DirectX-Specs/d3d/RelaxedCasting.html} (and
+ note that QRhi always uses fully typed formats for textures.) This enum
+ value has been introduced in Qt 6.8.
*/
/*!
@@ -4537,6 +4555,87 @@ void QRhiTexture::setNativeLayout(int layout)
*/
/*!
+ \struct QRhiTexture::ViewFormat
+ \inmodule QtGui
+ \since 6.8
+ \brief Specifies the view format for reading or writing from or to the texture.
+
+ \note This is a RHI API with limited compatibility guarantees, see \l QRhi
+ for details.
+ */
+
+/*!
+ \variable QRhiTexture::ViewFormat::format
+ */
+
+/*!
+ \variable QRhiTexture::ViewFormat::srgb
+ */
+
+/*!
+ \fn QRhiTexture::ViewFormat QRhiTexture::readViewFormat() const
+ \since 6.8
+ \return the view format used when sampling the texture. When not called, the view
+ format is assumed to be the same as format().
+ */
+
+/*!
+ \fn void QRhiTexture::setReadViewFormat(const ViewFormat &fmt)
+ \since 6.8
+
+ Sets the shader resource view format (or the format of the view used for
+ sampling the texture) to \a fmt. By default the same format (and sRGB-ness)
+ is used as the texture itself, and in most cases this function does not need
+ to be called.
+
+ This setting is only taken into account when the \l TextureViewFormat
+ feature is reported as supported.
+
+ \note This functionality is provided to allow "casting" between
+ non-sRGB and sRGB in order to get the shader reads perform, or not perform,
+ the implicit sRGB conversions. Other types of casting may or may not be
+ functional.
+ */
+
+/*!
+ \fn QRhiTexture::ViewFormat QRhiTexture::writeViewFormat() const
+ \since 6.8
+ \return the view format used when writing to the texture and when using it
+ with image load/store. When not called, the view format is assumed to be the
+ same as format().
+ */
+
+/*!
+ \fn void QRhiTexture::setWriteViewFormat(const ViewFormat &fmt)
+ \since 6.8
+
+ Sets the render target view format to \a fmt. By default the same format
+ (and sRGB-ness) is used as the texture itself, and in most cases this
+ function does not need to be called.
+
+ One common use case for providing a write view format is working with
+ externally provided textures that, outside of our control, use an sRGB
+ format with 3D APIs such as Vulkan or Direct 3D, but the rendering engine is
+ already prepared to handle linearization and conversion to sRGB at the end
+ of its shading pipeline. In this case what is wanted when rendering into
+ such a texture is a render target view (e.g. VkImageView) that has the same,
+ but non-sRGB format. (if e.g. from an OpenXR implementation one gets a
+ VK_FORMAT_R8G8B8A8_SRGB texture, it is likely that rendering into it should
+ be done using a VK_FORMAT_R8G8B8A8_UNORM view, if that is what the rendering
+ engine's pipeline requires; in this example one would call this function
+ with a ViewFormat that has a format of QRhiTexture::RGBA8 and \c srgb set to
+ \c false).
+
+ This setting is only taken into account when the \l TextureViewFormat
+ feature is reported as supported.
+
+ \note This functionality is provided to allow "casting" between
+ non-sRGB and sRGB in order to get the shader write not perform, or perform,
+ the implicit sRGB conversions. Other types of casting may or may not be
+ functional.
+ */
+
+/*!
\class QRhiSampler
\inmodule QtGui
\since 6.6
diff --git a/src/gui/rhi/qrhi.h b/src/gui/rhi/qrhi.h
index 6515abdded..30d2b3c909 100644
--- a/src/gui/rhi/qrhi.h
+++ b/src/gui/rhi/qrhi.h
@@ -991,6 +991,15 @@ public:
int sampleCount() const { return m_sampleCount; }
void setSampleCount(int s) { m_sampleCount = s; }
+ struct ViewFormat {
+ QRhiTexture::Format format;
+ bool srgb;
+ };
+ ViewFormat readViewFormat() const { return m_readViewFormat; }
+ void setReadViewFormat(const ViewFormat &fmt) { m_readViewFormat = fmt; }
+ ViewFormat writeViewFormat() const { return m_writeViewFormat; }
+ void setWriteViewFormat(const ViewFormat &fmt) { m_writeViewFormat = fmt; }
+
virtual bool create() = 0;
virtual NativeTexture nativeTexture();
virtual bool createFrom(NativeTexture src);
@@ -1007,6 +1016,8 @@ protected:
Flags m_flags;
int m_arrayRangeStart = -1;
int m_arrayRangeLength = -1;
+ ViewFormat m_readViewFormat = { UnknownFormat, false };
+ ViewFormat m_writeViewFormat = { UnknownFormat, false };
};
Q_DECLARE_OPERATORS_FOR_FLAGS(QRhiTexture::Flags)
@@ -1853,7 +1864,8 @@ public:
HalfAttributes,
RenderToOneDimensionalTexture,
ThreeDimensionalTextureMipmaps,
- MultiView
+ MultiView,
+ TextureViewFormat
};
enum BeginFrameFlag {
diff --git a/src/gui/rhi/qrhid3d11.cpp b/src/gui/rhi/qrhid3d11.cpp
index 3f8358b2b4..26e1f45dc7 100644
--- a/src/gui/rhi/qrhid3d11.cpp
+++ b/src/gui/rhi/qrhid3d11.cpp
@@ -598,6 +598,8 @@ bool QRhiD3D11::isFeatureSupported(QRhi::Feature feature) const
return true;
case QRhi::MultiView:
return false;
+ case QRhi::TextureViewFormat:
+ return false; // because we use fully typed formats for textures and relaxed casting is a D3D12 thing
default:
Q_UNREACHABLE();
return false;
diff --git a/src/gui/rhi/qrhid3d12.cpp b/src/gui/rhi/qrhid3d12.cpp
index 5a0219bbd6..08224984d2 100644
--- a/src/gui/rhi/qrhid3d12.cpp
+++ b/src/gui/rhi/qrhid3d12.cpp
@@ -459,9 +459,13 @@ bool QRhiD3D12::create(QRhi::Flags flags)
memset(timestampReadbackArea.mem.p, 0, readbackBufSize);
}
+ caps = {};
D3D12_FEATURE_DATA_D3D12_OPTIONS3 options3 = {};
- if (SUCCEEDED(dev->CheckFeatureSupport(D3D12_FEATURE_D3D12_OPTIONS3, &options3, sizeof(options3))))
+ if (SUCCEEDED(dev->CheckFeatureSupport(D3D12_FEATURE_D3D12_OPTIONS3, &options3, sizeof(options3)))) {
caps.multiView = options3.ViewInstancingTier != D3D12_VIEW_INSTANCING_TIER_NOT_SUPPORTED;
+ // https://microsoft.github.io/DirectX-Specs/d3d/RelaxedCasting.html
+ caps.textureViewFormat = options3.CastingFullyTypedFormatSupported;
+ }
deviceLost = false;
offscreenActive = false;
@@ -706,6 +710,8 @@ bool QRhiD3D12::isFeatureSupported(QRhi::Feature feature) const
return false; // we generate mipmaps ourselves with compute and this is not implemented
case QRhi::MultiView:
return caps.multiView;
+ case QRhi::TextureViewFormat:
+ return caps.textureViewFormat;
}
return false;
}
@@ -932,7 +938,7 @@ void QD3D12CommandBuffer::visitStorageImage(QD3D12Stage s,
const bool isArray = texD->m_flags.testFlag(QRhiTexture::TextureArray);
const bool is3D = texD->m_flags.testFlag(QRhiTexture::ThreeDimensional);
D3D12_UNORDERED_ACCESS_VIEW_DESC uavDesc = {};
- uavDesc.Format = texD->dxgiFormat;
+ uavDesc.Format = texD->rtFormat;
if (isCube) {
uavDesc.ViewDimension = D3D12_UAV_DIMENSION_TEXTURE2DARRAY;
uavDesc.Texture2DArray.MipSlice = UINT(d.level);
@@ -4146,8 +4152,28 @@ bool QD3D12Texture::prepareCreate(QSize *adjustedSize)
const QSize size = is1D ? QSize(qMax(1, m_pixelSize.width()), 1)
: (m_pixelSize.isEmpty() ? QSize(1, 1) : m_pixelSize);
- QRHI_RES_RHI(QRhiD3D12);
dxgiFormat = toD3DTextureFormat(m_format, m_flags);
+ if (isDepth) {
+ srvFormat = toD3DDepthTextureSRVFormat(m_format);
+ rtFormat = toD3DDepthTextureDSVFormat(m_format);
+ } else {
+ srvFormat = dxgiFormat;
+ rtFormat = dxgiFormat;
+ }
+ if (m_writeViewFormat.format != UnknownFormat) {
+ if (isDepth)
+ rtFormat = toD3DDepthTextureDSVFormat(m_writeViewFormat.format);
+ else
+ rtFormat = toD3DTextureFormat(m_writeViewFormat.format, m_writeViewFormat.srgb ? sRGB : Flags());
+ }
+ if (m_readViewFormat.format != UnknownFormat) {
+ if (isDepth)
+ srvFormat = toD3DDepthTextureSRVFormat(m_readViewFormat.format);
+ else
+ srvFormat = toD3DTextureFormat(m_readViewFormat.format, m_readViewFormat.srgb ? sRGB : Flags());
+ }
+
+ QRHI_RES_RHI(QRhiD3D12);
mipLevelCount = uint(hasMipMaps ? rhiD->q->mipLevelsForSize(size) : 1);
sampleDesc = rhiD->effectiveSampleDesc(m_sampleCount, dxgiFormat);
if (sampleDesc.Count > 1) {
@@ -4206,14 +4232,13 @@ bool QD3D12Texture::prepareCreate(QSize *adjustedSize)
bool QD3D12Texture::finishCreate()
{
QRHI_RES_RHI(QRhiD3D12);
- const bool isDepth = isDepthTextureFormat(m_format);
const bool isCube = m_flags.testFlag(CubeMap);
const bool is3D = m_flags.testFlag(ThreeDimensional);
const bool isArray = m_flags.testFlag(TextureArray);
const bool is1D = m_flags.testFlag(OneDimensional);
D3D12_SHADER_RESOURCE_VIEW_DESC srvDesc = {};
- srvDesc.Format = isDepth ? toD3DDepthTextureSRVFormat(m_format) : dxgiFormat;
+ srvDesc.Format = srvFormat;
srvDesc.Shader4ComponentMapping = D3D12_DEFAULT_SHADER_4_COMPONENT_MAPPING;
if (isCube) {
@@ -4572,7 +4597,7 @@ QRhiRenderPassDescriptor *QD3D12TextureRenderTarget::newCompatibleRenderPassDesc
QD3D12Texture *texD = QRHI_RES(QD3D12Texture, it->texture());
QD3D12RenderBuffer *rbD = QRHI_RES(QD3D12RenderBuffer, it->renderBuffer());
if (texD)
- rpD->colorFormat[rpD->colorAttachmentCount] = texD->dxgiFormat;
+ rpD->colorFormat[rpD->colorAttachmentCount] = texD->rtFormat;
else if (rbD)
rpD->colorFormat[rpD->colorAttachmentCount] = rbD->dxgiFormat;
rpD->colorAttachmentCount += 1;
@@ -4623,7 +4648,7 @@ bool QD3D12TextureRenderTarget::create()
const bool isMultiView = it->multiViewCount() >= 2;
UINT layerCount = isMultiView ? UINT(it->multiViewCount()) : 1;
D3D12_RENDER_TARGET_VIEW_DESC rtvDesc = {};
- rtvDesc.Format = toD3DTextureFormat(texD->format(), texD->flags());
+ rtvDesc.Format = texD->rtFormat;
if (texD->flags().testFlag(QRhiTexture::CubeMap)) {
rtvDesc.ViewDimension = D3D12_RTV_DIMENSION_TEXTURE2DARRAY;
rtvDesc.Texture2DArray.MipSlice = UINT(colorAtt.level());
@@ -4697,7 +4722,7 @@ bool QD3D12TextureRenderTarget::create()
return false;
}
D3D12_DEPTH_STENCIL_VIEW_DESC dsvDesc = {};
- dsvDesc.Format = toD3DDepthTextureDSVFormat(depthTexD->format());
+ dsvDesc.Format = depthTexD->rtFormat;
dsvDesc.ViewDimension = depthTexD->sampleDesc.Count > 1 ? D3D12_DSV_DIMENSION_TEXTURE2DMS
: D3D12_DSV_DIMENSION_TEXTURE2D;
if (depthTexD->flags().testFlag(QRhiTexture::TextureArray)) {
diff --git a/src/gui/rhi/qrhid3d12_p.h b/src/gui/rhi/qrhid3d12_p.h
index c6d4123c09..3f9abbb5ac 100644
--- a/src/gui/rhi/qrhid3d12_p.h
+++ b/src/gui/rhi/qrhid3d12_p.h
@@ -731,6 +731,8 @@ struct QD3D12Texture : public QRhiTexture
QD3D12ObjectHandle handle;
QD3D12Descriptor srv;
DXGI_FORMAT dxgiFormat;
+ DXGI_FORMAT srvFormat;
+ DXGI_FORMAT rtFormat; // RTV/DSV/UAV
uint mipLevelCount;
DXGI_SAMPLE_DESC sampleDesc;
uint generation = 0;
@@ -1235,6 +1237,7 @@ public:
struct {
bool multiView = false;
+ bool textureViewFormat = false;
} caps;
};
diff --git a/src/gui/rhi/qrhigles2.cpp b/src/gui/rhi/qrhigles2.cpp
index de1edb70b5..aeaecf8995 100644
--- a/src/gui/rhi/qrhigles2.cpp
+++ b/src/gui/rhi/qrhigles2.cpp
@@ -245,7 +245,7 @@ QT_BEGIN_NAMESPACE
#endif
#ifndef GL_FRAMEBUFFER_SRGB
-#define GL_FRAMEBUFFER_SRGB 0x8DB9
+#define GL_FRAMEBUFFER_SRGB 0x8DB9
#endif
#ifndef GL_READ_FRAMEBUFFER
@@ -892,7 +892,13 @@ bool QRhiGles2::create(QRhi::Flags flags)
#else
caps.needsDepthStencilCombinedAttach = false;
#endif
- caps.srgbCapableDefaultFramebuffer = f->hasOpenGLExtension(QOpenGLExtensions::SRGBFrameBuffer);
+
+ // QOpenGLExtensions::SRGBFrameBuffer is not useful here. We need to know if
+ // controlling the sRGB-on-shader-write state is supported, not that if the
+ // default framebuffer is sRGB-capable. And there are two different
+ // extensions for desktop and ES.
+ caps.srgbWriteControl = ctx->hasExtension("GL_EXT_framebuffer_sRGB") || ctx->hasExtension("GL_EXT_sRGB_write_control");
+
caps.coreProfile = actualFormat.profile() == QSurfaceFormat::CoreProfile;
if (caps.gles)
@@ -1426,6 +1432,8 @@ bool QRhiGles2::isFeatureSupported(QRhi::Feature feature) const
return caps.texture3D;
case QRhi::MultiView:
return caps.multiView && caps.maxTextureArraySize > 0;
+ case QRhi::TextureViewFormat:
+ return false;
default:
Q_UNREACHABLE_RETURN(false);
}
@@ -3281,7 +3289,7 @@ void QRhiGles2::executeCommandBuffer(QRhiCommandBuffer *cb)
}
if (caps.hasDrawBuffersFunc)
f->glDrawBuffers(bufs.count(), bufs.constData());
- if (caps.srgbCapableDefaultFramebuffer) {
+ if (caps.srgbWriteControl) {
if (cmd.args.bindFramebuffer.srgb)
f->glEnable(GL_FRAMEBUFFER_SRGB);
else
diff --git a/src/gui/rhi/qrhigles2_p.h b/src/gui/rhi/qrhigles2_p.h
index 215f075753..c5194b0100 100644
--- a/src/gui/rhi/qrhigles2_p.h
+++ b/src/gui/rhi/qrhigles2_p.h
@@ -981,7 +981,7 @@ public:
depthTexture(false),
packedDepthStencil(false),
needsDepthStencilCombinedAttach(false),
- srgbCapableDefaultFramebuffer(false),
+ srgbWriteControl(false),
coreProfile(false),
uniformBuffers(false),
elementIndexUint(false),
@@ -1037,7 +1037,7 @@ public:
uint depthTexture : 1;
uint packedDepthStencil : 1;
uint needsDepthStencilCombinedAttach : 1;
- uint srgbCapableDefaultFramebuffer : 1;
+ uint srgbWriteControl : 1;
uint coreProfile : 1;
uint uniformBuffers : 1;
uint elementIndexUint : 1;
diff --git a/src/gui/rhi/qrhimetal.mm b/src/gui/rhi/qrhimetal.mm
index 0c3e9dcb31..7530186f11 100644
--- a/src/gui/rhi/qrhimetal.mm
+++ b/src/gui/rhi/qrhimetal.mm
@@ -850,6 +850,8 @@ bool QRhiMetal::isFeatureSupported(QRhi::Feature feature) const
return true;
case QRhi::MultiView:
return caps.multiView;
+ case QRhi::TextureViewFormat:
+ return false;
default:
Q_UNREACHABLE();
return false;
diff --git a/src/gui/rhi/qrhivulkan.cpp b/src/gui/rhi/qrhivulkan.cpp
index 874d5a1253..2775530d6a 100644
--- a/src/gui/rhi/qrhivulkan.cpp
+++ b/src/gui/rhi/qrhivulkan.cpp
@@ -1449,7 +1449,7 @@ bool QRhiVulkan::createOffscreenRenderPass(QVkRenderPassDescriptor *rpD,
QVkTexture *texD = QRHI_RES(QVkTexture, it->texture());
QVkRenderBuffer *rbD = QRHI_RES(QVkRenderBuffer, it->renderBuffer());
Q_ASSERT(texD || rbD);
- const VkFormat vkformat = texD ? texD->vkformat : rbD->vkformat;
+ const VkFormat vkformat = texD ? texD->viewFormat : rbD->vkformat;
const VkSampleCountFlagBits samples = texD ? texD->samples : rbD->samples;
VkAttachmentDescription attDesc = {};
@@ -1481,7 +1481,7 @@ bool QRhiVulkan::createOffscreenRenderPass(QVkRenderPassDescriptor *rpD,
rpD->hasDepthStencil = depthStencilBuffer || depthTexture;
if (rpD->hasDepthStencil) {
- const VkFormat dsFormat = depthTexture ? QRHI_RES(QVkTexture, depthTexture)->vkformat
+ const VkFormat dsFormat = depthTexture ? QRHI_RES(QVkTexture, depthTexture)->viewFormat
: QRHI_RES(QVkRenderBuffer, depthStencilBuffer)->vkformat;
const VkSampleCountFlagBits samples = depthTexture ? QRHI_RES(QVkTexture, depthTexture)->samples
: QRHI_RES(QVkRenderBuffer, depthStencilBuffer)->samples;
@@ -1519,7 +1519,7 @@ bool QRhiVulkan::createOffscreenRenderPass(QVkRenderPassDescriptor *rpD,
}
VkAttachmentDescription attDesc = {};
- attDesc.format = dstFormat;
+ attDesc.format = rtexD->viewFormat;
attDesc.samples = VK_SAMPLE_COUNT_1_BIT;
attDesc.loadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE; // ignored
attDesc.storeOp = VK_ATTACHMENT_STORE_OP_STORE;
@@ -3020,7 +3020,7 @@ void QRhiVulkan::updateShaderResourceBindings(QRhiShaderResourceBindings *srb, i
case QRhiShaderResourceBinding::ImageLoadStore:
{
QVkTexture *texD = QRHI_RES(QVkTexture, b->u.simage.tex);
- VkImageView view = texD->imageViewForLevel(b->u.simage.level);
+ VkImageView view = texD->perLevelImageViewForLoadStore(b->u.simage.level);
if (view) {
writeInfo.descriptorType = VK_DESCRIPTOR_TYPE_STORAGE_IMAGE;
bd.simage.id = texD->m_id;
@@ -3929,6 +3929,7 @@ void QRhiVulkan::executeDeferredReleases(bool forced)
df->vkDestroyImageView(dev, e.textureRenderTarget.rtv[att], nullptr);
df->vkDestroyImageView(dev, e.textureRenderTarget.resrtv[att], nullptr);
}
+ df->vkDestroyImageView(dev, e.textureRenderTarget.dsv, nullptr);
break;
case QRhiVulkan::DeferredReleaseEntry::RenderPass:
df->vkDestroyRenderPass(dev, e.renderPass.rp, nullptr);
@@ -4588,6 +4589,8 @@ bool QRhiVulkan::isFeatureSupported(QRhi::Feature feature) const
return true;
case QRhi::MultiView:
return caps.multiView;
+ case QRhi::TextureViewFormat:
+ return true;
default:
Q_UNREACHABLE_RETURN(false);
}
@@ -6228,6 +6231,15 @@ bool QVkTexture::prepareCreate(QSize *adjustedSize)
QRHI_RES_RHI(QRhiVulkan);
vkformat = toVkTextureFormat(m_format, m_flags);
+ if (m_writeViewFormat.format != UnknownFormat)
+ viewFormat = toVkTextureFormat(m_writeViewFormat.format, m_writeViewFormat.srgb ? sRGB : Flags());
+ else
+ viewFormat = vkformat;
+ if (m_readViewFormat.format != UnknownFormat)
+ viewFormatForSampling = toVkTextureFormat(m_readViewFormat.format, m_readViewFormat.srgb ? sRGB : Flags());
+ else
+ viewFormatForSampling = vkformat;
+
VkFormatProperties props;
rhiD->f->vkGetPhysicalDeviceFormatProperties(rhiD->physDev, vkformat, &props);
const bool canSampleOptimal = (props.optimalTilingFeatures & VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT);
@@ -6323,7 +6335,7 @@ bool QVkTexture::finishCreate()
: (is3D ? VK_IMAGE_VIEW_TYPE_3D
: (is1D ? (isArray ? VK_IMAGE_VIEW_TYPE_1D_ARRAY : VK_IMAGE_VIEW_TYPE_1D)
: (isArray ? VK_IMAGE_VIEW_TYPE_2D_ARRAY : VK_IMAGE_VIEW_TYPE_2D)));
- viewInfo.format = vkformat;
+ viewInfo.format = viewFormatForSampling;
viewInfo.components.r = VK_COMPONENT_SWIZZLE_R;
viewInfo.components.g = VK_COMPONENT_SWIZZLE_G;
viewInfo.components.b = VK_COMPONENT_SWIZZLE_B;
@@ -6469,7 +6481,7 @@ void QVkTexture::setNativeLayout(int layout)
usageState.layout = VkImageLayout(layout);
}
-VkImageView QVkTexture::imageViewForLevel(int level)
+VkImageView QVkTexture::perLevelImageViewForLoadStore(int level)
{
Q_ASSERT(level >= 0 && level < int(mipLevelCount));
if (perLevelImageViews[level] != VK_NULL_HANDLE)
@@ -6489,7 +6501,7 @@ VkImageView QVkTexture::imageViewForLevel(int level)
: (is3D ? VK_IMAGE_VIEW_TYPE_3D
: (is1D ? (isArray ? VK_IMAGE_VIEW_TYPE_1D_ARRAY : VK_IMAGE_VIEW_TYPE_1D)
: (isArray ? VK_IMAGE_VIEW_TYPE_2D_ARRAY : VK_IMAGE_VIEW_TYPE_2D)));
- viewInfo.format = vkformat;
+ viewInfo.format = viewFormat; // this is writeViewFormat, regardless of Load, Store, or LoadStore; intentional
viewInfo.components.r = VK_COMPONENT_SWIZZLE_R;
viewInfo.components.g = VK_COMPONENT_SWIZZLE_G;
viewInfo.components.b = VK_COMPONENT_SWIZZLE_B;
@@ -6826,6 +6838,9 @@ void QVkTextureRenderTarget::destroy()
resrtv[att] = VK_NULL_HANDLE;
}
+ e.textureRenderTarget.dsv = dsv;
+ dsv = VK_NULL_HANDLE;
+
QRHI_RES_RHI(QRhiVulkan);
if (rhiD) {
rhiD->releaseQueue.append(e);
@@ -6889,7 +6904,7 @@ bool QVkTextureRenderTarget::create()
viewInfo.viewType = is1D ? VK_IMAGE_VIEW_TYPE_1D
: (isMultiView ? VK_IMAGE_VIEW_TYPE_2D_ARRAY
: VK_IMAGE_VIEW_TYPE_2D);
- viewInfo.format = texD->vkformat;
+ viewInfo.format = texD->viewFormat;
viewInfo.components.r = VK_COMPONENT_SWIZZLE_R;
viewInfo.components.g = VK_COMPONENT_SWIZZLE_G;
viewInfo.components.b = VK_COMPONENT_SWIZZLE_B;
@@ -6923,7 +6938,25 @@ bool QVkTextureRenderTarget::create()
if (hasDepthStencil) {
if (m_desc.depthTexture()) {
QVkTexture *depthTexD = QRHI_RES(QVkTexture, m_desc.depthTexture());
- views.append(depthTexD->imageView);
+ // need a dedicated view just because viewFormat may differ from vkformat
+ VkImageViewCreateInfo viewInfo = {};
+ viewInfo.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO;
+ viewInfo.image = depthTexD->image;
+ viewInfo.viewType = d.multiViewCount > 1 ? VK_IMAGE_VIEW_TYPE_2D_ARRAY : VK_IMAGE_VIEW_TYPE_2D;
+ viewInfo.format = depthTexD->viewFormat;
+ viewInfo.components.r = VK_COMPONENT_SWIZZLE_R;
+ viewInfo.components.g = VK_COMPONENT_SWIZZLE_G;
+ viewInfo.components.b = VK_COMPONENT_SWIZZLE_B;
+ viewInfo.components.a = VK_COMPONENT_SWIZZLE_A;
+ viewInfo.subresourceRange.aspectMask = VK_IMAGE_ASPECT_DEPTH_BIT | VK_IMAGE_ASPECT_STENCIL_BIT;
+ viewInfo.subresourceRange.levelCount = 1;
+ viewInfo.subresourceRange.layerCount = qMax<uint32_t>(1, d.multiViewCount);
+ VkResult err = rhiD->df->vkCreateImageView(rhiD->dev, &viewInfo, nullptr, &dsv);
+ if (err != VK_SUCCESS) {
+ qWarning("Failed to create depth-stencil image view for rt: %d", err);
+ return false;
+ }
+ views.append(dsv);
if (d.colorAttCount == 0) {
d.pixelSize = depthTexD->pixelSize();
d.sampleCount = depthTexD->samples;
@@ -6955,7 +6988,7 @@ bool QVkTextureRenderTarget::create()
viewInfo.image = resTexD->image;
viewInfo.viewType = d.multiViewCount ? VK_IMAGE_VIEW_TYPE_2D_ARRAY
: VK_IMAGE_VIEW_TYPE_2D;
- viewInfo.format = resTexD->vkformat;
+ viewInfo.format = resTexD->viewFormat;
viewInfo.components.r = VK_COMPONENT_SWIZZLE_R;
viewInfo.components.g = VK_COMPONENT_SWIZZLE_G;
viewInfo.components.b = VK_COMPONENT_SWIZZLE_B;
diff --git a/src/gui/rhi/qrhivulkan_p.h b/src/gui/rhi/qrhivulkan_p.h
index d2b91a424e..a364634676 100644
--- a/src/gui/rhi/qrhivulkan_p.h
+++ b/src/gui/rhi/qrhivulkan_p.h
@@ -103,7 +103,7 @@ struct QVkTexture : public QRhiTexture
bool prepareCreate(QSize *adjustedSize = nullptr);
bool finishCreate();
- VkImageView imageViewForLevel(int level);
+ VkImageView perLevelImageViewForLoadStore(int level);
VkImage image = VK_NULL_HANDLE;
VkImageView imageView = VK_NULL_HANDLE;
@@ -124,6 +124,8 @@ struct QVkTexture : public QRhiTexture
VkFormat vkformat;
uint mipLevelCount = 0;
VkSampleCountFlagBits samples;
+ VkFormat viewFormat;
+ VkFormat viewFormatForSampling;
int lastActiveFrameSlot = -1;
uint generation = 0;
friend class QRhiVulkan;
@@ -212,6 +214,7 @@ struct QVkTextureRenderTarget : public QRhiTextureRenderTarget
QVkRenderTargetData d;
VkImageView rtv[QVkRenderTargetData::MAX_COLOR_ATTACHMENTS];
+ VkImageView dsv = VK_NULL_HANDLE;
VkImageView resrtv[QVkRenderTargetData::MAX_COLOR_ATTACHMENTS];
int lastActiveFrameSlot = -1;
friend class QRhiVulkan;
@@ -996,6 +999,7 @@ public:
VkFramebuffer fb;
VkImageView rtv[QVkRenderTargetData::MAX_COLOR_ATTACHMENTS];
VkImageView resrtv[QVkRenderTargetData::MAX_COLOR_ATTACHMENTS];
+ VkImageView dsv;
} textureRenderTarget;
struct {
VkRenderPass rp;
diff --git a/tests/auto/gui/rhi/qrhi/tst_qrhi.cpp b/tests/auto/gui/rhi/qrhi/tst_qrhi.cpp
index 586b5938ac..5dc17a15ea 100644
--- a/tests/auto/gui/rhi/qrhi/tst_qrhi.cpp
+++ b/tests/auto/gui/rhi/qrhi/tst_qrhi.cpp
@@ -424,7 +424,8 @@ void tst_QRhi::create()
QRhi::HalfAttributes,
QRhi::RenderToOneDimensionalTexture,
QRhi::ThreeDimensionalTextureMipmaps,
- QRhi::MultiView
+ QRhi::MultiView,
+ QRhi::TextureViewFormat
};
for (size_t i = 0; i <sizeof(features) / sizeof(QRhi::Feature); ++i)
rhi->isFeatureSupported(features[i]);