rhi: Allow testing QRhiRenderPassDescriptors for compatibility
For Metal and Vulkan this needs actual work because that's where the concept of renderpass descriptors is relevant. GL and D3D can just return true always. The big benefit of this is that Qt Quick can now compare renderpass descriptors via isCompatible() for its pipeline cache (similarly to how it is already using isLayoutCompatible() for srbs), and so renderpass descriptors for layers (Item.layer, ShaderEffect) will typically be compatible and so can pick up pipelines created by other layers from the cache. Also add autotests for shader resource binding and renderpass descriptor compatibility. Task-number: QTBUG-80318 Change-Id: I0008bc51c4ee13b0113d2c8caf799e1257f18a18 Reviewed-by: Paul Olav Tvete <paul.tvete@qt.io>bb10
parent
48d2ffd397
commit
64760504e7
|
|
@ -2318,6 +2318,24 @@ QRhiResource::Type QRhiRenderPassDescriptor::resourceType() const
|
|||
return RenderPassDescriptor;
|
||||
}
|
||||
|
||||
/*!
|
||||
\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
|
||||
QRhiGraphicsPipeline::setRenderPassDescriptor().
|
||||
|
||||
The concept of the compatibility of renderpass descriptors is similar to
|
||||
the \l{QRhiShaderResourceBindings::isLayoutCompatible}{layout
|
||||
compatibility} of QRhiShaderResourceBindings instances. They allow better
|
||||
reuse of QRhiGraphicsPipeline instances: for example, a
|
||||
QRhiGraphicsPipeline instance cache is expected to use these functions to
|
||||
look for a matching pipeline, instead of just comparing pointers, thus
|
||||
allowing a different QRhiRenderPassDescriptor and
|
||||
QRhiShaderResourceBindings to be used in combination with the pipeline, as
|
||||
long as they are compatible.
|
||||
*/
|
||||
|
||||
/*!
|
||||
\return a pointer to a backend-specific QRhiNativeHandles subclass, such as
|
||||
QRhiVulkanRenderPassNativeHandles. The returned value is null when exposing
|
||||
|
|
|
|||
|
|
@ -911,6 +911,7 @@ class Q_GUI_EXPORT QRhiRenderPassDescriptor : public QRhiResource
|
|||
public:
|
||||
QRhiResource::Type resourceType() const override;
|
||||
|
||||
virtual bool isCompatible(const QRhiRenderPassDescriptor *other) const = 0;
|
||||
virtual const QRhiNativeHandles *nativeHandles();
|
||||
|
||||
protected:
|
||||
|
|
|
|||
|
|
@ -2940,6 +2940,12 @@ void QD3D11RenderPassDescriptor::release()
|
|||
// nothing to do here
|
||||
}
|
||||
|
||||
bool QD3D11RenderPassDescriptor::isCompatible(const QRhiRenderPassDescriptor *other) const
|
||||
{
|
||||
Q_UNUSED(other);
|
||||
return true;
|
||||
}
|
||||
|
||||
QD3D11ReferenceRenderTarget::QD3D11ReferenceRenderTarget(QRhiImplementation *rhi)
|
||||
: QRhiRenderTarget(rhi),
|
||||
d(rhi)
|
||||
|
|
|
|||
|
|
@ -136,6 +136,7 @@ struct QD3D11RenderPassDescriptor : public QRhiRenderPassDescriptor
|
|||
QD3D11RenderPassDescriptor(QRhiImplementation *rhi);
|
||||
~QD3D11RenderPassDescriptor();
|
||||
void release() override;
|
||||
bool isCompatible(const QRhiRenderPassDescriptor *other) const override;
|
||||
};
|
||||
|
||||
struct QD3D11RenderTargetData
|
||||
|
|
|
|||
|
|
@ -3455,6 +3455,12 @@ void QGles2RenderPassDescriptor::release()
|
|||
// nothing to do here
|
||||
}
|
||||
|
||||
bool QGles2RenderPassDescriptor::isCompatible(const QRhiRenderPassDescriptor *other) const
|
||||
{
|
||||
Q_UNUSED(other);
|
||||
return true;
|
||||
}
|
||||
|
||||
QGles2ReferenceRenderTarget::QGles2ReferenceRenderTarget(QRhiImplementation *rhi)
|
||||
: QRhiRenderTarget(rhi),
|
||||
d(rhi)
|
||||
|
|
|
|||
|
|
@ -185,6 +185,7 @@ struct QGles2RenderPassDescriptor : public QRhiRenderPassDescriptor
|
|||
QGles2RenderPassDescriptor(QRhiImplementation *rhi);
|
||||
~QGles2RenderPassDescriptor();
|
||||
void release() override;
|
||||
bool isCompatible(const QRhiRenderPassDescriptor *other) const override;
|
||||
};
|
||||
|
||||
struct QGles2RenderTargetData
|
||||
|
|
|
|||
|
|
@ -2658,6 +2658,32 @@ void QMetalRenderPassDescriptor::release()
|
|||
// nothing to do here
|
||||
}
|
||||
|
||||
bool QMetalRenderPassDescriptor::isCompatible(const QRhiRenderPassDescriptor *other) const
|
||||
{
|
||||
if (!other)
|
||||
return false;
|
||||
|
||||
const QMetalRenderPassDescriptor *o = QRHI_RES(const QMetalRenderPassDescriptor, other);
|
||||
|
||||
if (colorAttachmentCount != o->colorAttachmentCount)
|
||||
return false;
|
||||
|
||||
if (hasDepthStencil != o->hasDepthStencil)
|
||||
return false;
|
||||
|
||||
for (int i = 0; i < colorAttachmentCount; ++i) {
|
||||
if (colorFormat[i] != o->colorFormat[i])
|
||||
return false;
|
||||
}
|
||||
|
||||
if (hasDepthStencil) {
|
||||
if (dsFormat != o->dsFormat)
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
QMetalReferenceRenderTarget::QMetalReferenceRenderTarget(QRhiImplementation *rhi)
|
||||
: QRhiRenderTarget(rhi),
|
||||
d(new QMetalRenderTargetData)
|
||||
|
|
|
|||
|
|
@ -138,6 +138,7 @@ struct QMetalRenderPassDescriptor : public QRhiRenderPassDescriptor
|
|||
QMetalRenderPassDescriptor(QRhiImplementation *rhi);
|
||||
~QMetalRenderPassDescriptor();
|
||||
void release() override;
|
||||
bool isCompatible(const QRhiRenderPassDescriptor *other) const override;
|
||||
|
||||
// there is no MTLRenderPassDescriptor here as one will be created for each pass in beginPass()
|
||||
|
||||
|
|
|
|||
|
|
@ -690,6 +690,12 @@ void QNullRenderPassDescriptor::release()
|
|||
{
|
||||
}
|
||||
|
||||
bool QNullRenderPassDescriptor::isCompatible(const QRhiRenderPassDescriptor *other) const
|
||||
{
|
||||
Q_UNUSED(other);
|
||||
return true;
|
||||
}
|
||||
|
||||
QNullReferenceRenderTarget::QNullReferenceRenderTarget(QRhiImplementation *rhi)
|
||||
: QRhiRenderTarget(rhi),
|
||||
d(rhi)
|
||||
|
|
|
|||
|
|
@ -101,6 +101,7 @@ struct QNullRenderPassDescriptor : public QRhiRenderPassDescriptor
|
|||
QNullRenderPassDescriptor(QRhiImplementation *rhi);
|
||||
~QNullRenderPassDescriptor();
|
||||
void release() override;
|
||||
bool isCompatible(const QRhiRenderPassDescriptor *other) const override;
|
||||
};
|
||||
|
||||
struct QNullRenderTargetData
|
||||
|
|
|
|||
|
|
@ -1032,54 +1032,62 @@ VkFormat QRhiVulkan::optimalDepthStencilFormat()
|
|||
return optimalDsFormat;
|
||||
}
|
||||
|
||||
bool QRhiVulkan::createDefaultRenderPass(VkRenderPass *rp, bool hasDepthStencil, VkSampleCountFlagBits samples, VkFormat colorFormat)
|
||||
bool QRhiVulkan::createDefaultRenderPass(QVkRenderPassDescriptor *rpD, bool hasDepthStencil, VkSampleCountFlagBits samples, VkFormat colorFormat)
|
||||
{
|
||||
VkAttachmentDescription attDesc[3];
|
||||
memset(attDesc, 0, sizeof(attDesc));
|
||||
|
||||
// attachment list layout is color (1), ds (0-1), resolve (0-1)
|
||||
|
||||
attDesc[0].format = colorFormat;
|
||||
attDesc[0].samples = samples;
|
||||
attDesc[0].loadOp = VK_ATTACHMENT_LOAD_OP_CLEAR;
|
||||
attDesc[0].storeOp = samples > VK_SAMPLE_COUNT_1_BIT ? VK_ATTACHMENT_STORE_OP_DONT_CARE : VK_ATTACHMENT_STORE_OP_STORE;
|
||||
attDesc[0].stencilLoadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE;
|
||||
attDesc[0].stencilStoreOp = VK_ATTACHMENT_STORE_OP_DONT_CARE;
|
||||
attDesc[0].initialLayout = VK_IMAGE_LAYOUT_UNDEFINED;
|
||||
attDesc[0].finalLayout = samples > VK_SAMPLE_COUNT_1_BIT ? VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL : VK_IMAGE_LAYOUT_PRESENT_SRC_KHR;
|
||||
VkAttachmentDescription attDesc;
|
||||
memset(&attDesc, 0, sizeof(attDesc));
|
||||
attDesc.format = colorFormat;
|
||||
attDesc.samples = samples;
|
||||
attDesc.loadOp = VK_ATTACHMENT_LOAD_OP_CLEAR;
|
||||
attDesc.storeOp = samples > VK_SAMPLE_COUNT_1_BIT ? VK_ATTACHMENT_STORE_OP_DONT_CARE : VK_ATTACHMENT_STORE_OP_STORE;
|
||||
attDesc.stencilLoadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE;
|
||||
attDesc.stencilStoreOp = VK_ATTACHMENT_STORE_OP_DONT_CARE;
|
||||
attDesc.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED;
|
||||
attDesc.finalLayout = samples > VK_SAMPLE_COUNT_1_BIT ? VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL : VK_IMAGE_LAYOUT_PRESENT_SRC_KHR;
|
||||
rpD->attDescs.append(attDesc);
|
||||
|
||||
// clear on load + no store + lazy alloc + transient image should play
|
||||
// nicely with tiled GPUs (no physical backing necessary for ds buffer)
|
||||
attDesc[1].format = optimalDepthStencilFormat();
|
||||
attDesc[1].samples = samples;
|
||||
attDesc[1].loadOp = VK_ATTACHMENT_LOAD_OP_CLEAR;
|
||||
attDesc[1].storeOp = VK_ATTACHMENT_STORE_OP_DONT_CARE;
|
||||
attDesc[1].stencilLoadOp = VK_ATTACHMENT_LOAD_OP_CLEAR;
|
||||
attDesc[1].stencilStoreOp = VK_ATTACHMENT_STORE_OP_DONT_CARE;
|
||||
attDesc[1].initialLayout = VK_IMAGE_LAYOUT_UNDEFINED;
|
||||
attDesc[1].finalLayout = VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL;
|
||||
rpD->colorRefs.append({ 0, VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL });
|
||||
|
||||
if (samples > VK_SAMPLE_COUNT_1_BIT) {
|
||||
attDesc[2].format = colorFormat;
|
||||
attDesc[2].samples = VK_SAMPLE_COUNT_1_BIT;
|
||||
attDesc[2].loadOp = VK_ATTACHMENT_LOAD_OP_CLEAR;
|
||||
attDesc[2].storeOp = VK_ATTACHMENT_STORE_OP_STORE;
|
||||
attDesc[2].stencilLoadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE;
|
||||
attDesc[2].stencilStoreOp = VK_ATTACHMENT_STORE_OP_DONT_CARE;
|
||||
attDesc[2].initialLayout = VK_IMAGE_LAYOUT_UNDEFINED;
|
||||
attDesc[2].finalLayout = VK_IMAGE_LAYOUT_PRESENT_SRC_KHR;
|
||||
if (hasDepthStencil) {
|
||||
// clear on load + no store + lazy alloc + transient image should play
|
||||
// nicely with tiled GPUs (no physical backing necessary for ds buffer)
|
||||
memset(&attDesc, 0, sizeof(attDesc));
|
||||
attDesc.format = optimalDepthStencilFormat();
|
||||
attDesc.samples = samples;
|
||||
attDesc.loadOp = VK_ATTACHMENT_LOAD_OP_CLEAR;
|
||||
attDesc.storeOp = VK_ATTACHMENT_STORE_OP_DONT_CARE;
|
||||
attDesc.stencilLoadOp = VK_ATTACHMENT_LOAD_OP_CLEAR;
|
||||
attDesc.stencilStoreOp = VK_ATTACHMENT_STORE_OP_DONT_CARE;
|
||||
attDesc.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED;
|
||||
attDesc.finalLayout = VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL;
|
||||
rpD->attDescs.append(attDesc);
|
||||
|
||||
rpD->dsRef = { 1, VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL };
|
||||
}
|
||||
|
||||
VkAttachmentReference colorRef = { 0, VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL };
|
||||
VkAttachmentReference dsRef = { 1, VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL };
|
||||
VkAttachmentReference resolveRef = { 2, VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL };
|
||||
if (samples > VK_SAMPLE_COUNT_1_BIT) {
|
||||
memset(&attDesc, 0, sizeof(attDesc));
|
||||
attDesc.format = colorFormat;
|
||||
attDesc.samples = VK_SAMPLE_COUNT_1_BIT;
|
||||
attDesc.loadOp = VK_ATTACHMENT_LOAD_OP_CLEAR;
|
||||
attDesc.storeOp = VK_ATTACHMENT_STORE_OP_STORE;
|
||||
attDesc.stencilLoadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE;
|
||||
attDesc.stencilStoreOp = VK_ATTACHMENT_STORE_OP_DONT_CARE;
|
||||
attDesc.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED;
|
||||
attDesc.finalLayout = VK_IMAGE_LAYOUT_PRESENT_SRC_KHR;
|
||||
rpD->attDescs.append(attDesc);
|
||||
|
||||
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 = &colorRef;
|
||||
subpassDesc.pDepthStencilAttachment = hasDepthStencil ? &dsRef : nullptr;
|
||||
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 subpassDep;
|
||||
|
|
@ -1095,7 +1103,7 @@ bool QRhiVulkan::createDefaultRenderPass(VkRenderPass *rp, bool hasDepthStencil,
|
|||
memset(&rpInfo, 0, sizeof(rpInfo));
|
||||
rpInfo.sType = VK_STRUCTURE_TYPE_RENDER_PASS_CREATE_INFO;
|
||||
rpInfo.attachmentCount = 1;
|
||||
rpInfo.pAttachments = attDesc;
|
||||
rpInfo.pAttachments = rpD->attDescs.constData();
|
||||
rpInfo.subpassCount = 1;
|
||||
rpInfo.pSubpasses = &subpassDesc;
|
||||
rpInfo.dependencyCount = 1;
|
||||
|
|
@ -1106,19 +1114,21 @@ bool QRhiVulkan::createDefaultRenderPass(VkRenderPass *rp, bool hasDepthStencil,
|
|||
|
||||
if (samples > VK_SAMPLE_COUNT_1_BIT) {
|
||||
rpInfo.attachmentCount += 1;
|
||||
subpassDesc.pResolveAttachments = &resolveRef;
|
||||
subpassDesc.pResolveAttachments = rpD->resolveRefs.constData();
|
||||
}
|
||||
|
||||
VkResult err = df->vkCreateRenderPass(dev, &rpInfo, nullptr, rp);
|
||||
VkResult err = df->vkCreateRenderPass(dev, &rpInfo, nullptr, &rpD->rp);
|
||||
if (err != VK_SUCCESS) {
|
||||
qWarning("Failed to create renderpass: %d", err);
|
||||
return false;
|
||||
}
|
||||
|
||||
rpD->hasDepthStencil = hasDepthStencil;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool QRhiVulkan::createOffscreenRenderPass(VkRenderPass *rp,
|
||||
bool QRhiVulkan::createOffscreenRenderPass(QVkRenderPassDescriptor *rpD,
|
||||
const QRhiColorAttachment *firstColorAttachment,
|
||||
const QRhiColorAttachment *lastColorAttachment,
|
||||
bool preserveColor,
|
||||
|
|
@ -1126,10 +1136,6 @@ bool QRhiVulkan::createOffscreenRenderPass(VkRenderPass *rp,
|
|||
QRhiRenderBuffer *depthStencilBuffer,
|
||||
QRhiTexture *depthTexture)
|
||||
{
|
||||
QVarLengthArray<VkAttachmentDescription, 8> attDescs;
|
||||
QVarLengthArray<VkAttachmentReference, 8> colorRefs;
|
||||
QVarLengthArray<VkAttachmentReference, 8> resolveRefs;
|
||||
|
||||
// attachment list layout is color (0-8), ds (0-1), resolve (0-8)
|
||||
|
||||
for (auto it = firstColorAttachment; it != lastColorAttachment; ++it) {
|
||||
|
|
@ -1150,14 +1156,14 @@ bool QRhiVulkan::createOffscreenRenderPass(VkRenderPass *rp,
|
|||
// this has to interact correctly with activateTextureRenderTarget(), hence leaving in COLOR_ATT
|
||||
attDesc.initialLayout = preserveColor ? VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL : VK_IMAGE_LAYOUT_UNDEFINED;
|
||||
attDesc.finalLayout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL;
|
||||
attDescs.append(attDesc);
|
||||
rpD->attDescs.append(attDesc);
|
||||
|
||||
const VkAttachmentReference ref = { uint32_t(attDescs.count() - 1), VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL };
|
||||
colorRefs.append(ref);
|
||||
const VkAttachmentReference ref = { uint32_t(rpD->attDescs.count() - 1), VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL };
|
||||
rpD->colorRefs.append(ref);
|
||||
}
|
||||
|
||||
const bool hasDepthStencil = depthStencilBuffer || depthTexture;
|
||||
if (hasDepthStencil) {
|
||||
rpD->hasDepthStencil = depthStencilBuffer || depthTexture;
|
||||
if (rpD->hasDepthStencil) {
|
||||
const VkFormat dsFormat = depthTexture ? QRHI_RES(QVkTexture, depthTexture)->vkformat
|
||||
: QRHI_RES(QVkRenderBuffer, depthStencilBuffer)->vkformat;
|
||||
const VkSampleCountFlagBits samples = depthTexture ? QRHI_RES(QVkTexture, depthTexture)->samples
|
||||
|
|
@ -1174,9 +1180,9 @@ bool QRhiVulkan::createOffscreenRenderPass(VkRenderPass *rp,
|
|||
attDesc.stencilStoreOp = storeOp;
|
||||
attDesc.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED;
|
||||
attDesc.finalLayout = VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL;
|
||||
attDescs.append(attDesc);
|
||||
rpD->attDescs.append(attDesc);
|
||||
}
|
||||
VkAttachmentReference dsRef = { uint32_t(attDescs.count() - 1), VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL };
|
||||
rpD->dsRef = { uint32_t(rpD->attDescs.count() - 1), VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL };
|
||||
|
||||
for (auto it = firstColorAttachment; it != lastColorAttachment; ++it) {
|
||||
if (it->resolveTexture()) {
|
||||
|
|
@ -1194,37 +1200,37 @@ bool QRhiVulkan::createOffscreenRenderPass(VkRenderPass *rp,
|
|||
attDesc.stencilStoreOp = VK_ATTACHMENT_STORE_OP_DONT_CARE;
|
||||
attDesc.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED;
|
||||
attDesc.finalLayout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL;
|
||||
attDescs.append(attDesc);
|
||||
rpD->attDescs.append(attDesc);
|
||||
|
||||
const VkAttachmentReference ref = { uint32_t(attDescs.count() - 1), VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL };
|
||||
resolveRefs.append(ref);
|
||||
const VkAttachmentReference ref = { uint32_t(rpD->attDescs.count() - 1), VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL };
|
||||
rpD->resolveRefs.append(ref);
|
||||
} else {
|
||||
const VkAttachmentReference ref = { VK_ATTACHMENT_UNUSED, VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL };
|
||||
resolveRefs.append(ref);
|
||||
rpD->resolveRefs.append(ref);
|
||||
}
|
||||
}
|
||||
|
||||
VkSubpassDescription subpassDesc;
|
||||
memset(&subpassDesc, 0, sizeof(subpassDesc));
|
||||
subpassDesc.pipelineBindPoint = VK_PIPELINE_BIND_POINT_GRAPHICS;
|
||||
subpassDesc.colorAttachmentCount = uint32_t(colorRefs.count());
|
||||
Q_ASSERT(colorRefs.count() == resolveRefs.count());
|
||||
subpassDesc.pColorAttachments = !colorRefs.isEmpty() ? colorRefs.constData() : nullptr;
|
||||
subpassDesc.pDepthStencilAttachment = hasDepthStencil ? &dsRef : nullptr;
|
||||
subpassDesc.pResolveAttachments = !resolveRefs.isEmpty() ? resolveRefs.constData() : nullptr;
|
||||
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;
|
||||
|
||||
VkRenderPassCreateInfo rpInfo;
|
||||
memset(&rpInfo, 0, sizeof(rpInfo));
|
||||
rpInfo.sType = VK_STRUCTURE_TYPE_RENDER_PASS_CREATE_INFO;
|
||||
rpInfo.attachmentCount = uint32_t(attDescs.count());
|
||||
rpInfo.pAttachments = attDescs.constData();
|
||||
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 to
|
||||
// generate barriers
|
||||
|
||||
VkResult err = df->vkCreateRenderPass(dev, &rpInfo, nullptr, rp);
|
||||
VkResult err = df->vkCreateRenderPass(dev, &rpInfo, nullptr, &rpD->rp);
|
||||
if (err != VK_SUCCESS) {
|
||||
qWarning("Failed to create renderpass: %d", err);
|
||||
return false;
|
||||
|
|
@ -5501,6 +5507,61 @@ void QVkRenderPassDescriptor::release()
|
|||
rhiD->unregisterResource(this);
|
||||
}
|
||||
|
||||
static inline bool attachmentDescriptionEquals(const VkAttachmentDescription &a, const VkAttachmentDescription &b)
|
||||
{
|
||||
return a.format == b.format
|
||||
&& a.samples == b.samples
|
||||
&& a.loadOp == b.loadOp
|
||||
&& a.storeOp == b.storeOp
|
||||
&& a.stencilLoadOp == b.stencilLoadOp
|
||||
&& a.stencilStoreOp == b.stencilStoreOp
|
||||
&& a.initialLayout == b.initialLayout
|
||||
&& a.finalLayout == b.finalLayout;
|
||||
}
|
||||
|
||||
bool QVkRenderPassDescriptor::isCompatible(const QRhiRenderPassDescriptor *other) const
|
||||
{
|
||||
if (!other)
|
||||
return false;
|
||||
|
||||
const QVkRenderPassDescriptor *o = QRHI_RES(const QVkRenderPassDescriptor, other);
|
||||
|
||||
if (attDescs.count() != o->attDescs.count())
|
||||
return false;
|
||||
if (colorRefs.count() != o->colorRefs.count())
|
||||
return false;
|
||||
if (resolveRefs.count() != o->resolveRefs.count())
|
||||
return false;
|
||||
if (hasDepthStencil != o->hasDepthStencil)
|
||||
return false;
|
||||
|
||||
for (int i = 0, ie = colorRefs.count(); i != ie; ++i) {
|
||||
const uint32_t attIdx = colorRefs[i].attachment;
|
||||
if (attIdx != o->colorRefs[i].attachment)
|
||||
return false;
|
||||
if (attIdx != VK_ATTACHMENT_UNUSED && !attachmentDescriptionEquals(attDescs[attIdx], o->attDescs[attIdx]))
|
||||
return false;
|
||||
}
|
||||
|
||||
if (hasDepthStencil) {
|
||||
const uint32_t attIdx = dsRef.attachment;
|
||||
if (attIdx != o->dsRef.attachment)
|
||||
return false;
|
||||
if (attIdx != VK_ATTACHMENT_UNUSED && !attachmentDescriptionEquals(attDescs[attIdx], o->attDescs[attIdx]))
|
||||
return false;
|
||||
}
|
||||
|
||||
for (int i = 0, ie = resolveRefs.count(); i != ie; ++i) {
|
||||
const uint32_t attIdx = resolveRefs[i].attachment;
|
||||
if (attIdx != o->resolveRefs[i].attachment)
|
||||
return false;
|
||||
if (attIdx != VK_ATTACHMENT_UNUSED && !attachmentDescriptionEquals(attDescs[attIdx], o->attDescs[attIdx]))
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
const QRhiNativeHandles *QVkRenderPassDescriptor::nativeHandles()
|
||||
{
|
||||
nativeHandlesStruct.renderPass = rp;
|
||||
|
|
@ -5584,7 +5645,7 @@ QRhiRenderPassDescriptor *QVkTextureRenderTarget::newCompatibleRenderPassDescrip
|
|||
|
||||
QRHI_RES_RHI(QRhiVulkan);
|
||||
QVkRenderPassDescriptor *rp = new QVkRenderPassDescriptor(m_rhi);
|
||||
if (!rhiD->createOffscreenRenderPass(&rp->rp,
|
||||
if (!rhiD->createOffscreenRenderPass(rp,
|
||||
m_desc.cbeginColorAttachments(),
|
||||
m_desc.cendColorAttachments(),
|
||||
m_flags.testFlag(QRhiTextureRenderTarget::PreserveColorContents),
|
||||
|
|
@ -6285,7 +6346,7 @@ QRhiRenderPassDescriptor *QVkSwapChain::newCompatibleRenderPassDescriptor()
|
|||
|
||||
QRHI_RES_RHI(QRhiVulkan);
|
||||
QVkRenderPassDescriptor *rp = new QVkRenderPassDescriptor(m_rhi);
|
||||
if (!rhiD->createDefaultRenderPass(&rp->rp,
|
||||
if (!rhiD->createDefaultRenderPass(rp,
|
||||
m_depthStencil != nullptr,
|
||||
samples,
|
||||
colorFormat))
|
||||
|
|
|
|||
|
|
@ -171,10 +171,16 @@ struct QVkRenderPassDescriptor : public QRhiRenderPassDescriptor
|
|||
QVkRenderPassDescriptor(QRhiImplementation *rhi);
|
||||
~QVkRenderPassDescriptor();
|
||||
void release() override;
|
||||
bool isCompatible(const QRhiRenderPassDescriptor *other) const override;
|
||||
const QRhiNativeHandles *nativeHandles() override;
|
||||
|
||||
VkRenderPass rp = VK_NULL_HANDLE;
|
||||
bool ownsRp = false;
|
||||
QVarLengthArray<VkAttachmentDescription, 8> attDescs;
|
||||
QVarLengthArray<VkAttachmentReference, 8> colorRefs;
|
||||
QVarLengthArray<VkAttachmentReference, 8> resolveRefs;
|
||||
bool hasDepthStencil = false;
|
||||
VkAttachmentReference dsRef;
|
||||
QRhiVulkanRenderPassNativeHandles nativeHandlesStruct;
|
||||
int lastActiveFrameSlot = -1;
|
||||
};
|
||||
|
|
@ -727,11 +733,11 @@ public:
|
|||
|
||||
VkFormat optimalDepthStencilFormat();
|
||||
VkSampleCountFlagBits effectiveSampleCount(int sampleCount);
|
||||
bool createDefaultRenderPass(VkRenderPass *rp,
|
||||
bool createDefaultRenderPass(QVkRenderPassDescriptor *rpD,
|
||||
bool hasDepthStencil,
|
||||
VkSampleCountFlagBits samples,
|
||||
VkFormat colorFormat);
|
||||
bool createOffscreenRenderPass(VkRenderPass *rp,
|
||||
bool createOffscreenRenderPass(QVkRenderPassDescriptor *rpD,
|
||||
const QRhiColorAttachment *firstColorAttachment,
|
||||
const QRhiColorAttachment *lastColorAttachment,
|
||||
bool preserveColor,
|
||||
|
|
|
|||
|
|
@ -91,6 +91,10 @@ private slots:
|
|||
void renderToTextureTexturedQuadAndUniformBuffer();
|
||||
void renderToWindowSimple_data();
|
||||
void renderToWindowSimple();
|
||||
void srbLayoutCompatibility_data();
|
||||
void srbLayoutCompatibility();
|
||||
void renderPassDescriptorCompatibility_data();
|
||||
void renderPassDescriptorCompatibility();
|
||||
|
||||
private:
|
||||
struct {
|
||||
|
|
@ -1728,5 +1732,318 @@ void tst_QRhi::renderToWindowSimple()
|
|||
QVERIFY(redCount < blueCount);
|
||||
}
|
||||
|
||||
void tst_QRhi::srbLayoutCompatibility_data()
|
||||
{
|
||||
rhiTestData();
|
||||
}
|
||||
|
||||
void tst_QRhi::srbLayoutCompatibility()
|
||||
{
|
||||
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 texture resource updates");
|
||||
|
||||
QScopedPointer<QRhiTexture> texture(rhi->newTexture(QRhiTexture::RGBA8, QSize(512, 512)));
|
||||
QVERIFY(texture->build());
|
||||
QScopedPointer<QRhiSampler> sampler(rhi->newSampler(QRhiSampler::Nearest, QRhiSampler::Nearest, QRhiSampler::None,
|
||||
QRhiSampler::ClampToEdge, QRhiSampler::ClampToEdge));
|
||||
QVERIFY(sampler->build());
|
||||
QScopedPointer<QRhiSampler> otherSampler(rhi->newSampler(QRhiSampler::Nearest, QRhiSampler::Nearest, QRhiSampler::None,
|
||||
QRhiSampler::ClampToEdge, QRhiSampler::ClampToEdge));
|
||||
QVERIFY(otherSampler->build());
|
||||
QScopedPointer<QRhiBuffer> buf(rhi->newBuffer(QRhiBuffer::Dynamic, QRhiBuffer::UniformBuffer, 1024));
|
||||
QVERIFY(buf->build());
|
||||
QScopedPointer<QRhiBuffer> otherBuf(rhi->newBuffer(QRhiBuffer::Dynamic, QRhiBuffer::UniformBuffer, 256));
|
||||
QVERIFY(otherBuf->build());
|
||||
|
||||
// empty (compatible)
|
||||
{
|
||||
QScopedPointer<QRhiShaderResourceBindings> srb1(rhi->newShaderResourceBindings());
|
||||
QVERIFY(srb1->build());
|
||||
|
||||
QScopedPointer<QRhiShaderResourceBindings> srb2(rhi->newShaderResourceBindings());
|
||||
QVERIFY(srb2->build());
|
||||
|
||||
QVERIFY(srb1->isLayoutCompatible(srb2.data()));
|
||||
QVERIFY(srb2->isLayoutCompatible(srb1.data()));
|
||||
}
|
||||
|
||||
// different count (not compatible)
|
||||
{
|
||||
QScopedPointer<QRhiShaderResourceBindings> srb1(rhi->newShaderResourceBindings());
|
||||
QVERIFY(srb1->build());
|
||||
|
||||
QScopedPointer<QRhiShaderResourceBindings> srb2(rhi->newShaderResourceBindings());
|
||||
srb2->setBindings({
|
||||
QRhiShaderResourceBinding::sampledTexture(0, QRhiShaderResourceBinding::FragmentStage, texture.data(), sampler.data())
|
||||
});
|
||||
QVERIFY(srb2->build());
|
||||
|
||||
QVERIFY(!srb1->isLayoutCompatible(srb2.data()));
|
||||
QVERIFY(!srb2->isLayoutCompatible(srb1.data()));
|
||||
}
|
||||
|
||||
// full match (compatible)
|
||||
{
|
||||
QScopedPointer<QRhiShaderResourceBindings> srb1(rhi->newShaderResourceBindings());
|
||||
srb1->setBindings({
|
||||
QRhiShaderResourceBinding::uniformBuffer(0, QRhiShaderResourceBinding::VertexStage, buf.data()),
|
||||
QRhiShaderResourceBinding::sampledTexture(1, QRhiShaderResourceBinding::FragmentStage, texture.data(), sampler.data())
|
||||
});
|
||||
QVERIFY(srb1->build());
|
||||
|
||||
QScopedPointer<QRhiShaderResourceBindings> srb2(rhi->newShaderResourceBindings());
|
||||
srb2->setBindings({
|
||||
QRhiShaderResourceBinding::uniformBuffer(0, QRhiShaderResourceBinding::VertexStage, buf.data()),
|
||||
QRhiShaderResourceBinding::sampledTexture(1, QRhiShaderResourceBinding::FragmentStage, texture.data(), sampler.data())
|
||||
});
|
||||
QVERIFY(srb2->build());
|
||||
|
||||
QVERIFY(srb1->isLayoutCompatible(srb2.data()));
|
||||
QVERIFY(srb2->isLayoutCompatible(srb1.data()));
|
||||
}
|
||||
|
||||
// different visibility (not compatible)
|
||||
{
|
||||
QScopedPointer<QRhiShaderResourceBindings> srb1(rhi->newShaderResourceBindings());
|
||||
srb1->setBindings({
|
||||
QRhiShaderResourceBinding::uniformBuffer(0, QRhiShaderResourceBinding::VertexStage | QRhiShaderResourceBinding::FragmentStage, buf.data()),
|
||||
});
|
||||
QVERIFY(srb1->build());
|
||||
|
||||
QScopedPointer<QRhiShaderResourceBindings> srb2(rhi->newShaderResourceBindings());
|
||||
srb2->setBindings({
|
||||
QRhiShaderResourceBinding::uniformBuffer(0, QRhiShaderResourceBinding::VertexStage, buf.data()),
|
||||
});
|
||||
QVERIFY(srb2->build());
|
||||
|
||||
QVERIFY(!srb1->isLayoutCompatible(srb2.data()));
|
||||
QVERIFY(!srb2->isLayoutCompatible(srb1.data()));
|
||||
}
|
||||
|
||||
// different binding points (not compatible)
|
||||
{
|
||||
QScopedPointer<QRhiShaderResourceBindings> srb1(rhi->newShaderResourceBindings());
|
||||
srb1->setBindings({
|
||||
QRhiShaderResourceBinding::uniformBuffer(0, QRhiShaderResourceBinding::VertexStage, buf.data()),
|
||||
});
|
||||
QVERIFY(srb1->build());
|
||||
|
||||
QScopedPointer<QRhiShaderResourceBindings> srb2(rhi->newShaderResourceBindings());
|
||||
srb2->setBindings({
|
||||
QRhiShaderResourceBinding::uniformBuffer(1, QRhiShaderResourceBinding::VertexStage, buf.data()),
|
||||
});
|
||||
QVERIFY(srb2->build());
|
||||
|
||||
QVERIFY(!srb1->isLayoutCompatible(srb2.data()));
|
||||
QVERIFY(!srb2->isLayoutCompatible(srb1.data()));
|
||||
}
|
||||
|
||||
// different buffer region offset and size (compatible)
|
||||
{
|
||||
QScopedPointer<QRhiShaderResourceBindings> srb1(rhi->newShaderResourceBindings());
|
||||
srb1->setBindings({
|
||||
QRhiShaderResourceBinding::uniformBuffer(0, QRhiShaderResourceBinding::VertexStage, buf.data(), rhi->ubufAligned(1), 128),
|
||||
QRhiShaderResourceBinding::sampledTexture(1, QRhiShaderResourceBinding::FragmentStage, texture.data(), sampler.data())
|
||||
});
|
||||
QVERIFY(srb1->build());
|
||||
|
||||
QScopedPointer<QRhiShaderResourceBindings> srb2(rhi->newShaderResourceBindings());
|
||||
srb2->setBindings({
|
||||
QRhiShaderResourceBinding::uniformBuffer(0, QRhiShaderResourceBinding::VertexStage, buf.data()),
|
||||
QRhiShaderResourceBinding::sampledTexture(1, QRhiShaderResourceBinding::FragmentStage, texture.data(), sampler.data())
|
||||
});
|
||||
QVERIFY(srb2->build());
|
||||
|
||||
QVERIFY(srb1->isLayoutCompatible(srb2.data()));
|
||||
QVERIFY(srb2->isLayoutCompatible(srb1.data()));
|
||||
}
|
||||
|
||||
// different resources (compatible)
|
||||
{
|
||||
QScopedPointer<QRhiShaderResourceBindings> srb1(rhi->newShaderResourceBindings());
|
||||
srb1->setBindings({
|
||||
QRhiShaderResourceBinding::uniformBuffer(0, QRhiShaderResourceBinding::VertexStage, otherBuf.data()),
|
||||
QRhiShaderResourceBinding::sampledTexture(1, QRhiShaderResourceBinding::FragmentStage, texture.data(), otherSampler.data())
|
||||
});
|
||||
QVERIFY(srb1->build());
|
||||
|
||||
QScopedPointer<QRhiShaderResourceBindings> srb2(rhi->newShaderResourceBindings());
|
||||
srb2->setBindings({
|
||||
QRhiShaderResourceBinding::uniformBuffer(0, QRhiShaderResourceBinding::VertexStage, buf.data()),
|
||||
QRhiShaderResourceBinding::sampledTexture(1, QRhiShaderResourceBinding::FragmentStage, texture.data(), sampler.data())
|
||||
});
|
||||
QVERIFY(srb2->build());
|
||||
|
||||
QVERIFY(srb1->isLayoutCompatible(srb2.data()));
|
||||
QVERIFY(srb2->isLayoutCompatible(srb1.data()));
|
||||
}
|
||||
}
|
||||
|
||||
void tst_QRhi::renderPassDescriptorCompatibility_data()
|
||||
{
|
||||
rhiTestData();
|
||||
}
|
||||
|
||||
void tst_QRhi::renderPassDescriptorCompatibility()
|
||||
{
|
||||
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 texture resource updates");
|
||||
|
||||
// Note that checking compatibility is only relevant with backends where
|
||||
// there is a concept of renderpass descriptions (Vulkan, and partially
|
||||
// Metal). It is perfectly fine for isCompatible() to always return true
|
||||
// when that is not the case (D3D11, OpenGL). Hence the 'if (Vulkan or
|
||||
// Metal)' for all the negative tests. Also note "partial" for Metal:
|
||||
// resolve textures for examples have no effect on compatibility with Metal.
|
||||
|
||||
// tex and tex2 have the same format
|
||||
QScopedPointer<QRhiTexture> tex(rhi->newTexture(QRhiTexture::RGBA8, QSize(512, 512), 1, QRhiTexture::RenderTarget));
|
||||
QVERIFY(tex->build());
|
||||
QScopedPointer<QRhiTexture> tex2(rhi->newTexture(QRhiTexture::RGBA8, QSize(512, 512), 1, QRhiTexture::RenderTarget));
|
||||
QVERIFY(tex2->build());
|
||||
|
||||
QScopedPointer<QRhiRenderBuffer> ds(rhi->newRenderBuffer(QRhiRenderBuffer::DepthStencil, QSize(512, 512)));
|
||||
QVERIFY(ds->build());
|
||||
|
||||
// two texture rendertargets with tex and tex2 as color0 (compatible)
|
||||
{
|
||||
QScopedPointer<QRhiTextureRenderTarget> rt(rhi->newTextureRenderTarget({ tex.data() }));
|
||||
QScopedPointer<QRhiRenderPassDescriptor> rpDesc(rt->newCompatibleRenderPassDescriptor());
|
||||
rt->setRenderPassDescriptor(rpDesc.data());
|
||||
QVERIFY(rt->build());
|
||||
|
||||
QScopedPointer<QRhiTextureRenderTarget> rt2(rhi->newTextureRenderTarget({ tex2.data() }));
|
||||
QScopedPointer<QRhiRenderPassDescriptor> rpDesc2(rt2->newCompatibleRenderPassDescriptor());
|
||||
rt2->setRenderPassDescriptor(rpDesc2.data());
|
||||
QVERIFY(rt2->build());
|
||||
|
||||
QVERIFY(rpDesc->isCompatible(rpDesc2.data()));
|
||||
QVERIFY(rpDesc2->isCompatible(rpDesc.data()));
|
||||
}
|
||||
|
||||
// two texture rendertargets with tex and tex2 as color0, and a depth-stencil attachment as well (compatible)
|
||||
{
|
||||
QRhiTextureRenderTargetDescription desc({ tex.data() }, ds.data());
|
||||
QScopedPointer<QRhiTextureRenderTarget> rt(rhi->newTextureRenderTarget(desc));
|
||||
QScopedPointer<QRhiRenderPassDescriptor> rpDesc(rt->newCompatibleRenderPassDescriptor());
|
||||
rt->setRenderPassDescriptor(rpDesc.data());
|
||||
QVERIFY(rt->build());
|
||||
|
||||
QScopedPointer<QRhiTextureRenderTarget> rt2(rhi->newTextureRenderTarget(desc));
|
||||
QScopedPointer<QRhiRenderPassDescriptor> rpDesc2(rt2->newCompatibleRenderPassDescriptor());
|
||||
rt2->setRenderPassDescriptor(rpDesc2.data());
|
||||
QVERIFY(rt2->build());
|
||||
|
||||
QVERIFY(rpDesc->isCompatible(rpDesc2.data()));
|
||||
QVERIFY(rpDesc2->isCompatible(rpDesc.data()));
|
||||
}
|
||||
|
||||
// now one of them does not have the ds attachment (not compatible)
|
||||
{
|
||||
QScopedPointer<QRhiTextureRenderTarget> rt(rhi->newTextureRenderTarget({ { tex.data() }, ds.data() }));
|
||||
QScopedPointer<QRhiRenderPassDescriptor> rpDesc(rt->newCompatibleRenderPassDescriptor());
|
||||
rt->setRenderPassDescriptor(rpDesc.data());
|
||||
QVERIFY(rt->build());
|
||||
|
||||
QScopedPointer<QRhiTextureRenderTarget> rt2(rhi->newTextureRenderTarget({ tex.data() }));
|
||||
QScopedPointer<QRhiRenderPassDescriptor> rpDesc2(rt2->newCompatibleRenderPassDescriptor());
|
||||
rt2->setRenderPassDescriptor(rpDesc2.data());
|
||||
QVERIFY(rt2->build());
|
||||
|
||||
if (impl == QRhi::Vulkan || impl == QRhi::Metal) {
|
||||
QVERIFY(!rpDesc->isCompatible(rpDesc2.data()));
|
||||
QVERIFY(!rpDesc2->isCompatible(rpDesc.data()));
|
||||
}
|
||||
}
|
||||
|
||||
if (rhi->isFeatureSupported(QRhi::MultisampleRenderBuffer)) {
|
||||
// resolve attachments (compatible)
|
||||
{
|
||||
QScopedPointer<QRhiRenderBuffer> msaaRenderBuffer(rhi->newRenderBuffer(QRhiRenderBuffer::Color, QSize(512, 512), 4));
|
||||
QVERIFY(msaaRenderBuffer->build());
|
||||
QScopedPointer<QRhiRenderBuffer> msaaRenderBuffer2(rhi->newRenderBuffer(QRhiRenderBuffer::Color, QSize(512, 512), 4));
|
||||
QVERIFY(msaaRenderBuffer2->build());
|
||||
|
||||
QRhiColorAttachment colorAtt(msaaRenderBuffer.data()); // color0, multisample
|
||||
colorAtt.setResolveTexture(tex.data()); // resolved into a non-msaa texture
|
||||
QScopedPointer<QRhiTextureRenderTarget> rt(rhi->newTextureRenderTarget({ colorAtt }));
|
||||
QScopedPointer<QRhiRenderPassDescriptor> rpDesc(rt->newCompatibleRenderPassDescriptor());
|
||||
rt->setRenderPassDescriptor(rpDesc.data());
|
||||
QVERIFY(rt->build());
|
||||
|
||||
QRhiColorAttachment colorAtt2(msaaRenderBuffer2.data()); // color0, multisample
|
||||
colorAtt2.setResolveTexture(tex2.data()); // resolved into a non-msaa texture
|
||||
QScopedPointer<QRhiTextureRenderTarget> rt2(rhi->newTextureRenderTarget({ colorAtt2 }));
|
||||
QScopedPointer<QRhiRenderPassDescriptor> rpDesc2(rt2->newCompatibleRenderPassDescriptor());
|
||||
rt2->setRenderPassDescriptor(rpDesc2.data());
|
||||
QVERIFY(rt2->build());
|
||||
|
||||
QVERIFY(rpDesc->isCompatible(rpDesc2.data()));
|
||||
QVERIFY(rpDesc2->isCompatible(rpDesc.data()));
|
||||
}
|
||||
|
||||
// missing resolve for one of them (not compatible)
|
||||
{
|
||||
QScopedPointer<QRhiRenderBuffer> msaaRenderBuffer(rhi->newRenderBuffer(QRhiRenderBuffer::Color, QSize(512, 512), 4));
|
||||
QVERIFY(msaaRenderBuffer->build());
|
||||
QScopedPointer<QRhiRenderBuffer> msaaRenderBuffer2(rhi->newRenderBuffer(QRhiRenderBuffer::Color, QSize(512, 512), 4));
|
||||
QVERIFY(msaaRenderBuffer2->build());
|
||||
|
||||
QRhiColorAttachment colorAtt(msaaRenderBuffer.data()); // color0, multisample
|
||||
colorAtt.setResolveTexture(tex.data()); // resolved into a non-msaa texture
|
||||
QScopedPointer<QRhiTextureRenderTarget> rt(rhi->newTextureRenderTarget({ colorAtt }));
|
||||
QScopedPointer<QRhiRenderPassDescriptor> rpDesc(rt->newCompatibleRenderPassDescriptor());
|
||||
rt->setRenderPassDescriptor(rpDesc.data());
|
||||
QVERIFY(rt->build());
|
||||
|
||||
QRhiColorAttachment colorAtt2(msaaRenderBuffer2.data()); // color0, multisample
|
||||
QScopedPointer<QRhiTextureRenderTarget> rt2(rhi->newTextureRenderTarget({ colorAtt2 }));
|
||||
QScopedPointer<QRhiRenderPassDescriptor> rpDesc2(rt2->newCompatibleRenderPassDescriptor());
|
||||
rt2->setRenderPassDescriptor(rpDesc2.data());
|
||||
QVERIFY(rt2->build());
|
||||
|
||||
if (impl == QRhi::Vulkan) { // no Metal here
|
||||
QVERIFY(!rpDesc->isCompatible(rpDesc2.data()));
|
||||
QVERIFY(!rpDesc2->isCompatible(rpDesc.data()));
|
||||
}
|
||||
}
|
||||
} else {
|
||||
qDebug("Skipping multisample renderbuffer dependent tests");
|
||||
}
|
||||
|
||||
if (rhi->isTextureFormatSupported(QRhiTexture::RGBA32F)) {
|
||||
QScopedPointer<QRhiTexture> tex3(rhi->newTexture(QRhiTexture::RGBA32F, QSize(512, 512), 1, QRhiTexture::RenderTarget));
|
||||
QVERIFY(tex3->build());
|
||||
|
||||
// different texture formats (not compatible)
|
||||
{
|
||||
QScopedPointer<QRhiTextureRenderTarget> rt(rhi->newTextureRenderTarget({ tex.data() }));
|
||||
QScopedPointer<QRhiRenderPassDescriptor> rpDesc(rt->newCompatibleRenderPassDescriptor());
|
||||
rt->setRenderPassDescriptor(rpDesc.data());
|
||||
QVERIFY(rt->build());
|
||||
|
||||
QScopedPointer<QRhiTextureRenderTarget> rt2(rhi->newTextureRenderTarget({ tex3.data() }));
|
||||
QScopedPointer<QRhiRenderPassDescriptor> rpDesc2(rt2->newCompatibleRenderPassDescriptor());
|
||||
rt2->setRenderPassDescriptor(rpDesc2.data());
|
||||
QVERIFY(rt2->build());
|
||||
|
||||
if (impl == QRhi::Vulkan || impl == QRhi::Metal) {
|
||||
QVERIFY(!rpDesc->isCompatible(rpDesc2.data()));
|
||||
QVERIFY(!rpDesc2->isCompatible(rpDesc.data()));
|
||||
}
|
||||
}
|
||||
} else {
|
||||
qDebug("Skipping texture format dependent tests");
|
||||
}
|
||||
}
|
||||
|
||||
#include <tst_qrhi.moc>
|
||||
QTEST_MAIN(tst_QRhi)
|
||||
|
|
|
|||
Loading…
Reference in New Issue