diff --git a/src/gui/rhi/qrhi.cpp b/src/gui/rhi/qrhi.cpp index e924841375..072d100c59 100644 --- a/src/gui/rhi/qrhi.cpp +++ b/src/gui/rhi/qrhi.cpp @@ -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 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 diff --git a/src/gui/rhi/qrhi_p.h b/src/gui/rhi/qrhi_p.h index d31838f52f..acd04f299c 100644 --- a/src/gui/rhi/qrhi_p.h +++ b/src/gui/rhi/qrhi_p.h @@ -993,6 +993,8 @@ public: virtual QRhiRenderPassDescriptor *newCompatibleRenderPassDescriptor() const = 0; + virtual QVector serializedFormat() const = 0; + protected: QRhiRenderPassDescriptor(QRhiImplementation *rhi); }; diff --git a/src/gui/rhi/qrhid3d11.cpp b/src/gui/rhi/qrhid3d11.cpp index 71faece6c1..8e94d70502 100644 --- a/src/gui/rhi/qrhid3d11.cpp +++ b/src/gui/rhi/qrhid3d11.cpp @@ -3371,6 +3371,11 @@ QRhiRenderPassDescriptor *QD3D11RenderPassDescriptor::newCompatibleRenderPassDes return new QD3D11RenderPassDescriptor(m_rhi); } +QVector QD3D11RenderPassDescriptor::serializedFormat() const +{ + return {}; +} + QD3D11ReferenceRenderTarget::QD3D11ReferenceRenderTarget(QRhiImplementation *rhi) : QRhiRenderTarget(rhi), d(rhi) diff --git a/src/gui/rhi/qrhid3d11_p_p.h b/src/gui/rhi/qrhid3d11_p_p.h index 63705ccd0e..ddeb68a193 100644 --- a/src/gui/rhi/qrhid3d11_p_p.h +++ b/src/gui/rhi/qrhid3d11_p_p.h @@ -151,6 +151,7 @@ struct QD3D11RenderPassDescriptor : public QRhiRenderPassDescriptor void destroy() override; bool isCompatible(const QRhiRenderPassDescriptor *other) const override; QRhiRenderPassDescriptor *newCompatibleRenderPassDescriptor() const override; + QVector serializedFormat() const override; }; struct QD3D11RenderTargetData diff --git a/src/gui/rhi/qrhigles2.cpp b/src/gui/rhi/qrhigles2.cpp index 31b4d8cd51..04ad9d83dd 100644 --- a/src/gui/rhi/qrhigles2.cpp +++ b/src/gui/rhi/qrhigles2.cpp @@ -4769,6 +4769,11 @@ QRhiRenderPassDescriptor *QGles2RenderPassDescriptor::newCompatibleRenderPassDes return new QGles2RenderPassDescriptor(m_rhi); } +QVector QGles2RenderPassDescriptor::serializedFormat() const +{ + return {}; +} + QGles2ReferenceRenderTarget::QGles2ReferenceRenderTarget(QRhiImplementation *rhi) : QRhiRenderTarget(rhi), d(rhi) diff --git a/src/gui/rhi/qrhigles2_p_p.h b/src/gui/rhi/qrhigles2_p_p.h index ae5c90be24..4d18e9c60f 100644 --- a/src/gui/rhi/qrhigles2_p_p.h +++ b/src/gui/rhi/qrhigles2_p_p.h @@ -199,6 +199,7 @@ struct QGles2RenderPassDescriptor : public QRhiRenderPassDescriptor void destroy() override; bool isCompatible(const QRhiRenderPassDescriptor *other) const override; QRhiRenderPassDescriptor *newCompatibleRenderPassDescriptor() const override; + QVector serializedFormat() const override; }; struct QGles2RenderTargetData diff --git a/src/gui/rhi/qrhimetal.mm b/src/gui/rhi/qrhimetal.mm index e169550ee4..6bdcfd70c8 100644 --- a/src/gui/rhi/qrhimetal.mm +++ b/src/gui/rhi/qrhimetal.mm @@ -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 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; } diff --git a/src/gui/rhi/qrhimetal_p_p.h b/src/gui/rhi/qrhimetal_p_p.h index 2aafde7c0d..72a4d82888 100644 --- a/src/gui/rhi/qrhimetal_p_p.h +++ b/src/gui/rhi/qrhimetal_p_p.h @@ -146,6 +146,9 @@ struct QMetalRenderPassDescriptor : public QRhiRenderPassDescriptor void destroy() override; bool isCompatible(const QRhiRenderPassDescriptor *other) const override; QRhiRenderPassDescriptor *newCompatibleRenderPassDescriptor() const override; + QVector 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 serializedFormatData; }; struct QMetalRenderTargetData; diff --git a/src/gui/rhi/qrhinull.cpp b/src/gui/rhi/qrhinull.cpp index 4706b37461..894caf8f35 100644 --- a/src/gui/rhi/qrhinull.cpp +++ b/src/gui/rhi/qrhinull.cpp @@ -793,6 +793,11 @@ QRhiRenderPassDescriptor *QNullRenderPassDescriptor::newCompatibleRenderPassDesc return new QNullRenderPassDescriptor(m_rhi); } +QVector QNullRenderPassDescriptor::serializedFormat() const +{ + return {}; +} + QNullReferenceRenderTarget::QNullReferenceRenderTarget(QRhiImplementation *rhi) : QRhiRenderTarget(rhi), d(rhi) diff --git a/src/gui/rhi/qrhinull_p_p.h b/src/gui/rhi/qrhinull_p_p.h index 5905047fda..24fb426503 100644 --- a/src/gui/rhi/qrhinull_p_p.h +++ b/src/gui/rhi/qrhinull_p_p.h @@ -109,6 +109,7 @@ struct QNullRenderPassDescriptor : public QRhiRenderPassDescriptor void destroy() override; bool isCompatible(const QRhiRenderPassDescriptor *other) const override; QRhiRenderPassDescriptor *newCompatibleRenderPassDescriptor() const override; + QVector serializedFormat() const override; }; struct QNullRenderTargetData diff --git a/src/gui/rhi/qrhivulkan.cpp b/src/gui/rhi/qrhivulkan.cpp index 45fb106a10..c59b459a5b 100644 --- a/src/gui/rhi/qrhivulkan.cpp +++ b/src/gui/rhi/qrhivulkan.cpp @@ -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 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; } diff --git a/src/gui/rhi/qrhivulkan_p_p.h b/src/gui/rhi/qrhivulkan_p_p.h index 52473891d1..f2a3a809d3 100644 --- a/src/gui/rhi/qrhivulkan_p_p.h +++ b/src/gui/rhi/qrhivulkan_p_p.h @@ -186,8 +186,11 @@ struct QVkRenderPassDescriptor : public QRhiRenderPassDescriptor void destroy() override; bool isCompatible(const QRhiRenderPassDescriptor *other) const override; QRhiRenderPassDescriptor *newCompatibleRenderPassDescriptor() const override; + QVector serializedFormat() const override; const QRhiNativeHandles *nativeHandles() override; + void updateSerializedFormat(); + VkRenderPass rp = VK_NULL_HANDLE; bool ownsRp = false; QVarLengthArray attDescs; @@ -196,6 +199,7 @@ struct QVkRenderPassDescriptor : public QRhiRenderPassDescriptor QVarLengthArray subpassDeps; bool hasDepthStencil = false; VkAttachmentReference dsRef; + QVector serializedFormatData; QRhiVulkanRenderPassNativeHandles nativeHandlesStruct; int lastActiveFrameSlot = -1; }; diff --git a/tests/auto/gui/rhi/qrhi/tst_qrhi.cpp b/tests/auto/gui/rhi/qrhi/tst_qrhi.cpp index 8398dac3eb..6606589103 100644 --- a/tests/auto/gui/rhi/qrhi/tst_qrhi.cpp +++ b/tests/auto/gui/rhi/qrhi/tst_qrhi.cpp @@ -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 {