Fix unreasonable memory expectations in pipeline.render_to_image
authorArkadiusz Sarwa <arkadiusz.sarwa@mobica.com>
Tue, 12 Sep 2017 11:28:39 +0000 (13:28 +0200)
committerAlexander Galazin <Alexander.Galazin@arm.com>
Tue, 26 Sep 2017 10:59:49 +0000 (06:59 -0400)
Affects:
dEQP-VK.pipeline.render_to_image.*

Components: Vulkan

VK-GL-CTS issue: 526

Change-Id: I71ea0381663c39786b227ed712e1111ec3c71cfb
(cherry picked from commit 069aa0ad097f543438b39a260eea52278867ffa8)

external/vulkancts/modules/vulkan/pipeline/vktPipelineRenderToImageTests.cpp

index d11c802..89b4e2e 100644 (file)
@@ -694,18 +694,90 @@ void generateExpectedImage (const tcu::PixelBufferAccess& outputImage, const IVe
        }
 }
 
-VkDeviceSize getMaxDeviceHeapSize (const InstanceInterface& vki, const VkPhysicalDevice physDevice)
+deUint32 selectMatchingMemoryType (const VkPhysicalDeviceMemoryProperties& deviceMemProps, deUint32 allowedMemTypeBits, MemoryRequirement requirement)
 {
-       const VkPhysicalDeviceMemoryProperties  memoryProperties        = getPhysicalDeviceMemoryProperties(vki, physDevice);
-       VkDeviceSize                                                    memorySize                      = 0;
+       const deUint32  compatibleTypes = getCompatibleMemoryTypes(deviceMemProps, requirement);
+       const deUint32  candidates              = allowedMemTypeBits & compatibleTypes;
+
+       if (candidates == 0)
+               TCU_THROW(NotSupportedError, "No compatible memory type found");
+
+       return (deUint32)deCtz32(candidates);
+}
+
+IVec4 getMaxImageSize (const VkImageViewType viewType, const IVec4& sizeHint)
+{
+       //Limits have been taken from the vulkan specification
+       IVec4 size = IVec4(
+               sizeHint.x() != MAX_SIZE ? sizeHint.x() : 4096,
+               sizeHint.y() != MAX_SIZE ? sizeHint.y() : 4096,
+               sizeHint.z() != MAX_SIZE ? sizeHint.z() : 256,
+               sizeHint.w() != MAX_SIZE ? sizeHint.w() : 256);
 
-       for (deUint32 heapNdx = 0; heapNdx < memoryProperties.memoryHeapCount; ++heapNdx)
+       switch (viewType)
        {
-               if ((memoryProperties.memoryHeaps[heapNdx].flags & VK_MEMORY_HEAP_DEVICE_LOCAL_BIT) != 0)
-                       memorySize = std::max(memorySize, memoryProperties.memoryHeaps[heapNdx].size);
+               case VK_IMAGE_VIEW_TYPE_1D:
+               case VK_IMAGE_VIEW_TYPE_1D_ARRAY:
+                       size.x() = deMin32(4096, size.x());
+                       break;
+
+               case VK_IMAGE_VIEW_TYPE_2D:
+               case VK_IMAGE_VIEW_TYPE_2D_ARRAY:
+                       size.x() = deMin32(4096, size.x());
+                       size.y() = deMin32(4096, size.y());
+                       break;
+
+               case VK_IMAGE_VIEW_TYPE_3D:
+                       size.x() = deMin32(256, size.x());
+                       size.y() = deMin32(256, size.y());
+                       break;
+
+               case VK_IMAGE_VIEW_TYPE_CUBE:
+               case VK_IMAGE_VIEW_TYPE_CUBE_ARRAY:
+                       size.x() = deMin32(4096, size.x());
+                       size.y() = deMin32(4096, size.y());
+                       size.w() = deMin32(252, size.w());
+                       size.w() = NUM_CUBE_FACES * (size.w() / NUM_CUBE_FACES);        // round down to 6 faces
+                       break;
+
+               default:
+                       DE_ASSERT(0);
+                       return IVec4();
        }
 
-       return memorySize;
+       return size;
+}
+
+deUint32 getMemoryTypeNdx (Context& context, const CaseDef& caseDef)
+{
+       const DeviceInterface&                                  vk                                      = context.getDeviceInterface();
+       const InstanceInterface&                                vki                                     = context.getInstanceInterface();
+       const VkDevice                                                  device                          = context.getDevice();
+       const VkPhysicalDevice                                  physDevice                      = context.getPhysicalDevice();
+
+       const VkPhysicalDeviceMemoryProperties  memoryProperties        = getPhysicalDeviceMemoryProperties(vki, physDevice);
+       Move<VkImage>                                                   colorImage;
+       VkMemoryRequirements                                    memReqs;
+
+       const VkImageUsageFlags                                 imageUsage      = VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT | VK_IMAGE_USAGE_TRANSFER_SRC_BIT;
+       const IVec4                                                             imageSize       = getMaxImageSize(caseDef.viewType, caseDef.imageSizeHint);
+
+       //create image, don't bind any memory to it
+       colorImage      = makeImage(vk, device, getImageCreateFlags(caseDef.viewType), getImageType(caseDef.viewType), caseDef.colorFormat,
+                                                               imageSize.swizzle(0, 1, 2), 1u, imageSize.w(), imageUsage);
+
+       vk.getImageMemoryRequirements(device, *colorImage, &memReqs);
+       return selectMatchingMemoryType(memoryProperties, memReqs.memoryTypeBits, MemoryRequirement::Any);
+}
+
+VkDeviceSize getMaxDeviceHeapSize (Context& context, const CaseDef& caseDef)
+{
+       const InstanceInterface&                                vki                                     = context.getInstanceInterface();
+       const VkPhysicalDevice                                  physDevice                      = context.getPhysicalDevice();
+       const VkPhysicalDeviceMemoryProperties  memoryProperties        = getPhysicalDeviceMemoryProperties(vki, physDevice);
+       const deUint32                                                  memoryTypeNdx           = getMemoryTypeNdx (context, caseDef);
+
+       return memoryProperties.memoryHeaps[memoryProperties.memoryTypes[memoryTypeNdx].heapIndex].size;
 }
 
 //! Get a smaller image size. Returns a vector of zeroes, if it can't reduce more.
