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 native resources. That is only done when calling the \c create() function of a
subclass, for example, QRhiBuffer::create() or QRhiTexture::create(). subclass, for example, QRhiBuffer::create() or QRhiTexture::create().
\li The exception is \li The exceptions are
QRhiTextureRenderTarget::newCompatibleRenderPassDescriptor() and QRhiTextureRenderTarget::newCompatibleRenderPassDescriptor(),
QRhiSwapChain::newCompatibleRenderPassDescriptor(). There is no \c create() QRhiSwapChain::newCompatibleRenderPassDescriptor(), and
operation for these and the returned object is immediately active. 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 \li The resource objects themselves are treated as immutable: once a
resource has create() called, changing any parameters via the setters, such as, 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 \return true if the \a other QRhiRenderPassDescriptor is compatible with
this one, meaning \c this and \a other can be used interchangebly in 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 allowing a different QRhiRenderPassDescriptor and
QRhiShaderResourceBindings to be used in combination with the pipeline, as QRhiShaderResourceBindings to be used in combination with the pipeline, as
long as they are compatible. 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 bool isCompatible(const QRhiRenderPassDescriptor *other) const = 0;
virtual const QRhiNativeHandles *nativeHandles(); virtual const QRhiNativeHandles *nativeHandles();
virtual QRhiRenderPassDescriptor *newCompatibleRenderPassDescriptor() const = 0;
protected: protected:
QRhiRenderPassDescriptor(QRhiImplementation *rhi); QRhiRenderPassDescriptor(QRhiImplementation *rhi);
}; };

View File

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

View File

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

View File

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

View File

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

View File

@ -2900,6 +2900,16 @@ bool QMetalRenderPassDescriptor::isCompatible(const QRhiRenderPassDescriptor *ot
return true; 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) QMetalReferenceRenderTarget::QMetalReferenceRenderTarget(QRhiImplementation *rhi)
: QRhiRenderTarget(rhi), : QRhiRenderTarget(rhi),
d(new QMetalRenderTargetData) d(new QMetalRenderTargetData)

View File

@ -145,6 +145,7 @@ struct QMetalRenderPassDescriptor : public QRhiRenderPassDescriptor
~QMetalRenderPassDescriptor(); ~QMetalRenderPassDescriptor();
void destroy() override; void destroy() override;
bool isCompatible(const QRhiRenderPassDescriptor *other) const 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() // 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; return true;
} }
QRhiRenderPassDescriptor *QNullRenderPassDescriptor::newCompatibleRenderPassDescriptor() const
{
return new QNullRenderPassDescriptor(m_rhi);
}
QNullReferenceRenderTarget::QNullReferenceRenderTarget(QRhiImplementation *rhi) QNullReferenceRenderTarget::QNullReferenceRenderTarget(QRhiImplementation *rhi)
: QRhiRenderTarget(rhi), : QRhiRenderTarget(rhi),
d(rhi) d(rhi)

View File

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

View File

