rhi: Allow testing renderpass compatibility without the objects

Follow what has been done for QRhiShaderResourceBindings. Have a way
to retrieve an opaque blob (that just happens to be a list of integers)
so that a simple == comparison can be used to determine compatibility
even when the objects from which the blob was retrieved are no longer
alive.

The contract is the following:

bool a = rp1->isCompatible(rp2);
bool b = rp1->serializedFormat() == rp2->serializedFormat();
assert(a == b);

Pick-to: 6.2
Change-Id: I45e7d05eeb6dfa2b2de474da0a0644912aaf174a
Reviewed-by: Qt CI Bot <qt_ci_bot@qt-project.org>
Reviewed-by: Christian Strømme <christian.stromme@qt.io>
This commit is contained in:
Laszlo Agocs 2021-09-15 13:28:44 +02:00
parent c5a3cabce0
commit 43a42fa196
13 changed files with 132 additions and 1 deletions

View File

@ -2740,7 +2740,17 @@ QRhiResource::Type QRhiRenderPassDescriptor::resourceType() const
\l{QRhiTextureRenderTarget::newCompatibleRenderPassDescriptor()}{created}
from the same QRhiTextureRenderTarget are always compatible.
\sa newCompatibleRenderPassDescriptor()
Similarly to QRhiShaderResourceBindings, compatibility can also be tested
without having two existing objects available. Extracting the opaque blob by
calling serializedFormat() allows testing for compatibility by comparing the
returned vector to another QRhiRenderPassDescriptor's
serializedFormat(). This has benefits in certain situations, because it
allows testing the compatibility of a QRhiRenderPassDescriptor with a
QRhiGraphicsPipeline even when the QRhiRenderPassDescriptor the pipeline was
originally built was is no longer available (but the data returned from its
serializedFormat() still is).
\sa newCompatibleRenderPassDescriptor(), serializedFormat()
*/
/*!
@ -2764,6 +2774,18 @@ QRhiResource::Type QRhiRenderPassDescriptor::resourceType() const
\sa isCompatible()
*/
/*!
\fn QVector<quint32> QRhiRenderPassDescriptor::serializedFormat() const
\return a vector of integers containing an opaque blob describing the data
relevant for \l{isCompatible()}{compatibility}. Given two
QRhiRenderPassDescriptor objects \c rp1 and \c rp2, if the data returned
from this function is identical, then \c{rp1->isCompatible(rp2)}, and vice
versa hold true as well.
\sa isCompatible()
*/
/*!
\return a pointer to a backend-specific QRhiNativeHandles subclass, such as
QRhiVulkanRenderPassNativeHandles. The returned value is \nullptr when exposing

View File

@ -993,6 +993,8 @@ public:
virtual QRhiRenderPassDescriptor *newCompatibleRenderPassDescriptor() const = 0;
virtual QVector<quint32> serializedFormat() const = 0;
protected:
QRhiRenderPassDescriptor(QRhiImplementation *rhi);
};

View File

@ -3371,6 +3371,11 @@ QRhiRenderPassDescriptor *QD3D11RenderPassDescriptor::newCompatibleRenderPassDes
return new QD3D11RenderPassDescriptor(m_rhi);
}
QVector<quint32> QD3D11RenderPassDescriptor::serializedFormat() const
{
return {};
}
QD3D11ReferenceRenderTarget::QD3D11ReferenceRenderTarget(QRhiImplementation *rhi)
: QRhiRenderTarget(rhi),
d(rhi)

View File

@ -151,6 +151,7 @@ struct QD3D11RenderPassDescriptor : public QRhiRenderPassDescriptor
void destroy() override;
bool isCompatible(const QRhiRenderPassDescriptor *other) const override;
QRhiRenderPassDescriptor *newCompatibleRenderPassDescriptor() const override;
QVector<quint32> serializedFormat() const override;
};
struct QD3D11RenderTargetData

View File

@ -4769,6 +4769,11 @@ QRhiRenderPassDescriptor *QGles2RenderPassDescriptor::newCompatibleRenderPassDes
return new QGles2RenderPassDescriptor(m_rhi);
}
QVector<quint32> QGles2RenderPassDescriptor::serializedFormat() const
{
return {};
}
QGles2ReferenceRenderTarget::QGles2ReferenceRenderTarget(QRhiImplementation *rhi)
: QRhiRenderTarget(rhi),
d(rhi)

View File

@ -199,6 +199,7 @@ struct QGles2RenderPassDescriptor : public QRhiRenderPassDescriptor
void destroy() override;
bool isCompatible(const QRhiRenderPassDescriptor *other) const override;
QRhiRenderPassDescriptor *newCompatibleRenderPassDescriptor() const override;
QVector<quint32> serializedFormat() const override;
};
struct QGles2RenderTargetData

View File

@ -2924,6 +2924,7 @@ bool QMetalSampler::create()
QMetalRenderPassDescriptor::QMetalRenderPassDescriptor(QRhiImplementation *rhi)
: QRhiRenderPassDescriptor(rhi)
{
serializedFormatData.reserve(16);
}
QMetalRenderPassDescriptor::~QMetalRenderPassDescriptor()
@ -2962,6 +2963,18 @@ bool QMetalRenderPassDescriptor::isCompatible(const QRhiRenderPassDescriptor *ot
return true;
}
void QMetalRenderPassDescriptor::updateSerializedFormat()
{
serializedFormatData.clear();
auto p = std::back_inserter(serializedFormatData);
*p++ = colorAttachmentCount;
*p++ = hasDepthStencil;
for (int i = 0; i < colorAttachmentCount; ++i)
*p++ = colorFormat[i];
*p++ = hasDepthStencil ? dsFormat : 0;
}
QRhiRenderPassDescriptor *QMetalRenderPassDescriptor::newCompatibleRenderPassDescriptor() const
{
QMetalRenderPassDescriptor *rp = new QMetalRenderPassDescriptor(m_rhi);
@ -2969,9 +2982,15 @@ QRhiRenderPassDescriptor *QMetalRenderPassDescriptor::newCompatibleRenderPassDes
rp->hasDepthStencil = hasDepthStencil;
memcpy(rp->colorFormat, colorFormat, sizeof(colorFormat));
rp->dsFormat = dsFormat;
rp->updateSerializedFormat();
return rp;
}
QVector<quint32> QMetalRenderPassDescriptor::serializedFormat() const
{
return serializedFormatData;
}
QMetalReferenceRenderTarget::QMetalReferenceRenderTarget(QRhiImplementation *rhi)
: QRhiRenderTarget(rhi),
d(new QMetalRenderTargetData)
@ -3042,6 +3061,7 @@ QRhiRenderPassDescriptor *QMetalTextureRenderTarget::newCompatibleRenderPassDesc
else if (m_desc.depthStencilBuffer())
rpD->dsFormat = int(QRHI_RES(QMetalRenderBuffer, m_desc.depthStencilBuffer())->d->format);
rpD->updateSerializedFormat();
return rpD;
}
@ -3953,6 +3973,7 @@ QRhiRenderPassDescriptor *QMetalSwapChain::newCompatibleRenderPassDescriptor()
rpD->dsFormat = MTLPixelFormatDepth32Float_Stencil8;
#endif
rpD->updateSerializedFormat();
return rpD;
}

View File

@ -146,6 +146,9 @@ struct QMetalRenderPassDescriptor : public QRhiRenderPassDescriptor
void destroy() override;
bool isCompatible(const QRhiRenderPassDescriptor *other) const override;
QRhiRenderPassDescriptor *newCompatibleRenderPassDescriptor() const override;
QVector<quint32> serializedFormat() const override;
void updateSerializedFormat();
// there is no MTLRenderPassDescriptor here as one will be created for each pass in beginPass()
@ -155,6 +158,7 @@ struct QMetalRenderPassDescriptor : public QRhiRenderPassDescriptor
bool hasDepthStencil = false;
int colorFormat[MAX_COLOR_ATTACHMENTS];
int dsFormat;
QVector<quint32> serializedFormatData;
};
struct QMetalRenderTargetData;

View File

@ -793,6 +793,11 @@ QRhiRenderPassDescriptor *QNullRenderPassDescriptor::newCompatibleRenderPassDesc
return new QNullRenderPassDescriptor(m_rhi);
}
QVector<quint32> QNullRenderPassDescriptor::serializedFormat() const
{
return {};
}
QNullReferenceRenderTarget::QNullReferenceRenderTarget(QRhiImplementation *rhi)
: QRhiRenderTarget(rhi),
d(rhi)

View File

@ -109,6 +109,7 @@ struct QNullRenderPassDescriptor : public QRhiRenderPassDescriptor
void destroy() override;
bool isCompatible(const QRhiRenderPassDescriptor *other) const override;
QRhiRenderPassDescriptor *newCompatibleRenderPassDescriptor() const override;
QVector<quint32> serializedFormat() const override;
};
struct QNullRenderTargetData

View File

@ -6131,6 +6131,7 @@ bool QVkSampler::create()
QVkRenderPassDescriptor::QVkRenderPassDescriptor(QRhiImplementation *rhi)
: QRhiRenderPassDescriptor(rhi)
{
serializedFormatData.reserve(32);
}
QVkRenderPassDescriptor::~QVkRenderPassDescriptor()
@ -6223,6 +6224,46 @@ bool QVkRenderPassDescriptor::isCompatible(const QRhiRenderPassDescriptor *other
return true;
}
void QVkRenderPassDescriptor::updateSerializedFormat()
{
serializedFormatData.clear();
auto p = std::back_inserter(serializedFormatData);
*p++ = attDescs.count();
*p++ = colorRefs.count();
*p++ = resolveRefs.count();
*p++ = hasDepthStencil;
auto serializeAttachmentData = [&p](const VkAttachmentDescription &a, bool used) {
*p++ = used ? a.format : 0;
*p++ = used ? a.samples : 0;
*p++ = used ? a.loadOp : 0;
*p++ = used ? a.storeOp : 0;
*p++ = used ? a.stencilLoadOp : 0;
*p++ = used ? a.stencilStoreOp : 0;
*p++ = used ? a.initialLayout : 0;
*p++ = used ? a.finalLayout : 0;
};
for (int i = 0, ie = colorRefs.count(); i != ie; ++i) {
const uint32_t attIdx = colorRefs[i].attachment;
*p++ = attIdx;
serializeAttachmentData(attDescs[attIdx], attIdx != VK_ATTACHMENT_UNUSED);
}
if (hasDepthStencil) {
const uint32_t attIdx = dsRef.attachment;
*p++ = attIdx;
serializeAttachmentData(attDescs[attIdx], attIdx != VK_ATTACHMENT_UNUSED);
}
for (int i = 0, ie = resolveRefs.count(); i != ie; ++i) {
const uint32_t attIdx = resolveRefs[i].attachment;
*p++ = attIdx;
serializeAttachmentData(attDescs[attIdx], attIdx != VK_ATTACHMENT_UNUSED);
}
}
QRhiRenderPassDescriptor *QVkRenderPassDescriptor::newCompatibleRenderPassDescriptor() const
{
QVkRenderPassDescriptor *rpD = new QVkRenderPassDescriptor(m_rhi);
@ -6247,10 +6288,16 @@ QRhiRenderPassDescriptor *QVkRenderPassDescriptor::newCompatibleRenderPassDescri
return nullptr;
}
rpD->updateSerializedFormat();
rhiD->registerResource(rpD);
return rpD;
}
QVector<quint32> QVkRenderPassDescriptor::serializedFormat() const
{
return serializedFormatData;
}
const QRhiNativeHandles *QVkRenderPassDescriptor::nativeHandles()
{
nativeHandlesStruct.renderPass = rp;
@ -6348,6 +6395,7 @@ QRhiRenderPassDescriptor *QVkTextureRenderTarget::newCompatibleRenderPassDescrip
}
rp->ownsRp = true;
rp->updateSerializedFormat();
rhiD->registerResource(rp);
return rp;
}
@ -7122,6 +7170,7 @@ QRhiRenderPassDescriptor *QVkSwapChain::newCompatibleRenderPassDescriptor()
}
rp->ownsRp = true;
rp->updateSerializedFormat();
rhiD->registerResource(rp);
return rp;
}

View File

@ -186,8 +186,11 @@ struct QVkRenderPassDescriptor : public QRhiRenderPassDescriptor
void destroy() override;
bool isCompatible(const QRhiRenderPassDescriptor *other) const override;
QRhiRenderPassDescriptor *newCompatibleRenderPassDescriptor() const override;
QVector<quint32> serializedFormat() const override;
const QRhiNativeHandles *nativeHandles() override;
void updateSerializedFormat();
VkRenderPass rp = VK_NULL_HANDLE;
bool ownsRp = false;
QVarLengthArray<VkAttachmentDescription, 8> attDescs;
@ -196,6 +199,7 @@ struct QVkRenderPassDescriptor : public QRhiRenderPassDescriptor
QVarLengthArray<VkSubpassDependency, 2> subpassDeps;
bool hasDepthStencil = false;
VkAttachmentReference dsRef;
QVector<quint32> serializedFormatData;
QRhiVulkanRenderPassNativeHandles nativeHandlesStruct;
int lastActiveFrameSlot = -1;
};

View File

@ -3505,6 +3505,7 @@ void tst_QRhi::renderPassDescriptorCompatibility()
QVERIFY(rpDesc->isCompatible(rpDesc2.data()));
QVERIFY(rpDesc2->isCompatible(rpDesc.data()));
QCOMPARE(rpDesc->serializedFormat(), rpDesc2->serializedFormat());
}
// two texture rendertargets with tex and tex2 as color0, and a depth-stencil attachment as well (compatible)
@ -3522,6 +3523,7 @@ void tst_QRhi::renderPassDescriptorCompatibility()
QVERIFY(rpDesc->isCompatible(rpDesc2.data()));
QVERIFY(rpDesc2->isCompatible(rpDesc.data()));
QCOMPARE(rpDesc->serializedFormat(), rpDesc2->serializedFormat());
}
// now one of them does not have the ds attachment (not compatible)
@ -3536,9 +3538,13 @@ void tst_QRhi::renderPassDescriptorCompatibility()
rt2->setRenderPassDescriptor(rpDesc2.data());
QVERIFY(rt2->create());
// these backends have a real concept of rp compatibility, with those we
// know that incompatibility must be reported; verify this
if (impl == QRhi::Vulkan || impl == QRhi::Metal) {
QVERIFY(!rpDesc->isCompatible(rpDesc2.data()));
QVERIFY(!rpDesc2->isCompatible(rpDesc.data()));
QVERIFY(!rpDesc->serializedFormat().isEmpty());
QVERIFY(rpDesc->serializedFormat() != rpDesc2->serializedFormat());
}
}
@ -3566,6 +3572,7 @@ void tst_QRhi::renderPassDescriptorCompatibility()
QVERIFY(rpDesc->isCompatible(rpDesc2.data()));
QVERIFY(rpDesc2->isCompatible(rpDesc.data()));
QCOMPARE(rpDesc->serializedFormat(), rpDesc2->serializedFormat());
}
// missing resolve for one of them (not compatible)
@ -3591,6 +3598,8 @@ void tst_QRhi::renderPassDescriptorCompatibility()
if (impl == QRhi::Vulkan) { // no Metal here
QVERIFY(!rpDesc->isCompatible(rpDesc2.data()));
QVERIFY(!rpDesc2->isCompatible(rpDesc.data()));
QVERIFY(!rpDesc->serializedFormat().isEmpty());
QVERIFY(rpDesc->serializedFormat() != rpDesc2->serializedFormat());
}
}
} else {
@ -3616,6 +3625,8 @@ void tst_QRhi::renderPassDescriptorCompatibility()
if (impl == QRhi::Vulkan || impl == QRhi::Metal) {
QVERIFY(!rpDesc->isCompatible(rpDesc2.data()));
QVERIFY(!rpDesc2->isCompatible(rpDesc.data()));
QVERIFY(!rpDesc->serializedFormat().isEmpty());
QVERIFY(rpDesc->serializedFormat() != rpDesc2->serializedFormat());
}
}
} else {