@@ -743,51 +815,6 @@ bool isDepthStencilFormatSupported (const InstanceInterface& vki, const VkPhysic
        return (properties.optimalTilingFeatures & VK_FORMAT_FEATURE_DEPTH_STENCIL_ATTACHMENT_BIT) != 0;
 }
 
-IVec4 getMaxImageSize (const VkPhysicalDeviceLimits& limits, const VkImageViewType viewType, const IVec4& sizeHint, const bool useDepthStencil)
-{
-       // If we use a layered D/S together with a 3D image, we have to use the smallest common limit
-       const int maxDepth = (useDepthStencil ? deMin32(static_cast<int>(limits.maxImageArrayLayers), static_cast<int>(limits.maxImageDimension3D))
-                                                                                 : static_cast<int>(limits.maxImageDimension3D));
-
-       // Images have to respect framebuffer limits and image limits (the framebuffer is not layered in this case)
-       IVec4 size = IVec4(
-               sizeHint.x() != MAX_SIZE ? sizeHint.x() : static_cast<int>(limits.maxFramebufferWidth),
-               sizeHint.y() != MAX_SIZE ? sizeHint.y() : static_cast<int>(limits.maxFramebufferHeight),
-               sizeHint.z() != MAX_SIZE ? sizeHint.z() : maxDepth,
-               sizeHint.w() != MAX_SIZE ? sizeHint.w() : static_cast<int>(limits.maxImageArrayLayers));
-
-       switch (viewType)
-       {
-               case VK_IMAGE_VIEW_TYPE_1D:
-               case VK_IMAGE_VIEW_TYPE_1D_ARRAY:
-                       size.x() = deMin32(size.x(), limits.maxImageDimension1D);
-                       break;
-
-               case VK_IMAGE_VIEW_TYPE_2D:
-               case VK_IMAGE_VIEW_TYPE_2D_ARRAY:
-                       size.x() = deMin32(size.x(), limits.maxImageDimension2D);
-                       size.y() = deMin32(size.y(), limits.maxImageDimension2D);
-                       break;
-
-               case VK_IMAGE_VIEW_TYPE_3D:
-                       size.x() = deMin32(size.x(), limits.maxImageDimension3D);
-                       size.y() = deMin32(size.y(), limits.maxImageDimension3D);
-                       break;
-
-               case VK_IMAGE_VIEW_TYPE_CUBE:
-               case VK_IMAGE_VIEW_TYPE_CUBE_ARRAY:
-                       size.x() = size.y() = deMin32(size.x(), limits.maxImageDimensionCube);
-                       size.w() = NUM_CUBE_FACES * (size.w() / NUM_CUBE_FACES);        // round down to 6 faces
-                       break;
-
-               default:
-                       DE_ASSERT(0);
-                       return IVec4();
-       }
-
-       return size;
-}
-
 VkImageAspectFlags getFormatAspectFlags (const VkFormat format)
 {
        if (format == VK_FORMAT_UNDEFINED)
@@ -860,7 +887,7 @@ void initPrograms (SourceCollections& programCollection, const CaseDef caseDef)
 }
 
 //! See testAttachmentSize() description