@ -1174,6 +1174,28 @@ VkFormat QRhiVulkan::optimalDepthStencilFormat()
return optimalDsFormat; 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) bool QRhiVulkan::createDefaultRenderPass(QVkRenderPassDescriptor *rpD, bool hasDepthStencil, VkSampleCountFlagBits samples, VkFormat colorFormat)
{ {
// attachment list layout is color (1), ds (0-1), resolve (0-1) // 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->colorRefs.append({ 0, VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL });
rpD->hasDepthStencil = hasDepthStencil;
if (hasDepthStencil) { if (hasDepthStencil) {
// clear on load + no store + lazy alloc + transient image should play // clear on load + no store + lazy alloc + transient image should play
// nicely with tiled GPUs (no physical backing necessary for ds buffer) // 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 }); 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. // Replace the first implicit dep (TOP_OF_PIPE / ALL_COMMANDS) with our own.
VkSubpassDependency subpassDeps[2]; VkSubpassDependency subpassDep;
memset(subpassDeps, 0, sizeof(subpassDeps)); memset(&subpassDep, 0, sizeof(subpassDep));
subpassDeps[0].srcSubpass = VK_SUBPASS_EXTERNAL; subpassDep.srcSubpass = VK_SUBPASS_EXTERNAL;
subpassDeps[0].dstSubpass = 0; subpassDep.dstSubpass = 0;
subpassDeps[0].srcStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT; subpassDep.srcStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT;
subpassDeps[0].dstStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT; subpassDep.dstStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT;
subpassDeps[0].srcAccessMask = 0; subpassDep.srcAccessMask = 0;
subpassDeps[0].dstAccessMask = VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT; subpassDep.dstAccessMask = VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT;
rpD->subpassDeps.append(subpassDep);
if (hasDepthStencil) { if (hasDepthStencil) {
subpassDeps[1].srcSubpass = VK_SUBPASS_EXTERNAL; memset(&subpassDep, 0, sizeof(subpassDep));
subpassDeps[1].dstSubpass = 0; subpassDep.srcSubpass = VK_SUBPASS_EXTERNAL;
subpassDeps[1].srcStageMask = VK_PIPELINE_STAGE_EARLY_FRAGMENT_TESTS_BIT subpassDep.dstSubpass = 0;
subpassDep.srcStageMask = VK_PIPELINE_STAGE_EARLY_FRAGMENT_TESTS_BIT
| VK_PIPELINE_STAGE_LATE_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; | VK_PIPELINE_STAGE_LATE_FRAGMENT_TESTS_BIT;
subpassDeps[1].srcAccessMask = VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT; subpassDep.srcAccessMask = VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT;
subpassDeps[1].dstAccessMask = VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_READ_BIT subpassDep.dstAccessMask = VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_READ_BIT
| VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT; | VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT;
rpD->subpassDeps.append(subpassDep);
} }
VkRenderPassCreateInfo rpInfo; VkRenderPassCreateInfo rpInfo;
memset(&rpInfo, 0, sizeof(rpInfo)); VkSubpassDescription subpassDesc;
rpInfo.sType = VK_STRUCTURE_TYPE_RENDER_PASS_CREATE_INFO; fillRenderPassCreateInfo(&rpInfo, &subpassDesc, rpD);
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();
}
VkResult err = df->vkCreateRenderPass(dev, &rpInfo, nullptr, &rpD->rp); VkResult err = df->vkCreateRenderPass(dev, &rpInfo, nullptr, &rpD->rp);
if (err != VK_SUCCESS) { if (err != VK_SUCCESS) {
@ -1278,8 +1282,6 @@ bool QRhiVulkan::createDefaultRenderPass(QVkRenderPassDescriptor *rpD, bool hasD
return false; return false;
} }
rpD->hasDepthStencil = hasDepthStencil;
return true; return true;
} }
@ -1377,25 +1379,14 @@ bool QRhiVulkan::createOffscreenRenderPass(QVkRenderPassDescriptor *rpD,
} }
} }
VkSubpassDescription subpassDesc; // rpD->subpassDeps stays empty: don't yet know the correct initial/final
memset(&subpassDesc, 0, sizeof(subpassDesc)); // access and stage stuff for the implicit deps at this point, so leave it
subpassDesc.pipelineBindPoint = VK_PIPELINE_BIND_POINT_GRAPHICS; // to the resource tracking and activateTextureRenderTarget() to generate
subpassDesc.colorAttachmentCount = uint32_t(rpD->colorRefs.count()); // barriers.
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;
VkRenderPassCreateInfo rpInfo; VkRenderPassCreateInfo rpInfo;
memset(&rpInfo, 0, sizeof(rpInfo)); VkSubpassDescription subpassDesc;
rpInfo.sType = VK_STRUCTURE_TYPE_RENDER_PASS_CREATE_INFO; fillRenderPassCreateInfo(&rpInfo, &subpassDesc, rpD);
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
VkResult err = df->vkCreateRenderPass(dev, &rpInfo, nullptr, &rpD->rp); VkResult err = df->vkCreateRenderPass(dev, &rpInfo, nullptr, &rpD->rp);
if (err != VK_SUCCESS) { if (err != VK_SUCCESS) {
@ -6149,9 +6140,39 @@ bool QVkRenderPassDescriptor::isCompatible(const QRhiRenderPassDescriptor *other
return false; return false;
} }
// subpassDeps is not included
return true; 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() const QRhiNativeHandles *QVkRenderPassDescriptor::nativeHandles()
{ {
nativeHandlesStruct.renderPass = rp; nativeHandlesStruct.renderPass = rp;

View File

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

View File

@ -97,6 +97,8 @@ private slots:
void srbWithNoResource(); void srbWithNoResource();
void renderPassDescriptorCompatibility_data(); void renderPassDescriptorCompatibility_data();
void renderPassDescriptorCompatibility(); void renderPassDescriptorCompatibility();
void renderPassDescriptorClone_data();
void renderPassDescriptorClone();
void renderToTextureSimple_data(); void renderToTextureSimple_data();
void renderToTextureSimple(); void renderToTextureSimple();
@ -3361,7 +3363,7 @@ void tst_QRhi::renderPassDescriptorCompatibility()
QScopedPointer<QRhi> rhi(QRhi::create(impl, initParams, QRhi::Flags(), nullptr)); QScopedPointer<QRhi> rhi(QRhi::create(impl, initParams, QRhi::Flags(), nullptr));
if (!rhi) 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 // Note that checking compatibility is only relevant with backends where
// there is a concept of renderpass descriptions (Vulkan, and partially // 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() void tst_QRhi::pipelineCache_data()
{ {
rhiTestData(); rhiTestData();