Merge "rhi: Make it possible to clone a QRhiRenderPassDescriptor"

This commit is contained in:
Laszlo Agocs 2021-03-16 16:33:27 +01:00 committed by Qt CI Bot
commit 98e4652a96
13 changed files with 191 additions and 64 deletions

View File

@ -158,10 +158,12 @@ Q_LOGGING_CATEGORY(QRHI_LOG_INFO, "qt.rhi.general")
native resources. That is only done when calling the \c create() function of a
subclass, for example, QRhiBuffer::create() or QRhiTexture::create().
\li The exception is
QRhiTextureRenderTarget::newCompatibleRenderPassDescriptor() and
QRhiSwapChain::newCompatibleRenderPassDescriptor(). There is no \c create()
operation for these and the returned object is immediately active.
\li The exceptions are
QRhiTextureRenderTarget::newCompatibleRenderPassDescriptor(),
QRhiSwapChain::newCompatibleRenderPassDescriptor(), and
QRhiRenderPassDescriptor::newCompatibleRenderPassDescriptor(). There is no
\c create() operation for these and the returned object is immediately
active.
\li The resource objects themselves are treated as immutable: once a
resource has create() called, changing any parameters via the setters, such as,
@ -2605,7 +2607,7 @@ QRhiResource::Type QRhiRenderPassDescriptor::resourceType() const
}
/*!
\fn bool QRhiRenderPassDescriptor::isCompatible(const QRhiRenderPassDescriptor *other) const;
\fn bool QRhiRenderPassDescriptor::isCompatible(const QRhiRenderPassDescriptor *other) const
\return true if the \a other QRhiRenderPassDescriptor is compatible with
this one, meaning \c this and \a other can be used interchangebly in
@ -2620,6 +2622,34 @@ QRhiResource::Type QRhiRenderPassDescriptor::resourceType() const
allowing a different QRhiRenderPassDescriptor and
QRhiShaderResourceBindings to be used in combination with the pipeline, as
long as they are compatible.
The exact details of compatibility depend on the underlying graphics API.
Two renderpass descriptors
\l{QRhiTextureRenderTarget::newCompatibleRenderPassDescriptor()}{created}
from the same QRhiTextureRenderTarget are always compatible.
\sa newCompatibleRenderPassDescriptor()
*/
/*!
\fn QRhiRenderPassDescriptor *QRhiRenderPassDescriptor::newCompatibleRenderPassDescriptor() const
\return a new QRhiRenderPassDescriptor that is
\l{isCompatible()}{compatible} with this one.
This function allows cloning a QRhiRenderPassDescriptor. The returned
object is ready to be used, and the ownership is transferred to the caller.
Cloning a QRhiRenderPassDescriptor object can become useful in situations
where the object is stored in data structures related to graphics pipelines
(in order to allow creating new pipelines which in turn requires a
renderpass descriptor object), and the lifetime of the renderpass
descriptor created from a render target may be shorter than the pipelines.
(for example, because the engine manages and destroys renderpasses together
with the textures and render targets it was created from) In such a
situation, it can be beneficial to store a cloned version in the data
structures, and thus transferring ownership as well.
\sa isCompatible()
*/
/*!

View File

@ -951,6 +951,8 @@ public:
virtual bool isCompatible(const QRhiRenderPassDescriptor *other) const = 0;
virtual const QRhiNativeHandles *nativeHandles();
virtual QRhiRenderPassDescriptor *newCompatibleRenderPassDescriptor() const = 0;
protected:
QRhiRenderPassDescriptor(QRhiImplementation *rhi);
};

View File

@ -3281,6 +3281,11 @@ bool QD3D11RenderPassDescriptor::isCompatible(const QRhiRenderPassDescriptor *ot
return true;
}
QRhiRenderPassDescriptor *QD3D11RenderPassDescriptor::newCompatibleRenderPassDescriptor() const
{
return new QD3D11RenderPassDescriptor(m_rhi);
}
QD3D11ReferenceRenderTarget::QD3D11ReferenceRenderTarget(QRhiImplementation *rhi)
: QRhiRenderTarget(rhi),
d(rhi)

View File

@ -143,6 +143,7 @@ struct QD3D11RenderPassDescriptor : public QRhiRenderPassDescriptor
~QD3D11RenderPassDescriptor();
void destroy() override;
bool isCompatible(const QRhiRenderPassDescriptor *other) const override;
QRhiRenderPassDescriptor *newCompatibleRenderPassDescriptor() const override;
};
struct QD3D11RenderTargetData

View File

@ -4549,6 +4549,11 @@ bool QGles2RenderPassDescriptor::isCompatible(const QRhiRenderPassDescriptor *ot
return true;
}
QRhiRenderPassDescriptor *QGles2RenderPassDescriptor::newCompatibleRenderPassDescriptor() const
{
return new QGles2RenderPassDescriptor(m_rhi);
}
QGles2ReferenceRenderTarget::QGles2ReferenceRenderTarget(QRhiImplementation *rhi)
: QRhiRenderTarget(rhi),
d(rhi)

View File

@ -193,6 +193,7 @@ struct QGles2RenderPassDescriptor : public QRhiRenderPassDescriptor
~QGles2RenderPassDescriptor();
void destroy() override;
bool isCompatible(const QRhiRenderPassDescriptor *other) const override;
QRhiRenderPassDescriptor *newCompatibleRenderPassDescriptor() const override;
};
struct QGles2RenderTargetData

View File

@ -2900,6 +2900,16 @@ bool QMetalRenderPassDescriptor::isCompatible(const QRhiRenderPassDescriptor *ot
return true;
}
QRhiRenderPassDescriptor *QMetalRenderPassDescriptor::newCompatibleRenderPassDescriptor() const
{
QMetalRenderPassDescriptor *rp = new QMetalRenderPassDescriptor(m_rhi);
rp->colorAttachmentCount = colorAttachmentCount;
rp->hasDepthStencil = hasDepthStencil;
memcpy(rp->colorFormat, colorFormat, sizeof(colorFormat));
rp->dsFormat = dsFormat;
return rp;
}
QMetalReferenceRenderTarget::QMetalReferenceRenderTarget(QRhiImplementation *rhi)
: QRhiRenderTarget(rhi),
d(new QMetalRenderTargetData)

View File

@ -145,6 +145,7 @@ struct QMetalRenderPassDescriptor : public QRhiRenderPassDescriptor
~QMetalRenderPassDescriptor();
void destroy() override;
bool isCompatible(const QRhiRenderPassDescriptor *other) const override;
QRhiRenderPassDescriptor *newCompatibleRenderPassDescriptor() const override;
// there is no MTLRenderPassDescriptor here as one will be created for each pass in beginPass()

View File

@ -733,6 +733,11 @@ bool QNullRenderPassDescriptor::isCompatible(const QRhiRenderPassDescriptor *oth
return true;
}
QRhiRenderPassDescriptor *QNullRenderPassDescriptor::newCompatibleRenderPassDescriptor() const
{
return new QNullRenderPassDescriptor(m_rhi);
}
QNullReferenceRenderTarget::QNullReferenceRenderTarget(QRhiImplementation *rhi)
: QRhiRenderTarget(rhi),
d(rhi)

View File

@ -105,6 +105,7 @@ struct QNullRenderPassDescriptor : public QRhiRenderPassDescriptor
~QNullRenderPassDescriptor();
void destroy() override;
bool isCompatible(const QRhiRenderPassDescriptor *other) const override;
QRhiRenderPassDescriptor *newCompatibleRenderPassDescriptor() const override;
};
struct QNullRenderTargetData

View File

@ -1174,6 +1174,28 @@ VkFormat QRhiVulkan::optimalDepthStencilFormat()
return optimalDsFormat;
}
static void fillRenderPassCreateInfo(VkRenderPassCreateInfo *rpInfo,
VkSubpassDescription *subpassDesc,
QVkRenderPassDescriptor *rpD)
{
memset(subpassDesc, 0, sizeof(VkSubpassDescription));
subpassDesc->pipelineBindPoint = VK_PIPELINE_BIND_POINT_GRAPHICS;
subpassDesc->colorAttachmentCount = uint32_t(rpD->colorRefs.count());
Q_ASSERT(rpD->colorRefs.count() == rpD->resolveRefs.count());
subpassDesc->pColorAttachments = !rpD->colorRefs.isEmpty() ? rpD->colorRefs.constData() : nullptr;
subpassDesc->pDepthStencilAttachment = rpD->hasDepthStencil ? &rpD->dsRef : nullptr;
subpassDesc->pResolveAttachments = !rpD->resolveRefs.isEmpty() ? rpD->resolveRefs.constData() : nullptr;
memset(rpInfo, 0, sizeof(VkRenderPassCreateInfo));
rpInfo->sType = VK_STRUCTURE_TYPE_RENDER_PASS_CREATE_INFO;
rpInfo->attachmentCount = uint32_t(rpD->attDescs.count());
rpInfo->pAttachments = rpD->attDescs.constData();
rpInfo->subpassCount = 1;
rpInfo->pSubpasses = subpassDesc;
rpInfo->dependencyCount = uint32_t(rpD->subpassDeps.count());
rpInfo->pDependencies = !rpD->subpassDeps.isEmpty() ? rpD->subpassDeps.constData() : nullptr;
}
bool QRhiVulkan::createDefaultRenderPass(QVkRenderPassDescriptor *rpD, bool hasDepthStencil, VkSampleCountFlagBits samples, VkFormat colorFormat)
{
// attachment list layout is color (1), ds (0-1), resolve (0-1)
@ -1192,6 +1214,8 @@ bool QRhiVulkan::createDefaultRenderPass(QVkRenderPassDescriptor *rpD, bool hasD
rpD->colorRefs.append({ 0, VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL });
rpD->hasDepthStencil = hasDepthStencil;
if (hasDepthStencil) {
// clear on load + no store + lazy alloc + transient image should play
// nicely with tiled GPUs (no physical backing necessary for ds buffer)
@ -1224,53 +1248,33 @@ bool QRhiVulkan::createDefaultRenderPass(QVkRenderPassDescriptor *rpD, bool hasD
rpD->resolveRefs.append({ 2, VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL });
}
VkSubpassDescription subpassDesc;
memset(&subpassDesc, 0, sizeof(subpassDesc));
subpassDesc.pipelineBindPoint = VK_PIPELINE_BIND_POINT_GRAPHICS;
subpassDesc.colorAttachmentCount = 1;
subpassDesc.pColorAttachments = rpD->colorRefs.constData();
subpassDesc.pDepthStencilAttachment = hasDepthStencil ? &rpD->dsRef : nullptr;
// Replace the first implicit dep (TOP_OF_PIPE / ALL_COMMANDS) with our own.
VkSubpassDependency subpassDeps[2];
memset(subpassDeps, 0, sizeof(subpassDeps));
subpassDeps[0].srcSubpass = VK_SUBPASS_EXTERNAL;
subpassDeps[0].dstSubpass = 0;
subpassDeps[0].srcStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT;
subpassDeps[0].dstStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT;
subpassDeps[0].srcAccessMask = 0;
subpassDeps[0].dstAccessMask = VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT;
VkSubpassDependency subpassDep;
memset(&subpassDep, 0, sizeof(subpassDep));
subpassDep.srcSubpass = VK_SUBPASS_EXTERNAL;
subpassDep.dstSubpass = 0;
subpassDep.srcStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT;
subpassDep.dstStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT;
subpassDep.srcAccessMask = 0;
subpassDep.dstAccessMask = VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT;
rpD->subpassDeps.append(subpassDep);
if (hasDepthStencil) {
subpassDeps[1].srcSubpass = VK_SUBPASS_EXTERNAL;
subpassDeps[1].dstSubpass = 0;
subpassDeps[1].srcStageMask = VK_PIPELINE_STAGE_EARLY_FRAGMENT_TESTS_BIT
memset(&subpassDep, 0, sizeof(subpassDep));
subpassDep.srcSubpass = VK_SUBPASS_EXTERNAL;
subpassDep.dstSubpass = 0;
subpassDep.srcStageMask = VK_PIPELINE_STAGE_EARLY_FRAGMENT_TESTS_BIT
| VK_PIPELINE_STAGE_LATE_FRAGMENT_TESTS_BIT;
subpassDeps[1].dstStageMask = VK_PIPELINE_STAGE_EARLY_FRAGMENT_TESTS_BIT
subpassDep.dstStageMask = VK_PIPELINE_STAGE_EARLY_FRAGMENT_TESTS_BIT
| VK_PIPELINE_STAGE_LATE_FRAGMENT_TESTS_BIT;
subpassDeps[1].srcAccessMask = VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT;
subpassDeps[1].dstAccessMask = VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_READ_BIT
subpassDep.srcAccessMask = VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT;
subpassDep.dstAccessMask = VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_READ_BIT
| VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT;
rpD->subpassDeps.append(subpassDep);
}
VkRenderPassCreateInfo rpInfo;
memset(&rpInfo, 0, sizeof(rpInfo));
rpInfo.sType = VK_STRUCTURE_TYPE_RENDER_PASS_CREATE_INFO;
rpInfo.attachmentCount = 1;
rpInfo.pAttachments = rpD->attDescs.constData();
rpInfo.subpassCount = 1;
rpInfo.pSubpasses = &subpassDesc;
rpInfo.dependencyCount = 1;
rpInfo.pDependencies = subpassDeps;
if (hasDepthStencil) {
rpInfo.attachmentCount += 1;
rpInfo.dependencyCount += 1;
}
if (samples > VK_SAMPLE_COUNT_1_BIT) {
rpInfo.attachmentCount += 1;
subpassDesc.pResolveAttachments = rpD->resolveRefs.constData();
}
VkSubpassDescription subpassDesc;
fillRenderPassCreateInfo(&rpInfo, &subpassDesc, rpD);
VkResult err = df->vkCreateRenderPass(dev, &rpInfo, nullptr, &rpD->rp);
if (err != VK_SUCCESS) {
@ -1278,8 +1282,6 @@ bool QRhiVulkan::createDefaultRenderPass(QVkRenderPassDescriptor *rpD, bool hasD
return false;
}
rpD->hasDepthStencil = hasDepthStencil;
return true;
}
@ -1377,25 +1379,14 @@ bool QRhiVulkan::createOffscreenRenderPass(QVkRenderPassDescriptor *rpD,
}
}
VkSubpassDescription subpassDesc;
memset(&subpassDesc, 0, sizeof(subpassDesc));
subpassDesc.pipelineBindPoint = VK_PIPELINE_BIND_POINT_GRAPHICS;
subpassDesc.colorAttachmentCount = uint32_t(rpD->colorRefs.count());
Q_ASSERT(rpD->colorRefs.count() == rpD->resolveRefs.count());
subpassDesc.pColorAttachments = !rpD->colorRefs.isEmpty() ? rpD->colorRefs.constData() : nullptr;
subpassDesc.pDepthStencilAttachment = rpD->hasDepthStencil ? &rpD->dsRef : nullptr;
subpassDesc.pResolveAttachments = !rpD->resolveRefs.isEmpty() ? rpD->resolveRefs.constData() : nullptr;
// 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
// barriers.
VkRenderPassCreateInfo rpInfo;
memset(&rpInfo, 0, sizeof(rpInfo));
rpInfo.sType = VK_STRUCTURE_TYPE_RENDER_PASS_CREATE_INFO;
rpInfo.attachmentCount = uint32_t(rpD->attDescs.count());
rpInfo.pAttachments = rpD->attDescs.constData();
rpInfo.subpassCount = 1;
rpInfo.pSubpasses = &subpassDesc;
// 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 barriers
VkSubpassDescription subpassDesc;
fillRenderPassCreateInfo(&rpInfo, &subpassDesc, rpD);
VkResult err = df->vkCreateRenderPass(dev, &rpInfo, nullptr, &rpD->rp);
if (err != VK_SUCCESS) {
@ -6149,9 +6140,39 @@ bool QVkRenderPassDescriptor::isCompatible(const QRhiRenderPassDescriptor *other
return false;
}
// subpassDeps is not included
return true;
}
QRhiRenderPassDescriptor *QVkRenderPassDescriptor::newCompatibleRenderPassDescriptor() const
{
QVkRenderPassDescriptor *rpD = new QVkRenderPassDescriptor(m_rhi);
rpD->ownsRp = true;
rpD->attDescs = attDescs;
rpD->colorRefs = colorRefs;
rpD->resolveRefs = resolveRefs;
rpD->subpassDeps = subpassDeps;
rpD->hasDepthStencil = hasDepthStencil;
rpD->dsRef = dsRef;
VkRenderPassCreateInfo rpInfo;
VkSubpassDescription subpassDesc;
fillRenderPassCreateInfo(&rpInfo, &subpassDesc, rpD);
QRHI_RES_RHI(QRhiVulkan);
VkResult err = rhiD->df->vkCreateRenderPass(rhiD->dev, &rpInfo, nullptr, &rpD->rp);
if (err != VK_SUCCESS) {
qWarning("Failed to create renderpass: %d", err);
delete rpD;
return nullptr;
}
rhiD->registerResource(rpD);
return rpD;
}
const QRhiNativeHandles *QVkRenderPassDescriptor::nativeHandles()
{
nativeHandlesStruct.renderPass = rp;

View File

@ -185,6 +185,7 @@ struct QVkRenderPassDescriptor : public QRhiRenderPassDescriptor
~QVkRenderPassDescriptor();
void destroy() override;
bool isCompatible(const QRhiRenderPassDescriptor *other) const override;
QRhiRenderPassDescriptor *newCompatibleRenderPassDescriptor() const override;
const QRhiNativeHandles *nativeHandles() override;
VkRenderPass rp = VK_NULL_HANDLE;
@ -192,6 +193,7 @@ struct QVkRenderPassDescriptor : public QRhiRenderPassDescriptor
QVarLengthArray<VkAttachmentDescription, 8> attDescs;
QVarLengthArray<VkAttachmentReference, 8> colorRefs;
QVarLengthArray<VkAttachmentReference, 8> resolveRefs;
QVarLengthArray<VkSubpassDependency, 2> subpassDeps;
bool hasDepthStencil = false;
VkAttachmentReference dsRef;
QRhiVulkanRenderPassNativeHandles nativeHandlesStruct;

View File

@ -97,6 +97,8 @@ private slots:
void srbWithNoResource();
void renderPassDescriptorCompatibility_data();
void renderPassDescriptorCompatibility();
void renderPassDescriptorClone_data();
void renderPassDescriptorClone();
void renderToTextureSimple_data();
void renderToTextureSimple();
@ -3361,7 +3363,7 @@ void tst_QRhi::renderPassDescriptorCompatibility()
QScopedPointer<QRhi> rhi(QRhi::create(impl, initParams, QRhi::Flags(), nullptr));
if (!rhi)
QSKIP("QRhi could not be created, skipping testing texture resource updates");
QSKIP("QRhi could not be created, skipping testing renderpass descriptors");
// Note that checking compatibility is only relevant with backends where
// there is a concept of renderpass descriptions (Vulkan, and partially
@ -3511,6 +3513,47 @@ void tst_QRhi::renderPassDescriptorCompatibility()
}
}
void tst_QRhi::renderPassDescriptorClone_data()
{
rhiTestData();
}
void tst_QRhi::renderPassDescriptorClone()
{
QFETCH(QRhi::Implementation, impl);
QFETCH(QRhiInitParams *, initParams);
QScopedPointer<QRhi> rhi(QRhi::create(impl, initParams, QRhi::Flags(), nullptr));
if (!rhi)
QSKIP("QRhi could not be created, skipping testing renderpass descriptors");
// tex and tex2 have the same format
QScopedPointer<QRhiTexture> tex(rhi->newTexture(QRhiTexture::RGBA8, QSize(512, 512), 1, QRhiTexture::RenderTarget));
QVERIFY(tex->create());
QScopedPointer<QRhiTexture> tex2(rhi->newTexture(QRhiTexture::RGBA8, QSize(512, 512), 1, QRhiTexture::RenderTarget));
QVERIFY(tex2->create());
QScopedPointer<QRhiRenderBuffer> ds(rhi->newRenderBuffer(QRhiRenderBuffer::DepthStencil, QSize(512, 512)));
QVERIFY(ds->create());
QScopedPointer<QRhiTextureRenderTarget> rt(rhi->newTextureRenderTarget({ tex.data() }));
QScopedPointer<QRhiRenderPassDescriptor> rpDesc(rt->newCompatibleRenderPassDescriptor());
rt->setRenderPassDescriptor(rpDesc.data());
QVERIFY(rt->create());
QScopedPointer<QRhiRenderPassDescriptor> rpDescClone(rpDesc->newCompatibleRenderPassDescriptor());
QVERIFY(rpDescClone);
QVERIFY(rpDesc->isCompatible(rpDescClone.data()));
// rt and rt2 have the same set of attachments
QScopedPointer<QRhiTextureRenderTarget> rt2(rhi->newTextureRenderTarget({ tex2.data() }));
QScopedPointer<QRhiRenderPassDescriptor> rpDesc2(rt2->newCompatibleRenderPassDescriptor());
rt2->setRenderPassDescriptor(rpDesc2.data());
QVERIFY(rt2->create());
QVERIFY(rpDesc2->isCompatible(rpDescClone.data()));
}
void tst_QRhi::pipelineCache_data()
{
rhiTestData();