rhi: Add support for resolving depth-stencil

Add setDepthResolveTexture(). Should work similarly to the color
attachments' resolveTexture, but for depth or depth-stencil.

However, this is another fragmented feature.

- D3D11/12:

Not supported. AFAICS multisample resolve (ResolveSubresource) is just
not supported for depth or depth-stencil formats.

- Vulkan:

Not supported with Vulkan 1.0.

Supported with Vulkan 1.1 and the two extensions.
(VK_KHR_depth_stencil_resolve which in turn requires
VK_KHR_create_renderpass2 since the 1.0 structs are not extensible, so
now need to use VkRenderPassCreateInfo2 and all the '2' structs)

In Vulkan 1.2 the above are in core, without the KHR suffix, but we
cannot just use that because our main target, the Quest 3 (Android) is
Vulkan 1.1.  So 1.2 and up is ignored for now and we always look for
the 1.1 KHR extensions.

The depth resolve filter is forced for SAMPLE_0. AVG seems to be
supported on desktop (NVIDIA) at least, but that's not guaranteed, so
would need physical device support checks. On the Quest 3 it does not
seem to be supported. And in any case, other APIs such as Metal do not
have an AVG filter mode at all, so just use SAMPLE_0 always.

- OpenGL (not ES):

Should work, both when the multisample data is a renderbuffer and a
texture. Relies on glBlitFramebuffer with filter NEAREST. What it does
internally, with regards to the depth/stencil resolve mode, is not under
our control.

- OpenGL ES:

Should work when the multisample buffer is a texture. But it will not
work when a multisample renderbuffer (setDepthStencilBuffer, not
setDepthTexture) is used because the GLES-only multisample extensions
(GL_EXT_multisampled_render_to_texture,
GL_OVR_multiview_multisampled_render_to_texture, which we prefer over
the explicit resolve-based approach) work with textures only.

- Metal:

Should work.

Task-number: QTBUG-122292
Change-Id: Ifa7ca5e1be78227bd6bd7546dde3a62f7fdbc95e
Reviewed-by: Andy Nichols <andy.nichols@qt.io>
This commit is contained in:
Laszlo Agocs 2024-03-20 13:57:18 +01:00
parent faaee82129
commit e36c1a8d84
13 changed files with 865 additions and 75 deletions

View File

