blob: 411258368cff0fdfbf59771fbb9f034c720d638a [file] [log] [blame]
/*------------------------------------------------------------------------
* Vulkan Conformance Tests
* ------------------------
*
* Copyright (c) 2021 The Khronos Group Inc.
* Copyright (c) 2021 Valve Corporation.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*//*!
* \file
* \brief Tests using depth/stencil images as descriptors.
*//*--------------------------------------------------------------------*/
#include "vktImageDepthStencilDescriptorTests.hpp"
#include "vkBarrierUtil.hpp"
#include "vkTypeUtil.hpp"
#include "vkQueryUtil.hpp"
#include "vkImageUtil.hpp"
#include "vkImageWithMemory.hpp"
#include "vkBuilderUtil.hpp"
#include "vkObjUtil.hpp"
#include "vkCmdUtil.hpp"
#include "vkBufferWithMemory.hpp"
#include "tcuMaybe.hpp"
#include "tcuTextureUtil.hpp"
#include "tcuImageCompare.hpp"
#include "deUniquePtr.hpp"
#include "deStringUtil.hpp"
#include <iterator>
#include <vector>
#include <sstream>
#include <string>
namespace vkt
{
namespace image
{
namespace
{
using namespace vk;
VkExtent3D getExtent ()
{
return makeExtent3D(8u, 8u, 1u);
}
VkFormat getColorBufferFormat ()
{
return VK_FORMAT_R8G8B8A8_UNORM;
}
VkFormat getFloatStorageFormat ()
{
return VK_FORMAT_R32_SFLOAT;
}
VkFormat getUintStorageFormat ()
{
return VK_FORMAT_R32_UINT;
}
tcu::Maybe<std::string> layoutExtension (VkImageLayout layout)
{
std::string extension;
switch (layout)
{
case VK_IMAGE_LAYOUT_DEPTH_READ_ONLY_STENCIL_ATTACHMENT_OPTIMAL:
case VK_IMAGE_LAYOUT_DEPTH_ATTACHMENT_STENCIL_READ_ONLY_OPTIMAL:
extension = "VK_KHR_maintenance2";
break;
case VK_IMAGE_LAYOUT_DEPTH_READ_ONLY_OPTIMAL:
case VK_IMAGE_LAYOUT_STENCIL_READ_ONLY_OPTIMAL:
// Note: we will not be using separate depth/stencil layouts. There's a separate group of tests for that.
extension = "VK_KHR_separate_depth_stencil_layouts";
break;
default:
DE_ASSERT(false);
break;
}
if (!extension.empty())
return tcu::just(extension);
return tcu::Nothing;
}
// Types of access for an image aspect.
enum class AspectAccess
{
NONE = 0,
RO = 1, // Different subtypes, see below.
RW = 2, // This always means a normal read/write depth/stencil attachment (NOT a storage image).
};
std::ostream& operator<< (std::ostream& stream, AspectAccess access)
{
if (access == AspectAccess::NONE) stream << "none";
else if (access == AspectAccess::RO) stream << "ro";
else if (access == AspectAccess::RW) stream << "rw";
else DE_ASSERT(false);
return stream;
}
// Types of read-only accesses.
enum class ReadOnlyAccess
{
DS_ATTACHMENT = 0, // Depth/stencil attachment but read-only (writes not enabled).
INPUT_ATTACHMENT = 1, // Input attachment in the set.
SAMPLED = 2, // Sampled image.
};
std::ostream& operator<< (std::ostream& stream, ReadOnlyAccess access)
{
if (access == ReadOnlyAccess::DS_ATTACHMENT) stream << "att";
else if (access == ReadOnlyAccess::INPUT_ATTACHMENT) stream << "ia";
else if (access == ReadOnlyAccess::SAMPLED) stream << "sampled";
else DE_ASSERT(false);
return stream;
}
// A given layout gives different accesses to each aspect.
AspectAccess getLegalAccess (VkImageLayout layout, VkImageAspectFlagBits aspect)
{
DE_ASSERT(aspect == VK_IMAGE_ASPECT_DEPTH_BIT || aspect == VK_IMAGE_ASPECT_STENCIL_BIT);
if (layout == VK_IMAGE_LAYOUT_DEPTH_READ_ONLY_STENCIL_ATTACHMENT_OPTIMAL)
return ((aspect == VK_IMAGE_ASPECT_STENCIL_BIT) ? AspectAccess::RW : AspectAccess::RO);
else if (layout == VK_IMAGE_LAYOUT_DEPTH_ATTACHMENT_STENCIL_READ_ONLY_OPTIMAL)
return ((aspect == VK_IMAGE_ASPECT_DEPTH_BIT) ? AspectAccess::RW : AspectAccess::RO);
else if (layout == VK_IMAGE_LAYOUT_DEPTH_READ_ONLY_OPTIMAL)
return ((aspect == VK_IMAGE_ASPECT_DEPTH_BIT) ? AspectAccess::RO : AspectAccess::NONE);
else if (layout == VK_IMAGE_LAYOUT_STENCIL_READ_ONLY_OPTIMAL)
return ((aspect == VK_IMAGE_ASPECT_STENCIL_BIT) ? AspectAccess::RO : AspectAccess::NONE);
DE_ASSERT(false);
return AspectAccess::NONE; // Unreachable.
}
using ROAccessVec = std::vector<ReadOnlyAccess>;
std::ostream& operator<< (std::ostream& stream, const ROAccessVec& vec)
{
for (size_t i = 0u; i < vec.size(); ++i)
stream << ((i > 0u) ? "_" : "") << vec[i];
return stream;
}
// We cannot access depth/stencil images both as a depth/stencil attachment and an input attachment at the same time if they have
// both aspects, because input attachments can only have one aspect.
bool incompatibleInputAttachmentAccess (AspectAccess depthAccess, const ROAccessVec* depthROAccesses, AspectAccess stencilAccess, const ROAccessVec* stencilROAccesses)
{
const bool depthAsDSAttachment = (depthAccess == AspectAccess::RW || (depthAccess == AspectAccess::RO && de::contains(begin(*depthROAccesses), end(*depthROAccesses), ReadOnlyAccess::DS_ATTACHMENT)));
const bool stencilAsDSAttachment = (stencilAccess == AspectAccess::RW || (stencilAccess == AspectAccess::RO && de::contains(begin(*stencilROAccesses), end(*stencilROAccesses), ReadOnlyAccess::DS_ATTACHMENT)));
const bool depthAsInputAttachment = (depthAccess == AspectAccess::RO && de::contains(begin(*depthROAccesses), end(*depthROAccesses), ReadOnlyAccess::INPUT_ATTACHMENT));
const bool stencilAsInputAttachment = (stencilAccess == AspectAccess::RO && de::contains(begin(*stencilROAccesses), end(*stencilROAccesses), ReadOnlyAccess::INPUT_ATTACHMENT));
return ((depthAsDSAttachment && stencilAsInputAttachment) || (stencilAsDSAttachment && depthAsInputAttachment));
}
VkImageUsageFlags getReadOnlyUsageFlags (const ROAccessVec& readOnlyAccesses)
{
VkImageUsageFlags usageFlags = 0u;
for (const auto& access : readOnlyAccesses)
{
if (access == ReadOnlyAccess::DS_ATTACHMENT)
usageFlags |= VK_IMAGE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT;
else if (access == ReadOnlyAccess::INPUT_ATTACHMENT)
usageFlags |= VK_IMAGE_USAGE_INPUT_ATTACHMENT_BIT;
else if (access == ReadOnlyAccess::SAMPLED)
usageFlags |= VK_IMAGE_USAGE_SAMPLED_BIT;
else
DE_ASSERT(false);
}
return usageFlags;
}
// Resources needed for an aspect that will be used as a descriptor in shaders (sampled or input attachment).
struct InputOutputDescriptor
{
uint32_t binding;
tcu::Maybe<uint32_t> inputAttachmentIndex;
VkImageAspectFlagBits aspect;
};
using IODescVec = std::vector<InputOutputDescriptor>;
// Test parameters.
struct TestParams
{
VkFormat format; // Image format.
VkImageLayout layout; // Layout being tested.
AspectAccess depthAccess; // Type of access that will be used for depth (must be legal).
AspectAccess stencilAccess; // Type of access that will be used for stencil (must be legal).
tcu::Maybe<ROAccessVec> depthROAccesses; // Types of read-only accesses for depth (used when depthAccess is RO).
tcu::Maybe<ROAccessVec> stencilROAccesses; // Types of read-only accesses for stencil (used when stencilAccess is RO).
VkImageUsageFlags getUsageFlags () const;
// Get a list of descriptors needed according to the given test parameters.
IODescVec getDescriptors () const;
// Does this case need a depth/stencil attachment?
bool dsAttachmentNeeded () const;
// Does this case use the depth aspect as an input attachment?
bool depthAsInputAttachment () const;
// Does this case use the stencil aspect as an input attachment?
bool stencilAsInputAttachment () const;
// Does this case need an input attachment?
bool inputAttachmentNeeded () const;
// Does this case need a depth/stencil attachment as a depth buffer?
bool depthBufferNeeded () const;
// Does this case need the pipeline depth test enabled?
bool needsDepthTest () const;
// Does this case need the stencil test enabled?
bool needsStencilTest () const;
};
VkImageUsageFlags TestParams::getUsageFlags () const
{
VkImageUsageFlags usageFlags = (VK_IMAGE_USAGE_TRANSFER_SRC_BIT | VK_IMAGE_USAGE_TRANSFER_DST_BIT);
if (depthAccess == AspectAccess::RW || stencilAccess == AspectAccess::RW)
usageFlags |= VK_IMAGE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT;
if (depthAccess == AspectAccess::RO)
{
DE_ASSERT(static_cast<bool>(depthROAccesses));
usageFlags |= getReadOnlyUsageFlags(*depthROAccesses);
}
if (stencilAccess == AspectAccess::RO)
{
DE_ASSERT(static_cast<bool>(stencilROAccesses));
usageFlags |= getReadOnlyUsageFlags(*stencilROAccesses);
}
return usageFlags;
}
void addDescriptors (IODescVec& descriptors, uint32_t& inputAttachmentCount, const ROAccessVec& accesses, VkImageAspectFlagBits aspect)
{
for (const auto& access : accesses)
{
// Get a new binding number and a new input attachment index if needed, then append the new descriptor to the list if
// appropriate.
InputOutputDescriptor descriptor =
{
static_cast<uint32_t>(descriptors.size()), // uint32_t binding;
tcu::Nothing, // tcu::Maybe<uint32_t> inputAttachmentIndex;
aspect, // VkImageAspectFlagBits aspect;
};
if (access == ReadOnlyAccess::INPUT_ATTACHMENT)
descriptor.inputAttachmentIndex = tcu::just(inputAttachmentCount++);
if (access == ReadOnlyAccess::INPUT_ATTACHMENT || access == ReadOnlyAccess::SAMPLED)
descriptors.push_back(descriptor);
}
}
IODescVec TestParams::getDescriptors () const
{
IODescVec descriptors;
uint32_t inputAttachmentCount = 0u;
if (static_cast<bool>(depthROAccesses))
addDescriptors(descriptors, inputAttachmentCount, *depthROAccesses, VK_IMAGE_ASPECT_DEPTH_BIT);
if (static_cast<bool>(stencilROAccesses))
addDescriptors(descriptors, inputAttachmentCount, *stencilROAccesses, VK_IMAGE_ASPECT_STENCIL_BIT);
return descriptors;
}
bool TestParams::dsAttachmentNeeded () const
{
// The depth/stencil attachment is needed if the image is going to be used as a depth/stencil attachment or an input attachment.
return (inputAttachmentNeeded() || depthBufferNeeded());
}
bool TestParams::depthAsInputAttachment () const
{
return (depthAccess == AspectAccess::RO && de::contains(begin(*depthROAccesses), end(*depthROAccesses), ReadOnlyAccess::INPUT_ATTACHMENT));
}
bool TestParams::stencilAsInputAttachment () const
{
return (stencilAccess == AspectAccess::RO && de::contains(begin(*stencilROAccesses), end(*stencilROAccesses), ReadOnlyAccess::INPUT_ATTACHMENT));
}
bool TestParams::inputAttachmentNeeded () const
{
// An input attachment is needed if any of the depth or stencil aspects include a read-only access as an input attachment.
return (depthAsInputAttachment() || stencilAsInputAttachment());
}
bool TestParams::depthBufferNeeded () const
{
// The depth buffer is needed if any of the depth or stencil aspects include a read-write or read-only DS access.
return (needsDepthTest() || needsStencilTest());
}
bool TestParams::needsDepthTest () const
{
// The depth test is needed if the depth aspect includes a read-write or read-only DS access.
return (depthAccess == AspectAccess::RW || (depthAccess == AspectAccess::RO && de::contains(begin(*depthROAccesses), end(*depthROAccesses), ReadOnlyAccess::DS_ATTACHMENT)));
}
bool TestParams::needsStencilTest () const
{
// The stencil test is needed if the stencil aspect includes a read-write or read-only DS access.
return (stencilAccess == AspectAccess::RW || (stencilAccess == AspectAccess::RO && de::contains(begin(*stencilROAccesses), end(*stencilROAccesses), ReadOnlyAccess::DS_ATTACHMENT)));
}
class DepthStencilDescriptorCase : public vkt::TestCase
{
public:
DepthStencilDescriptorCase (tcu::TestContext& testCtx, const std::string& name, const std::string& description, const TestParams& params);
virtual ~DepthStencilDescriptorCase (void) {}
void checkSupport (Context& context) const override;
TestInstance* createInstance (Context& context) const override;
void initPrograms (vk::SourceCollections& programCollection) const override;
protected:
TestParams m_params;
};
class DepthStencilDescriptorInstance : public vkt::TestInstance
{
public:
DepthStencilDescriptorInstance (Context& context, const TestParams& params);
virtual ~DepthStencilDescriptorInstance (void) {}
tcu::TestStatus iterate() override;
protected:
// Must match the shaders.
struct PushConstantData
{
float colorR;
float colorG;
float colorB;
float colorA;
float depth;
PushConstantData ()
: colorR (0.0f)
, colorG (0.0f)
, colorB (0.0f)
, colorA (0.0f)
, depth (0.0f)
{}
PushConstantData (const tcu::Vec4& color, float depth_)
: colorR (color.x())
, colorG (color.y())
, colorB (color.z())
, colorA (color.w())
, depth (depth_)
{}
};
TestParams m_params;
};
DepthStencilDescriptorCase::DepthStencilDescriptorCase (tcu::TestContext& testCtx, const std::string& name, const std::string& description, const TestParams& params)
: vkt::TestCase (testCtx, name, description)
, m_params (params)
{}
void DepthStencilDescriptorCase::checkSupport (Context& context) const
{
context.requireDeviceFunctionality("VK_KHR_create_renderpass2");
const auto requiredExtension = layoutExtension(m_params.layout);
if (requiredExtension)
context.requireDeviceFunctionality(*requiredExtension);
// Check format support.
const auto& vki = context.getInstanceInterface();
const auto physDev = context.getPhysicalDevice();
const auto imgType = VK_IMAGE_TYPE_2D;
const auto tiling = VK_IMAGE_TILING_OPTIMAL;
const auto usage = m_params.getUsageFlags();
VkImageFormatProperties formatProperties;
const auto res = vki.getPhysicalDeviceImageFormatProperties(physDev, m_params.format, imgType, tiling, usage, 0u, &formatProperties);
if (res == VK_ERROR_FORMAT_NOT_SUPPORTED)
TCU_THROW(NotSupportedError, "Format does not support required properties");
else if (res != VK_SUCCESS)
TCU_FAIL("vkGetPhysicalDeviceImageFormatProperties returned " + de::toString(res));
}
TestInstance* DepthStencilDescriptorCase::createInstance (Context& context) const
{
return new DepthStencilDescriptorInstance(context, m_params);
}
void DepthStencilDescriptorCase::initPrograms (vk::SourceCollections& programCollection) const
{
std::ostringstream vert;
vert
<< "#version 450\n"
<< "\n"
<< "layout(push_constant, std430) uniform PushConstantBlock {\n"
<< " float colorR;\n"
<< " float colorG;\n"
<< " float colorB;\n"
<< " float colorA;\n"
<< " float depth;\n"
<< "} pc;\n"
<< "\n"
<< "vec2 vertexPositions[3] = vec2[](\n"
<< " vec2(-1.0, -1.0),\n"
<< " vec2(-1.0, 3.0),\n"
<< " vec2( 3.0, -1.0));\n"
<< "\n"
<< "void main () {\n"
<< " gl_Position = vec4(vertexPositions[gl_VertexIndex], pc.depth, 1.0);\n"
<< "}\n"
;
programCollection.glslSources.add("vert") << glu::VertexSource(vert.str());
// When any of the image aspects is going to be used as an input attachment or sampled image, we need an input descriptor and an
// output descriptor to verify reading from it.
std::ostringstream descriptorsDecl;
std::ostringstream descriptorsSideEffects;
const auto descriptors = m_params.getDescriptors();
// Samplers set (set number 2).
descriptorsDecl
<< "layout (set=2, binding=0) uniform sampler globalSampler;\n" // Sampler with float border color (depth).
<< "layout (set=2, binding=1) uniform sampler uglobalSampler;\n" // Sampler with int border color (stencil).
;
for (const auto& descriptor : descriptors)
{
const auto prefix = ((descriptor.aspect == VK_IMAGE_ASPECT_STENCIL_BIT) ? "u" : "");
const auto suffix = ((descriptor.aspect == VK_IMAGE_ASPECT_STENCIL_BIT) ? "ui" : "f");
std::ostringstream loadOp;
// Input descriptor declaration.
if (static_cast<bool>(descriptor.inputAttachmentIndex))
{
const auto& iaIndex = *descriptor.inputAttachmentIndex;
descriptorsDecl << "layout (input_attachment_index=" << iaIndex << ", set=0, binding=" << descriptor.binding << ") uniform " << prefix << "subpassInput attachment" << descriptor.binding << ";\n";
loadOp << "subpassLoad(attachment" << descriptor.binding << ")";
}
else
{
descriptorsDecl << "layout (set=0, binding=" << descriptor.binding << ") uniform " << prefix << "texture2D sampledImage" << descriptor.binding << ";\n";
loadOp << "texture(" << prefix << "sampler2D(sampledImage" << descriptor.binding << ", " << prefix << "globalSampler), gl_FragCoord.xy)"; // This needs a sampler with unnormalizedCoordinates == VK_TRUE.
}
// Output descriptor declaration (output descriptors in set 1).
descriptorsDecl << "layout (r32" << suffix << ", set=1, binding=" << descriptor.binding << ") uniform " << prefix << "image2D storage" << descriptor.binding << ";\n";
// The corresponding side effect.
descriptorsSideEffects << " imageStore(storage" << descriptor.binding << ", ivec2(gl_FragCoord.xy), " << loadOp.str() << ");\n";
}
std::ostringstream frag;
frag
<< "#version 450\n"
<< "\n"
<< "layout(location=0) out vec4 outColor;\n"
<< "layout(push_constant, std430) uniform PushConstantBlock {\n"
<< " float colorR;\n"
<< " float colorG;\n"
<< " float colorB;\n"
<< " float colorA;\n"
<< " float depth;\n"
<< "} pc;\n"
<< "\n"
<< descriptorsDecl.str()
<< "\n"
<< "void main () {\n"
<< descriptorsSideEffects.str()
<< " outColor = vec4(pc.colorR, pc.colorG, pc.colorB, pc.colorA);\n"
<< "}\n"
;
programCollection.glslSources.add("frag") << glu::FragmentSource(frag.str());
}
DepthStencilDescriptorInstance::DepthStencilDescriptorInstance (Context& context, const TestParams& params)
: vkt::TestInstance (context)
, m_params (params)
{}
VkFormat getAspectStorageFormat (VkImageAspectFlagBits aspect)
{
return ((aspect == VK_IMAGE_ASPECT_DEPTH_BIT) ? getFloatStorageFormat() : getUintStorageFormat());
}
VkDeviceSize getCopyBufferSize (const tcu::TextureFormat& format, const VkExtent3D& extent)
{
return static_cast<VkDeviceSize>(static_cast<uint32_t>(tcu::getPixelSize(format)) * extent.width * extent.height * extent.depth);
}
tcu::TestStatus DepthStencilDescriptorInstance::iterate ()
{
const auto& vkd = m_context.getDeviceInterface();
const auto device = m_context.getDevice();
auto& alloc = m_context.getDefaultAllocator();
const auto qIndex = m_context.getUniversalQueueFamilyIndex();
const auto queue = m_context.getUniversalQueue();
const auto extent = getExtent();
const auto usage = m_params.getUsageFlags();
const auto colorUsage = (VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT | VK_IMAGE_USAGE_TRANSFER_SRC_BIT | VK_IMAGE_USAGE_TRANSFER_DST_BIT);
const auto colorFormat = getColorBufferFormat();
const auto tcuColorFormat = mapVkFormat(colorFormat);
const auto storageUsage = (VK_IMAGE_USAGE_STORAGE_BIT | VK_IMAGE_USAGE_TRANSFER_SRC_BIT | VK_IMAGE_USAGE_TRANSFER_DST_BIT);
const auto stageFlags = VK_SHADER_STAGE_FRAGMENT_BIT;
const auto tcuFormat = mapVkFormat(m_params.format);
const auto hasDepth = tcu::hasDepthComponent(tcuFormat.order);
const auto hasStencil = tcu::hasStencilComponent(tcuFormat.order);
const auto colorSRR = makeImageSubresourceRange(VK_IMAGE_ASPECT_COLOR_BIT, 0u, 1u, 0u, 1u);
const auto outputImgLayout = VK_IMAGE_LAYOUT_GENERAL;
const auto colorLayout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL;
// Copy formats.
const auto tcuDepthFormat = (hasDepth ? getDepthCopyFormat(m_params.format) : tcu::TextureFormat());
const auto tcuStencilFormat = (hasStencil ? getStencilCopyFormat(m_params.format) : tcu::TextureFormat());
// These must match the depth test configuration when enabled.
const float depthClearValue = 0.5f;
const float depthFailValue = 1.0f;
const float depthPassValue = 0.0f;
// These must match the stencil test configuration when enabled.
const uint32_t stencilClearVal = 100u;
const uint32_t stencilFailVal = 200u;
const uint32_t stencilPassVal = 10u;
// For the color buffer.
const tcu::Vec4 colorClearVal (0.0f, 0.0f, 0.0f, 1.0f);
const tcu::Vec4 colorFailVal (1.0f, 0.0f, 0.0f, 1.0f);
const tcu::Vec4 colorPassVal (0.0f, 1.0f, 0.0f, 1.0f);
// Will the test update the depth/stencil buffer?
const auto stencilWrites = (m_params.stencilAccess == AspectAccess::RW);
const auto depthWrites = (m_params.depthAccess == AspectAccess::RW);
// Create color attachment.
const VkImageCreateInfo colorBufferInfo =
{
VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO, // VkStructureType sType;
nullptr, // const void* pNext;
0u, // VkImageCreateFlags flags;
VK_IMAGE_TYPE_2D, // VkImageType imageType;
colorFormat, // VkFormat format;
extent, // VkExtent3D extent;
1u, // uint32_t mipLevels;
1u, // uint32_t arrayLayers;
VK_SAMPLE_COUNT_1_BIT, // VkSampleCountFlagBits samples;
VK_IMAGE_TILING_OPTIMAL, // VkImageTiling tiling;
colorUsage, // VkImageUsageFlags usage;
VK_SHARING_MODE_EXCLUSIVE, // VkSharingMode sharingMode;
0u, // uint32_t queueFamilyIndexCount;
nullptr, // const uint32_t* pQueueFamilyIndices;
VK_IMAGE_LAYOUT_UNDEFINED, // VkImageLayout initialLayout;
};
ImageWithMemory colorBuffer (vkd, device, alloc, colorBufferInfo, MemoryRequirement::Any);
const auto colorView = makeImageView(vkd, device, colorBuffer.get(), VK_IMAGE_VIEW_TYPE_2D, colorFormat, colorSRR);
// Create depth/stencil image.
const VkImageCreateInfo dsImageInfo =
{
VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO, // VkStructureType sType;
nullptr, // const void* pNext;
0u, // VkImageCreateFlags flags;
VK_IMAGE_TYPE_2D, // VkImageType imageType;
m_params.format, // VkFormat format;
extent, // VkExtent3D extent;
1u, // uint32_t mipLevels;
1u, // uint32_t arrayLayers;
VK_SAMPLE_COUNT_1_BIT, // VkSampleCountFlagBits samples;
VK_IMAGE_TILING_OPTIMAL, // VkImageTiling tiling;
usage, // VkImageUsageFlags usage;
VK_SHARING_MODE_EXCLUSIVE, // VkSharingMode sharingMode;
0u, // uint32_t queueFamilyIndexCount;
nullptr, // const uint32_t* pQueueFamilyIndices;
VK_IMAGE_LAYOUT_UNDEFINED, // VkImageLayout initialLayout;
};
ImageWithMemory dsImage(vkd, device, alloc, dsImageInfo, MemoryRequirement::Any);
const auto depthStencilSRR = makeImageSubresourceRange(getImageAspectFlags(tcuFormat), 0u, 1u, 0u, 1u);
const auto depthSRR = makeImageSubresourceRange(VK_IMAGE_ASPECT_DEPTH_BIT, 0u, 1u, 0u, 1u);
const auto stencilSRR = makeImageSubresourceRange(VK_IMAGE_ASPECT_STENCIL_BIT, 0u, 1u, 0u, 1u);
const auto dsImageView = makeImageView(vkd, device, dsImage.get(), VK_IMAGE_VIEW_TYPE_2D, m_params.format, depthStencilSRR);
Move<VkImageView> depthOnlyView;
Move<VkImageView> stencilOnlyView;
if (hasDepth)
depthOnlyView = makeImageView(vkd, device, dsImage.get(), VK_IMAGE_VIEW_TYPE_2D, m_params.format, depthSRR);
if (hasStencil)
stencilOnlyView = makeImageView(vkd, device, dsImage.get(), VK_IMAGE_VIEW_TYPE_2D, m_params.format, stencilSRR);
// Get expected descriptors and create output images for them.
const auto descriptors = m_params.getDescriptors();
std::vector<de::MovePtr<ImageWithMemory>> outputImages;
std::vector<Move<VkImageView>> outputImageViews;
outputImages.reserve(descriptors.size());
outputImageViews.reserve(descriptors.size());
for (const auto& desc : descriptors)
{
// Floating point images to copy the depth aspect and unsigned int images to copy the stencil aspect.
const auto imageFormat = getAspectStorageFormat(desc.aspect);
const VkImageCreateInfo createInfo =
{
VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO, // VkStructureType sType;
nullptr, // const void* pNext;
0u, // VkImageCreateFlags flags;
VK_IMAGE_TYPE_2D, // VkImageType imageType;
imageFormat, // VkFormat format;
extent, // VkExtent3D extent;
1u, // uint32_t mipLevels;
1u, // uint32_t arrayLayers;
VK_SAMPLE_COUNT_1_BIT, // VkSampleCountFlagBits samples;
VK_IMAGE_TILING_OPTIMAL, // VkImageTiling tiling;
storageUsage, // VkImageUsageFlags usage;
VK_SHARING_MODE_EXCLUSIVE, // VkSharingMode sharingMode;
0u, // uint32_t queueFamilyIndexCount;
nullptr, // const uint32_t* pQueueFamilyIndices;
VK_IMAGE_LAYOUT_UNDEFINED, // VkImageLayout initialLayout;
};
outputImages.push_back(de::MovePtr<ImageWithMemory>(new ImageWithMemory(vkd, device, alloc, createInfo, MemoryRequirement::Any)));
outputImageViews.push_back(makeImageView(vkd, device, outputImages.back()->get(), VK_IMAGE_VIEW_TYPE_2D, imageFormat, colorSRR));
}
// Create samplers.
Move<VkSampler> samplerFloat;
Move<VkSampler> samplerInt;
{
VkSamplerCreateInfo samplerCreateInfo =
{
VK_STRUCTURE_TYPE_SAMPLER_CREATE_INFO, // VkStructureType sType;
nullptr, // const void* pNext;
0u, // VkSamplerCreateFlags flags;
VK_FILTER_NEAREST, // VkFilter magFilter;
VK_FILTER_NEAREST, // VkFilter minFilter;
VK_SAMPLER_MIPMAP_MODE_NEAREST, // VkSamplerMipmapMode mipmapMode;
VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE, // VkSamplerAddressMode addressModeU;
VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE, // VkSamplerAddressMode addressModeV;
VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE, // VkSamplerAddressMode addressModeW;
0.0f, // float mipLodBias;
VK_FALSE, // VkBool32 anisotropyEnable;
0.0f, // float maxAnisotropy;
VK_FALSE, // VkBool32 compareEnable;
VK_COMPARE_OP_LAST, // VkCompareOp compareOp;
0.0f, // float minLod;
0.0f, // float maxLod;
VK_BORDER_COLOR_FLOAT_OPAQUE_BLACK, // VkBorderColor borderColor;
VK_TRUE, // VkBool32 unnormalizedCoordinates;
};
// Note the samplers are created with unnormalizedCoordinates as per how they are used in shader code.
samplerFloat = createSampler(vkd, device, &samplerCreateInfo);
samplerCreateInfo.borderColor = VK_BORDER_COLOR_INT_OPAQUE_BLACK;
samplerInt = createSampler(vkd, device, &samplerCreateInfo);
}
// Create input and output descriptor set layouts.
Move<VkDescriptorSetLayout> inputSetLayout;
Move<VkDescriptorSetLayout> outputSetLayout;
Move<VkDescriptorSetLayout> samplerSetLayout;
{
DescriptorSetLayoutBuilder inputLayoutBuilder;
for (const auto& desc : descriptors)
{
if (static_cast<bool>(desc.inputAttachmentIndex))
inputLayoutBuilder.addSingleBinding(VK_DESCRIPTOR_TYPE_INPUT_ATTACHMENT, stageFlags);
else
inputLayoutBuilder.addSingleBinding(VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE, stageFlags);
}
inputSetLayout = inputLayoutBuilder.build(vkd, device);
}
{
DescriptorSetLayoutBuilder outputLayoutBuilder;
for (size_t i = 0; i < descriptors.size(); ++i)
outputLayoutBuilder.addSingleBinding(VK_DESCRIPTOR_TYPE_STORAGE_IMAGE, stageFlags);
outputSetLayout = outputLayoutBuilder.build(vkd, device);
}
{
DescriptorSetLayoutBuilder samplerLayoutBuilder;
samplerLayoutBuilder.addSingleBinding(VK_DESCRIPTOR_TYPE_SAMPLER, stageFlags);
samplerLayoutBuilder.addSingleBinding(VK_DESCRIPTOR_TYPE_SAMPLER, stageFlags);
samplerSetLayout = samplerLayoutBuilder.build(vkd, device);
}
const std::vector<VkDescriptorSetLayout> setLayouts = { inputSetLayout.get(), outputSetLayout.get(), samplerSetLayout.get() };
// Descriptor pool and descriptor sets.
Move<VkDescriptorPool> descriptorPool;
Move<VkDescriptorSet> inputSet;
Move<VkDescriptorSet> outputSet;
Move<VkDescriptorSet> samplerSet;
{
DescriptorPoolBuilder poolBuilder;
// Input descriptors.
for (const auto& desc : descriptors)
{
if (static_cast<bool>(desc.inputAttachmentIndex))
poolBuilder.addType(VK_DESCRIPTOR_TYPE_INPUT_ATTACHMENT);
else
poolBuilder.addType(VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE);
}
// Output descriptors.
poolBuilder.addType(VK_DESCRIPTOR_TYPE_STORAGE_IMAGE, static_cast<uint32_t>(descriptors.size()));
// Global samplers.
poolBuilder.addType(VK_DESCRIPTOR_TYPE_SAMPLER, 2u);
descriptorPool = poolBuilder.build(vkd, device, VK_DESCRIPTOR_POOL_CREATE_FREE_DESCRIPTOR_SET_BIT, static_cast<uint32_t>(setLayouts.size()));
}
inputSet = makeDescriptorSet(vkd, device, descriptorPool.get(), inputSetLayout.get());
outputSet = makeDescriptorSet(vkd, device, descriptorPool.get(), outputSetLayout.get());
samplerSet = makeDescriptorSet(vkd, device, descriptorPool.get(), samplerSetLayout.get());
const std::vector<VkDescriptorSet> descriptorSets = { inputSet.get(), outputSet.get(), samplerSet.get() };
// Update descriptor sets.
{
DescriptorSetUpdateBuilder inputUpdateBuilder;
DescriptorSetUpdateBuilder outputUpdateBuilder;
DescriptorSetUpdateBuilder samplerUpdateBuilder;
std::vector<VkDescriptorImageInfo> inputImgInfos;
std::vector<VkDescriptorImageInfo> outputImgInfos;
std::vector<VkDescriptorImageInfo> samplerImgInfos;
// Make sure addresses are stable (pushing elements back while taking their addresses).
inputImgInfos.reserve(descriptors.size());
outputImgInfos.reserve(descriptors.size());
samplerImgInfos.reserve(2u);
for (size_t descriptorIdx = 0u; descriptorIdx < descriptors.size(); ++descriptorIdx)
{
const auto& desc = descriptors[descriptorIdx];
const auto isIA = static_cast<bool>(desc.inputAttachmentIndex);
const auto location = DescriptorSetUpdateBuilder::Location::binding(desc.binding);
// Input descriptors.
const auto inType = (isIA ? VK_DESCRIPTOR_TYPE_INPUT_ATTACHMENT : VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE);
const auto view = ((desc.aspect == VK_IMAGE_ASPECT_DEPTH_BIT) ? depthOnlyView.get() : stencilOnlyView.get());
inputImgInfos.push_back(makeDescriptorImageInfo(DE_NULL, view, m_params.layout));
inputUpdateBuilder.writeSingle(inputSet.get(), location, inType, &inputImgInfos.back());
// Output descriptors.
outputImgInfos.push_back(makeDescriptorImageInfo(DE_NULL, outputImageViews[descriptorIdx].get(), outputImgLayout));
outputUpdateBuilder.writeSingle(outputSet.get(), location, VK_DESCRIPTOR_TYPE_STORAGE_IMAGE, &outputImgInfos.back());
}
inputUpdateBuilder.update(vkd, device);
outputUpdateBuilder.update(vkd, device);
// Samplers.
samplerImgInfos.push_back(makeDescriptorImageInfo(samplerFloat.get(), DE_NULL, VK_IMAGE_LAYOUT_UNDEFINED));
samplerUpdateBuilder.writeSingle(samplerSet.get(), DescriptorSetUpdateBuilder::Location::binding(0u), VK_DESCRIPTOR_TYPE_SAMPLER, &samplerImgInfos.back());
samplerImgInfos.push_back(makeDescriptorImageInfo(samplerInt.get(), DE_NULL, VK_IMAGE_LAYOUT_UNDEFINED));
samplerUpdateBuilder.writeSingle(samplerSet.get(), DescriptorSetUpdateBuilder::Location::binding(1u), VK_DESCRIPTOR_TYPE_SAMPLER, &samplerImgInfos.back());
samplerUpdateBuilder.update(vkd, device);
}
PushConstantData pcData;
const auto pcStages = (VK_SHADER_STAGE_VERTEX_BIT | VK_SHADER_STAGE_FRAGMENT_BIT);
const auto pcSize = static_cast<uint32_t>(sizeof(pcData));
const auto pcRange = makePushConstantRange(pcStages, 0u, pcSize);
// Pipeline layout.
const auto pipelineLayout = makePipelineLayout(vkd, device, static_cast<uint32_t>(setLayouts.size()), de::dataOrNull(setLayouts), 1u, &pcRange);
// Render pass.
Move<VkRenderPass> renderPass;
{
std::vector<VkAttachmentDescription2> attachmentDescriptions;
std::vector<VkAttachmentReference2> attachmentReferences;
const VkAttachmentDescription2 colorAttachmentDesc =
{
VK_STRUCTURE_TYPE_ATTACHMENT_DESCRIPTION_2, // VkStructureType sType;
nullptr, // const void* pNext;
0u, // VkAttachmentDescriptionFlags flags;
colorFormat, // VkFormat format;
VK_SAMPLE_COUNT_1_BIT, // VkSampleCountFlagBits samples;
VK_ATTACHMENT_LOAD_OP_LOAD, // VkAttachmentLoadOp loadOp;
VK_ATTACHMENT_STORE_OP_STORE, // VkAttachmentStoreOp storeOp;
VK_ATTACHMENT_LOAD_OP_DONT_CARE, // VkAttachmentLoadOp stencilLoadOp;
VK_ATTACHMENT_STORE_OP_DONT_CARE, // VkAttachmentStoreOp stencilStoreOp;
colorLayout, // VkImageLayout initialLayout;
colorLayout, // VkImageLayout finalLayout;
};
attachmentDescriptions.push_back(colorAttachmentDesc);
const VkAttachmentReference2 colorAttachmentRef =
{
VK_STRUCTURE_TYPE_ATTACHMENT_REFERENCE_2, // VkStructureType sType;
nullptr, // const void* pNext;
0u, // uint32_t attachment;
colorLayout, // VkImageLayout layout;
VK_IMAGE_ASPECT_COLOR_BIT, // VkImageAspectFlags aspectMask;
};
attachmentReferences.push_back(colorAttachmentRef);
const auto needsIA = m_params.inputAttachmentNeeded();
const auto needsDepthBuffer = m_params.depthBufferNeeded();
DE_ASSERT(!(needsIA && needsDepthBuffer));
if (m_params.dsAttachmentNeeded())
{
const VkAttachmentDescription2 dsAttachmentDesc =
{
VK_STRUCTURE_TYPE_ATTACHMENT_DESCRIPTION_2,
nullptr,
0u,
m_params.format,
VK_SAMPLE_COUNT_1_BIT,
(hasDepth ? VK_ATTACHMENT_LOAD_OP_LOAD : VK_ATTACHMENT_LOAD_OP_DONT_CARE),
((hasDepth && depthWrites) ? VK_ATTACHMENT_STORE_OP_STORE : VK_ATTACHMENT_STORE_OP_DONT_CARE),
(hasStencil ? VK_ATTACHMENT_LOAD_OP_LOAD : VK_ATTACHMENT_LOAD_OP_DONT_CARE),
((hasStencil && stencilWrites) ? VK_ATTACHMENT_STORE_OP_STORE : VK_ATTACHMENT_STORE_OP_DONT_CARE),
m_params.layout,
m_params.layout,
};
attachmentDescriptions.push_back(dsAttachmentDesc);
const VkAttachmentReference2 dsAttachmentRef =
{
VK_STRUCTURE_TYPE_ATTACHMENT_REFERENCE_2, // VkStructureType sType;
nullptr, // const void* pNext;
1u, // uint32_t attachment;
m_params.layout, // VkImageLayout layout;
// VkImageAspectFlags aspectMask;
( (m_params.depthAsInputAttachment() ? static_cast<VkImageAspectFlags>(VK_IMAGE_ASPECT_DEPTH_BIT) : 0u)
| (m_params.stencilAsInputAttachment() ? static_cast<VkImageAspectFlags>(VK_IMAGE_ASPECT_STENCIL_BIT) : 0u)),
};
attachmentReferences.push_back(dsAttachmentRef);
}
const VkSubpassDescription2 subpassDesc =
{
VK_STRUCTURE_TYPE_SUBPASS_DESCRIPTION_2, // VkStructureType sType;
nullptr, // const void* pNext;
0u, // VkSubpassDescriptionFlags flags;
VK_PIPELINE_BIND_POINT_GRAPHICS, // VkPipelineBindPoint pipelineBindPoint;
0u, // uint32_t viewMask;
(needsIA ? 1u : 0u), // uint32_t inputAttachmentCount;
(needsIA ? &attachmentReferences.at(1) : nullptr), // const VkAttachmentReference* pInputAttachments;
1u, // uint32_t colorAttachmentCount;
&attachmentReferences.at(0), // const VkAttachmentReference* pColorAttachments;
nullptr, // const VkAttachmentReference* pResolveAttachments;
(needsDepthBuffer ? &attachmentReferences.at(1) : nullptr), // const VkAttachmentReference* pDepthStencilAttachment;
0u, // uint32_t preserveAttachmentCount;
nullptr, // const uint32_t* pPreserveAttachments;
};
const VkRenderPassCreateInfo2 renderPassCreateInfo =
{
VK_STRUCTURE_TYPE_RENDER_PASS_CREATE_INFO_2, // VkStructureType sType;
nullptr, // const void* pNext;
0u, // VkRenderPassCreateFlags flags;
static_cast<uint32_t>(attachmentDescriptions.size()), // uint32_t attachmentCount;
de::dataOrNull(attachmentDescriptions), // const VkAttachmentDescription* pAttachments;
1u, // uint32_t subpassCount;
&subpassDesc, // const VkSubpassDescription* pSubpasses;
0u, // uint32_t dependencyCount;
nullptr, // const VkSubpassDependency* pDependencies;
0u, // uint32_t correlatedViewMaskCount;
nullptr, // const uint32_t* pCorrelatedViewMasks;
};
renderPass = createRenderPass2(vkd, device, &renderPassCreateInfo);
}
// Framebuffer.
std::vector<VkImageView> framebufferViews;
framebufferViews.push_back(colorView.get());
if (m_params.dsAttachmentNeeded())
framebufferViews.push_back(dsImageView.get());
const auto framebuffer = makeFramebuffer(vkd, device, renderPass.get(), static_cast<uint32_t>(framebufferViews.size()), de::dataOrNull(framebufferViews), extent.width, extent.height);
// Pipeline.
std::vector<Move<VkPipeline>> graphicsPipelines;
{
const auto vertModule = createShaderModule(vkd, device, m_context.getBinaryCollection().get("vert"), 0u);
const auto fragModule = createShaderModule(vkd, device, m_context.getBinaryCollection().get("frag"), 0u);
const VkPipelineVertexInputStateCreateInfo vertexInputInfo = initVulkanStructure();
VkPipelineInputAssemblyStateCreateInfo inputAssemblyInfo = initVulkanStructure();
VkPipelineViewportStateCreateInfo viewportInfo = initVulkanStructure();
VkPipelineRasterizationStateCreateInfo rasterizationInfo = initVulkanStructure();
VkPipelineMultisampleStateCreateInfo multisampleInfo = initVulkanStructure();
VkPipelineDepthStencilStateCreateInfo dsStateInfo = initVulkanStructure();
VkPipelineColorBlendStateCreateInfo colorBlendInfo = initVulkanStructure();
VkPipelineColorBlendAttachmentState colorBlendAttState = {};
// Topology.
inputAssemblyInfo.topology = VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST;
// Viewports and scissors.
const auto viewport = makeViewport(extent);
const auto scissor = makeRect2D(extent);
viewportInfo.viewportCount = 1u;
viewportInfo.pViewports = &viewport;
viewportInfo.scissorCount = 1u;
viewportInfo.pScissors = &scissor;
// Line width.
rasterizationInfo.lineWidth = 1.0f;
// Multisample state.
multisampleInfo.rasterizationSamples = VK_SAMPLE_COUNT_1_BIT;
// Depth/stencil state. This depends on the test parameters.
if (m_params.needsDepthTest())
dsStateInfo.depthTestEnable = VK_TRUE;
if (depthWrites)
dsStateInfo.depthWriteEnable = VK_TRUE;
dsStateInfo.depthCompareOp = VK_COMPARE_OP_LESS;
if (m_params.needsStencilTest())
dsStateInfo.stencilTestEnable = VK_TRUE;
const auto stencilOpState = makeStencilOpState(VK_STENCIL_OP_KEEP, // failOp
(stencilWrites ? VK_STENCIL_OP_REPLACE : VK_STENCIL_OP_KEEP), // passOp
VK_STENCIL_OP_KEEP, // depthFailOp
VK_COMPARE_OP_LESS, // compareOp
0xFFu, // compareMask
(stencilWrites ? 0xFFu : 0u), // writeMask
stencilFailVal); // reference
dsStateInfo.front = stencilOpState;
dsStateInfo.back = stencilOpState;
colorBlendAttState.colorWriteMask = (VK_COLOR_COMPONENT_R_BIT | VK_COLOR_COMPONENT_G_BIT | VK_COLOR_COMPONENT_B_BIT | VK_COLOR_COMPONENT_A_BIT);
colorBlendAttState.blendEnable = VK_FALSE;
colorBlendInfo.attachmentCount = 1u;
colorBlendInfo.pAttachments = &colorBlendAttState;
graphicsPipelines.push_back(makeGraphicsPipeline(vkd, device, pipelineLayout.get(),
vertModule.get(), DE_NULL, DE_NULL, DE_NULL, fragModule.get(), // Shader modules.
renderPass.get(), 0u /*subpass*/,
&vertexInputInfo, &inputAssemblyInfo, nullptr, &viewportInfo, &rasterizationInfo, &multisampleInfo, &dsStateInfo, &colorBlendInfo, nullptr));
// When the stencil test is enabled, we need a second pipeline changing the reference value so the stencil test passes the second time.
if (m_params.needsStencilTest())
{
dsStateInfo.front.reference = stencilPassVal;
dsStateInfo.back.reference = stencilPassVal;
graphicsPipelines.push_back(makeGraphicsPipeline(vkd, device, pipelineLayout.get(),
vertModule.get(), DE_NULL, DE_NULL, DE_NULL, fragModule.get(), // Shader modules.
renderPass.get(), 0u /*subpass*/,
&vertexInputInfo, &inputAssemblyInfo, nullptr, &viewportInfo, &rasterizationInfo, &multisampleInfo, &dsStateInfo, &colorBlendInfo, nullptr));
}
}
// Command pool and buffer.
const auto cmdPool = makeCommandPool(vkd, device, qIndex);
const auto cmdBufferPtr = allocateCommandBuffer(vkd, device, cmdPool.get(), VK_COMMAND_BUFFER_LEVEL_PRIMARY);
const auto cmdBuffer = cmdBufferPtr.get();
const auto renderArea = makeRect2D(extent);
// Output buffers to check the color attachment, depth/stencil attachment and output storage images.
using BufferPtr = de::MovePtr<BufferWithMemory>;
BufferPtr colorVerifBuffer;
BufferPtr depthVerifBuffer;
BufferPtr stencilVerifBuffer;
std::vector<BufferPtr> storageVerifBuffers;
{
const auto colorVerifBufferSize = getCopyBufferSize(tcuColorFormat, extent);
const auto colorVerifBufferInfo = makeBufferCreateInfo(colorVerifBufferSize, VK_BUFFER_USAGE_TRANSFER_DST_BIT);
colorVerifBuffer = BufferPtr(new BufferWithMemory(vkd, device, alloc, colorVerifBufferInfo, MemoryRequirement::HostVisible));
}
if (hasDepth)
{
const auto depthVerifBufferSize = getCopyBufferSize(tcuDepthFormat, extent);
const auto depthVerifBufferInfo = makeBufferCreateInfo(depthVerifBufferSize, VK_BUFFER_USAGE_TRANSFER_DST_BIT);
depthVerifBuffer = BufferPtr(new BufferWithMemory(vkd, device, alloc, depthVerifBufferInfo, MemoryRequirement::HostVisible));
}
if (hasStencil)
{
const auto stencilVerifBufferSize = getCopyBufferSize(tcuStencilFormat, extent);
const auto stencilVerifBufferInfo = makeBufferCreateInfo(stencilVerifBufferSize, VK_BUFFER_USAGE_TRANSFER_DST_BIT);
stencilVerifBuffer = BufferPtr(new BufferWithMemory(vkd, device, alloc, stencilVerifBufferInfo, MemoryRequirement::HostVisible));
}
storageVerifBuffers.reserve(descriptors.size());
for (const auto& desc : descriptors)
{
const auto storageFormat = getAspectStorageFormat(desc.aspect);
const auto tcuStorageFormat = mapVkFormat(storageFormat);
const auto descVerifBufferSize = getCopyBufferSize(tcuStorageFormat, extent);
const auto descVerifBufferInfo = makeBufferCreateInfo(descVerifBufferSize, VK_BUFFER_USAGE_TRANSFER_DST_BIT);
storageVerifBuffers.emplace_back(new BufferWithMemory(vkd, device, alloc, descVerifBufferInfo, MemoryRequirement::HostVisible));
}
beginCommandBuffer(vkd, cmdBuffer);
// Transition layout for output images.
std::vector<VkImageMemoryBarrier> outputImgBarriers;
for (const auto& outputImg : outputImages)
outputImgBarriers.push_back(makeImageMemoryBarrier(0u, (VK_ACCESS_SHADER_READ_BIT | VK_ACCESS_SHADER_WRITE_BIT), VK_IMAGE_LAYOUT_UNDEFINED, VK_IMAGE_LAYOUT_GENERAL, outputImg->get(), colorSRR));
vkd.cmdPipelineBarrier(cmdBuffer, VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT, VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT, 0u, 0u, nullptr, 0u, nullptr, static_cast<uint32_t>(outputImgBarriers.size()), de::dataOrNull(outputImgBarriers));
// Clear color and depth/stencil buffer.
const auto colorPreTransferBarrier = makeImageMemoryBarrier(0u, VK_ACCESS_TRANSFER_WRITE_BIT, VK_IMAGE_LAYOUT_UNDEFINED, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, colorBuffer.get(), colorSRR);
const auto dsPreTransferBarrier = makeImageMemoryBarrier(0u, VK_ACCESS_TRANSFER_WRITE_BIT, VK_IMAGE_LAYOUT_UNDEFINED, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, dsImage.get(), depthStencilSRR);
const std::vector<VkImageMemoryBarrier> preTransferBarriers = { colorPreTransferBarrier, dsPreTransferBarrier };
vkd.cmdPipelineBarrier(cmdBuffer, VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT, VK_PIPELINE_STAGE_TRANSFER_BIT, 0u, 0u, nullptr, 0u, nullptr,
static_cast<uint32_t>(preTransferBarriers.size()), de::dataOrNull(preTransferBarriers));
const auto colorClearValue = makeClearValueColorVec4(colorClearVal);
const auto dsClearValue = makeClearValueDepthStencil(depthClearValue, stencilClearVal);
vkd.cmdClearColorImage(cmdBuffer, colorBuffer.get(), VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, &colorClearValue.color, 1u, &colorSRR);
vkd.cmdClearDepthStencilImage(cmdBuffer, dsImage.get(), VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, &dsClearValue.depthStencil, 1u, &depthStencilSRR);
const auto colorPostTransferBarrier = makeImageMemoryBarrier(VK_ACCESS_TRANSFER_WRITE_BIT, 0u, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, colorLayout, colorBuffer.get(), colorSRR);
const auto dsPostTransferBarrier = makeImageMemoryBarrier(VK_ACCESS_TRANSFER_WRITE_BIT, 0u, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, m_params.layout, dsImage.get(), depthStencilSRR);
const std::vector<VkImageMemoryBarrier> postTransferBarriers = { colorPostTransferBarrier, dsPostTransferBarrier };
vkd.cmdPipelineBarrier(cmdBuffer, VK_PIPELINE_STAGE_TRANSFER_BIT, VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT, 0u, 0u, nullptr, 0u, nullptr,
static_cast<uint32_t>(postTransferBarriers.size()), de::dataOrNull(postTransferBarriers));
// Render pass.
beginRenderPass(vkd, cmdBuffer, renderPass.get(), framebuffer.get(), renderArea);
vkd.cmdBindDescriptorSets(cmdBuffer, VK_PIPELINE_BIND_POINT_GRAPHICS, pipelineLayout.get(),
0u, static_cast<uint32_t>(descriptorSets.size()), de::dataOrNull(descriptorSets), 0u, nullptr);
const auto useSecondDraw = m_params.depthBufferNeeded();
vkd.cmdBindPipeline(cmdBuffer, VK_PIPELINE_BIND_POINT_GRAPHICS, graphicsPipelines.at(0).get());
{
if (useSecondDraw)
{
// Two draws: the first draw will use the red color.
pcData = PushConstantData(colorFailVal, (m_params.needsDepthTest() ? depthFailValue : depthPassValue));
}
else
{
// If there will be no more draws, the first one needs to pass and use the right color.
pcData = PushConstantData(colorPassVal, depthPassValue);
}
vkd.cmdPushConstants(cmdBuffer, pipelineLayout.get(), pcStages, 0u, pcSize, &pcData);
vkd.cmdDraw(cmdBuffer, 3u, 1u, 0u, 0u);
}
if (useSecondDraw)
{
// The second draw, if used, always needs to pass and use the right color.
if (m_params.needsStencilTest())
{
// Pipeline with a good stencil reference value.
DE_ASSERT(graphicsPipelines.size() > 1);
vkd.cmdBindPipeline(cmdBuffer, VK_PIPELINE_BIND_POINT_GRAPHICS, graphicsPipelines.at(1).get());
}
pcData = PushConstantData(colorPassVal, depthPassValue);
vkd.cmdPushConstants(cmdBuffer, pipelineLayout.get(), pcStages, 0u, pcSize, &pcData);
vkd.cmdDraw(cmdBuffer, 3u, 1u, 0u, 0u);
}
endRenderPass(vkd, cmdBuffer);
// Copy color attachment.
{
const auto colorLayers = makeImageSubresourceLayers(VK_IMAGE_ASPECT_COLOR_BIT, 0u, 0u, 1u);
const auto copyRegion = makeBufferImageCopy(extent, colorLayers);
const auto colorPostWriteBarrier = makeImageMemoryBarrier(VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT, VK_ACCESS_TRANSFER_READ_BIT, colorLayout, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, colorBuffer.get(), colorSRR);
vkd.cmdPipelineBarrier(cmdBuffer, VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT, VK_PIPELINE_STAGE_TRANSFER_BIT, 0u, 0u, nullptr, 0u, nullptr, 1u, &colorPostWriteBarrier);
vkd.cmdCopyImageToBuffer(cmdBuffer, colorBuffer.get(), VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, colorVerifBuffer->get(), 1u, &copyRegion);
}
// Copy aspects of DS attachment.
{
const auto dsPostWriteBarrier = makeImageMemoryBarrier(VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT, VK_ACCESS_TRANSFER_READ_BIT, m_params.layout, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, dsImage.get(), depthStencilSRR);
const auto fragmentTestStages = (VK_PIPELINE_STAGE_EARLY_FRAGMENT_TESTS_BIT | VK_PIPELINE_STAGE_LATE_FRAGMENT_TESTS_BIT);
vkd.cmdPipelineBarrier(cmdBuffer, fragmentTestStages, VK_PIPELINE_STAGE_TRANSFER_BIT, 0u, 0u, nullptr, 0u, nullptr, 1u, &dsPostWriteBarrier);
if (hasDepth)
{
const auto depthLayers = makeImageSubresourceLayers(VK_IMAGE_ASPECT_DEPTH_BIT, 0u, 0u, 1u);
const auto copyRegion = makeBufferImageCopy(extent, depthLayers);
vkd.cmdCopyImageToBuffer(cmdBuffer, dsImage.get(), VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, depthVerifBuffer->get(), 1u, &copyRegion);
}
if (hasStencil)
{
const auto stencilLayers = makeImageSubresourceLayers(VK_IMAGE_ASPECT_STENCIL_BIT, 0u, 0u, 1u);
const auto copyRegion = makeBufferImageCopy(extent, stencilLayers);
vkd.cmdCopyImageToBuffer(cmdBuffer, dsImage.get(), VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, stencilVerifBuffer->get(), 1u, &copyRegion);
}
}
// Copy storage images.
{
std::vector<VkImageMemoryBarrier> storagePostBarriers;
storagePostBarriers.reserve(outputImages.size());
for (const auto& outImg : outputImages)
storagePostBarriers.push_back(makeImageMemoryBarrier(VK_ACCESS_SHADER_WRITE_BIT, VK_ACCESS_TRANSFER_READ_BIT, VK_IMAGE_LAYOUT_GENERAL, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, outImg->get(), colorSRR));
vkd.cmdPipelineBarrier(cmdBuffer, VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT, VK_PIPELINE_STAGE_TRANSFER_BIT, 0u, 0u, nullptr, 0u, nullptr, static_cast<uint32_t>(storagePostBarriers.size()), de::dataOrNull(storagePostBarriers));
const auto colorLayers = makeImageSubresourceLayers(VK_IMAGE_ASPECT_COLOR_BIT, 0u, 0u, 1u);
const auto copyRegion = makeBufferImageCopy(extent, colorLayers);
DE_ASSERT(outputImages.size() == storageVerifBuffers.size());
for (size_t i = 0u; i < outputImages.size(); ++i)
vkd.cmdCopyImageToBuffer(cmdBuffer, outputImages[i]->get(), VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, storageVerifBuffers[i]->get(), 1u, &copyRegion);
}
// Transfer to host barrier for buffers.
const auto transferToHostBarrier = makeMemoryBarrier(VK_ACCESS_TRANSFER_WRITE_BIT, VK_ACCESS_HOST_READ_BIT);
vkd.cmdPipelineBarrier(cmdBuffer, VK_PIPELINE_STAGE_TRANSFER_BIT, VK_PIPELINE_STAGE_HOST_BIT, 0u, 1u, &transferToHostBarrier, 0u, nullptr, 0u, nullptr);
endCommandBuffer(vkd, cmdBuffer);
submitCommandsAndWait(vkd, device, queue, cmdBuffer);
// Verify the different buffers.
const tcu::IVec3 iExtent (static_cast<int>(extent.width), static_cast<int>(extent.height), static_cast<int>(extent.depth));
auto& log = m_context.getTestContext().getLog();
// Verify color buffer contents.
{
auto& verifAlloc = colorVerifBuffer->getAllocation();
invalidateAlloc(vkd, device, verifAlloc);
tcu::ConstPixelBufferAccess colorPixels(tcuColorFormat, iExtent, verifAlloc.getHostPtr());
if (!tcu::floatThresholdCompare(log, "ColorResult", "", colorPassVal, colorPixels, tcu::Vec4(0.0f), tcu::COMPARE_LOG_ON_ERROR))
TCU_FAIL("Unexpected color buffer contents; check log for details");
}
// Verify depth buffer contents.
if (hasDepth)
{
auto& verifAlloc = depthVerifBuffer->getAllocation();
invalidateAlloc(vkd, device, verifAlloc);
tcu::TextureLevel referenceDepth (tcuDepthFormat, iExtent.x(), iExtent.y(), iExtent.z());
auto referenceAccess = referenceDepth.getAccess();
const auto refDepthVal = (depthWrites ? depthPassValue : depthClearValue);
for (int z = 0; z < iExtent.z(); ++z)
for (int y = 0; y < iExtent.y(); ++y)
for (int x = 0; x < iExtent.x(); ++x)
referenceAccess.setPixDepth(refDepthVal, x, y, z);
tcu::ConstPixelBufferAccess depthPixels(tcuDepthFormat, iExtent, verifAlloc.getHostPtr());
if (!tcu::dsThresholdCompare(log, "DepthResult", "", referenceAccess, depthPixels, 0.1f, tcu::COMPARE_LOG_ON_ERROR))
TCU_FAIL("Unexpected value in depth buffer; check log for details");
}
// Verify stencil buffer contents.
if (hasStencil)
{
auto& verifAlloc = stencilVerifBuffer->getAllocation();
invalidateAlloc(vkd, device, verifAlloc);
tcu::TextureLevel referenceStencil (tcuStencilFormat, iExtent.x(), iExtent.y(), iExtent.z());
auto referenceAccess = referenceStencil.getAccess();
const auto refStencilVal = static_cast<int>(stencilWrites ? stencilPassVal : stencilClearVal);
for (int z = 0; z < iExtent.z(); ++z)
for (int y = 0; y < iExtent.y(); ++y)
for (int x = 0; x < iExtent.x(); ++x)
referenceAccess.setPixStencil(refStencilVal, x, y, z);
tcu::ConstPixelBufferAccess stencilPixels(tcuStencilFormat, iExtent, verifAlloc.getHostPtr());
if (!tcu::dsThresholdCompare(log, "StencilResult", "", referenceAccess, stencilPixels, 0.0f, tcu::COMPARE_LOG_ON_ERROR))
TCU_FAIL("Unexpected value in stencil buffer; check log for details");
}
// Verify output images.
for (size_t bufferIdx = 0u; bufferIdx < storageVerifBuffers.size(); ++bufferIdx)
{
const auto& verifBuffer = storageVerifBuffers[bufferIdx];
auto& verifAlloc = verifBuffer->getAllocation();
invalidateAlloc(vkd, device, verifAlloc);
const auto bufferFormat = getAspectStorageFormat(descriptors.at(bufferIdx).aspect);
const auto tcuBufferFormat = mapVkFormat(bufferFormat);
tcu::ConstPixelBufferAccess colorPixels (tcuBufferFormat, iExtent, verifAlloc.getHostPtr());
const std::string resultName = "Storage" + de::toString(bufferIdx);
if (descriptors.at(bufferIdx).aspect == VK_IMAGE_ASPECT_DEPTH_BIT)
{
if (!tcu::floatThresholdCompare(log, resultName.c_str(), "", tcu::Vec4(depthClearValue, 0.0f, 0.0f, 1.0f), colorPixels, tcu::Vec4(0.1f, 0.0f, 0.0f, 0.0f), tcu::COMPARE_LOG_ON_ERROR))
TCU_FAIL("Unexpected value in depth storage buffer " + de::toString(bufferIdx) + "; check log for details");
}
else if (descriptors.at(bufferIdx).aspect == VK_IMAGE_ASPECT_STENCIL_BIT)
{
tcu::TextureLevel stencilRef(tcuBufferFormat, iExtent.x(), iExtent.y(), iExtent.z());
auto colorPIxels = stencilRef.getAccess();
for (int z = 0; z < iExtent.z(); ++z)
for (int y = 0; y < iExtent.y(); ++y)
for (int x = 0; x < iExtent.x(); ++x)
colorPIxels.setPixel(tcu::UVec4(stencilClearVal, 0u, 0u, 0u), x, y, z);
if (!tcu::intThresholdCompare(log, resultName.c_str(), "", colorPIxels, colorPixels, tcu::UVec4(0u), tcu::COMPARE_LOG_ON_ERROR))
TCU_FAIL("Unexpected value in stencil storage buffer " + de::toString(bufferIdx) + "; check log for details");
}
else
DE_ASSERT(false);
}
return tcu::TestStatus::pass("Pass");
}
} // anonymous
tcu::TestCaseGroup* createImageDepthStencilDescriptorTests (tcu::TestContext& testCtx)
{
using TestCaseGroupPtr = de::MovePtr<tcu::TestCaseGroup>;
const VkFormat kDepthStencilFormats[] =
{
VK_FORMAT_D16_UNORM,
VK_FORMAT_X8_D24_UNORM_PACK32,
VK_FORMAT_D32_SFLOAT,
VK_FORMAT_S8_UINT,
VK_FORMAT_D16_UNORM_S8_UINT,
VK_FORMAT_D24_UNORM_S8_UINT,
VK_FORMAT_D32_SFLOAT_S8_UINT,
};
// Layouts used in these tests as VkDescriptorImageInfo::imageLayout.
const VkImageLayout kTestedLayouts[] =
{
VK_IMAGE_LAYOUT_DEPTH_READ_ONLY_STENCIL_ATTACHMENT_OPTIMAL,
VK_IMAGE_LAYOUT_DEPTH_ATTACHMENT_STENCIL_READ_ONLY_OPTIMAL,
VK_IMAGE_LAYOUT_DEPTH_READ_ONLY_OPTIMAL,
VK_IMAGE_LAYOUT_STENCIL_READ_ONLY_OPTIMAL,
};
// Types of read-only combinations to test.
ROAccessVec kReadOnlyDSAttachment = { ReadOnlyAccess::DS_ATTACHMENT };
ROAccessVec kReadOnlyInputAttachment = { ReadOnlyAccess::INPUT_ATTACHMENT };
ROAccessVec kReadOnlySampled = { ReadOnlyAccess::SAMPLED };
ROAccessVec kReadOnlyDSSampled = { ReadOnlyAccess::DS_ATTACHMENT, ReadOnlyAccess::SAMPLED };
ROAccessVec kReadOnlyInputSampled = { ReadOnlyAccess::INPUT_ATTACHMENT, ReadOnlyAccess::SAMPLED };
const ROAccessVec* kROAccessCases[] =
{
&kReadOnlyDSAttachment,
&kReadOnlyInputAttachment,
&kReadOnlySampled,
&kReadOnlyDSSampled,
&kReadOnlyInputSampled,
};
const auto kLayoutPrefixLen = std::string("VK_IMAGE_LAYOUT_").size();
const auto kFormatPrefixLen = std::string("VK_FORMAT_").size();
TestCaseGroupPtr mainGroup(new tcu::TestCaseGroup(testCtx, "depth_stencil_descriptor", "Tests using depth/stencil images as descriptors"));
for (const auto& layout : kTestedLayouts)
{
const auto layoutStr = de::toString(layout);
const auto layoutGroupName = de::toLower(layoutStr.substr(kLayoutPrefixLen));
const auto layoutGroupDesc = "Tests using the " + layoutStr + " layout";
TestCaseGroupPtr layoutGroup(new tcu::TestCaseGroup(testCtx, layoutGroupName.c_str(), layoutGroupDesc.c_str()));
for (const auto& format : kDepthStencilFormats)
{
const auto formatStr = de::toString(format);
const auto formatGroupName = de::toLower(formatStr.substr(kFormatPrefixLen));
const auto formatGroupDesc = "Tests using the " + formatStr + " format";
TestCaseGroupPtr formatGroup(new tcu::TestCaseGroup(testCtx, formatGroupName.c_str(), formatGroupDesc.c_str()));
const auto depthAccess = getLegalAccess(layout, VK_IMAGE_ASPECT_DEPTH_BIT);
const auto stencilAccess = getLegalAccess(layout, VK_IMAGE_ASPECT_STENCIL_BIT);
const auto tcuFormat = mapVkFormat(format);
const auto hasDepthAccess = (depthAccess != AspectAccess::NONE);
const auto hasStencilAccess = (stencilAccess != AspectAccess::NONE);
const auto hasDepth = tcu::hasDepthComponent(tcuFormat.order);
const auto hasStencil = tcu::hasStencilComponent(tcuFormat.order);
if (hasDepthAccess != hasDepth)
continue;
if (hasStencilAccess != hasStencil)
continue;
if (depthAccess == AspectAccess::RO)
{
for (const auto& depthROCase : kROAccessCases)
{
const std::string depthPart = "depth_" + de::toString(*depthROCase);
if (stencilAccess == AspectAccess::RO)
{
for (const auto& stencilROCase : kROAccessCases)
{
if (incompatibleInputAttachmentAccess(depthAccess, depthROCase, stencilAccess, stencilROCase))
continue;
const std::string stencilPart = "_stencil_" + de::toString(*stencilROCase);
const TestParams params =
{
format, // VkFormat format;
layout, // VkImageLayout layout;
depthAccess, // AspectAccess depthAccess;
stencilAccess, // AspectAccess stencilAccess;
tcu::just(*depthROCase), // tcu::Maybe<ROAccessVec> depthROAccesses;
tcu::just(*stencilROCase), // tcu::Maybe<ROAccessVec> stencilROAccesses;
};
formatGroup->addChild(new DepthStencilDescriptorCase(testCtx, depthPart + stencilPart, "", params));
}
}
else
{
if (incompatibleInputAttachmentAccess(depthAccess, depthROCase, stencilAccess, nullptr))
continue;
const std::string stencilPart = "_stencil_" + de::toString(stencilAccess);
const TestParams params =
{
format, // VkFormat format;
layout, // VkImageLayout layout;
depthAccess, // AspectAccess depthAccess;
stencilAccess, // AspectAccess stencilAccess;
tcu::just(*depthROCase), // tcu::Maybe<ROAccessVec> depthROAccesses;
tcu::Nothing, // tcu::Maybe<ROAccessVec> stencilROAccesses;
};
formatGroup->addChild(new DepthStencilDescriptorCase(testCtx, depthPart + stencilPart, "", params));
}
}
}
else
{
const std::string depthPart = "depth_" + de::toString(depthAccess);
if (stencilAccess == AspectAccess::RO)
{
for (const auto& stencilROCase : kROAccessCases)
{
if (incompatibleInputAttachmentAccess(depthAccess, nullptr, stencilAccess, stencilROCase))
continue;
const std::string stencilPart = "_stencil_" + de::toString(*stencilROCase);
const TestParams params =
{
format, // VkFormat format;
layout, // VkImageLayout layout;
depthAccess, // AspectAccess depthAccess;
stencilAccess, // AspectAccess stencilAccess;
tcu::Nothing, // tcu::Maybe<ROAccessVec> depthROAccesses;
tcu::just(*stencilROCase), // tcu::Maybe<ROAccessVec> stencilROAccesses;
};
formatGroup->addChild(new DepthStencilDescriptorCase(testCtx, depthPart + stencilPart, "", params));
}
}
else
{
if (incompatibleInputAttachmentAccess(depthAccess, nullptr, stencilAccess, nullptr))
continue;
const std::string stencilPart = "_stencil_" + de::toString(stencilAccess);
const TestParams params =
{
format, // VkFormat format;
layout, // VkImageLayout layout;
depthAccess, // AspectAccess depthAccess;
stencilAccess, // AspectAccess stencilAccess;
tcu::Nothing, // tcu::Maybe<ROAccessVec> depthROAccesses;
tcu::Nothing, // tcu::Maybe<ROAccessVec> stencilROAccesses;
};
formatGroup->addChild(new DepthStencilDescriptorCase(testCtx, depthPart + stencilPart, "", params));
}
}
layoutGroup->addChild(formatGroup.release());
}
mainGroup->addChild(layoutGroup.release());
}
return mainGroup.release();
}
} // image
} // vkt