| /*------------------------------------------------------------------------- |
| * 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 "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); |
| } |
| |
| //! 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; |
| } |
| |
| 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); |
| } |
| |
| 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); |
| } |
| |
| void requireBufferSparseFeatures (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 verifyBufferRequirements (tcu::ResultCollector& result, |
| const VkPhysicalDeviceMemoryProperties& deviceMemoryProperties, |
| const VkMemoryRequirements& requirements, |
| const VkMemoryRequirements& allUsageFlagsRequirements, |
| const VkPhysicalDeviceLimits& limits, |
| const VkBufferCreateFlags bufferFlags, |
| const VkBufferUsageFlags usage) |
| { |
| if (result.check(requirements.memoryTypeBits != 0, "VkMemoryRequirements memoryTypeBits has no bits set")) |
| { |
| typedef std::vector<deUint32>::const_iterator IndexIterator; |
| const std::vector<deUint32> usedMemoryTypeIndices = bitsToIndices(requirements.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>(requirements.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(requirements.alignment >= limits.minTexelBufferOffsetAlignment, |
| "VkMemoryRequirements alignment doesn't respect minTexelBufferOffsetAlignment"); |
| } |
| |
| if (usage & VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT) |
| { |
| result.check(requirements.alignment >= limits.minUniformBufferOffsetAlignment, |
| "VkMemoryRequirements alignment doesn't respect minUniformBufferOffsetAlignment"); |
| } |
| |
| if (usage & VK_BUFFER_USAGE_STORAGE_BUFFER_BIT) |
| { |
| result.check(requirements.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((requirements.memoryTypeBits & allUsageFlagsRequirements.memoryTypeBits) == allUsageFlagsRequirements.memoryTypeBits, |
| "Memory type bits aren't a superset of memory type bits for all usage flags combined"); |
| } |
| } |
| |
| tcu::TestStatus testBuffer (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(); |
| |
| requireBufferSparseFeatures(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); |
| const VkMemoryRequirements allUsageFlagsRequirements = getBufferMemoryRequirements(vk, device, 1024, bufferFlags, allUsageFlags); // doesn't depend on size |
| tcu::TestLog& log = context.getTestContext().getLog(); |
| bool allPass = true; |
| |
| const VkDeviceSize sizeCases[] = |
| { |
| 1 * 1024, |
| 8 * 1024, |
| 64 * 1024, |
| 1024 * 1024, |
| }; |
| |
| 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; |
| |
| const VkMemoryRequirements requirements = getBufferMemoryRequirements(vk, device, *pSize, bufferFlags, usage); |
| tcu::ResultCollector result (log, "ERROR: "); |
| |
| // Check: |
| // - requirements for a particular buffer usage |
| // - memoryTypeBits are a subset of bits for requirements with all usage flags combined |
| verifyBufferRequirements(result, memoryProperties, requirements, allUsageFlagsRequirements, 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(requirements.memoryTypeBits == previousMemoryTypeBits, |
| "memoryTypeBits differ from the ones in the previous buffer size"); |
| |
| result.check(requirements.alignment == previousAlignment, |
| "alignment differs from the one in the previous buffer size"); |
| } |
| |
| if (result.getResult() != QP_TEST_RESULT_PASS) |
| allPass = false; |
| |
| previousMemoryTypeBits = requirements.memoryTypeBits; |
| previousAlignment = requirements.alignment; |
| } |
| |
| if (!allPass) |
| break; |
| } |
| |
| return allPass ? tcu::TestStatus::pass("Pass") : tcu::TestStatus::fail("Some memory requirements were incorrect"); |
| } |
| |
| void requireImageSparseFeatures (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"); |
| } |
| |
| bool imageUsageMatchesFormatFeatures (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 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 (!imageUsageMatchesFormatFeatures(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 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 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; |
| } |
| |
| void verifyImageRequirements (tcu::ResultCollector& result, |
| const VkPhysicalDeviceMemoryProperties& deviceMemoryProperties, |
| const VkMemoryRequirements& requirements, |
| const VkImageCreateInfo& imageInfo) |
| { |
| if (result.check(requirements.memoryTypeBits != 0, "VkMemoryRequirements memoryTypeBits has no bits set")) |
| { |
| typedef std::vector<deUint32>::const_iterator IndexIterator; |
| const std::vector<deUint32> usedMemoryTypeIndices = bitsToIndices(requirements.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((imageInfo.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>(requirements.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(imageInfo.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"); |
| } |
| } |
| |
| std::string 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(); |
| } |
| |
| struct ImageParams |
| { |
| VkImageCreateFlags flags; |
| VkImageTiling tiling; |
| bool transient; |
| }; |
| |
| tcu::TestStatus testImage (Context& context, const ImageParams 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; |
| |
| requireImageSparseFeatures(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; |
| }; |
| |
| if (!isImageSupported(vki, physDevice, imageInfo)) |
| continue; |
| |
| log << tcu::TestLog::Message << "- " << getImageInfoString(imageInfo) << tcu::TestLog::EndMessage; |
| ++numCheckedImages; |
| |
| const Unique<VkImage> image (createImage(vk, device, &imageInfo)); |
| const VkMemoryRequirements requirements = getImageMemoryRequirements(vk, device, *image); |
| tcu::ResultCollector result (log, "ERROR: "); |
| |
| verifyImageRequirements(result, memoryProperties, requirements, imageInfo); |
| |
| // 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) || (requirements.memoryTypeBits == previousMemoryTypeBits), |
| "memoryTypeBits differ from the ones in the previous image configuration"); |
| |
| if (result.getResult() != QP_TEST_RESULT_PASS) |
| allPass = false; |
| |
| previousMemoryTypeBits = requirements.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"); |
| } |
| |
| void populateTestGroup (tcu::TestCaseGroup* group) |
| { |
| // Buffers |
| { |
| 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) |
| addFunctionCase(bufferGroup.get(), bufferCases[ndx].name, "", testBuffer, bufferCases[ndx].flags); |
| |
| group->addChild(bufferGroup.release()); |
| } |
| |
| // Images |
| { |
| 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) |
| { |
| ImageParams 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; |
| |
| addFunctionCase(imageGroup.get(), caseName.str(), "", testImage, params); |
| } |
| |
| group->addChild(imageGroup.release()); |
| } |
| } |
| |
| } // anonymous |
| |
| tcu::TestCaseGroup* createRequirementsTests (tcu::TestContext& testCtx) |
| { |
| return createTestGroup(testCtx, "requirements", "Buffer and image memory requirements", populateTestGroup); |
| } |
| |
| } // memory |
| } // vkt |