@ -1022,6 +1022,20 @@ Q_LOGGING_CATEGORY(QRHI_LOG_INFO, "qt.rhi.general")
\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.
\value ResolveDepthStencil Indicates that resolving a multisample depth or
depth-stencil texture is supported. Otherwise,
\l{QRhiTextureRenderTargetDescription::setDepthResolveTexture()}{setting a
depth resolve texture} is not functional and must be avoided. Direct 3D 11
and 12 have no support for resolving depth/depth-stencil formats, and
therefore this feature will never be supported with those. Vulkan 1.0 has no
API to request resolving a depth-stencil attachment. Therefore, with Vulkan
this feature will only be supported with Vulkan 1.2 and up, and on 1.1
implementations with the appropriate extensions present. This feature is
provided for the rare case when resolving into a non-multisample depth
texture becomes necessary, for example when rendering into an
OpenXR-provided depth texture (XR_KHR_composition_layer_depth). This enum
value has been introduced in Qt 6.8.
*/
/*!
@ -2692,6 +2706,39 @@ QRhiTextureRenderTargetDescription::QRhiTextureRenderTargetDescription(const QRh
\sa setDepthStencilBuffer()
*/
/*!
\fn QRhiTexture *QRhiTextureRenderTargetDescription::depthResolveTexture() const
\return the texture to which a multisample depth (or depth-stencil) texture
(or texture array) is resolved to. \nullptr if there is none, which is the
most common case.
\since 6.8
\sa QRhiColorAttachment::resolveTexture(), depthTexture()
*/
/*!
\fn void QRhiTextureRenderTargetDescription::setDepthResolveTexture(QRhiTexture *tex)
Sets the depth (or depth-stencil) resolve texture \a tex.
\a tex is expected to be a 2D texture or a 2D texture array with a format
matching the texture set via setDepthTexture().
\note Resolving depth (or depth-stencil) data is only functional when the
\l ResolveDepthStencil feature is reported as supported at run time. Support
for depth-stencil resolve is not universally available among the graphics
APIs. Designs assuming unconditional availability of depth-stencil resolve
are therefore non-portable, and should be avoided.
\note As an additional limitation for OpenGL ES in particular, setting a
depth resolve texture may only be functional in combination with
setDepthTexture(), not with setDepthStencilBuffer().
\since 6.8
\sa QRhiColorAttachment::setResolveTexture(), setDepthTexture()
*/
/*!
\class QRhiTextureSubresourceUploadDescription
\inmodule QtGui
@ -5088,8 +5135,9 @@ QRhiResource::Type QRhiSwapChainRenderTarget::resourceType() const
\value DoNotStoreDepthStencilContents Indicates that the contents of the
depth texture does not need to be written out. Relevant only when a
QRhiTexture, not QRhiRenderBuffer, is used as the depth-stencil buffer,
because for QRhiRenderBuffer this is implicit. This enum value is introduced
in Qt 6.8.
because for QRhiRenderBuffer this is implicit. When a depthResolveTexture is
set, the flag is not relevant, because the behavior is then as if the flag
was set. This enum value is introduced in Qt 6.8.
*/
/*!

View File

@ -642,10 +642,14 @@ public:
QRhiTexture *depthTexture() const { return m_depthTexture; }
void setDepthTexture(QRhiTexture *texture) { m_depthTexture = texture; }
QRhiTexture *depthResolveTexture() const { return m_depthResolveTexture; }
void setDepthResolveTexture(QRhiTexture *tex) { m_depthResolveTexture = tex; }
private:
QVarLengthArray<QRhiColorAttachment, 8> m_colorAttachments;
QRhiRenderBuffer *m_depthStencilBuffer = nullptr;
QRhiTexture *m_depthTexture = nullptr;
QRhiTexture *m_depthResolveTexture = nullptr;
};
class Q_GUI_EXPORT QRhiTextureSubresourceUploadDescription
@ -1866,7 +1870,8 @@ public:
RenderToOneDimensionalTexture,
ThreeDimensionalTextureMipmaps,
MultiView,
TextureViewFormat
TextureViewFormat,
ResolveDepthStencil
};
enum BeginFrameFlag {

View File

@ -600,6 +600,8 @@ bool QRhiD3D11::isFeatureSupported(QRhi::Feature feature) const
return false;
case QRhi::TextureViewFormat:
return false; // because we use fully typed formats for textures and relaxed casting is a D3D12 thing
case QRhi::ResolveDepthStencil:
return false;
default:
Q_UNREACHABLE();
return false;
@ -2119,6 +2121,8 @@ void QRhiD3D11::endPass(QRhiCommandBuffer *cb, QRhiResourceUpdateBatch *resource
cmd.args.resolveSubRes.srcSubRes = D3D11CalcSubresource(0, UINT(colorAtt.layer()), 1);
cmd.args.resolveSubRes.format = dstTexD->dxgiFormat;
}
if (rtTex->m_desc.depthResolveTexture())
qWarning("Resolving multisample depth-stencil buffers is not supported with D3D");
}
cbD->recordingPass = QD3D11CommandBuffer::NoPass;

View File

@ -712,6 +712,10 @@ bool QRhiD3D12::isFeatureSupported(QRhi::Feature feature) const
return caps.multiView;
case QRhi::TextureViewFormat:
return caps.textureViewFormat;
case QRhi::ResolveDepthStencil:
// there is no Multisample Resolve support for depth/stencil formats
// https://learn.microsoft.com/en-us/windows/win32/direct3ddxgi/hardware-support-for-direct3d-12-1-formats
return false;
}
return false;
}
@ -1960,7 +1964,8 @@ void QRhiD3D12::endPass(QRhiCommandBuffer *cb, QRhiResourceUpdateBatch *resource
dstTexD->dxgiFormat);
}
}
if (rtTex->m_desc.depthResolveTexture())
qWarning("Resolving multisample depth-stencil buffers is not supported with D3D");
}
cbD->recordingPass = QD3D12CommandBuffer::NoPass;

View File

@ -1457,6 +1457,8 @@ bool QRhiGles2::isFeatureSupported(QRhi::Feature feature) const
return caps.multiView && caps.maxTextureArraySize > 0;
case QRhi::TextureViewFormat:
return false;
case QRhi::ResolveDepthStencil:
return true;
default:
Q_UNREACHABLE_RETURN(false);
}
@ -3580,21 +3582,47 @@ void QRhiGles2::executeCommandBuffer(QRhiCommandBuffer *cb)
GLuint fbo[2];
f->glGenFramebuffers(2, fbo);
f->glBindFramebuffer(GL_READ_FRAMEBUFFER, fbo[0]);
f->glFramebufferRenderbuffer(GL_READ_FRAMEBUFFER, GL_COLOR_ATTACHMENT0,
GL_RENDERBUFFER, cmd.args.blitFromRenderbuffer.renderbuffer);
const bool ds = cmd.args.blitFromRenderbuffer.isDepthStencil;
if (ds) {
f->glFramebufferRenderbuffer(GL_READ_FRAMEBUFFER, GL_DEPTH_ATTACHMENT,
GL_RENDERBUFFER, cmd.args.blitFromRenderbuffer.renderbuffer);
f->glFramebufferRenderbuffer(GL_READ_FRAMEBUFFER, GL_STENCIL_ATTACHMENT,
GL_RENDERBUFFER, cmd.args.blitFromRenderbuffer.renderbuffer);
} else {
f->glFramebufferRenderbuffer(GL_READ_FRAMEBUFFER, GL_COLOR_ATTACHMENT0,
GL_RENDERBUFFER, cmd.args.blitFromRenderbuffer.renderbuffer);
}
f->glBindFramebuffer(GL_DRAW_FRAMEBUFFER, fbo[1]);
if (cmd.args.blitFromRenderbuffer.target == GL_TEXTURE_3D || cmd.args.blitFromRenderbuffer.target == GL_TEXTURE_2D_ARRAY) {
f->glFramebufferTextureLayer(GL_DRAW_FRAMEBUFFER, GL_COLOR_ATTACHMENT0,
cmd.args.blitFromRenderbuffer.dstTexture,
cmd.args.blitFromRenderbuffer.dstLevel,
cmd.args.blitFromRenderbuffer.dstLayer);
if (ds) {
f->glFramebufferTextureLayer(GL_DRAW_FRAMEBUFFER, GL_DEPTH_ATTACHMENT,
cmd.args.blitFromRenderbuffer.dstTexture,
cmd.args.blitFromRenderbuffer.dstLevel,
cmd.args.blitFromRenderbuffer.dstLayer);
f->glFramebufferTextureLayer(GL_DRAW_FRAMEBUFFER, GL_STENCIL_ATTACHMENT,
cmd.args.blitFromRenderbuffer.dstTexture,
cmd.args.blitFromRenderbuffer.dstLevel,
cmd.args.blitFromRenderbuffer.dstLayer);
} else {
f->glFramebufferTextureLayer(GL_DRAW_FRAMEBUFFER, GL_COLOR_ATTACHMENT0,
cmd.args.blitFromRenderbuffer.dstTexture,
cmd.args.blitFromRenderbuffer.dstLevel,
cmd.args.blitFromRenderbuffer.dstLayer);
}
} else {
f->glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, cmd.args.blitFromRenderbuffer.target,
cmd.args.blitFromRenderbuffer.dstTexture, cmd.args.blitFromRenderbuffer.dstLevel);
if (ds) {
f->glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, cmd.args.blitFromRenderbuffer.target,
cmd.args.blitFromRenderbuffer.dstTexture, cmd.args.blitFromRenderbuffer.dstLevel);
f->glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER, GL_STENCIL_ATTACHMENT, cmd.args.blitFromRenderbuffer.target,
cmd.args.blitFromRenderbuffer.dstTexture, cmd.args.blitFromRenderbuffer.dstLevel);
} else {
f->glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, cmd.args.blitFromRenderbuffer.target,
cmd.args.blitFromRenderbuffer.dstTexture, cmd.args.blitFromRenderbuffer.dstLevel);
}
}
f->glBlitFramebuffer(0, 0, cmd.args.blitFromRenderbuffer.w, cmd.args.blitFromRenderbuffer.h,
0, 0, cmd.args.blitFromRenderbuffer.w, cmd.args.blitFromRenderbuffer.h,
GL_COLOR_BUFFER_BIT,
ds ? GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT : GL_COLOR_BUFFER_BIT,
GL_NEAREST); // Qt 5 used Nearest when resolving samples, stick to that
f->glBindFramebuffer(GL_FRAMEBUFFER, ctx->defaultFramebufferObject());
f->glDeleteFramebuffers(2, fbo);
@ -3609,28 +3637,65 @@ void QRhiGles2::executeCommandBuffer(QRhiCommandBuffer *cb)
GLuint fbo[2];
f->glGenFramebuffers(2, fbo);
f->glBindFramebuffer(GL_READ_FRAMEBUFFER, fbo[0]);
const bool ds = cmd.args.blitFromTexture.isDepthStencil;
if (cmd.args.blitFromTexture.srcTarget == GL_TEXTURE_2D_MULTISAMPLE_ARRAY) {
f->glFramebufferTextureLayer(GL_READ_FRAMEBUFFER, GL_COLOR_ATTACHMENT0,
cmd.args.blitFromTexture.srcTexture,
cmd.args.blitFromTexture.srcLevel,
cmd.args.blitFromTexture.srcLayer);
if (ds) {
f->glFramebufferTextureLayer(GL_READ_FRAMEBUFFER, GL_DEPTH_ATTACHMENT,
cmd.args.blitFromTexture.srcTexture,
cmd.args.blitFromTexture.srcLevel,
cmd.args.blitFromTexture.srcLayer);
f->glFramebufferTextureLayer(GL_READ_FRAMEBUFFER, GL_STENCIL_ATTACHMENT,
cmd.args.blitFromTexture.srcTexture,
cmd.args.blitFromTexture.srcLevel,
cmd.args.blitFromTexture.srcLayer);
} else {
f->glFramebufferTextureLayer(GL_READ_FRAMEBUFFER, GL_COLOR_ATTACHMENT0,
cmd.args.blitFromTexture.srcTexture,
cmd.args.blitFromTexture.srcLevel,
cmd.args.blitFromTexture.srcLayer);
}
} else {
f->glFramebufferTexture2D(GL_READ_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, cmd.args.blitFromTexture.srcTarget,
cmd.args.blitFromTexture.srcTexture, cmd.args.blitFromTexture.srcLevel);
if (ds) {
f->glFramebufferTexture2D(GL_READ_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, cmd.args.blitFromTexture.srcTarget,
cmd.args.blitFromTexture.srcTexture, cmd.args.blitFromTexture.srcLevel);
f->glFramebufferTexture2D(GL_READ_FRAMEBUFFER, GL_STENCIL_ATTACHMENT, cmd.args.blitFromTexture.srcTarget,
cmd.args.blitFromTexture.srcTexture, cmd.args.blitFromTexture.srcLevel);
} else {
f->glFramebufferTexture2D(GL_READ_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, cmd.args.blitFromTexture.srcTarget,
cmd.args.blitFromTexture.srcTexture, cmd.args.blitFromTexture.srcLevel);
}
}
f->glBindFramebuffer(GL_DRAW_FRAMEBUFFER, fbo[1]);
if (cmd.args.blitFromTexture.dstTarget == GL_TEXTURE_3D || cmd.args.blitFromTexture.dstTarget == GL_TEXTURE_2D_ARRAY) {
f->glFramebufferTextureLayer(GL_DRAW_FRAMEBUFFER, GL_COLOR_ATTACHMENT0,
cmd.args.blitFromTexture.dstTexture,
cmd.args.blitFromTexture.dstLevel,
cmd.args.blitFromTexture.dstLayer);
if (ds) {
f->glFramebufferTextureLayer(GL_DRAW_FRAMEBUFFER, GL_DEPTH_ATTACHMENT,
cmd.args.blitFromTexture.dstTexture,
cmd.args.blitFromTexture.dstLevel,
cmd.args.blitFromTexture.dstLayer);
f->glFramebufferTextureLayer(GL_DRAW_FRAMEBUFFER, GL_STENCIL_ATTACHMENT,
cmd.args.blitFromTexture.dstTexture,
cmd.args.blitFromTexture.dstLevel,
cmd.args.blitFromTexture.dstLayer);
} else {
f->glFramebufferTextureLayer(GL_DRAW_FRAMEBUFFER, GL_COLOR_ATTACHMENT0,
cmd.args.blitFromTexture.dstTexture,
cmd.args.blitFromTexture.dstLevel,
cmd.args.blitFromTexture.dstLayer);
}
} else {
f->glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, cmd.args.blitFromTexture.dstTarget,
cmd.args.blitFromTexture.dstTexture, cmd.args.blitFromTexture.dstLevel);
if (ds) {
f->glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, cmd.args.blitFromTexture.dstTarget,
cmd.args.blitFromTexture.dstTexture, cmd.args.blitFromTexture.dstLevel);
f->glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER, GL_STENCIL_ATTACHMENT, cmd.args.blitFromTexture.dstTarget,
cmd.args.blitFromTexture.dstTexture, cmd.args.blitFromTexture.dstLevel);
} else {
f->glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, cmd.args.blitFromTexture.dstTarget,
cmd.args.blitFromTexture.dstTexture, cmd.args.blitFromTexture.dstLevel);
}
}
f->glBlitFramebuffer(0, 0, cmd.args.blitFromTexture.w, cmd.args.blitFromTexture.h,
0, 0, cmd.args.blitFromTexture.w, cmd.args.blitFromTexture.h,
GL_COLOR_BUFFER_BIT,
ds ? GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT : GL_COLOR_BUFFER_BIT,
GL_NEAREST); // Qt 5 used Nearest when resolving samples, stick to that
f->glBindFramebuffer(GL_FRAMEBUFFER, ctx->defaultFramebufferObject());
f->glDeleteFramebuffers(2, fbo);
@ -4514,6 +4579,7 @@ void QRhiGles2::endPass(QRhiCommandBuffer *cb, QRhiResourceUpdateBatch *resource
const bool hasZ = resolveTexD->m_flags.testFlag(QRhiTexture::ThreeDimensional)
|| resolveTexD->m_flags.testFlag(QRhiTexture::TextureArray);
cmd.args.blitFromRenderbuffer.dstLayer = hasZ ? colorAtt.resolveLayer() : 0;
cmd.args.blitFromRenderbuffer.isDepthStencil = false;
} else if (caps.glesMultisampleRenderToTexture) {
// Nothing to do, resolving into colorAtt.resolveTexture() is automatic,
// colorAtt.texture() is in fact not used for anything.
@ -4550,12 +4616,53 @@ void QRhiGles2::endPass(QRhiCommandBuffer *cb, QRhiResourceUpdateBatch *resource
cmd.args.blitFromTexture.dstLayer = 0;
if (resolveTexD->m_flags.testFlag(QRhiTexture::ThreeDimensional) || resolveTexD->m_flags.testFlag(QRhiTexture::TextureArray))
cmd.args.blitFromTexture.dstLayer = dstLayer;
cmd.args.blitFromTexture.isDepthStencil = false;
}
}
}
const bool mayDiscardDepthStencil = rtTex->m_desc.depthStencilBuffer()
|| (rtTex->m_desc.depthTexture() && rtTex->m_flags.testFlag(QRhiTextureRenderTarget::DoNotStoreDepthStencilContents));
if (rtTex->m_desc.depthResolveTexture()) {
QGles2Texture *depthResolveTexD = QRHI_RES(QGles2Texture, rtTex->m_desc.depthResolveTexture());
const QSize size = depthResolveTexD->pixelSize();
if (rtTex->m_desc.depthStencilBuffer()) {
QGles2RenderBuffer *rbD = QRHI_RES(QGles2RenderBuffer, rtTex->m_desc.depthStencilBuffer());
QGles2CommandBuffer::Command &cmd(cbD->commands.get());
cmd.cmd = QGles2CommandBuffer::Command::BlitFromRenderbuffer;
cmd.args.blitFromRenderbuffer.renderbuffer = rbD->renderbuffer;
cmd.args.blitFromRenderbuffer.w = size.width();
cmd.args.blitFromRenderbuffer.h = size.height();
cmd.args.blitFromRenderbuffer.target = depthResolveTexD->target;
cmd.args.blitFromRenderbuffer.dstTexture = depthResolveTexD->texture;
cmd.args.blitFromRenderbuffer.dstLevel = 0;
cmd.args.blitFromRenderbuffer.dstLayer = 0;
cmd.args.blitFromRenderbuffer.isDepthStencil = true;
} else if (caps.glesMultisampleRenderToTexture) {
// Nothing to do, resolving into depthResolveTexture() is automatic.
} else {
QGles2Texture *depthTexD = QRHI_RES(QGles2Texture, rtTex->m_desc.depthTexture());
const int resolveCount = depthTexD->arraySize() >= 2 ? depthTexD->arraySize() : 1;
for (int resolveIdx = 0; resolveIdx < resolveCount; ++resolveIdx) {
QGles2CommandBuffer::Command &cmd(cbD->commands.get());
cmd.cmd = QGles2CommandBuffer::Command::BlitFromTexture;
cmd.args.blitFromTexture.srcTarget = depthTexD->target;
cmd.args.blitFromTexture.srcTexture = depthTexD->texture;
cmd.args.blitFromTexture.srcLevel = 0;
cmd.args.blitFromTexture.srcLayer = resolveIdx;
cmd.args.blitFromTexture.w = size.width();
cmd.args.blitFromTexture.h = size.height();
cmd.args.blitFromTexture.dstTarget = depthResolveTexD->target;
cmd.args.blitFromTexture.dstTexture = depthResolveTexD->texture;
cmd.args.blitFromTexture.dstLevel = 0;
cmd.args.blitFromTexture.dstLayer = resolveIdx;
cmd.args.blitFromTexture.isDepthStencil = true;
}
}
}
const bool mayDiscardDepthStencil =
(rtTex->m_desc.depthStencilBuffer()
|| (rtTex->m_desc.depthTexture() && rtTex->m_flags.testFlag(QRhiTextureRenderTarget::DoNotStoreDepthStencilContents)))
&& !rtTex->m_desc.depthResolveTexture();
if (mayDiscardDepthStencil) {
QGles2CommandBuffer::Command &cmd(cbD->commands.get());
cmd.cmd = QGles2CommandBuffer::Command::InvalidateFramebuffer;
@ -5923,11 +6030,24 @@ bool QGles2TextureRenderTarget::create()
} else {
QGles2Texture *depthTexD = QRHI_RES(QGles2Texture, m_desc.depthTexture());
if (multiViewCount < 2) {
rhiD->f->glFramebufferTexture2D(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, depthTexD->target,
depthTexD->texture, 0);
if (rhiD->isStencilSupportingFormat(depthTexD->format())) {
rhiD->f->glFramebufferTexture2D(GL_FRAMEBUFFER, GL_STENCIL_ATTACHMENT, depthTexD->target,
if (depthTexD->sampleCount() > 1 && rhiD->caps.glesMultisampleRenderToTexture && m_desc.depthResolveTexture()) {
// Special path for GLES and
// GL_EXT_multisampled_render_to_texture, for depth-stencil.
// Relevant only when depthResolveTexture is set.
QGles2Texture *depthResolveTexD = QRHI_RES(QGles2Texture, m_desc.depthResolveTexture());
rhiD->glFramebufferTexture2DMultisampleEXT(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, depthResolveTexD->target,
depthResolveTexD->texture, 0, depthTexD->sampleCount());
if (rhiD->isStencilSupportingFormat(depthResolveTexD->format())) {
rhiD->glFramebufferTexture2DMultisampleEXT(GL_FRAMEBUFFER, GL_STENCIL_ATTACHMENT, depthResolveTexD->target,
depthResolveTexD->texture, 0, depthTexD->sampleCount());
}
} else {
rhiD->f->glFramebufferTexture2D(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, depthTexD->target,
depthTexD->texture, 0);
if (rhiD->isStencilSupportingFormat(depthTexD->format())) {
rhiD->f->glFramebufferTexture2D(GL_FRAMEBUFFER, GL_STENCIL_ATTACHMENT, depthTexD->target,
depthTexD->texture, 0);
}
}
} else {
if (depthTexD->sampleCount() > 1 && rhiD->caps.glesMultiviewMultisampleRenderToTexture) {
@ -5944,37 +6064,55 @@ bool QGles2TextureRenderTarget::create()
// multisample-multiview-auto-resolving version (which in
// turn is not supported on desktop GL e.g. by NVIDIA), too
// bad we have a multisample depth texture array here as
// every other API out there requires that. So create a
// temporary one ignoring what the user has already created.
// every other API out there requires that. So, in absence
// of a depthResolveTexture, create a temporary one ignoring
// what the user has already created.
//
// (also, why on Earth would we want to waste time on
// resolving the throwaway depth-stencil buffer? Hopefully
// the invalidation at the end of the pass avoids that...)
if (!m_flags.testFlag(DoNotStoreDepthStencilContents)) {
if (!m_flags.testFlag(DoNotStoreDepthStencilContents) && !m_desc.depthResolveTexture()) {
qWarning("Attempted to create a multiview+multisample QRhiTextureRenderTarget, but DoNotStoreDepthStencilContents was not set."
" This path has no choice but to behave as if DoNotStoreDepthStencilContents was set, because QRhi is forced to create"
" a throwaway non-multisample depth texture here. Set the flag to silence this warning.");
" a throwaway non-multisample depth texture here. Set the flag to silence this warning, or set a depthResolveTexture.");
}
if (!nonMsaaThrowawayDepthTexture) {
rhiD->f->glGenTextures(1, &nonMsaaThrowawayDepthTexture);
rhiD->f->glBindTexture(GL_TEXTURE_2D_ARRAY, nonMsaaThrowawayDepthTexture);
rhiD->f->glTexStorage3D(GL_TEXTURE_2D_ARRAY, 1, GL_DEPTH24_STENCIL8,
depthTexD->pixelSize().width(), depthTexD->pixelSize().height(), multiViewCount);
if (m_desc.depthResolveTexture()) {
QGles2Texture *depthResolveTexD = QRHI_RES(QGles2Texture, m_desc.depthResolveTexture());
rhiD->glFramebufferTextureMultisampleMultiviewOVR(GL_FRAMEBUFFER,
GL_DEPTH_ATTACHMENT,
depthResolveTexD->texture,
0,
depthTexD->sampleCount(),
0,
multiViewCount);
if (rhiD->isStencilSupportingFormat(depthResolveTexD->format())) {
rhiD->glFramebufferTextureMultisampleMultiviewOVR(GL_FRAMEBUFFER,
GL_STENCIL_ATTACHMENT,
depthResolveTexD->texture,
0,
depthTexD->sampleCount(),
0,
multiViewCount);
}
} else {
if (!nonMsaaThrowawayDepthTexture) {
rhiD->f->glGenTextures(1, &nonMsaaThrowawayDepthTexture);
rhiD->f->glBindTexture(GL_TEXTURE_2D_ARRAY, nonMsaaThrowawayDepthTexture);
rhiD->f->glTexStorage3D(GL_TEXTURE_2D_ARRAY, 1, GL_DEPTH24_STENCIL8,
depthTexD->pixelSize().width(), depthTexD->pixelSize().height(), multiViewCount);
}
rhiD->glFramebufferTextureMultisampleMultiviewOVR(GL_FRAMEBUFFER,
GL_DEPTH_ATTACHMENT,
nonMsaaThrowawayDepthTexture,
0,
depthTexD->sampleCount(),
0,
multiViewCount);
rhiD->glFramebufferTextureMultisampleMultiviewOVR(GL_FRAMEBUFFER,
GL_STENCIL_ATTACHMENT,
nonMsaaThrowawayDepthTexture,
0,
depthTexD->sampleCount(),
0,
multiViewCount);
}
rhiD->glFramebufferTextureMultisampleMultiviewOVR(GL_FRAMEBUFFER,
GL_DEPTH_ATTACHMENT,
nonMsaaThrowawayDepthTexture,
0,
depthTexD->sampleCount(),
0,
multiViewCount);
rhiD->glFramebufferTextureMultisampleMultiviewOVR(GL_FRAMEBUFFER,
GL_STENCIL_ATTACHMENT,
nonMsaaThrowawayDepthTexture,
0,
depthTexD->sampleCount(),
0,
multiViewCount);
} else {
// The depth texture here must be an array with at least
// multiViewCount elements, and the format should be D24 or D32F

View File

@ -507,6 +507,7 @@ struct QGles2CommandBuffer : public QRhiCommandBuffer
GLuint dstTexture;
int dstLevel;
int dstLayer;
bool isDepthStencil;
} blitFromRenderbuffer;
struct {
GLenum srcTarget;
@ -519,6 +520,7 @@ struct QGles2CommandBuffer : public QRhiCommandBuffer
GLuint dstTexture;
int dstLevel;
int dstLayer;
bool isDepthStencil;
} blitFromTexture;
struct {
GLenum target;

View File

@ -363,6 +363,7 @@ struct QMetalRenderTargetData
struct {
ColorAtt colorAtt[QMetalRenderPassDescriptor::MAX_COLOR_ATTACHMENTS];
id<MTLTexture> dsTex = nil;
id<MTLTexture> dsResolveTex = nil;
bool hasStencil = false;
bool depthNeedsStore = false;
bool preserveColor = false;
@ -854,6 +855,8 @@ bool QRhiMetal::isFeatureSupported(QRhi::Feature feature) const
return caps.multiView;
case QRhi::TextureViewFormat:
return false;
case QRhi::ResolveDepthStencil:
return true;
default:
Q_UNREACHABLE();
return false;
@ -2368,6 +2371,7 @@ QRhi::FrameOpResult QRhiMetal::beginFrame(QRhiSwapChain *swapChain, QRhi::BeginF
swapChainD->rtWrapper.d->fb.colorAtt[0] = colorAtt;
swapChainD->rtWrapper.d->fb.dsTex = swapChainD->ds ? swapChainD->ds->d->tex : nil;
swapChainD->rtWrapper.d->fb.dsResolveTex = nil;
swapChainD->rtWrapper.d->fb.hasStencil = swapChainD->ds ? true : false;
swapChainD->rtWrapper.d->fb.depthNeedsStore = false;
@ -2979,6 +2983,8 @@ void QRhiMetal::beginPass(QRhiCommandBuffer *cb,
QRHI_RES(QMetalRenderBuffer, rtTex->m_desc.depthStencilBuffer())->lastActiveFrameSlot = currentFrameSlot;
if (rtTex->m_desc.depthTexture())
QRHI_RES(QMetalTexture, rtTex->m_desc.depthTexture())->lastActiveFrameSlot = currentFrameSlot;
if (rtTex->m_desc.depthResolveTexture())
QRHI_RES(QMetalTexture, rtTex->m_desc.depthResolveTexture())->lastActiveFrameSlot = currentFrameSlot;
}
break;
default:
@ -3006,6 +3012,15 @@ void QRhiMetal::beginPass(QRhiCommandBuffer *cb,
cbD->d->currentPassRpDesc.stencilAttachment.texture = rtD->fb.hasStencil ? rtD->fb.dsTex : nil;
if (rtD->fb.depthNeedsStore) // Depth/Stencil is set to DontCare by default, override if needed
cbD->d->currentPassRpDesc.depthAttachment.storeAction = MTLStoreActionStore;
if (rtD->fb.dsResolveTex) {
cbD->d->currentPassRpDesc.depthAttachment.storeAction = rtD->fb.depthNeedsStore ? MTLStoreActionStoreAndMultisampleResolve
: MTLStoreActionMultisampleResolve;
cbD->d->currentPassRpDesc.depthAttachment.resolveTexture = rtD->fb.dsResolveTex;
if (rtD->fb.hasStencil) {
cbD->d->currentPassRpDesc.stencilAttachment.resolveTexture = rtD->fb.dsResolveTex;
cbD->d->currentPassRpDesc.stencilAttachment.storeAction = cbD->d->currentPassRpDesc.depthAttachment.storeAction;
}
}
}
cbD->d->currentRenderPassEncoder = [cbD->d->cb renderCommandEncoderWithDescriptor: cbD->d->currentPassRpDesc];
@ -4229,7 +4244,7 @@ bool QMetalTextureRenderTarget::create()
QMetalTexture *depthTexD = QRHI_RES(QMetalTexture, m_desc.depthTexture());
d->fb.dsTex = depthTexD->d->tex;
d->fb.hasStencil = rhiD->isStencilSupportingFormat(depthTexD->format());
d->fb.depthNeedsStore = !m_flags.testFlag(DoNotStoreDepthStencilContents);
d->fb.depthNeedsStore = !m_flags.testFlag(DoNotStoreDepthStencilContents) && !m_desc.depthResolveTexture();
d->fb.preserveDs = m_flags.testFlag(QRhiTextureRenderTarget::PreserveDepthStencilContents);
if (d->colorAttCount == 0) {
d->pixelSize = depthTexD->pixelSize();
@ -4246,6 +4261,10 @@ bool QMetalTextureRenderTarget::create()
d->sampleCount = depthRbD->samples;
}
}
if (m_desc.depthResolveTexture()) {
QMetalTexture *depthResolveTexD = QRHI_RES(QMetalTexture, m_desc.depthResolveTexture());
d->fb.dsResolveTex = depthResolveTexD->d->tex;
}
d->dsAttCount = 1;
} else {
d->dsAttCount = 0;

View File

@ -334,7 +334,9 @@ QByteArrayList QRhiVulkanInitParams::preferredExtensionsForImportedDevice()
{
return {
QByteArrayLiteral("VK_KHR_swapchain"),
QByteArrayLiteral("VK_EXT_vertex_attribute_divisor")
QByteArrayLiteral("VK_EXT_vertex_attribute_divisor"),
QByteArrayLiteral("VK_KHR_create_renderpass2"),
QByteArrayLiteral("VK_KHR_depth_stencil_resolve")
};
}
@ -428,6 +430,8 @@ bool QRhiVulkan::create(QRhi::Flags flags)
for (const char *ext : inst->extensions())
qCDebug(QRHI_LOG_INFO, " %s", ext);
}
caps = {};
caps.debugUtils = inst->extensions().contains(QByteArrayLiteral("VK_EXT_debug_utils"));
QList<VkQueueFamilyProperties> queueFamilyProps;
@ -659,7 +663,6 @@ bool QRhiVulkan::create(QRhi::Flags flags)
}
}
caps.vertexAttribDivisor = false;
#ifdef VK_EXT_vertex_attribute_divisor
if (devExts.contains(VK_EXT_VERTEX_ATTRIBUTE_DIVISOR_EXTENSION_NAME)) {
if (hasPhysDevProp2) {
@ -669,6 +672,20 @@ bool QRhiVulkan::create(QRhi::Flags flags)
}
#endif
#ifdef VK_KHR_create_renderpass2
if (devExts.contains(VK_KHR_CREATE_RENDERPASS_2_EXTENSION_NAME)) {
requestedDevExts.append(VK_KHR_CREATE_RENDERPASS_2_EXTENSION_NAME);
caps.renderPass2KHR = true;
}
#endif
#ifdef VK_KHR_depth_stencil_resolve
if (devExts.contains(VK_KHR_DEPTH_STENCIL_RESOLVE_EXTENSION_NAME)) {
requestedDevExts.append(VK_KHR_DEPTH_STENCIL_RESOLVE_EXTENSION_NAME);
caps.depthStencilResolveKHR = true;
}
#endif
for (const QByteArray &ext : requestedDeviceExtensions) {
if (!ext.isEmpty() && !requestedDevExts.contains(ext)) {
if (devExts.contains(ext)) {
@ -750,6 +767,13 @@ bool QRhiVulkan::create(QRhi::Flags flags)
}
} else {
qCDebug(QRHI_LOG_INFO, "Using imported device %p", dev);
// Here we have no way to tell if the extensions got enabled or not.
// Pretend it's all there and supported. If getProcAddress fails, we'll
// handle that gracefully.
caps.vertexAttribDivisor = true;
caps.renderPass2KHR = true;
caps.depthStencilResolveKHR = true;
}
vkGetPhysicalDeviceSurfaceCapabilitiesKHR = reinterpret_cast<PFN_vkGetPhysicalDeviceSurfaceCapabilitiesKHR>(
@ -807,6 +831,18 @@ bool QRhiVulkan::create(QRhi::Flags flags)
caps.multiView = multiviewFeaturesIfApi11.multiview;
#endif
// With Vulkan 1.2 renderpass2 and depth_stencil_resolve are core, but we
// have to support the case of 1.1 + extensions, in particular for the Quest
// 3 (Android, Vulkan 1.1 at the time of writing). Therefore, always rely on
// the KHR extension for now.
#ifdef VK_KHR_create_renderpass2
if (caps.renderPass2KHR) {
vkCreateRenderPass2KHR = reinterpret_cast<PFN_vkCreateRenderPass2KHR>(f->vkGetDeviceProcAddr(dev, "vkCreateRenderPass2KHR"));
if (!vkCreateRenderPass2KHR) // handle it gracefully, the caps flag may be incorrect when using an imported VkDevice
caps.renderPass2KHR = false;
}
#endif
if (!importedAllocator) {
VmaVulkanFunctions funcs = {};
funcs.vkGetInstanceProcAddr = wrap_vkGetInstanceProcAddr;
@ -1331,6 +1367,7 @@ bool QRhiVulkan::createDefaultRenderPass(QVkRenderPassDescriptor *rpD, bool hasD
rpD->colorRefs.append({ 0, VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL });
rpD->hasDepthStencil = hasDepthStencil;
rpD->hasDepthStencilResolve = false;
rpD->multiViewCount = 0;
if (hasDepthStencil) {
@ -1434,6 +1471,132 @@ struct MultiViewRenderPassSetupHelper
#endif
};
#ifdef VK_KHR_create_renderpass2
// Effectively converts a VkRenderPassCreateInfo into a VkRenderPassCreateInfo2,
// adding depth-stencil resolve support. Assumes a single subpass and no subpass
// dependencies.
struct RenderPass2SetupHelper
{
bool prepare(VkRenderPassCreateInfo2 *rpInfo2, const VkRenderPassCreateInfo *rpInfo, const QVkRenderPassDescriptor *rpD, int multiViewCount) {
*rpInfo2 = {};
viewMask = 0;
if (multiViewCount >= 2) {
for (uint32_t i = 0; i < uint32_t(multiViewCount); ++i)
viewMask |= (1 << i);
}
attDescs2.resize(rpInfo->attachmentCount);
for (qsizetype i = 0; i < attDescs2.count(); ++i) {
VkAttachmentDescription2KHR &att2(attDescs2[i]);
const VkAttachmentDescription &att(rpInfo->pAttachments[i]);
att2 = {};
att2.sType = VK_STRUCTURE_TYPE_ATTACHMENT_DESCRIPTION_2;
att2.flags = att.flags;
att2.format = att.format;
att2.samples = att.samples;
att2.loadOp = att.loadOp;
att2.storeOp = att.storeOp;
att2.stencilLoadOp = att.stencilLoadOp;
att2.stencilStoreOp = att.stencilStoreOp;
att2.initialLayout = att.initialLayout;
att2.finalLayout = att.finalLayout;
}
attRefs2.clear();
subpass2 = {};
subpass2.sType = VK_STRUCTURE_TYPE_SUBPASS_DESCRIPTION_2_KHR;
const VkSubpassDescription &subpassDesc(rpInfo->pSubpasses[0]);
subpass2.flags = subpassDesc.flags;
subpass2.pipelineBindPoint = subpassDesc.pipelineBindPoint;
if (multiViewCount >= 2)
subpass2.viewMask = viewMask;
// color attachment refs
qsizetype startIndex = attRefs2.count();
for (uint32_t j = 0; j < subpassDesc.colorAttachmentCount; ++j) {
attRefs2.append({});
VkAttachmentReference2KHR &attref2(attRefs2.last());
const VkAttachmentReference &attref(subpassDesc.pColorAttachments[j]);
attref2.sType = VK_STRUCTURE_TYPE_ATTACHMENT_REFERENCE_2_KHR;
attref2.attachment = attref.attachment;
attref2.layout = attref.layout;
attref2.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
}
subpass2.colorAttachmentCount = subpassDesc.colorAttachmentCount;
subpass2.pColorAttachments = attRefs2.constData() + startIndex;
// color resolve refs
if (subpassDesc.pResolveAttachments) {
startIndex = attRefs2.count();
for (uint32_t j = 0; j < subpassDesc.colorAttachmentCount; ++j) {
attRefs2.append({});
VkAttachmentReference2KHR &attref2(attRefs2.last());
const VkAttachmentReference &attref(subpassDesc.pResolveAttachments[j]);
attref2.sType = VK_STRUCTURE_TYPE_ATTACHMENT_REFERENCE_2_KHR;
attref2.attachment = attref.attachment;
attref2.layout = attref.layout;
attref2.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
}
subpass2.pResolveAttachments = attRefs2.constData() + startIndex;
}
// depth-stencil ref
if (subpassDesc.pDepthStencilAttachment) {
startIndex = attRefs2.count();
attRefs2.append({});
VkAttachmentReference2KHR &attref2(attRefs2.last());
const VkAttachmentReference &attref(*subpassDesc.pDepthStencilAttachment);
attref2.sType = VK_STRUCTURE_TYPE_ATTACHMENT_REFERENCE_2_KHR;
attref2.attachment = attref.attachment;
attref2.layout = attref.layout;
attref2.aspectMask = VK_IMAGE_ASPECT_DEPTH_BIT | VK_IMAGE_ASPECT_STENCIL_BIT;
subpass2.pDepthStencilAttachment = attRefs2.constData() + startIndex;
}
// depth-stencil resolve ref
#ifdef VK_KHR_depth_stencil_resolve
dsResolveDesc = {};
if (rpD->hasDepthStencilResolve) {
startIndex = attRefs2.count();
attRefs2.append({});
VkAttachmentReference2KHR &attref2(attRefs2.last());
attref2.sType = VK_STRUCTURE_TYPE_ATTACHMENT_REFERENCE_2_KHR;
attref2.attachment = rpD->dsResolveRef.attachment;
attref2.layout = rpD->dsResolveRef.layout;
attref2.aspectMask = VK_IMAGE_ASPECT_DEPTH_BIT | VK_IMAGE_ASPECT_STENCIL_BIT;
dsResolveDesc.sType = VK_STRUCTURE_TYPE_SUBPASS_DESCRIPTION_DEPTH_STENCIL_RESOLVE_KHR;
dsResolveDesc.depthResolveMode = VK_RESOLVE_MODE_SAMPLE_ZERO_BIT;
dsResolveDesc.stencilResolveMode = VK_RESOLVE_MODE_SAMPLE_ZERO_BIT;
dsResolveDesc.pDepthStencilResolveAttachment = attRefs2.constData() + startIndex;
subpass2.pNext = &dsResolveDesc;
}
#endif
rpInfo2->sType = VK_STRUCTURE_TYPE_RENDER_PASS_CREATE_INFO_2_KHR;
rpInfo2->pNext = nullptr; // the 1.1 VkRenderPassMultiviewCreateInfo is part of the '2' structs
rpInfo2->flags = rpInfo->flags;
rpInfo2->attachmentCount = rpInfo->attachmentCount;
rpInfo2->pAttachments = attDescs2.constData();
rpInfo2->subpassCount = 1;
rpInfo2->pSubpasses = &subpass2;
if (multiViewCount >= 2) {
rpInfo2->correlatedViewMaskCount = 1;
rpInfo2->pCorrelatedViewMasks = &viewMask;
}
return true;
}
QVarLengthArray<VkAttachmentDescription2KHR, 8> attDescs2;
QVarLengthArray<VkAttachmentReference2KHR, 8> attRefs2;
VkSubpassDescription2KHR subpass2;
#ifdef VK_KHR_depth_stencil_resolve
VkSubpassDescriptionDepthStencilResolveKHR dsResolveDesc;
#endif
uint32_t viewMask;
};
#endif // VK_KHR_create_renderpass2
bool QRhiVulkan::createOffscreenRenderPass(QVkRenderPassDescriptor *rpD,
const QRhiColorAttachment *firstColorAttachment,
const QRhiColorAttachment *lastColorAttachment,
@ -1441,9 +1604,10 @@ bool QRhiVulkan::createOffscreenRenderPass(QVkRenderPassDescriptor *rpD,
bool preserveDs,
bool storeDs,
QRhiRenderBuffer *depthStencilBuffer,
QRhiTexture *depthTexture)
QRhiTexture *depthTexture,
QRhiTexture *depthResolveTexture)
{
// attachment list layout is color (0-8), ds (0-1), resolve (0-8)
// attachment list layout is color (0-8), ds (0-1), resolve (0-8), ds resolve (0-1)
int multiViewCount = 0;
for (auto it = firstColorAttachment; it != lastColorAttachment; ++it) {
@ -1539,6 +1703,31 @@ bool QRhiVulkan::createOffscreenRenderPass(QVkRenderPassDescriptor *rpD,
}
Q_ASSERT(rpD->colorRefs.size() == rpD->resolveRefs.size());
rpD->hasDepthStencilResolve = rpD->hasDepthStencil && depthResolveTexture;
if (rpD->hasDepthStencilResolve) {
QVkTexture *rtexD = QRHI_RES(QVkTexture, depthResolveTexture);
if (rtexD->samples > VK_SAMPLE_COUNT_1_BIT)
qWarning("Resolving into a multisample depth texture is not supported");
QVkTexture *texD = QRHI_RES(QVkTexture, depthResolveTexture);
if (texD->vkformat != rtexD->vkformat) {
qWarning("Multisample resolve between different depth-stencil formats (%d and %d) is not supported.",
int(texD->vkformat), int(rtexD->vkformat));
}
VkAttachmentDescription attDesc = {};
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;
attDesc.stencilLoadOp = attDesc.loadOp;
attDesc.stencilStoreOp = attDesc.storeOp;
attDesc.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED;
attDesc.finalLayout = VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL;
rpD->attDescs.append(attDesc);
}
rpD->dsResolveRef = { uint32_t(rpD->attDescs.size() - 1), VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL };
// rpD->subpassDeps stays empty: don't yet know the correct initial/final
// access and stage stuff for the implicit deps at this point, so leave it
// to the resource tracking and activateTextureRenderTarget() to generate
@ -1552,10 +1741,31 @@ bool QRhiVulkan::createOffscreenRenderPass(QVkRenderPassDescriptor *rpD,
if (!multiViewHelper.prepare(&rpInfo, multiViewCount, caps.multiView))
return false;
VkResult err = df->vkCreateRenderPass(dev, &rpInfo, nullptr, &rpD->rp);
if (err != VK_SUCCESS) {
qWarning("Failed to create renderpass: %d", err);
return false;
#ifdef VK_KHR_create_renderpass2
if (rpD->hasDepthStencilResolve && caps.renderPass2KHR) {
// Use the KHR extension, not the 1.2 core API, in order to support Vulkan 1.1.
VkRenderPassCreateInfo2KHR rpInfo2;
RenderPass2SetupHelper rp2Helper;
if (!rp2Helper.prepare(&rpInfo2, &rpInfo, rpD, multiViewCount))
return false;
VkResult err = vkCreateRenderPass2KHR(dev, &rpInfo2, nullptr, &rpD->rp);
if (err != VK_SUCCESS) {
qWarning("Failed to create renderpass (using VkRenderPassCreateInfo2KHR): %d", err);
return false;
}
} else
#endif
{
if (rpD->hasDepthStencilResolve) {
qWarning("Resolving multisample depth-stencil buffers is not supported without "
"VK_KHR_depth_stencil_resolve and VK_KHR_create_renderpass2");
}
VkResult err = df->vkCreateRenderPass(dev, &rpInfo, nullptr, &rpD->rp);
if (err != VK_SUCCESS) {
qWarning("Failed to create renderpass: %d", err);
return false;
}
}
return true;
@ -2487,6 +2697,13 @@ void QRhiVulkan::activateTextureRenderTarget(QVkCommandBuffer *cbD, QVkTextureRe
QRhiPassResourceTracker::TexDepthOutputStage);
depthTexD->lastActiveFrameSlot = currentFrameSlot;
}
if (rtD->m_desc.depthResolveTexture()) {
QVkTexture *depthResolveTexD = QRHI_RES(QVkTexture, rtD->m_desc.depthResolveTexture());
trackedRegisterTexture(&passResTracker, depthResolveTexD,
QRhiPassResourceTracker::TexDepthOutput,
QRhiPassResourceTracker::TexDepthOutputStage);
depthResolveTexD->lastActiveFrameSlot = currentFrameSlot;
}
}
void QRhiVulkan::resourceUpdate(QRhiCommandBuffer *cb, QRhiResourceUpdateBatch *resourceUpdates)
@ -2628,6 +2845,11 @@ void QRhiVulkan::beginPass(QRhiCommandBuffer *cb,
float(colorClearValue.alphaF()) } };
cvs.append(cv);
}
for (int i = 0; i < rtD->dsResolveAttCount; ++i) {
VkClearValue cv;
cv.depthStencil = { depthStencilClearValue.depthClearValue(), depthStencilClearValue.stencilClearValue() };
cvs.append(cv);
}
rpBeginInfo.clearValueCount = uint32_t(cvs.size());
QVkCommandBuffer::Command &cmd(cbD->commands.get());
@ -3931,6 +4153,7 @@ void QRhiVulkan::executeDeferredReleases(bool forced)
df->vkDestroyImageView(dev, e.textureRenderTarget.resrtv[att], nullptr);
}
df->vkDestroyImageView(dev, e.textureRenderTarget.dsv, nullptr);
df->vkDestroyImageView(dev, e.textureRenderTarget.resdsv, nullptr);
break;
case QRhiVulkan::DeferredReleaseEntry::RenderPass:
df->vkDestroyRenderPass(dev, e.renderPass.rp, nullptr);
@ -4592,6 +4815,8 @@ bool QRhiVulkan::isFeatureSupported(QRhi::Feature feature) const
return caps.multiView;
case QRhi::TextureViewFormat:
return true;
case QRhi::ResolveDepthStencil:
return caps.renderPass2KHR && caps.depthStencilResolveKHR;
default:
Q_UNREACHABLE_RETURN(false);
}
@ -6590,7 +6815,7 @@ bool QVkSampler::create()
QVkRenderPassDescriptor::QVkRenderPassDescriptor(QRhiImplementation *rhi)
: QRhiRenderPassDescriptor(rhi)
{
serializedFormatData.reserve(32);
serializedFormatData.reserve(64);
}
QVkRenderPassDescriptor::~QVkRenderPassDescriptor()
@ -6653,6 +6878,8 @@ bool QVkRenderPassDescriptor::isCompatible(const QRhiRenderPassDescriptor *other
return false;
if (hasDepthStencil != o->hasDepthStencil)
return false;
if (hasDepthStencilResolve != o->hasDepthStencilResolve)
return false;
if (multiViewCount != o->multiViewCount)
return false;
@ -6680,6 +6907,14 @@ bool QVkRenderPassDescriptor::isCompatible(const QRhiRenderPassDescriptor *other
return false;
}
if (hasDepthStencilResolve) {
const uint32_t attIdx = dsResolveRef.attachment;
if (attIdx != o->dsResolveRef.attachment)
return false;
if (attIdx != VK_ATTACHMENT_UNUSED && !attachmentDescriptionEquals(attDescs[attIdx], o->attDescs[attIdx]))
return false;
}
// subpassDeps is not included
return true;
@ -6694,6 +6929,7 @@ void QVkRenderPassDescriptor::updateSerializedFormat()
*p++ = colorRefs.size();
*p++ = resolveRefs.size();
*p++ = hasDepthStencil;
*p++ = hasDepthStencilResolve;
*p++ = multiViewCount;
auto serializeAttachmentData = [this, &p](uint32_t attIdx) {
@ -6726,6 +6962,12 @@ void QVkRenderPassDescriptor::updateSerializedFormat()
*p++ = attIdx;
serializeAttachmentData(attIdx);
}
if (hasDepthStencilResolve) {
const uint32_t attIdx = dsResolveRef.attachment;
*p++ = attIdx;
serializeAttachmentData(attIdx);
}
}
QRhiRenderPassDescriptor *QVkRenderPassDescriptor::newCompatibleRenderPassDescriptor() const
@ -6738,8 +6980,10 @@ QRhiRenderPassDescriptor *QVkRenderPassDescriptor::newCompatibleRenderPassDescri
rpD->resolveRefs = resolveRefs;
rpD->subpassDeps = subpassDeps;
rpD->hasDepthStencil = hasDepthStencil;
rpD->hasDepthStencilResolve = hasDepthStencilResolve;
rpD->multiViewCount = multiViewCount;
rpD->dsRef = dsRef;
rpD->dsResolveRef = dsResolveRef;
VkRenderPassCreateInfo rpInfo;
VkSubpassDescription subpassDesc;
@ -6842,6 +7086,8 @@ void QVkTextureRenderTarget::destroy()
e.textureRenderTarget.dsv = dsv;
dsv = VK_NULL_HANDLE;
e.textureRenderTarget.resdsv = resdsv;
resdsv = VK_NULL_HANDLE;
QRHI_RES_RHI(QRhiVulkan);
if (rhiD) {
@ -6861,9 +7107,10 @@ QRhiRenderPassDescriptor *QVkTextureRenderTarget::newCompatibleRenderPassDescrip
m_desc.cendColorAttachments(),
m_flags.testFlag(QRhiTextureRenderTarget::PreserveColorContents),
m_flags.testFlag(QRhiTextureRenderTarget::PreserveDepthStencilContents),
m_desc.depthTexture() && !m_flags.testFlag(DoNotStoreDepthStencilContents),
m_desc.depthTexture() && !m_flags.testFlag(DoNotStoreDepthStencilContents) && !m_desc.depthResolveTexture(),
m_desc.depthStencilBuffer(),
m_desc.depthTexture()))
m_desc.depthTexture(),
m_desc.depthResolveTexture()))
{
delete rp;
return nullptr;
@ -7010,6 +7257,36 @@ bool QVkTextureRenderTarget::create()
}
}
if (m_desc.depthResolveTexture()) {
QVkTexture *resTexD = QRHI_RES(QVkTexture, m_desc.depthResolveTexture());
Q_ASSERT(resTexD->flags().testFlag(QRhiTexture::RenderTarget));
VkImageViewCreateInfo viewInfo = {};
viewInfo.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO;
viewInfo.image = resTexD->image;
viewInfo.viewType = d.multiViewCount ? VK_IMAGE_VIEW_TYPE_2D_ARRAY
: VK_IMAGE_VIEW_TYPE_2D;
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;
viewInfo.components.a = VK_COMPONENT_SWIZZLE_A;
viewInfo.subresourceRange.aspectMask = VK_IMAGE_ASPECT_DEPTH_BIT | VK_IMAGE_ASPECT_STENCIL_BIT;
viewInfo.subresourceRange.baseMipLevel = 0;
viewInfo.subresourceRange.levelCount = 1;
viewInfo.subresourceRange.baseArrayLayer = 0;
viewInfo.subresourceRange.layerCount = qMax<uint32_t>(1, d.multiViewCount);
VkResult err = rhiD->df->vkCreateImageView(rhiD->dev, &viewInfo, nullptr, &resdsv);
if (err != VK_SUCCESS) {
qWarning("Failed to create render target depth resolve image view: %d", err);
return false;
}
views.append(resdsv);
d.dsResolveAttCount = 1;
} else {
d.dsResolveAttCount = 0;
}
if (!m_renderPassDesc)
qWarning("QVkTextureRenderTarget: No renderpass descriptor set. See newCompatibleRenderPassDescriptor() and setRenderPassDescriptor().");
@ -7019,7 +7296,7 @@ bool QVkTextureRenderTarget::create()
VkFramebufferCreateInfo fbInfo = {};
fbInfo.sType = VK_STRUCTURE_TYPE_FRAMEBUFFER_CREATE_INFO;
fbInfo.renderPass = d.rp->rp;
fbInfo.attachmentCount = uint32_t(d.colorAttCount + d.dsAttCount + d.resolveAttCount);
fbInfo.attachmentCount = uint32_t(d.colorAttCount + d.dsAttCount + d.resolveAttCount + d.dsResolveAttCount);
fbInfo.pAttachments = views.constData();
fbInfo.width = uint32_t(d.pixelSize.width());
fbInfo.height = uint32_t(d.pixelSize.height());
@ -7870,6 +8147,7 @@ bool QVkSwapChain::createOrResize()
rtWrapper.d.dsAttCount = 0;
ds = nullptr;
}
rtWrapper.d.dsResolveAttCount = 0;
if (samples > VK_SAMPLE_COUNT_1_BIT)
rtWrapper.d.resolveAttCount = 1;
else
@ -7886,7 +8164,7 @@ bool QVkSwapChain::createOrResize()
VkFramebufferCreateInfo fbInfo = {};
fbInfo.sType = VK_STRUCTURE_TYPE_FRAMEBUFFER_CREATE_INFO;
fbInfo.renderPass = rtWrapper.d.rp->rp;
fbInfo.attachmentCount = uint32_t(rtWrapper.d.colorAttCount + rtWrapper.d.dsAttCount + rtWrapper.d.resolveAttCount);
fbInfo.attachmentCount = uint32_t(rtWrapper.d.colorAttCount + rtWrapper.d.dsAttCount + rtWrapper.d.resolveAttCount + rtWrapper.d.dsResolveAttCount);
fbInfo.pAttachments = views;
fbInfo.width = uint32_t(pixelSize.width());
fbInfo.height = uint32_t(pixelSize.height());
@ -7916,6 +8194,7 @@ bool QVkSwapChain::createOrResize()
rtWrapperRight.d.dsAttCount = 0;
ds = nullptr;
}
rtWrapperRight.d.dsResolveAttCount = 0;
if (samples > VK_SAMPLE_COUNT_1_BIT)
rtWrapperRight.d.resolveAttCount = 1;
else
@ -7934,7 +8213,7 @@ bool QVkSwapChain::createOrResize()
fbInfo.sType = VK_STRUCTURE_TYPE_FRAMEBUFFER_CREATE_INFO;
fbInfo.renderPass = rtWrapperRight.d.rp->rp;
fbInfo.attachmentCount = uint32_t(rtWrapperRight.d.colorAttCount + rtWrapperRight.d.dsAttCount
+ rtWrapperRight.d.resolveAttCount);
+ rtWrapperRight.d.resolveAttCount + rtWrapperRight.d.dsResolveAttCount);
fbInfo.pAttachments = views;
fbInfo.width = uint32_t(pixelSize.width());
fbInfo.height = uint32_t(pixelSize.height());

View File

@ -164,8 +164,10 @@ struct QVkRenderPassDescriptor : public QRhiRenderPassDescriptor
QVarLengthArray<VkAttachmentReference, 8> resolveRefs;
QVarLengthArray<VkSubpassDependency, 2> subpassDeps;
bool hasDepthStencil = false;
bool hasDepthStencilResolve = false;
uint32_t multiViewCount = 0;
VkAttachmentReference dsRef;
VkAttachmentReference dsResolveRef;
QVector<quint32> serializedFormatData;
QRhiVulkanRenderPassNativeHandles nativeHandlesStruct;
int lastActiveFrameSlot = -1;
@ -181,6 +183,7 @@ struct QVkRenderTargetData
int colorAttCount = 0;
int dsAttCount = 0;
int resolveAttCount = 0;
int dsResolveAttCount = 0;
int multiViewCount = 0;
QRhiRenderTargetAttachmentTracker::ResIdList currentResIdList;
static const int MAX_COLOR_ATTACHMENTS = 8;
@ -216,6 +219,7 @@ struct QVkTextureRenderTarget : public QRhiTextureRenderTarget
VkImageView rtv[QVkRenderTargetData::MAX_COLOR_ATTACHMENTS];
VkImageView dsv = VK_NULL_HANDLE;
VkImageView resrtv[QVkRenderTargetData::MAX_COLOR_ATTACHMENTS];
VkImageView resdsv = VK_NULL_HANDLE;
int lastActiveFrameSlot = -1;
friend class QRhiVulkan;
};
@ -775,7 +779,8 @@ public:
bool preserveDs,
bool storeDs,
QRhiRenderBuffer *depthStencilBuffer,
QRhiTexture *depthTexture);
QRhiTexture *depthTexture,
QRhiTexture *depthResolveTexture);
bool ensurePipelineCache(const void *initialData = nullptr, size_t initialDataSize = 0);
VkShaderModule createShader(const QByteArray &spirv);
@ -876,6 +881,10 @@ public:
PFN_vkGetPhysicalDeviceSurfaceFormatsKHR vkGetPhysicalDeviceSurfaceFormatsKHR;
PFN_vkGetPhysicalDeviceSurfacePresentModesKHR vkGetPhysicalDeviceSurfacePresentModesKHR;
#ifdef VK_KHR_create_renderpass2
PFN_vkCreateRenderPass2KHR vkCreateRenderPass2KHR = nullptr;
#endif
struct {
bool compute = false;
bool wideLines = false;
@ -886,6 +895,8 @@ public:
bool geometryShader = false;
bool nonFillPolygonMode = false;
bool multiView = false;
bool renderPass2KHR = false;
bool depthStencilResolveKHR = false;
QVersionNumber apiVersion;
} caps;
@ -1001,6 +1012,7 @@ public:
VkImageView rtv[QVkRenderTargetData::MAX_COLOR_ATTACHMENTS];
VkImageView resrtv[QVkRenderTargetData::MAX_COLOR_ATTACHMENTS];
VkImageView dsv;
VkImageView resdsv;
} textureRenderTarget;
struct {
VkRenderPass rp;

View File

@ -425,7 +425,8 @@ void tst_QRhi::create()
QRhi::RenderToOneDimensionalTexture,
QRhi::ThreeDimensionalTextureMipmaps,
QRhi::MultiView,
QRhi::TextureViewFormat
QRhi::TextureViewFormat,
QRhi::ResolveDepthStencil
};
for (size_t i = 0; i <sizeof(features) / sizeof(QRhi::Feature); ++i)
rhi->isFeatureSupported(features[i]);

View File

@ -33,6 +33,7 @@ add_subdirectory(tex1d)
add_subdirectory(displacement)
add_subdirectory(imguirenderer)
add_subdirectory(multiview)
add_subdirectory(msaatextureresolve)
if(QT_FEATURE_widgets)
add_subdirectory(rhiwidgetproto)
endif()

View File

@ -0,0 +1,41 @@
# Copyright (C) 2024 The Qt Company Ltd.
# SPDX-License-Identifier: BSD-3-Clause
if(NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT)
cmake_minimum_required(VERSION 3.16)
project(msaatextureresolve LANGUAGES CXX)
find_package(Qt6BuildInternals REQUIRED COMPONENTS STANDALONE_TEST)
endif()
qt_internal_add_manual_test(msaatextureresolve
GUI
SOURCES
msaatextureresolve.cpp
LIBRARIES
Qt::Gui
Qt::GuiPrivate
)
# Resources:
set_source_files_properties("../shared/color.frag.qsb"
PROPERTIES QT_RESOURCE_ALIAS "color.frag.qsb"
)
set_source_files_properties("../shared/color.vert.qsb"
PROPERTIES QT_RESOURCE_ALIAS "color.vert.qsb"
)
set_source_files_properties("../shared/texture.frag.qsb"
PROPERTIES QT_RESOURCE_ALIAS "texture.frag.qsb"
)
set_source_files_properties("../shared/texture.vert.qsb"
PROPERTIES QT_RESOURCE_ALIAS "texture.vert.qsb"
)
qt_internal_add_resource(msaatextureresolve "msaatextureresolve"
PREFIX
"/"
FILES
"../shared/color.frag.qsb"
"../shared/color.vert.qsb"
"../shared/texture.frag.qsb"
"../shared/texture.vert.qsb"
)

View File

@ -0,0 +1,235 @@
// Copyright (C) 2024 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#include "../shared/examplefw.h"
// Uses a multisample texture both for color and depth-stencil, renders into
// those, and then resolves into non-multisample textures. Also the
// depth-stencil, in order to exercise that rarely used path. If that is
// functional, will not be visible on-screen. Frame captures with tools such as
// RenderDoc can be used to verify that there is indeed a non-multisample depth
// texture generated.
static float vertexData[] =
{ // Y up, CCW
-0.5f, 0.5f, 0.0f, 0.0f,
-0.5f, -0.5f, 0.0f, 1.0f,
0.5f, -0.5f, 1.0f, 1.0f,
0.5f, 0.5f, 1.0f, 0.0f
};
static quint16 indexData[] =
{
0, 1, 2, 0, 2, 3
};
static float triangleData[] =
{ // Y up, CCW
0.0f, 0.5f, 1.0f, 0.0f, 0.0f,
-0.5f, -0.5f, 0.0f, 1.0f, 0.0f,
0.5f, -0.5f, 0.0f, 0.0f, 1.0f,
};
struct {
QList<QRhiResource *> releasePool;
QRhiBuffer *vbuf = nullptr;
QRhiBuffer *ibuf = nullptr;
QRhiBuffer *ubuf = nullptr;
QRhiTexture *msaaColorTexture = nullptr;
QRhiTexture *msaaDepthTexture = nullptr;
QRhiTextureRenderTarget *rt = nullptr;
QRhiRenderPassDescriptor *rtRp = nullptr;
QRhiTexture *tex = nullptr;
QRhiTexture *depthTex = nullptr;
QRhiSampler *sampler = nullptr;
QRhiBuffer *triUbuf = nullptr;
QRhiShaderResourceBindings *triSrb = nullptr;
QRhiGraphicsPipeline *triPs = nullptr;
QRhiShaderResourceBindings *srb = nullptr;
QRhiGraphicsPipeline *ps = nullptr;
QRhiResourceUpdateBatch *initialUpdates = nullptr;
QMatrix4x4 triBaseMvp;
float triRot = 0;
QMatrix4x4 winProj;
} d;
void Window::customInit()
{
if (!m_r->isFeatureSupported(QRhi::MultisampleTexture))
qFatal("Multisample textures not supported by this backend");
// Skip the check for ResolveDepthStencil, and let it run regardless. When
// not supported, it won't be functional, but should be handled somewhat
// gracefully.
d.vbuf = m_r->newBuffer(QRhiBuffer::Immutable, QRhiBuffer::VertexBuffer, sizeof(vertexData) + sizeof(triangleData));
d.vbuf->create();
d.releasePool << d.vbuf;
d.ibuf = m_r->newBuffer(QRhiBuffer::Immutable, QRhiBuffer::IndexBuffer, sizeof(indexData));
d.ibuf->create();
d.releasePool << d.ibuf;
d.ubuf = m_r->newBuffer(QRhiBuffer::Dynamic, QRhiBuffer::UniformBuffer, 68);
d.ubuf->create();
d.releasePool << d.ubuf;
d.msaaColorTexture = m_r->newTexture(QRhiTexture::RGBA8, QSize(512, 512), 4, QRhiTexture::RenderTarget); // 4x MSAA
d.msaaColorTexture->create();
d.releasePool << d.msaaColorTexture;
d.msaaDepthTexture = m_r->newTexture(QRhiTexture::D24S8, QSize(512, 512), 4, QRhiTexture::RenderTarget); // 4x MSAA
d.msaaDepthTexture->create();
d.releasePool << d.msaaDepthTexture;
// the non-msaa texture that will be the destination in the resolve
d.tex = m_r->newTexture(QRhiTexture::RGBA8, d.msaaColorTexture->pixelSize(), 1, QRhiTexture::RenderTarget);
d.releasePool << d.tex;
d.tex->create();
d.depthTex = m_r->newTexture(QRhiTexture::D24S8, d.msaaDepthTexture->pixelSize(), 1, QRhiTexture::RenderTarget);
d.releasePool << d.depthTex;
d.depthTex->create();
QRhiTextureRenderTargetDescription rtDesc;
QRhiColorAttachment rtAtt(d.msaaColorTexture);
rtAtt.setResolveTexture(d.tex);
rtDesc.setColorAttachments({ rtAtt });
rtDesc.setDepthTexture(d.msaaDepthTexture);
rtDesc.setDepthResolveTexture(d.depthTex);
d.rt = m_r->newTextureRenderTarget(rtDesc);
d.releasePool << d.rt;
d.rtRp = d.rt->newCompatibleRenderPassDescriptor();
d.releasePool << d.rtRp;
d.rt->setRenderPassDescriptor(d.rtRp);
d.rt->create();
d.triUbuf = m_r->newBuffer(QRhiBuffer::Dynamic, QRhiBuffer::UniformBuffer, 68);
d.releasePool << d.triUbuf;
d.triUbuf->create();
d.triSrb = m_r->newShaderResourceBindings();
d.releasePool << d.triSrb;
d.triSrb->setBindings({
QRhiShaderResourceBinding::uniformBuffer(0, QRhiShaderResourceBinding::VertexStage | QRhiShaderResourceBinding::FragmentStage, d.triUbuf)
});
d.triSrb->create();
d.triPs = m_r->newGraphicsPipeline();
d.releasePool << d.triPs;
d.triPs->setDepthTest(true);
d.triPs->setDepthWrite(true);
d.triPs->setSampleCount(4); // must match the render target
d.triPs->setShaderStages({
{ QRhiShaderStage::Vertex, getShader(QLatin1String(":/color.vert.qsb")) },
{ QRhiShaderStage::Fragment, getShader(QLatin1String(":/color.frag.qsb")) }
});
QRhiVertexInputLayout inputLayout;
inputLayout.setBindings({
{ 5 * sizeof(float) }
});
inputLayout.setAttributes({
{ 0, 0, QRhiVertexInputAttribute::Float2, 0 },
{ 0, 1, QRhiVertexInputAttribute::Float3, quint32(2 * sizeof(float)) }
});
d.triPs->setVertexInputLayout(inputLayout);
d.triPs->setShaderResourceBindings(d.triSrb);
d.triPs->setRenderPassDescriptor(d.rtRp);
d.triPs->create();
d.sampler = m_r->newSampler(QRhiSampler::Linear, QRhiSampler::Linear, QRhiSampler::None,
QRhiSampler::ClampToEdge, QRhiSampler::ClampToEdge);
d.releasePool << d.sampler;
d.sampler->create();
d.srb = m_r->newShaderResourceBindings();
d.releasePool << d.srb;
d.srb->setBindings({
QRhiShaderResourceBinding::uniformBuffer(0, QRhiShaderResourceBinding::VertexStage | QRhiShaderResourceBinding::FragmentStage, d.ubuf),
QRhiShaderResourceBinding::sampledTexture(1, QRhiShaderResourceBinding::FragmentStage, d.tex, d.sampler)
});
d.srb->create();
d.ps = m_r->newGraphicsPipeline();
d.releasePool << d.ps;
d.ps->setShaderStages({
{ QRhiShaderStage::Vertex, getShader(QLatin1String(":/texture.vert.qsb")) },
{ QRhiShaderStage::Fragment, getShader(QLatin1String(":/texture.frag.qsb")) }
});
inputLayout.setBindings({
{ 4 * sizeof(float) }
});
inputLayout.setAttributes({
{ 0, 0, QRhiVertexInputAttribute::Float2, 0 },
{ 0, 1, QRhiVertexInputAttribute::Float2, quint32(2 * sizeof(float)) }
});
d.ps->setVertexInputLayout(inputLayout);
d.ps->setShaderResourceBindings(d.srb);
d.ps->setRenderPassDescriptor(m_rp);
d.ps->create();
d.initialUpdates = m_r->nextResourceUpdateBatch();
d.initialUpdates->uploadStaticBuffer(d.vbuf, 0, sizeof(vertexData), vertexData);
d.initialUpdates->uploadStaticBuffer(d.vbuf, sizeof(vertexData), sizeof(triangleData), triangleData);
d.initialUpdates->uploadStaticBuffer(d.ibuf, indexData);
d.triBaseMvp = m_r->clipSpaceCorrMatrix();
d.triBaseMvp.perspective(45.0f, d.msaaColorTexture->pixelSize().width() / float(d.msaaColorTexture->pixelSize().height()), 0.01f, 1000.0f);
d.triBaseMvp.translate(0, 0, -2);
float opacity = 1.0f;
d.initialUpdates->updateDynamicBuffer(d.triUbuf, 64, 4, &opacity);
qint32 flip = m_r->isYUpInFramebuffer() ? 1 : 0;
d.initialUpdates->updateDynamicBuffer(d.ubuf, 64, 4, &flip);
}
void Window::customRelease()
{
qDeleteAll(d.releasePool);
d.releasePool.clear();
}
void Window::customRender()
{
QRhiCommandBuffer *cb = m_sc->currentFrameCommandBuffer();
QRhiResourceUpdateBatch *u = m_r->nextResourceUpdateBatch();
if (d.initialUpdates) {
u->merge(d.initialUpdates);
d.initialUpdates->release();
d.initialUpdates = nullptr;
}
QMatrix4x4 triMvp = d.triBaseMvp;
triMvp.rotate(d.triRot, 0, 1, 0);
d.triRot += 1;
u->updateDynamicBuffer(d.triUbuf, 0, 64, triMvp.constData());
if (d.winProj != m_proj) {
d.winProj = m_proj;
QMatrix4x4 mvp = m_proj;
mvp.scale(2.5f);
u->updateDynamicBuffer(d.ubuf, 0, 64, mvp.constData());
}
// offscreen (triangle, msaa)
cb->beginPass(d.rt, QColor::fromRgbF(0.5f, 0.2f, 0.0f, 1.0f), { 1.0f, 0 }, u);
cb->setGraphicsPipeline(d.triPs);
cb->setViewport({ 0, 0, float(d.msaaColorTexture->pixelSize().width()), float(d.msaaColorTexture->pixelSize().height()) });
cb->setShaderResources();
QRhiCommandBuffer::VertexInput vbufBinding(d.vbuf, quint32(sizeof(vertexData)));
cb->setVertexInput(0, 1, &vbufBinding);
cb->draw(3);
cb->endPass();
// onscreen (quad)
const QSize outputSizeInPixels = m_sc->currentPixelSize();
cb->beginPass(m_sc->currentFrameRenderTarget(), m_clearColor, { 1.0f, 0 });
cb->setGraphicsPipeline(d.ps);
cb->setViewport({ 0, 0, float(outputSizeInPixels.width()), float(outputSizeInPixels.height()) });
cb->setShaderResources();
vbufBinding.second = 0;
cb->setVertexInput(0, 1, &vbufBinding, d.ibuf, 0, QRhiCommandBuffer::IndexUInt16);
cb->drawIndexed(6);
cb->endPass();
}