blob: d6134220e4624ded3ef503bfcc8b146ff3ae2ea5 [file] [log] [blame]
/*-------------------------------------------------------------------------
* Vulkan Conformance Tests
* ------------------------
*
* Copyright (c) 2016 The Khronos Group Inc.
*
* 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 Buffer and image memory requirements tests.
*//*--------------------------------------------------------------------*/
#include "vktMemoryRequirementsTests.hpp"
#include "vktTestCaseUtil.hpp"
#include "vktTestGroupUtil.hpp"
#include "vkDefs.hpp"
#include "vkRef.hpp"
#include "vkRefUtil.hpp"
#include "vkMemUtil.hpp"
#include "vkQueryUtil.hpp"
#include "vkStrUtil.hpp"
#include "vkTypeUtil.hpp"
#include "deUniquePtr.hpp"
#include "deStringUtil.hpp"
#include "deSTLUtil.hpp"
#include "tcuResultCollector.hpp"
#include "tcuTestLog.hpp"
namespace vkt
{
namespace memory
{
namespace
{
using namespace vk;
using de::MovePtr;
Move<VkBuffer> makeBuffer (const DeviceInterface& vk, const VkDevice device, const VkDeviceSize size, const VkBufferCreateFlags flags, const VkBufferUsageFlags usage)
{
const VkBufferCreateInfo createInfo =
{
VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO, // VkStructureType sType;
DE_NULL, // const void* pNext;
flags, // VkBufferCreateFlags flags;
size, // VkDeviceSize size;
usage, // VkBufferUsageFlags usage;
VK_SHARING_MODE_EXCLUSIVE, // VkSharingMode sharingMode;
0u, // uint32_t queueFamilyIndexCount;
DE_NULL, // const uint32_t* pQueueFamilyIndices;
};
return createBuffer(vk, device, &createInfo);
}
VkMemoryRequirements getBufferMemoryRequirements (const DeviceInterface& vk, const VkDevice device, const VkDeviceSize size, const VkBufferCreateFlags flags, const VkBufferUsageFlags usage)
{
const Unique<VkBuffer> buffer(makeBuffer(vk, device, size, flags, usage));
return getBufferMemoryRequirements(vk, device, *buffer);
}
VkMemoryRequirements getBufferMemoryRequirements2 (const DeviceInterface& vk, const VkDevice device, const VkDeviceSize size, const VkBufferCreateFlags flags, const VkBufferUsageFlags usage, void* next = DE_NULL)
{
const Unique<VkBuffer> buffer (makeBuffer(vk, device, size, flags, usage));
VkBufferMemoryRequirementsInfo2KHR info =
{
VK_STRUCTURE_TYPE_BUFFER_MEMORY_REQUIREMENTS_INFO_2_KHR, // VkStructureType sType
DE_NULL, // const void* pNext
*buffer // VkBuffer buffer
};
VkMemoryRequirements2KHR req2 =
{
VK_STRUCTURE_TYPE_MEMORY_REQUIREMENTS_2_KHR, // VkStructureType sType
next, // void* pNext
{0, 0, 0} // VkMemoryRequirements memoryRequirements
};
vk.getBufferMemoryRequirements2KHR(device, &info, &req2);
return req2.memoryRequirements;
}
VkMemoryRequirements getImageMemoryRequirements2 (const DeviceInterface& vk, const VkDevice device, const VkImageCreateInfo& createInfo, void* next = DE_NULL)
{
const Unique<VkImage> image(createImage(vk, device, &createInfo));
VkImageMemoryRequirementsInfo2KHR info =
{
VK_STRUCTURE_TYPE_IMAGE_MEMORY_REQUIREMENTS_INFO_2_KHR, // VkStructureType sType
DE_NULL, // const void* pNext
*image // VkImage image
};
VkMemoryRequirements2KHR req2 =
{
VK_STRUCTURE_TYPE_MEMORY_REQUIREMENTS_2_KHR, // VkStructureType sType
next, // void* pNext
{0, 0, 0} // VkMemoryRequirements memoryRequirements
};
vk.getImageMemoryRequirements2KHR(device, &info, &req2);
return req2.memoryRequirements;
}
//! Get an index of each set bit, starting from the least significant bit.
std::vector<deUint32> bitsToIndices (deUint32 bits)
{
std::vector<deUint32> indices;
for (deUint32 i = 0u; bits != 0u; ++i, bits >>= 1)
{
if (bits & 1u)
indices.push_back(i);
}
return indices;
}
template<typename T>
T nextEnum (T value)
{
return static_cast<T>(static_cast<deUint32>(value) + 1);
}
template<typename T>
T nextFlag (T value)
{
if (value)
return static_cast<T>(static_cast<deUint32>(value) << 1);
else
return static_cast<T>(1);
}
template<typename T>
T nextFlagExcluding (T value, T excludedFlags)
{
deUint32 tmp = static_cast<deUint32>(value);
while ((tmp = nextFlag(tmp)) & static_cast<deUint32>(excludedFlags));
return static_cast<T>(tmp);
}
class IBufferMemoryRequirements
{
public:
virtual void populateTestGroup (tcu::TestCaseGroup* group) = 0;
protected:
virtual void addFunctionTestCase (tcu::TestCaseGroup* group,
const std::string& name,
const std::string& desc,
VkBufferCreateFlags arg0) = 0;
virtual tcu::TestStatus execTest (Context& context,
const VkBufferCreateFlags bufferFlags) = 0;
virtual void preTestChecks (Context& context,
const InstanceInterface& vki,
const VkPhysicalDevice physDevice,
const VkBufferCreateFlags flags) = 0;
virtual void updateMemoryRequirements (const DeviceInterface& vk,
const VkDevice device,
const VkDeviceSize size,
const VkBufferCreateFlags flags,
const VkBufferUsageFlags usage,
const bool all) = 0;
virtual void verifyMemoryRequirements (tcu::ResultCollector& result,
const VkPhysicalDeviceMemoryProperties& deviceMemoryProperties,
const VkPhysicalDeviceLimits& limits,
const VkBufferCreateFlags bufferFlags,
const VkBufferUsageFlags usage) = 0;
};
class BufferMemoryRequirementsOriginal : public IBufferMemoryRequirements
{
static tcu::TestStatus testEntryPoint (Context& context,
const VkBufferCreateFlags bufferFlags);
public:
virtual void populateTestGroup (tcu::TestCaseGroup* group);
protected:
virtual void addFunctionTestCase (tcu::TestCaseGroup* group,
const std::string& name,
const std::string& desc,
VkBufferCreateFlags arg0);
virtual tcu::TestStatus execTest (Context& context,
const VkBufferCreateFlags bufferFlags);
virtual void preTestChecks (Context& context,
const InstanceInterface& vki,
const VkPhysicalDevice physDevice,
const VkBufferCreateFlags flags);
virtual void updateMemoryRequirements (const DeviceInterface& vk,
const VkDevice device,
const VkDeviceSize size,
const VkBufferCreateFlags flags,
const VkBufferUsageFlags usage,
const bool all);
virtual void verifyMemoryRequirements (tcu::ResultCollector& result,
const VkPhysicalDeviceMemoryProperties& deviceMemoryProperties,
const VkPhysicalDeviceLimits& limits,
const VkBufferCreateFlags bufferFlags,
const VkBufferUsageFlags usage);
protected:
VkMemoryRequirements m_allUsageFlagsRequirements;
VkMemoryRequirements m_currentTestRequirements;
};
tcu::TestStatus BufferMemoryRequirementsOriginal::testEntryPoint (Context& context, const VkBufferCreateFlags bufferFlags)
{
BufferMemoryRequirementsOriginal test;
return test.execTest(context, bufferFlags);
}
void BufferMemoryRequirementsOriginal::populateTestGroup (tcu::TestCaseGroup* group)
{
const struct
{
VkBufferCreateFlags flags;
const char* const name;
} bufferCases[] =
{
{ (VkBufferCreateFlags)0, "regular" },
{ VK_BUFFER_CREATE_SPARSE_BINDING_BIT, "sparse" },
{ VK_BUFFER_CREATE_SPARSE_BINDING_BIT | VK_BUFFER_CREATE_SPARSE_RESIDENCY_BIT, "sparse_residency" },
{ VK_BUFFER_CREATE_SPARSE_BINDING_BIT | VK_BUFFER_CREATE_SPARSE_ALIASED_BIT, "sparse_aliased" },
{ VK_BUFFER_CREATE_SPARSE_BINDING_BIT | VK_BUFFER_CREATE_SPARSE_RESIDENCY_BIT | VK_BUFFER_CREATE_SPARSE_ALIASED_BIT, "sparse_residency_aliased" },
};
de::MovePtr<tcu::TestCaseGroup> bufferGroup(new tcu::TestCaseGroup(group->getTestContext(), "buffer", ""));
for (int ndx = 0; ndx < DE_LENGTH_OF_ARRAY(bufferCases); ++ndx)
addFunctionTestCase(bufferGroup.get(), bufferCases[ndx].name, "", bufferCases[ndx].flags);
group->addChild(bufferGroup.release());
}
void BufferMemoryRequirementsOriginal::addFunctionTestCase (tcu::TestCaseGroup* group,
const std::string& name,
const std::string& desc,
VkBufferCreateFlags arg0)
{
addFunctionCase(group, name, desc, testEntryPoint, arg0);
}
tcu::TestStatus BufferMemoryRequirementsOriginal::execTest (Context& context, const VkBufferCreateFlags bufferFlags)
{
const DeviceInterface& vk = context.getDeviceInterface();
const InstanceInterface& vki = context.getInstanceInterface();
const VkDevice device = context.getDevice();
const VkPhysicalDevice physDevice = context.getPhysicalDevice();
preTestChecks(context, vki, physDevice, bufferFlags);
const VkPhysicalDeviceMemoryProperties memoryProperties = getPhysicalDeviceMemoryProperties(vki, physDevice);
const VkPhysicalDeviceLimits limits = getPhysicalDeviceProperties(vki, physDevice).limits;
const VkBufferUsageFlags allUsageFlags = static_cast<VkBufferUsageFlags>((VK_BUFFER_USAGE_INDIRECT_BUFFER_BIT << 1) - 1);
tcu::TestLog& log = context.getTestContext().getLog();
bool allPass = true;
const VkDeviceSize sizeCases[] =
{
1 * 1024,
8 * 1024,
64 * 1024,
1024 * 1024,
};
// Updates m_allUsageFlags* fields
updateMemoryRequirements(vk, device, 1024, bufferFlags, allUsageFlags, true); // doesn't depend on size
for (VkBufferUsageFlags usage = VK_BUFFER_USAGE_TRANSFER_SRC_BIT; usage <= VK_BUFFER_USAGE_INDIRECT_BUFFER_BIT; usage = nextFlag(usage))
{
deUint32 previousMemoryTypeBits = 0u;
VkDeviceSize previousAlignment = 0u;
log << tcu::TestLog::Message << "Verify a buffer with usage flags: " << de::toString(getBufferUsageFlagsStr(usage)) << tcu::TestLog::EndMessage;
for (const VkDeviceSize* pSize = sizeCases; pSize < sizeCases + DE_LENGTH_OF_ARRAY(sizeCases); ++pSize)
{
log << tcu::TestLog::Message << "- size " << *pSize << " bytes" << tcu::TestLog::EndMessage;
tcu::ResultCollector result(log, "ERROR: ");
// Updates m_allUsageFlags* fields
updateMemoryRequirements(vk, device, *pSize, bufferFlags, usage, false);
// Check:
// - requirements for a particular buffer usage
// - memoryTypeBits are a subset of bits for requirements with all usage flags combined
verifyMemoryRequirements(result, memoryProperties, limits, bufferFlags, usage);
// Check that for the same usage and create flags:
// - memoryTypeBits are the same
// - alignment is the same
if (pSize > sizeCases)
{
result.check(m_currentTestRequirements.memoryTypeBits == previousMemoryTypeBits,
"memoryTypeBits differ from the ones in the previous buffer size");
result.check(m_currentTestRequirements.alignment == previousAlignment,
"alignment differs from the one in the previous buffer size");
}
if (result.getResult() != QP_TEST_RESULT_PASS)
allPass = false;
previousMemoryTypeBits = m_currentTestRequirements.memoryTypeBits;
previousAlignment = m_currentTestRequirements.alignment;
}
if (!allPass)
break;
}
return allPass ? tcu::TestStatus::pass("Pass") : tcu::TestStatus::fail("Some memory requirements were incorrect");
}
void BufferMemoryRequirementsOriginal::preTestChecks (Context& ,
const InstanceInterface& vki,
const VkPhysicalDevice physDevice,
const VkBufferCreateFlags flags)
{
const VkPhysicalDeviceFeatures features = getPhysicalDeviceFeatures(vki, physDevice);
if ((flags & VK_BUFFER_CREATE_SPARSE_BINDING_BIT) && !features.sparseBinding)
TCU_THROW(NotSupportedError, "Feature not supported: sparseBinding");
if ((flags & VK_BUFFER_CREATE_SPARSE_RESIDENCY_BIT) && !features.sparseResidencyBuffer)
TCU_THROW(NotSupportedError, "Feature not supported: sparseResidencyBuffer");
if ((flags & VK_BUFFER_CREATE_SPARSE_ALIASED_BIT) && !features.sparseResidencyAliased)
TCU_THROW(NotSupportedError, "Feature not supported: sparseResidencyAliased");
}
void BufferMemoryRequirementsOriginal::updateMemoryRequirements (const DeviceInterface& vk,
const VkDevice device,
const VkDeviceSize size,
const VkBufferCreateFlags flags,
const VkBufferUsageFlags usage,
const bool all)
{
if (all)
{
m_allUsageFlagsRequirements = getBufferMemoryRequirements(vk, device, size, flags, usage);
}
else
{
m_currentTestRequirements = getBufferMemoryRequirements(vk, device, size, flags, usage);
}
}
void BufferMemoryRequirementsOriginal::verifyMemoryRequirements (tcu::ResultCollector& result,
const VkPhysicalDeviceMemoryProperties& deviceMemoryProperties,
const VkPhysicalDeviceLimits& limits,
const VkBufferCreateFlags bufferFlags,
const VkBufferUsageFlags usage)
{
if (result.check(m_currentTestRequirements.memoryTypeBits != 0, "VkMemoryRequirements memoryTypeBits has no bits set"))
{
typedef std::vector<deUint32>::const_iterator IndexIterator;
const std::vector<deUint32> usedMemoryTypeIndices = bitsToIndices(m_currentTestRequirements.memoryTypeBits);
bool deviceLocalMemoryFound = false;
bool hostVisibleCoherentMemoryFound = false;
for (IndexIterator memoryTypeNdx = usedMemoryTypeIndices.begin(); memoryTypeNdx != usedMemoryTypeIndices.end(); ++memoryTypeNdx)
{
if (*memoryTypeNdx >= deviceMemoryProperties.memoryTypeCount)
{
result.fail("VkMemoryRequirements memoryTypeBits contains bits for non-existing memory types");
continue;
}
const VkMemoryPropertyFlags memoryPropertyFlags = deviceMemoryProperties.memoryTypes[*memoryTypeNdx].propertyFlags;
if (memoryPropertyFlags & VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT)
deviceLocalMemoryFound = true;
if (memoryPropertyFlags & (VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT))
hostVisibleCoherentMemoryFound = true;
result.check((memoryPropertyFlags & VK_MEMORY_PROPERTY_LAZILY_ALLOCATED_BIT) == 0u,
"Memory type includes VK_MEMORY_PROPERTY_LAZILY_ALLOCATED_BIT");
}
result.check(deIsPowerOfTwo64(static_cast<deUint64>(m_currentTestRequirements.alignment)) == DE_TRUE,
"VkMemoryRequirements alignment isn't power of two");
if (usage & (VK_BUFFER_USAGE_UNIFORM_TEXEL_BUFFER_BIT | VK_BUFFER_USAGE_UNIFORM_TEXEL_BUFFER_BIT))
{
result.check(m_currentTestRequirements.alignment >= limits.minTexelBufferOffsetAlignment,
"VkMemoryRequirements alignment doesn't respect minTexelBufferOffsetAlignment");
}
if (usage & VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT)
{
result.check(m_currentTestRequirements.alignment >= limits.minUniformBufferOffsetAlignment,
"VkMemoryRequirements alignment doesn't respect minUniformBufferOffsetAlignment");
}
if (usage & VK_BUFFER_USAGE_STORAGE_BUFFER_BIT)
{
result.check(m_currentTestRequirements.alignment >= limits.minStorageBufferOffsetAlignment,
"VkMemoryRequirements alignment doesn't respect minStorageBufferOffsetAlignment");
}
result.check(deviceLocalMemoryFound,
"None of the required memory types included VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT");
result.check((bufferFlags & VK_BUFFER_CREATE_SPARSE_BINDING_BIT) || hostVisibleCoherentMemoryFound,
"Required memory type doesn't include VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT and VK_MEMORY_PROPERTY_HOST_COHERENT_BIT");
result.check((m_currentTestRequirements.memoryTypeBits & m_allUsageFlagsRequirements.memoryTypeBits) == m_allUsageFlagsRequirements.memoryTypeBits,
"Memory type bits aren't a superset of memory type bits for all usage flags combined");
}
}
class BufferMemoryRequirementsExtended : public BufferMemoryRequirementsOriginal
{
static tcu::TestStatus testEntryPoint (Context& context,
const VkBufferCreateFlags bufferFlags);
protected:
virtual void addFunctionTestCase (tcu::TestCaseGroup* group,
const std::string& name,
const std::string& desc,
VkBufferCreateFlags arg0);
virtual void preTestChecks (Context& context,
const InstanceInterface& vki,
const VkPhysicalDevice physDevice,
const VkBufferCreateFlags flags);
virtual void updateMemoryRequirements (const DeviceInterface& vk,
const VkDevice device,
const VkDeviceSize size,
const VkBufferCreateFlags flags,
const VkBufferUsageFlags usage,
const bool all);
};
tcu::TestStatus BufferMemoryRequirementsExtended::testEntryPoint (Context& context, const VkBufferCreateFlags bufferFlags)
{
BufferMemoryRequirementsExtended test;
return test.execTest(context, bufferFlags);
}
void BufferMemoryRequirementsExtended::addFunctionTestCase (tcu::TestCaseGroup* group,
const std::string& name,
const std::string& desc,
VkBufferCreateFlags arg0)
{
addFunctionCase(group, name, desc, testEntryPoint, arg0);
}
void BufferMemoryRequirementsExtended::preTestChecks (Context& context,
const InstanceInterface& vki,
const VkPhysicalDevice physDevice,
const VkBufferCreateFlags flags)
{
const std::string extensionName("VK_KHR_get_memory_requirements2");
if (!de::contains(context.getDeviceExtensions().begin(), context.getDeviceExtensions().end(), extensionName))
TCU_THROW(NotSupportedError, std::string(extensionName + " is not supported").c_str());
BufferMemoryRequirementsOriginal::preTestChecks(context, vki, physDevice, flags);
}
void BufferMemoryRequirementsExtended::updateMemoryRequirements (const DeviceInterface& vk,
const VkDevice device,
const VkDeviceSize size,
const VkBufferCreateFlags flags,
const VkBufferUsageFlags usage,
const bool all)
{
if (all)
{
m_allUsageFlagsRequirements = getBufferMemoryRequirements2(vk, device, size, flags, usage);
}
else
{
m_currentTestRequirements = getBufferMemoryRequirements2(vk, device, size, flags, usage);
}
}
struct ImageTestParams
{
VkImageCreateFlags flags;
VkImageTiling tiling;
bool transient;
};
class IImageMemoryRequirements
{
public:
virtual void populateTestGroup (tcu::TestCaseGroup* group) = 0;
protected:
virtual void addFunctionTestCase (tcu::TestCaseGroup* group,
const std::string& name,
const std::string& desc,
const ImageTestParams arg0) = 0;
virtual tcu::TestStatus execTest (Context& context,
const ImageTestParams bufferFlags) = 0;
virtual void preTestChecks (Context& context,
const InstanceInterface& vki,
const VkPhysicalDevice physDevice,
const VkImageCreateFlags flags) = 0;
virtual void updateMemoryRequirements (const DeviceInterface& vk,
const VkDevice device) = 0;
virtual void verifyMemoryRequirements (tcu::ResultCollector& result,
const VkPhysicalDeviceMemoryProperties& deviceMemoryProperties) = 0;
};
class ImageMemoryRequirementsOriginal : public IImageMemoryRequirements
{
static tcu::TestStatus testEntryPoint (Context& context,
const ImageTestParams params);
public:
virtual void populateTestGroup (tcu::TestCaseGroup* group);
protected:
virtual void addFunctionTestCase (tcu::TestCaseGroup* group,
const std::string& name,
const std::string& desc,
const ImageTestParams arg0);
virtual tcu::TestStatus execTest (Context& context,
const ImageTestParams params);
virtual void preTestChecks (Context& context,
const InstanceInterface& vki,
const VkPhysicalDevice physDevice,
const VkImageCreateFlags flags);
virtual void updateMemoryRequirements (const DeviceInterface& vk,
const VkDevice device);
virtual void verifyMemoryRequirements (tcu::ResultCollector& result,
const VkPhysicalDeviceMemoryProperties& deviceMemoryProperties);
private:
virtual bool isUsageMatchesFeatures (const VkImageUsageFlags usage,
const VkFormatFeatureFlags featureFlags);
virtual bool isImageSupported (const InstanceInterface& vki,
const VkPhysicalDevice physDevice,
const VkImageCreateInfo& info);
virtual VkExtent3D makeExtentForImage (const VkImageType imageType);
virtual bool isFormatMatchingAspect (const VkFormat format,
const VkImageAspectFlags aspect);
virtual std::string getImageInfoString (const VkImageCreateInfo& imageInfo);
protected:
VkImageCreateInfo m_currentTestImageInfo;
VkMemoryRequirements m_currentTestRequirements;
};
tcu::TestStatus ImageMemoryRequirementsOriginal::testEntryPoint (Context& context, const ImageTestParams params)
{
ImageMemoryRequirementsOriginal test;
return test.execTest(context, params);
}
void ImageMemoryRequirementsOriginal::populateTestGroup (tcu::TestCaseGroup* group)
{
const struct
{
VkImageCreateFlags flags;
bool transient;
const char* const name;
} imageFlagsCases[] =
{
{ (VkImageCreateFlags)0, false, "regular" },
{ (VkImageCreateFlags)0, true, "transient" },
{ VK_IMAGE_CREATE_SPARSE_BINDING_BIT, false, "sparse" },
{ VK_IMAGE_CREATE_SPARSE_BINDING_BIT | VK_IMAGE_CREATE_SPARSE_RESIDENCY_BIT, false, "sparse_residency" },
{ VK_IMAGE_CREATE_SPARSE_BINDING_BIT | VK_IMAGE_CREATE_SPARSE_ALIASED_BIT, false, "sparse_aliased" },
{ VK_IMAGE_CREATE_SPARSE_BINDING_BIT | VK_IMAGE_CREATE_SPARSE_RESIDENCY_BIT | VK_IMAGE_CREATE_SPARSE_ALIASED_BIT, false, "sparse_residency_aliased" },
};
de::MovePtr<tcu::TestCaseGroup> imageGroup(new tcu::TestCaseGroup(group->getTestContext(), "image", ""));
for (int flagsNdx = 0; flagsNdx < DE_LENGTH_OF_ARRAY(imageFlagsCases); ++flagsNdx)
for (int tilingNdx = 0; tilingNdx <= 1; ++tilingNdx)
{
ImageTestParams params;
std::ostringstream caseName;
params.flags = imageFlagsCases[flagsNdx].flags;
params.transient = imageFlagsCases[flagsNdx].transient;
caseName << imageFlagsCases[flagsNdx].name;
if (tilingNdx != 0)
{
params.tiling = VK_IMAGE_TILING_OPTIMAL;
caseName << "_tiling_optimal";
}
else
{
params.tiling = VK_IMAGE_TILING_LINEAR;
caseName << "_tiling_linear";
}
if ((params.flags & VK_IMAGE_CREATE_SPARSE_RESIDENCY_BIT) && (params.tiling == VK_IMAGE_TILING_LINEAR))
continue;
addFunctionTestCase(imageGroup.get(), caseName.str(), "", params);
}
group->addChild(imageGroup.release());
}
void ImageMemoryRequirementsOriginal::addFunctionTestCase (tcu::TestCaseGroup* group,
const std::string& name,
const std::string& desc,
const ImageTestParams arg0)
{
addFunctionCase(group, name, desc, testEntryPoint, arg0);
}
void ImageMemoryRequirementsOriginal::preTestChecks (Context& ,
const InstanceInterface& vki,
const VkPhysicalDevice physDevice,
const VkImageCreateFlags createFlags)
{
const VkPhysicalDeviceFeatures features = getPhysicalDeviceFeatures(vki, physDevice);
if ((createFlags & VK_IMAGE_CREATE_SPARSE_BINDING_BIT) && !features.sparseBinding)
TCU_THROW(NotSupportedError, "Feature not supported: sparseBinding");
if ((createFlags & VK_IMAGE_CREATE_SPARSE_RESIDENCY_BIT) && !(features.sparseResidencyImage2D || features.sparseResidencyImage3D))
TCU_THROW(NotSupportedError, "Feature not supported: sparseResidencyImage (2D and 3D)");
if ((createFlags & VK_IMAGE_CREATE_SPARSE_ALIASED_BIT) && !features.sparseResidencyAliased)
TCU_THROW(NotSupportedError, "Feature not supported: sparseResidencyAliased");
}
void ImageMemoryRequirementsOriginal::updateMemoryRequirements (const DeviceInterface& vk,
const VkDevice device)
{
const Unique<VkImage> image(createImage(vk, device, &m_currentTestImageInfo));
m_currentTestRequirements = getImageMemoryRequirements(vk, device, *image);
}
void ImageMemoryRequirementsOriginal::verifyMemoryRequirements (tcu::ResultCollector& result,
const VkPhysicalDeviceMemoryProperties& deviceMemoryProperties)
{
if (result.check(m_currentTestRequirements.memoryTypeBits != 0, "VkMemoryRequirements memoryTypeBits has no bits set"))
{
typedef std::vector<deUint32>::const_iterator IndexIterator;
const std::vector<deUint32> usedMemoryTypeIndices = bitsToIndices(m_currentTestRequirements.memoryTypeBits);
bool deviceLocalMemoryFound = false;
bool hostVisibleCoherentMemoryFound = false;
for (IndexIterator memoryTypeNdx = usedMemoryTypeIndices.begin(); memoryTypeNdx != usedMemoryTypeIndices.end(); ++memoryTypeNdx)
{
if (*memoryTypeNdx >= deviceMemoryProperties.memoryTypeCount)
{
result.fail("VkMemoryRequirements memoryTypeBits contains bits for non-existing memory types");
continue;
}
const VkMemoryPropertyFlags memoryPropertyFlags = deviceMemoryProperties.memoryTypes[*memoryTypeNdx].propertyFlags;
if (memoryPropertyFlags & VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT)
deviceLocalMemoryFound = true;
if (memoryPropertyFlags & (VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT))
hostVisibleCoherentMemoryFound = true;
if (memoryPropertyFlags & VK_MEMORY_PROPERTY_LAZILY_ALLOCATED_BIT)
{
result.check((m_currentTestImageInfo.usage & VK_IMAGE_USAGE_TRANSIENT_ATTACHMENT_BIT) != 0u,
"Memory type includes VK_MEMORY_PROPERTY_LAZILY_ALLOCATED_BIT for a non-transient attachment image");
}
}
result.check(deIsPowerOfTwo64(static_cast<deUint64>(m_currentTestRequirements.alignment)) == DE_TRUE,
"VkMemoryRequirements alignment isn't power of two");
result.check(deviceLocalMemoryFound,
"None of the required memory types included VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT");
result.check(m_currentTestImageInfo.tiling == VK_IMAGE_TILING_OPTIMAL || hostVisibleCoherentMemoryFound,
"Required memory type doesn't include VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT and VK_MEMORY_PROPERTY_HOST_COHERENT_BIT");
}
}
bool ImageMemoryRequirementsOriginal::isUsageMatchesFeatures (const VkImageUsageFlags usage, const VkFormatFeatureFlags featureFlags)
{
if ((usage & VK_IMAGE_USAGE_SAMPLED_BIT) && (featureFlags & VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT))
return true;
if ((usage & VK_IMAGE_USAGE_STORAGE_BIT) && (featureFlags & VK_FORMAT_FEATURE_STORAGE_IMAGE_BIT))
return true;
if ((usage & (VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT | VK_IMAGE_USAGE_INPUT_ATTACHMENT_BIT)) && (featureFlags & VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT))
return true;
if ((usage & VK_IMAGE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT) && (featureFlags & VK_FORMAT_FEATURE_DEPTH_STENCIL_ATTACHMENT_BIT))
return true;
return false;
}
//! This catches both invalid as well as legal but unsupported combinations of image parameters
bool ImageMemoryRequirementsOriginal::isImageSupported (const InstanceInterface& vki, const VkPhysicalDevice physDevice, const VkImageCreateInfo& info)
{
DE_ASSERT(info.extent.width >= 1u && info.extent.height >= 1u && info.extent.depth >= 1u);
if (info.imageType == VK_IMAGE_TYPE_1D)
{
DE_ASSERT(info.extent.height == 1u && info.extent.depth == 1u);
}
else if (info.imageType == VK_IMAGE_TYPE_2D)
{
DE_ASSERT(info.extent.depth == 1u);
if (info.flags & VK_IMAGE_CREATE_CUBE_COMPATIBLE_BIT)
{
DE_ASSERT(info.extent.width == info.extent.height);
DE_ASSERT(info.arrayLayers >= 6u && (info.arrayLayers % 6u) == 0u);
}
}
if ((info.flags & VK_IMAGE_CREATE_CUBE_COMPATIBLE_BIT) && info.imageType != VK_IMAGE_TYPE_2D)
return false;
if ((info.samples != VK_SAMPLE_COUNT_1_BIT) &&
(info.imageType != VK_IMAGE_TYPE_2D || (info.flags & VK_IMAGE_CREATE_CUBE_COMPATIBLE_BIT) || info.tiling != VK_IMAGE_TILING_OPTIMAL || info.mipLevels > 1u))
return false;
if ((info.usage & VK_IMAGE_USAGE_TRANSIENT_ATTACHMENT_BIT) &&
(info.usage & (VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT | VK_IMAGE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT | VK_IMAGE_USAGE_INPUT_ATTACHMENT_BIT)) == 0u)
return false;
const VkPhysicalDeviceFeatures features = getPhysicalDeviceFeatures(vki, physDevice);
if (info.flags & VK_IMAGE_CREATE_SPARSE_RESIDENCY_BIT)
{
DE_ASSERT(info.tiling == VK_IMAGE_TILING_OPTIMAL);
if (info.imageType == VK_IMAGE_TYPE_2D && !features.sparseResidencyImage2D)
return false;
if (info.imageType == VK_IMAGE_TYPE_3D && !features.sparseResidencyImage3D)
return false;
if (info.samples == VK_SAMPLE_COUNT_2_BIT && !features.sparseResidency2Samples)
return false;
if (info.samples == VK_SAMPLE_COUNT_4_BIT && !features.sparseResidency4Samples)
return false;
if (info.samples == VK_SAMPLE_COUNT_8_BIT && !features.sparseResidency8Samples)
return false;
if (info.samples == VK_SAMPLE_COUNT_16_BIT && !features.sparseResidency16Samples)
return false;
if (info.samples == VK_SAMPLE_COUNT_32_BIT || info.samples == VK_SAMPLE_COUNT_64_BIT)
return false;
}
if (info.samples != VK_SAMPLE_COUNT_1_BIT && (info.usage & VK_IMAGE_USAGE_STORAGE_BIT) && !features.shaderStorageImageMultisample)
return false;
switch (info.format)
{
case VK_FORMAT_BC1_RGB_UNORM_BLOCK:
case VK_FORMAT_BC1_RGB_SRGB_BLOCK:
case VK_FORMAT_BC1_RGBA_UNORM_BLOCK:
case VK_FORMAT_BC1_RGBA_SRGB_BLOCK:
case VK_FORMAT_BC2_UNORM_BLOCK:
case VK_FORMAT_BC2_SRGB_BLOCK:
case VK_FORMAT_BC3_UNORM_BLOCK:
case VK_FORMAT_BC3_SRGB_BLOCK:
case VK_FORMAT_BC4_UNORM_BLOCK:
case VK_FORMAT_BC4_SNORM_BLOCK:
case VK_FORMAT_BC5_UNORM_BLOCK:
case VK_FORMAT_BC5_SNORM_BLOCK:
case VK_FORMAT_BC6H_UFLOAT_BLOCK:
case VK_FORMAT_BC6H_SFLOAT_BLOCK:
case VK_FORMAT_BC7_UNORM_BLOCK:
case VK_FORMAT_BC7_SRGB_BLOCK:
if (!features.textureCompressionBC)
return false;
break;
case VK_FORMAT_ETC2_R8G8B8_UNORM_BLOCK:
case VK_FORMAT_ETC2_R8G8B8_SRGB_BLOCK:
case VK_FORMAT_ETC2_R8G8B8A1_UNORM_BLOCK:
case VK_FORMAT_ETC2_R8G8B8A1_SRGB_BLOCK:
case VK_FORMAT_ETC2_R8G8B8A8_UNORM_BLOCK:
case VK_FORMAT_ETC2_R8G8B8A8_SRGB_BLOCK:
case VK_FORMAT_EAC_R11_UNORM_BLOCK:
case VK_FORMAT_EAC_R11_SNORM_BLOCK:
case VK_FORMAT_EAC_R11G11_UNORM_BLOCK:
case VK_FORMAT_EAC_R11G11_SNORM_BLOCK:
if (!features.textureCompressionETC2)
return false;
break;
case VK_FORMAT_ASTC_4x4_UNORM_BLOCK:
case VK_FORMAT_ASTC_4x4_SRGB_BLOCK:
case VK_FORMAT_ASTC_5x4_UNORM_BLOCK:
case VK_FORMAT_ASTC_5x4_SRGB_BLOCK:
case VK_FORMAT_ASTC_5x5_UNORM_BLOCK:
case VK_FORMAT_ASTC_5x5_SRGB_BLOCK:
case VK_FORMAT_ASTC_6x5_UNORM_BLOCK:
case VK_FORMAT_ASTC_6x5_SRGB_BLOCK:
case VK_FORMAT_ASTC_6x6_UNORM_BLOCK:
case VK_FORMAT_ASTC_6x6_SRGB_BLOCK:
case VK_FORMAT_ASTC_8x5_UNORM_BLOCK:
case VK_FORMAT_ASTC_8x5_SRGB_BLOCK:
case VK_FORMAT_ASTC_8x6_UNORM_BLOCK:
case VK_FORMAT_ASTC_8x6_SRGB_BLOCK:
case VK_FORMAT_ASTC_8x8_UNORM_BLOCK:
case VK_FORMAT_ASTC_8x8_SRGB_BLOCK:
case VK_FORMAT_ASTC_10x5_UNORM_BLOCK:
case VK_FORMAT_ASTC_10x5_SRGB_BLOCK:
case VK_FORMAT_ASTC_10x6_UNORM_BLOCK:
case VK_FORMAT_ASTC_10x6_SRGB_BLOCK:
case VK_FORMAT_ASTC_10x8_UNORM_BLOCK:
case VK_FORMAT_ASTC_10x8_SRGB_BLOCK:
case VK_FORMAT_ASTC_10x10_UNORM_BLOCK:
case VK_FORMAT_ASTC_10x10_SRGB_BLOCK:
case VK_FORMAT_ASTC_12x10_UNORM_BLOCK:
case VK_FORMAT_ASTC_12x10_SRGB_BLOCK:
case VK_FORMAT_ASTC_12x12_UNORM_BLOCK:
case VK_FORMAT_ASTC_12x12_SRGB_BLOCK:
if (!features.textureCompressionASTC_LDR)
return false;
break;
default:
break;
}
const VkFormatProperties formatProperties = getPhysicalDeviceFormatProperties(vki, physDevice, info.format);
const VkFormatFeatureFlags formatFeatures = (info.tiling == VK_IMAGE_TILING_LINEAR ? formatProperties.linearTilingFeatures
: formatProperties.optimalTilingFeatures);
if (!isUsageMatchesFeatures(info.usage, formatFeatures))
return false;
VkImageFormatProperties imageFormatProperties;
const VkResult result = vki.getPhysicalDeviceImageFormatProperties(
physDevice, info.format, info.imageType, info.tiling, info.usage, info.flags, &imageFormatProperties);
if (result == VK_SUCCESS)
{
if (info.arrayLayers > imageFormatProperties.maxArrayLayers)
return false;
if (info.mipLevels > imageFormatProperties.maxMipLevels)
return false;
if ((info.samples & imageFormatProperties.sampleCounts) == 0u)
return false;
}
return result == VK_SUCCESS;
}
VkExtent3D ImageMemoryRequirementsOriginal::makeExtentForImage (const VkImageType imageType)
{
VkExtent3D extent = { 64u, 64u, 4u };
if (imageType == VK_IMAGE_TYPE_1D)
extent.height = extent.depth = 1u;
else if (imageType == VK_IMAGE_TYPE_2D)
extent.depth = 1u;
return extent;
}
bool ImageMemoryRequirementsOriginal::isFormatMatchingAspect (const VkFormat format, const VkImageAspectFlags aspect)
{
DE_ASSERT(aspect == VK_IMAGE_ASPECT_COLOR_BIT || aspect == (VK_IMAGE_ASPECT_DEPTH_BIT | VK_IMAGE_ASPECT_STENCIL_BIT));
// D/S formats are laid out next to each other in the enum
const bool isDepthStencilFormat = (format >= VK_FORMAT_D16_UNORM && format <= VK_FORMAT_D32_SFLOAT_S8_UINT);
return (aspect == (VK_IMAGE_ASPECT_DEPTH_BIT | VK_IMAGE_ASPECT_STENCIL_BIT)) == isDepthStencilFormat;
}
std::string ImageMemoryRequirementsOriginal::getImageInfoString (const VkImageCreateInfo& imageInfo)
{
std::ostringstream str;
switch (imageInfo.imageType)
{
case VK_IMAGE_TYPE_1D: str << "1D "; break;
case VK_IMAGE_TYPE_2D: str << "2D "; break;
case VK_IMAGE_TYPE_3D: str << "3D "; break;
default: break;
}
switch (imageInfo.tiling)
{
case VK_IMAGE_TILING_OPTIMAL: str << "(optimal) "; break;
case VK_IMAGE_TILING_LINEAR: str << "(linear) "; break;
default: break;
}
str << "extent:[" << imageInfo.extent.width << ", " << imageInfo.extent.height << ", " << imageInfo.extent.depth << "] ";
str << imageInfo.format << " ";
str << "samples:" << static_cast<deUint32>(imageInfo.samples) << " ";
str << "flags:" << static_cast<deUint32>(imageInfo.flags) << " ";
str << "usage:" << static_cast<deUint32>(imageInfo.usage) << " ";
return str.str();
}
tcu::TestStatus ImageMemoryRequirementsOriginal::execTest (Context& context, const ImageTestParams params)
{
const DeviceInterface& vk = context.getDeviceInterface();
const InstanceInterface& vki = context.getInstanceInterface();
const VkDevice device = context.getDevice();
const VkPhysicalDevice physDevice = context.getPhysicalDevice();
const VkImageCreateFlags sparseFlags = VK_IMAGE_CREATE_SPARSE_BINDING_BIT | VK_IMAGE_CREATE_SPARSE_RESIDENCY_BIT | VK_IMAGE_CREATE_SPARSE_ALIASED_BIT;
const VkImageUsageFlags transientFlags = VK_IMAGE_USAGE_TRANSIENT_ATTACHMENT_BIT;
preTestChecks(context, vki, physDevice, params.flags);
const VkPhysicalDeviceMemoryProperties memoryProperties = getPhysicalDeviceMemoryProperties(vki, physDevice);
const deUint32 notInitializedBits = ~0u;
const VkImageAspectFlags colorAspect = VK_IMAGE_ASPECT_COLOR_BIT;
const VkImageAspectFlags depthStencilAspect = VK_IMAGE_ASPECT_DEPTH_BIT | VK_IMAGE_ASPECT_STENCIL_BIT;
const VkImageAspectFlags allAspects[2] = { colorAspect, depthStencilAspect };
tcu::TestLog& log = context.getTestContext().getLog();
bool allPass = true;
deUint32 numCheckedImages = 0u;
log << tcu::TestLog::Message << "Verify memory requirements for the following parameter combinations:" << tcu::TestLog::EndMessage;
for (deUint32 loopAspectNdx = 0u; loopAspectNdx < DE_LENGTH_OF_ARRAY(allAspects); ++loopAspectNdx)
{
const VkImageAspectFlags aspect = allAspects[loopAspectNdx];
deUint32 previousMemoryTypeBits = notInitializedBits;
for (VkFormat loopFormat = VK_FORMAT_R4G4_UNORM_PACK8; loopFormat <= VK_FORMAT_ASTC_12x12_SRGB_BLOCK; loopFormat = nextEnum(loopFormat))
if (isFormatMatchingAspect(loopFormat, aspect))
{
// memoryTypeBits may differ between depth/stencil formats
if (aspect == depthStencilAspect)
previousMemoryTypeBits = notInitializedBits;
for (VkImageType loopImageType = VK_IMAGE_TYPE_1D; loopImageType != VK_IMAGE_TYPE_LAST; loopImageType = nextEnum(loopImageType))
for (VkImageCreateFlags loopCreateFlags = (VkImageCreateFlags)0; loopCreateFlags <= VK_IMAGE_CREATE_CUBE_COMPATIBLE_BIT; loopCreateFlags = nextFlagExcluding(loopCreateFlags, sparseFlags))
for (VkImageUsageFlags loopUsageFlags = VK_IMAGE_USAGE_TRANSFER_SRC_BIT; loopUsageFlags <= VK_IMAGE_USAGE_INPUT_ATTACHMENT_BIT; loopUsageFlags = nextFlagExcluding(loopUsageFlags, transientFlags))
for (VkSampleCountFlagBits loopSampleCount = VK_SAMPLE_COUNT_1_BIT; loopSampleCount <= VK_SAMPLE_COUNT_16_BIT; loopSampleCount = nextFlag(loopSampleCount))
{
const VkImageCreateFlags actualCreateFlags = loopCreateFlags | params.flags;
const VkImageUsageFlags actualUsageFlags = loopUsageFlags | (params.transient ? VK_IMAGE_USAGE_TRANSIENT_ATTACHMENT_BIT : (VkImageUsageFlagBits)0);
const bool isCube = (actualCreateFlags & VK_IMAGE_CREATE_CUBE_COMPATIBLE_BIT) != 0u;
const VkImageCreateInfo imageInfo =
{
VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO, // VkStructureType sType;
DE_NULL, // const void* pNext;
actualCreateFlags, // VkImageCreateFlags flags;
loopImageType, // VkImageType imageType;
loopFormat, // VkFormat format;
makeExtentForImage(loopImageType), // VkExtent3D extent;
1u, // uint32_t mipLevels;
(isCube ? 6u : 1u), // uint32_t arrayLayers;
loopSampleCount, // VkSampleCountFlagBits samples;
params.tiling, // VkImageTiling tiling;
actualUsageFlags, // VkImageUsageFlags usage;
VK_SHARING_MODE_EXCLUSIVE, // VkSharingMode sharingMode;
0u, // uint32_t queueFamilyIndexCount;
DE_NULL, // const uint32_t* pQueueFamilyIndices;
VK_IMAGE_LAYOUT_UNDEFINED, // VkImageLayout initialLayout;
};
m_currentTestImageInfo = imageInfo;
if (!isImageSupported(vki, physDevice, m_currentTestImageInfo))
continue;
log << tcu::TestLog::Message << "- " << getImageInfoString(m_currentTestImageInfo) << tcu::TestLog::EndMessage;
++numCheckedImages;
tcu::ResultCollector result(log, "ERROR: ");
updateMemoryRequirements(vk, device);
verifyMemoryRequirements(result, memoryProperties);
// For the same tiling, transient usage, and sparse flags, (and format, if D/S) memoryTypeBits must be the same for all images
result.check((previousMemoryTypeBits == notInitializedBits) || (m_currentTestRequirements.memoryTypeBits == previousMemoryTypeBits),
"memoryTypeBits differ from the ones in the previous image configuration");
if (result.getResult() != QP_TEST_RESULT_PASS)
allPass = false;
previousMemoryTypeBits = m_currentTestRequirements.memoryTypeBits;
}
}
}
if (numCheckedImages == 0u)
log << tcu::TestLog::Message << "NOTE: No supported image configurations -- nothing to check" << tcu::TestLog::EndMessage;
return allPass ? tcu::TestStatus::pass("Pass") : tcu::TestStatus::fail("Some memory requirements were incorrect");
}
class ImageMemoryRequirementsExtended : public ImageMemoryRequirementsOriginal
{
public:
static tcu::TestStatus testEntryPoint (Context& context,
const ImageTestParams params);
protected:
virtual void addFunctionTestCase (tcu::TestCaseGroup* group,
const std::string& name,
const std::string& desc,
const ImageTestParams arg0);
virtual void preTestChecks (Context& context,
const InstanceInterface& vki,
const VkPhysicalDevice physDevice,
const VkImageCreateFlags flags);
virtual void updateMemoryRequirements (const DeviceInterface& vk,
const VkDevice device);
};
tcu::TestStatus ImageMemoryRequirementsExtended::testEntryPoint (Context& context, const ImageTestParams params)
{
ImageMemoryRequirementsExtended test;
return test.execTest(context, params);
}
void ImageMemoryRequirementsExtended::addFunctionTestCase (tcu::TestCaseGroup* group,
const std::string& name,
const std::string& desc,
const ImageTestParams arg0)
{
addFunctionCase(group, name, desc, testEntryPoint, arg0);
}
void ImageMemoryRequirementsExtended::preTestChecks (Context& context,
const InstanceInterface& vki,
const VkPhysicalDevice physDevice,
const VkImageCreateFlags createFlags)
{
const std::string extensionName("VK_KHR_get_memory_requirements2");
if (!de::contains(context.getDeviceExtensions().begin(), context.getDeviceExtensions().end(), extensionName))
TCU_THROW(NotSupportedError, std::string(extensionName + " is not supported").c_str());
ImageMemoryRequirementsOriginal::preTestChecks (context, vki, physDevice, createFlags);
}
void ImageMemoryRequirementsExtended::updateMemoryRequirements (const DeviceInterface& vk,
const VkDevice device)
{
m_currentTestRequirements = getImageMemoryRequirements2(vk, device, m_currentTestImageInfo);
}
void populateCoreTestGroup (tcu::TestCaseGroup* group)
{
BufferMemoryRequirementsOriginal bufferTest;
ImageMemoryRequirementsOriginal imageTest;
bufferTest.populateTestGroup(group);
imageTest.populateTestGroup(group);
}
void populateExtendedTestGroup (tcu::TestCaseGroup* group)
{
BufferMemoryRequirementsExtended bufferTest;
ImageMemoryRequirementsExtended imageTest;
bufferTest.populateTestGroup(group);
imageTest.populateTestGroup(group);
}
} // anonymous
tcu::TestCaseGroup* createRequirementsTests (tcu::TestContext& testCtx)
{
de::MovePtr<tcu::TestCaseGroup> requirementsGroup(new tcu::TestCaseGroup(testCtx, "requirements", "Buffer and image memory requirements"));
requirementsGroup->addChild(createTestGroup(testCtx, "core", "Memory requirements tests with core functionality", populateCoreTestGroup));
requirementsGroup->addChild(createTestGroup(testCtx, "extended", "Memory requirements tests with extension VK_KHR_get_memory_requirements2", populateExtendedTestGroup));
return requirementsGroup.release();
}
} // memory
} // vkt