-tcu::TestStatus testWithSizeReduction (Context& context, const CaseDef& caseDef, const int sizeReductionIndex)
+tcu::TestStatus testWithSizeReduction (Context& context, const CaseDef& caseDef)
 {
        const DeviceInterface&                  vk                                      = context.getDeviceInterface();
        const InstanceInterface&                vki                                     = context.getInstanceInterface();
@@ -872,16 +899,67 @@ tcu::TestStatus testWithSizeReduction (Context& context, const CaseDef& caseDef,
 
        // The memory might be too small to allocate a largest possible attachment, so try to account for that.
        const bool                                              useDepthStencil         = (caseDef.depthStencilFormat != VK_FORMAT_UNDEFINED);
-       const VkDeviceSize                              deviceMemoryBudget      = getMaxDeviceHeapSize(vki, physDevice) >> 2;
-       IVec4                                                   imageSize                       = getMaxImageSize(context.getDeviceProperties().limits, caseDef.viewType, caseDef.imageSizeHint, useDepthStencil);
 
-       // Keep reducing the size, if needed
-       for (int i = 0; i < sizeReductionIndex; ++i)
+       IVec4                                                   imageSize                       = getMaxImageSize(caseDef.viewType, caseDef.imageSizeHint);
+       VkDeviceSize                                    colorSize                       = product(imageSize) * tcu::getPixelSize(mapVkFormat(caseDef.colorFormat));
+       VkDeviceSize                                    depthStencilSize        = (useDepthStencil ? product(imageSize) * tcu::getPixelSize(mapVkFormat(caseDef.depthStencilFormat)) : 0ull);
+
+       const VkDeviceSize                              reserveForChecking      = 500ull * 1024ull;     //left 512KB
+       const float                                             additionalMemory        = 1.15f;                        //left some free memory on device (15%)
+       VkDeviceSize                                    neededMemory            = static_cast<VkDeviceSize>(static_cast<float>(colorSize + depthStencilSize) * additionalMemory) + reserveForChecking;
+       VkDeviceSize                                    maxMemory                       = getMaxDeviceHeapSize(context, caseDef) >> 2;
+
+       const VkDeviceSize                              deviceMemoryBudget      = std::min(neededMemory, maxMemory);
+       bool                                                    allocationPossible      = false;
+
+       // Keep reducing the size, if image size is too big
+       while (neededMemory > deviceMemoryBudget)
        {
                imageSize = getReducedImageSize(caseDef, imageSize);
 
                if (imageSize == IVec4())
                        return tcu::TestStatus::fail("Couldn't create an image with required size");
+
+               colorSize                       = product(imageSize) * tcu::getPixelSize(mapVkFormat(caseDef.colorFormat));
+               depthStencilSize        = (useDepthStencil ? product(imageSize) * tcu::getPixelSize(mapVkFormat(caseDef.depthStencilFormat)) : 0ull);
+               neededMemory            = static_cast<VkDeviceSize>(static_cast<double>(colorSize + depthStencilSize) * additionalMemory);
+       }
+
+       // Keep reducing the size, if allocation return out of any memory
+       while (!allocationPossible)
+       {
+               VkDeviceMemory                          object                  = 0;
+               const VkMemoryAllocateInfo      allocateInfo    =
+               {
+                       VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO, //VkStructureType       sType;
+                       DE_NULL,                                                                //const void*           pNext;
+                       neededMemory,                                                   //VkDeviceSize          allocationSize;
+                       getMemoryTypeNdx(context, caseDef)              //deUint32                      memoryTypeIndex;
+               };
+
+               const VkResult                          result                  = vk.allocateMemory(device, &allocateInfo, DE_NULL, &object);
+
+               if (VK_ERROR_OUT_OF_DEVICE_MEMORY == result || VK_ERROR_OUT_OF_HOST_MEMORY == result)
+               {
+                       imageSize = getReducedImageSize(caseDef, imageSize);
+
+                       if (imageSize == IVec4())
+                               return tcu::TestStatus::fail("Couldn't create an image with required size");
+
+                       colorSize                       = product(imageSize) * tcu::getPixelSize(mapVkFormat(caseDef.colorFormat));
+                       depthStencilSize        = (useDepthStencil ? product(imageSize) * tcu::getPixelSize(mapVkFormat(caseDef.depthStencilFormat)) : 0ull);
+                       neededMemory            = static_cast<VkDeviceSize>(static_cast<double>(colorSize + depthStencilSize) * additionalMemory) + reserveForChecking;
+               }
+               else if (VK_SUCCESS != result)
+               {
+                       return tcu::TestStatus::fail("Couldn't allocate memory");
+               }
+               else
+               {
+                       //free memory using Move pointer
+                       Move<VkDeviceMemory> memoryAllocated (check<VkDeviceMemory>(object), Deleter<VkDeviceMemory>(vk, device, DE_NULL));
+                       allocationPossible = true;
+               }
        }
 
        context.getTestContext().getLog()
@@ -889,15 +967,11 @@ tcu::TestStatus testWithSizeReduction (Context& context, const CaseDef& caseDef,
 
        // "Slices" is either the depth of a 3D image, or the number of layers of an arrayed image
        const deInt32                                   numSlices                       = maxLayersOrDepth(imageSize);
-       const VkDeviceSize                              colorSize                       = product(imageSize) * tcu::getPixelSize(mapVkFormat(caseDef.colorFormat));
-       const VkDeviceSize                              depthStencilSize        = (useDepthStencil ? product(imageSize) * tcu::getPixelSize(mapVkFormat(caseDef.depthStencilFormat)) : 0ull);
+
 
        if (useDepthStencil && !isDepthStencilFormatSupported(vki, physDevice, caseDef.depthStencilFormat))
                TCU_THROW(NotSupportedError, "Unsupported depth/stencil format");
 
-       if (colorSize + depthStencilSize > deviceMemoryBudget)
-               throw OutOfMemoryError(VK_ERROR_OUT_OF_DEVICE_MEMORY, "Image size exceeds test's image memory budget");
-
        // Determine the verification bounds. The checked region will be in the center of the rendered image
        const IVec4     checkSize       = tcu::min(imageSize, IVec4(MAX_VERIFICATION_REGION_SIZE,
                                                                                                                MAX_VERIFICATION_REGION_SIZE,
@@ -1184,8 +1258,6 @@ tcu::TestStatus testAttachmentSize (Context& context, const CaseDef caseDef)
 {
        checkImageViewTypeRequirements(context, caseDef.viewType);
 
-       int sizeReductionIndex = 0;
-
        if (caseDef.allocationKind == ALLOCATION_KIND_DEDICATED)
        {
                const std::string extensionName("VK_KHR_dedicated_allocation");
@@ -1194,20 +1266,7 @@ tcu::TestStatus testAttachmentSize (Context& context, const CaseDef caseDef)
                        TCU_THROW(NotSupportedError, std::string(extensionName + " is not supported").c_str());
        }
 
-       for (;;)
-       {
-               try
-               {
-                       return testWithSizeReduction(context, caseDef, sizeReductionIndex);
-               }
-               catch (OutOfMemoryError& ex)
-               {
-                       context.getTestContext().getLog()
-                               << tcu::TestLog::Message << "-- OutOfMemoryError: " << ex.getMessage() << tcu::TestLog::EndMessage;
-
-                       ++sizeReductionIndex;
-               }
-       }
+       return testWithSizeReduction(context, caseDef);
        // Never reached
 }