Respect platform memory limits in dEQP-VK.memory.* tests
authorPyry Haulos <phaulos@google.com>
Wed, 20 Apr 2016 22:40:23 +0000 (15:40 -0700)
committerPyry Haulos <phaulos@google.com>
Fri, 22 Apr 2016 18:08:06 +0000 (11:08 -0700)
Bug: 28275297
Change-Id: I1860295e445d432df220ce6a40e31f065774dbb7

external/vulkancts/modules/vulkan/memory/vktMemoryAllocationTests.cpp
external/vulkancts/modules/vulkan/memory/vktMemoryMappingTests.cpp

index ec020b7..a2a2302 100644 (file)
 #include "tcuMaybe.hpp"
 #include "tcuResultCollector.hpp"
 #include "tcuTestLog.hpp"
+#include "tcuPlatform.hpp"
 
 #include "vkPlatform.hpp"
 #include "vkStrUtil.hpp"
 #include "vkRef.hpp"
 #include "vkDeviceUtil.hpp"
 #include "vkQueryUtil.hpp"
+#include "vkRefUtil.hpp"
+#include "vkAllocationCallbackUtil.hpp"
 
 #include "deUniquePtr.hpp"
 #include "deStringUtil.hpp"
@@ -53,6 +56,7 @@ namespace memory
 {
 namespace
 {
+
 enum
 {
        // The min max for allocation count is 4096. Use 4000 to take into account
@@ -237,6 +241,31 @@ tcu::TestStatus AllocateFreeTestInstance::iterate (void)
                return tcu::TestStatus(m_result.getResult(), m_result.getMessage());
 }
 
+size_t computeDeviceMemorySystemMemFootprint (const DeviceInterface& vk, VkDevice device)
+{
+       AllocationCallbackRecorder      callbackRecorder        (getSystemAllocator());
+
+       {
+               // 1 B allocation from memory type 0
+               const VkMemoryAllocateInfo      allocInfo       =
+               {
+                       VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO,
+                       DE_NULL,
+                       1u,
+                       0u,
+               };
+               const Unique<VkDeviceMemory>                    memory                  (allocateMemory(vk, device, &allocInfo));
+               AllocationCallbackValidationResults             validateRes;
+
+               validateAllocationCallbacks(callbackRecorder, &validateRes);
+
+               TCU_CHECK(validateRes.violations.empty());
+
+               return getLiveSystemAllocationTotal(validateRes)
+                          + sizeof(void*)*validateRes.liveAllocations.size(); // allocation overhead
+       }
+}
+
 struct MemoryType
 {
        deUint32                index;
@@ -261,27 +290,35 @@ struct Heap
 class RandomAllocFreeTestInstance : public TestInstance
 {
 public:
-                                               RandomAllocFreeTestInstance             (Context& context, deUint32 seed);
-                                               ~RandomAllocFreeTestInstance    (void);
+                                                               RandomAllocFreeTestInstance             (Context& context, deUint32 seed);
+                                                               ~RandomAllocFreeTestInstance    (void);
 
-       tcu::TestStatus         iterate                                                 (void);
+       tcu::TestStatus                         iterate                                                 (void);
 
 private:
-       const size_t            m_opCount;
-       deUint32                        m_memoryObjectCount;
-       size_t                          m_opNdx;
-       de::Random                      m_rng;
-       vector<Heap>            m_heaps;
-       vector<size_t>          m_nonFullHeaps;
-       vector<size_t>          m_nonEmptyHeaps;
+       const size_t                            m_opCount;
+       const size_t                            m_allocSysMemSize;
+       const PlatformMemoryLimits      m_memoryLimits;
+
+       deUint32                                        m_memoryObjectCount;
+       size_t                                          m_opNdx;
+       de::Random                                      m_rng;
+       vector<Heap>                            m_heaps;
+       VkDeviceSize                            m_totalSystemMem;
+       VkDeviceSize                            m_totalDeviceMem;
 };
 
-RandomAllocFreeTestInstance::RandomAllocFreeTestInstance       (Context& context, deUint32 seed)
+RandomAllocFreeTestInstance::RandomAllocFreeTestInstance (Context& context, deUint32 seed)
        : TestInstance                  (context)
        , m_opCount                             (128)
+       , m_allocSysMemSize             (computeDeviceMemorySystemMemFootprint(context.getDeviceInterface(), context.getDevice())
+                                                        + sizeof(MemoryObject))
+       , m_memoryLimits                (getMemoryLimits(context.getTestContext().getPlatform().getVulkanPlatform()))
        , m_memoryObjectCount   (0)
        , m_opNdx                               (0)
        , m_rng                                 (seed)
+       , m_totalSystemMem              (0)
+       , m_totalDeviceMem              (0)
 {
        const VkPhysicalDevice                                  physicalDevice          = context.getPhysicalDevice();
        const InstanceInterface&                                vki                                     = context.getInstanceInterface();
@@ -292,18 +329,13 @@ RandomAllocFreeTestInstance::RandomAllocFreeTestInstance  (Context& context, deUi
 
        m_heaps.resize(memoryProperties.memoryHeapCount);
 
-       m_nonFullHeaps.reserve(m_heaps.size());
-       m_nonEmptyHeaps.reserve(m_heaps.size());
-
        for (deUint32 heapNdx = 0; heapNdx < memoryProperties.memoryHeapCount; heapNdx++)
        {
                m_heaps[heapNdx].heap                   = memoryProperties.memoryHeaps[heapNdx];
                m_heaps[heapNdx].memoryUsage    = 0;
-               m_heaps[heapNdx].maxMemoryUsage = m_heaps[heapNdx].heap.size / 8;
+               m_heaps[heapNdx].maxMemoryUsage = m_heaps[heapNdx].heap.size / 2; /* Use at maximum 50% of heap */
 
                m_heaps[heapNdx].objects.reserve(100);
-
-               m_nonFullHeaps.push_back((size_t)heapNdx);
        }
 
        for (deUint32 memoryTypeNdx = 0; memoryTypeNdx < memoryProperties.memoryTypeCount; memoryTypeNdx++)
@@ -342,6 +374,12 @@ tcu::TestStatus RandomAllocFreeTestInstance::iterate (void)
        const VkDevice                  device                  = m_context.getDevice();
        const DeviceInterface&  vkd                             = m_context.getDeviceInterface();
        TestLog&                                log                             = m_context.getTestContext().getLog();
+       const bool                              isUMA                   = m_memoryLimits.totalDeviceLocalMemory == 0;
+       const VkDeviceSize              usedSysMem              = isUMA ? (m_totalDeviceMem+m_totalSystemMem) : m_totalSystemMem;
+       const bool                              canAllocateSys  = usedSysMem + m_allocSysMemSize + 1024 < m_memoryLimits.totalSystemMemory; // \note Always leave room for 1 KiB sys mem alloc
+       const bool                              canAllocateDev  = isUMA ? canAllocateSys : (m_totalDeviceMem + 16 < m_memoryLimits.totalDeviceLocalMemory);
+       vector<size_t>                  nonFullHeaps;
+       vector<size_t>                  nonEmptyHeaps;
        bool                                    allocateMore;
 
        if (m_opNdx == 0)
@@ -350,18 +388,40 @@ tcu::TestStatus RandomAllocFreeTestInstance::iterate (void)
                log << TestLog::Message << "Using max 1/8 of the memory in each memory heap." << TestLog::EndMessage;
        }
 
+       // Sort heaps based on whether allocations or frees are possible
+       for (size_t heapNdx = 0; heapNdx < m_heaps.size(); ++heapNdx)
+       {
+               const bool      isDeviceLocal   = (m_heaps[heapNdx].heap.flags & VK_MEMORY_HEAP_DEVICE_LOCAL_BIT) != 0;
+               const bool      isHeapFull              = m_heaps[heapNdx].memoryUsage >= m_heaps[heapNdx].maxMemoryUsage;
+               const bool      isHeapEmpty             = m_heaps[heapNdx].memoryUsage == 0;
+
+               if (!isHeapEmpty)
+                       nonEmptyHeaps.push_back(heapNdx);
+
+               if (!isHeapFull && ((isUMA && canAllocateSys) ||
+                                                       (!isUMA && isDeviceLocal && canAllocateDev) ||
+                                                       (!isUMA && !isDeviceLocal && canAllocateSys)))
+                       nonFullHeaps.push_back(heapNdx);
+       }
+
        if (m_opNdx >= m_opCount)
        {
-               if (m_nonEmptyHeaps.empty())
+               if (nonEmptyHeaps.empty())
                        return tcu::TestStatus::pass("Pass");
                else
                        allocateMore = false;
        }
-       else if (!m_nonEmptyHeaps.empty() && !m_nonFullHeaps.empty() && (m_memoryObjectCount < MAX_ALLOCATION_COUNT))
+       else if (!nonEmptyHeaps.empty() &&
+                        !nonFullHeaps.empty() &&
+                        (m_memoryObjectCount < MAX_ALLOCATION_COUNT) &&
+                        canAllocateSys)
                allocateMore = m_rng.getBool(); // Randomize if both operations are doable.
-       else if (m_nonEmptyHeaps.empty())
+       else if (nonEmptyHeaps.empty())
+       {
+               DE_ASSERT(canAllocateSys);
                allocateMore = true; // Allocate more if there are no objects to free.
-       else if (m_nonFullHeaps.empty())
+       }
+       else if (nonFullHeaps.empty() || !canAllocateSys)
                allocateMore = false; // Free objects if there is no free space for new objects.
        else
        {
@@ -371,17 +431,19 @@ tcu::TestStatus RandomAllocFreeTestInstance::iterate (void)
 
        if (allocateMore)
        {
-               const size_t            nonFullHeapNdx  = (size_t)(m_rng.getUint32() % (deUint32)m_nonFullHeaps.size());
-               const size_t            heapNdx                 = m_nonFullHeaps[nonFullHeapNdx];
+               const size_t            nonFullHeapNdx  = (size_t)(m_rng.getUint32() % (deUint32)nonFullHeaps.size());
+               const size_t            heapNdx                 = nonFullHeaps[nonFullHeapNdx];
                Heap&                           heap                    = m_heaps[heapNdx];
                const MemoryType&       memoryType              = m_rng.choose<MemoryType>(heap.types.begin(), heap.types.end());
-               const VkDeviceSize      allocationSize  = 1 + (m_rng.getUint64() % (deUint64)(heap.maxMemoryUsage - heap.memoryUsage));
-
+               const bool                      isDeviceLocal   = (heap.heap.flags & VK_MEMORY_HEAP_DEVICE_LOCAL_BIT) != 0;
+               const VkDeviceSize      maxAllocSize    = (isDeviceLocal && !isUMA)
+                                                                                       ? de::min(heap.maxMemoryUsage - heap.memoryUsage, (VkDeviceSize)m_memoryLimits.totalDeviceLocalMemory - m_totalDeviceMem)
+                                                                                       : de::min(heap.maxMemoryUsage - heap.memoryUsage, (VkDeviceSize)m_memoryLimits.totalSystemMemory - usedSysMem - m_allocSysMemSize);
+               const VkDeviceSize      allocationSize  = 1 + (m_rng.getUint64() % maxAllocSize);
 
                if ((allocationSize > (deUint64)(heap.maxMemoryUsage - heap.memoryUsage)) && (allocationSize != 1))
                        TCU_THROW(InternalError, "Test Error: trying to allocate memory more than the available heap size.");
 
-
                const MemoryObject object =
                {
                        (VkDeviceMemory)0,
@@ -402,53 +464,31 @@ tcu::TestStatus RandomAllocFreeTestInstance::iterate (void)
                TCU_CHECK(!!heap.objects.back().memory);
                m_memoryObjectCount++;
 
-               // If heap was empty add to the non empty heaps.
-               if (heap.memoryUsage == 0)
-               {
-                       DE_ASSERT(heap.objects.size() == 1);
-                       m_nonEmptyHeaps.push_back(heapNdx);
-               }
-               else
-                       DE_ASSERT(heap.objects.size() > 1);
-
-               heap.memoryUsage += allocationSize;
-
-               // If heap became full, remove from non full heaps.
-               if (heap.memoryUsage >= heap.maxMemoryUsage)
-               {
-                       m_nonFullHeaps[nonFullHeapNdx] = m_nonFullHeaps.back();
-                       m_nonFullHeaps.pop_back();
-               }
+               heap.memoryUsage                                                                                += allocationSize;
+               (isDeviceLocal ? m_totalDeviceMem : m_totalSystemMem)   += allocationSize;
+               m_totalSystemMem                                                                                += m_allocSysMemSize;
        }
        else
        {
-               const size_t            nonEmptyHeapNdx = (size_t)(m_rng.getUint32() % (deUint32)m_nonEmptyHeaps.size());
-               const size_t            heapNdx                 = m_nonEmptyHeaps[nonEmptyHeapNdx];
+               const size_t            nonEmptyHeapNdx = (size_t)(m_rng.getUint32() % (deUint32)nonEmptyHeaps.size());
+               const size_t            heapNdx                 = nonEmptyHeaps[nonEmptyHeapNdx];
                Heap&                           heap                    = m_heaps[heapNdx];
                const size_t            memoryObjectNdx = m_rng.getUint32() % heap.objects.size();
                MemoryObject&           memoryObject    = heap.objects[memoryObjectNdx];
+               const bool                      isDeviceLocal   = (heap.heap.flags & VK_MEMORY_HEAP_DEVICE_LOCAL_BIT) != 0;
 
                vkd.freeMemory(device, memoryObject.memory, (const VkAllocationCallbacks*)DE_NULL);
                memoryObject.memory = (VkDeviceMemory)0;
                m_memoryObjectCount--;
 
-               if (heap.memoryUsage >= heap.maxMemoryUsage && heap.memoryUsage - memoryObject.size < heap.maxMemoryUsage)
-                       m_nonFullHeaps.push_back(heapNdx);
-
-               heap.memoryUsage -= memoryObject.size;
+               heap.memoryUsage                                                                                -= memoryObject.size;
+               (isDeviceLocal ? m_totalDeviceMem : m_totalSystemMem)   -= memoryObject.size;
+               m_totalSystemMem                                                                                -= m_allocSysMemSize;
 
                heap.objects[memoryObjectNdx] = heap.objects.back();
                heap.objects.pop_back();
 
-               if (heap.memoryUsage == 0)
-               {
-                       DE_ASSERT(heap.objects.empty());
-
-                       m_nonEmptyHeaps[nonEmptyHeapNdx] = m_nonEmptyHeaps.back();
-                       m_nonEmptyHeaps.pop_back();
-               }
-               else
-                       DE_ASSERT(!heap.objects.empty());
+               DE_ASSERT(heap.memoryUsage == 0 || !heap.objects.empty());
        }
 
        m_opNdx++;
index 18b00f9..555a136 100644 (file)
 #include "tcuMaybe.hpp"
 #include "tcuResultCollector.hpp"
 #include "tcuTestLog.hpp"
+#include "tcuPlatform.hpp"
 
 #include "vkDeviceUtil.hpp"
 #include "vkPlatform.hpp"
 #include "vkQueryUtil.hpp"
 #include "vkRef.hpp"
+#include "vkRefUtil.hpp"
 #include "vkStrUtil.hpp"
+#include "vkAllocationCallbackUtil.hpp"
 
 #include "deRandom.hpp"
 #include "deSharedPtr.hpp"
 #include "deStringUtil.hpp"
 #include "deUniquePtr.hpp"
+#include "deSTLUtil.hpp"
 
 #include <string>
 #include <vector>
+#include <algorithm>
 
 using tcu::Maybe;
 using tcu::TestLog;
@@ -57,11 +62,37 @@ namespace vkt
 {
 namespace memory
 {
+
 namespace
 {
+
+size_t computeDeviceMemorySystemMemFootprint (const DeviceInterface& vk, VkDevice device)
+{
+       AllocationCallbackRecorder      callbackRecorder        (getSystemAllocator());
+
+       {
+               // 1 B allocation from memory type 0
+               const VkMemoryAllocateInfo      allocInfo       =
+               {
+                       VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO,
+                       DE_NULL,
+                       1u,
+                       0u,
+               };
+               const Unique<VkDeviceMemory>                    memory                  (allocateMemory(vk, device, &allocInfo));
+               AllocationCallbackValidationResults             validateRes;
+
+               validateAllocationCallbacks(callbackRecorder, &validateRes);
+
+               TCU_CHECK(validateRes.violations.empty());
+
+               return getLiveSystemAllocationTotal(validateRes)
+                          + sizeof(void*)*validateRes.liveAllocations.size(); // allocation overhead
+       }
+}
+
 Move<VkDeviceMemory> allocMemory (const DeviceInterface& vk, VkDevice device, VkDeviceSize pAllocInfo_allocationSize, deUint32 pAllocInfo_memoryTypeIndex)
 {
-       VkDeviceMemory object = 0;
        const VkMemoryAllocateInfo pAllocInfo =
        {
                VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO,
@@ -69,8 +100,7 @@ Move<VkDeviceMemory> allocMemory (const DeviceInterface& vk, VkDevice device, Vk
                pAllocInfo_allocationSize,
                pAllocInfo_memoryTypeIndex,
        };
-       VK_CHECK(vk.allocateMemory(device, &pAllocInfo, (const VkAllocationCallbacks*)DE_NULL, &object));
-       return Move<VkDeviceMemory>(check<VkDeviceMemory>(object), Deleter<VkDeviceMemory>(vk, device, (const VkAllocationCallbacks*)DE_NULL));
+       return allocateMemory(vk, device, &pAllocInfo);
 }
 
 struct MemoryRange
@@ -490,8 +520,8 @@ void MemoryObject::randomInvalidate (const DeviceInterface& vkd, VkDevice device
 
 enum
 {
-       // Use only 1/16 of each memory heap.
-       MAX_MEMORY_USAGE_DIV = 16
+       // Use only 1/2 of each memory heap.
+       MAX_MEMORY_USAGE_DIV = 2
 };
 
 template<typename T>
@@ -508,14 +538,63 @@ void removeFirstEqual (vector<T>& vec, const T& val)
        }
 }
 
+enum MemoryClass
+{
+       MEMORY_CLASS_SYSTEM = 0,
+       MEMORY_CLASS_DEVICE,
+
+       MEMORY_CLASS_LAST
+};
+
+// \todo [2016-04-20 pyry] Consider estimating memory fragmentation
+class TotalMemoryTracker
+{
+public:
+                                       TotalMemoryTracker      (void)
+       {
+               std::fill(DE_ARRAY_BEGIN(m_usage), DE_ARRAY_END(m_usage), 0);
+       }
+
+       void                    allocate                        (MemoryClass memClass, VkDeviceSize size)
+       {
+               m_usage[memClass] += size;
+       }
+
+       void                    free                            (MemoryClass memClass, VkDeviceSize size)
+       {
+               DE_ASSERT(size <= m_usage[memClass]);
+               m_usage[memClass] -= size;
+       }
+
+       VkDeviceSize    getUsage                        (MemoryClass memClass) const
+       {
+               return m_usage[memClass];
+       }
+
+       VkDeviceSize    getTotalUsage           (void) const
+       {
+               VkDeviceSize total = 0;
+               for (int ndx = 0; ndx < MEMORY_CLASS_LAST; ++ndx)
+                       total += getUsage((MemoryClass)ndx);
+               return total;
+       }
+
+private:
+       VkDeviceSize    m_usage[MEMORY_CLASS_LAST];
+};
+
 class MemoryHeap
 {
 public:
        MemoryHeap (const VkMemoryHeap&                 heap,
-                               const vector<deUint32>&         memoryTypes)
-               : m_heap                (heap)
-               , m_memoryTypes (memoryTypes)
-               , m_usage               (0)
+                               const vector<deUint32>&         memoryTypes,
+                               const PlatformMemoryLimits&     memoryLimits,
+                               TotalMemoryTracker&                     totalMemTracker)
+               : m_heap                        (heap)
+               , m_memoryTypes         (memoryTypes)
+               , m_limits                      (memoryLimits)
+               , m_totalMemTracker     (totalMemTracker)
+               , m_usage                       (0)
        {
        }
 
@@ -525,20 +604,24 @@ public:
                        delete *iter;
        }
 
-       bool                                                            full                    (void) const { return m_usage * MAX_MEMORY_USAGE_DIV >= m_heap.size; }
-       bool                                                            empty                   (void) const { return m_usage == 0; }
+       bool                                                            full                    (void) const { return getAvailableMem() == 0;   }
+       bool                                                            empty                   (void) const { return m_usage == 0;                             }
 
        MemoryObject*                                           allocateRandom  (const DeviceInterface& vkd, VkDevice device, de::Random& rng)
        {
-               const VkDeviceSize              size    = 1 + (rng.getUint64() % (de::max((deInt64)((m_heap.size / MAX_MEMORY_USAGE_DIV) - m_usage - 1ull), (deInt64)1)));
-               const deUint32                  type    = rng.choose<deUint32>(m_memoryTypes.begin(), m_memoryTypes.end());
+               const VkDeviceSize              availableMem    = getAvailableMem();
+
+               DE_ASSERT(availableMem > 0);
 
-               if ( (size > (VkDeviceSize)((m_heap.size / MAX_MEMORY_USAGE_DIV) - m_usage)) && (size != 1))
-                       TCU_THROW(InternalError, "Test Error: trying to allocate memory more than the available heap size.");
+               const VkDeviceSize              size                    = 1ull + (rng.getUint64() % availableMem);
+               const deUint32                  type                    = rng.choose<deUint32>(m_memoryTypes.begin(), m_memoryTypes.end());
+
+               DE_ASSERT(size <= availableMem);
 
                MemoryObject* const             object  = new MemoryObject(vkd, device, size, type);
 
                m_usage += size;
+               m_totalMemTracker.allocate(getMemoryClass(), size);
                m_objects.push_back(object);
 
                return object;
@@ -553,24 +636,80 @@ public:
        {
                removeFirstEqual(m_objects, object);
                m_usage -= object->getSize();
+               m_totalMemTracker.free(getMemoryClass(), object->getSize());
                delete object;
        }
 
 private:
-       VkMemoryHeap                    m_heap;
-       vector<deUint32>                m_memoryTypes;
+       MemoryClass                                                     getMemoryClass  (void) const
+       {
+               if ((m_heap.flags & VK_MEMORY_HEAP_DEVICE_LOCAL_BIT) != 0)
+                       return MEMORY_CLASS_DEVICE;
+               else
+                       return MEMORY_CLASS_SYSTEM;
+       }
 
-       VkDeviceSize                    m_usage;
-       vector<MemoryObject*>   m_objects;
+       VkDeviceSize                                            getAvailableMem (void) const
+       {
+               DE_ASSERT(m_usage <= m_heap.size/MAX_MEMORY_USAGE_DIV);
+
+               const VkDeviceSize      availableInHeap = m_heap.size/MAX_MEMORY_USAGE_DIV - m_usage;
+               const bool                      isUMA                   = m_limits.totalDeviceLocalMemory == 0;
+
+               if (isUMA)
+               {
+                       const VkDeviceSize      totalUsage      = m_totalMemTracker.getTotalUsage();
+                       const VkDeviceSize      totalSysMem     = (VkDeviceSize)m_limits.totalSystemMemory;
+
+                       DE_ASSERT(totalUsage <= totalSysMem);
+
+                       return de::min(availableInHeap, totalSysMem-totalUsage);
+               }
+               else
+               {
+                       const MemoryClass       memClass                = getMemoryClass();
+                       const VkDeviceSize      totalMemClass   = memClass == MEMORY_CLASS_SYSTEM
+                                                                                               ? (VkDeviceSize)m_limits.totalSystemMemory
+                                                                                               : m_limits.totalDeviceLocalMemory;
+                       const VkDeviceSize      usedMemClass    = m_totalMemTracker.getUsage(memClass);
+
+                       DE_ASSERT(usedMemClass <= totalMemClass);
+
+                       return de::min(availableInHeap, totalMemClass-usedMemClass);
+               }
+       }
+
+       const VkMemoryHeap                      m_heap;
+       const vector<deUint32>          m_memoryTypes;
+       const PlatformMemoryLimits&     m_limits;
+       TotalMemoryTracker&                     m_totalMemTracker;
+
+       VkDeviceSize                            m_usage;
+       vector<MemoryObject*>           m_objects;
 };
 
+size_t getMemoryObjectSystemSize (Context& context)
+{
+       return computeDeviceMemorySystemMemFootprint(context.getDeviceInterface(), context.getDevice())
+                  + sizeof(MemoryObject)
+                  + sizeof(de::SharedPtr<MemoryObject>);
+}
+
+size_t getMemoryMappingSystemSize (void)
+{
+       return sizeof(MemoryMapping) + sizeof(de::SharedPtr<MemoryMapping>);
+}
+
 class RandomMemoryMappingInstance : public TestInstance
 {
 public:
        RandomMemoryMappingInstance (Context& context, deUint32 seed)
-               : TestInstance  (context)
-               , m_rng                 (seed)
-               , m_opNdx               (0)
+               : TestInstance                          (context)
+               , m_memoryObjectSysMemSize      (getMemoryObjectSystemSize(context))
+               , m_memoryMappingSysMemSize     (getMemoryMappingSystemSize())
+               , m_memoryLimits                        (getMemoryLimits(context.getTestContext().getPlatform().getVulkanPlatform()))
+               , m_rng                                         (seed)
+               , m_opNdx                                       (0)
        {
                const VkPhysicalDevice                                  physicalDevice          = context.getPhysicalDevice();
                const InstanceInterface&                                vki                                     = context.getInstanceInterface();
@@ -592,10 +731,11 @@ public:
 
                                if (!memoryTypes[heapIndex].empty())
                                {
-                                       const de::SharedPtr<MemoryHeap> heap    (new MemoryHeap(heapInfo, memoryTypes[heapIndex]));
+                                       const de::SharedPtr<MemoryHeap> heap    (new MemoryHeap(heapInfo, memoryTypes[heapIndex], m_memoryLimits, m_totalMemTracker));
 
-                                       if (!heap->full())
-                                               m_nonFullHeaps.push_back(heap);
+                                       TCU_CHECK_INTERNAL(!heap->full());
+
+                                       m_memoryHeaps.push_back(heap);
                                }
                        }
                }
@@ -618,127 +758,165 @@ public:
                const VkDevice                  device                                          = m_context.getDevice();
                const DeviceInterface&  vkd                                                     = m_context.getDeviceInterface();
 
-               if (m_opNdx < opCount)
+               const VkDeviceSize              sysMemUsage                                     = (m_memoryLimits.totalDeviceLocalMemory == 0)
+                                                                                                                       ? m_totalMemTracker.getTotalUsage()
+                                                                                                                       : m_totalMemTracker.getUsage(MEMORY_CLASS_SYSTEM);
+
+               if (!m_memoryMappings.empty() && m_rng.getFloat() < memoryOpProbability)
                {
-                       if (!m_memoryMappings.empty() && m_rng.getFloat() < memoryOpProbability)
-                       {
-                               // Perform operations on mapped memory
-                               MemoryMapping* const    mapping = m_rng.choose<MemoryMapping*>(m_memoryMappings.begin(), m_memoryMappings.end());
+                       // Perform operations on mapped memory
+                       MemoryMapping* const    mapping = m_rng.choose<MemoryMapping*>(m_memoryMappings.begin(), m_memoryMappings.end());
 
-                               enum Op
-                               {
-                                       OP_READ = 0,
-                                       OP_WRITE,
-                                       OP_MODIFY,
-                                       OP_LAST
-                               };
+                       enum Op
+                       {
+                               OP_READ = 0,
+                               OP_WRITE,
+                               OP_MODIFY,
+                               OP_LAST
+                       };
 
-                               const Op op = (Op)(m_rng.getUint32() % OP_LAST);
+                       const Op op = (Op)(m_rng.getUint32() % OP_LAST);
 
-                               switch (op)
-                               {
-                                       case OP_READ:
-                                               mapping->randomRead(m_rng);
-                                               break;
+                       switch (op)
+                       {
+                               case OP_READ:
+                                       mapping->randomRead(m_rng);
+                                       break;
 
-                                       case OP_WRITE:
-                                               mapping->randomWrite(m_rng);
-                                               break;
+                               case OP_WRITE:
+                                       mapping->randomWrite(m_rng);
+                                       break;
 
-                                       case OP_MODIFY:
-                                               mapping->randomModify(m_rng);
-                                               break;
+                               case OP_MODIFY:
+                                       mapping->randomModify(m_rng);
+                                       break;
 
-                                       default:
-                                               DE_FATAL("Invalid operation");
-                               }
+                               default:
+                                       DE_FATAL("Invalid operation");
                        }
-                       else if (!m_mappedMemoryObjects.empty() && m_rng.getFloat() < flushInvalidateProbability)
-                       {
-                               MemoryObject* const     object  = m_rng.choose<MemoryObject*>(m_mappedMemoryObjects.begin(), m_mappedMemoryObjects.end());
+               }
+               else if (!m_mappedMemoryObjects.empty() && m_rng.getFloat() < flushInvalidateProbability)
+               {
+                       MemoryObject* const     object  = m_rng.choose<MemoryObject*>(m_mappedMemoryObjects.begin(), m_mappedMemoryObjects.end());
 
-                               if (m_rng.getBool())
-                                       object->randomFlush(vkd, device, m_rng);
-                               else
-                                       object->randomInvalidate(vkd, device, m_rng);
-                       }
-                       else if (!m_mappedMemoryObjects.empty() && m_rng.getFloat() < unmapProbability)
-                       {
-                               // Unmap memory object
-                               MemoryObject* const     object  = m_rng.choose<MemoryObject*>(m_mappedMemoryObjects.begin(), m_mappedMemoryObjects.end());
+                       if (m_rng.getBool())
+                               object->randomFlush(vkd, device, m_rng);
+                       else
+                               object->randomInvalidate(vkd, device, m_rng);
+               }
+               else if (!m_mappedMemoryObjects.empty() && m_rng.getFloat() < unmapProbability)
+               {
+                       // Unmap memory object
+                       MemoryObject* const     object  = m_rng.choose<MemoryObject*>(m_mappedMemoryObjects.begin(), m_mappedMemoryObjects.end());
 
-                               // Remove mapping
-                               removeFirstEqual(m_memoryMappings, object->getMapping());
+                       // Remove mapping
+                       removeFirstEqual(m_memoryMappings, object->getMapping());
 
-                               object->unmap();
-                               removeFirstEqual(m_mappedMemoryObjects, object);
-                               m_nonMappedMemoryObjects.push_back(object);
-                       }
-                       else if (!m_nonMappedMemoryObjects.empty() && m_rng.getFloat() < mapProbability)
+                       object->unmap();
+                       removeFirstEqual(m_mappedMemoryObjects, object);
+                       m_nonMappedMemoryObjects.push_back(object);
+
+                       m_totalMemTracker.free(MEMORY_CLASS_SYSTEM, (VkDeviceSize)m_memoryMappingSysMemSize);
+               }
+               else if (!m_nonMappedMemoryObjects.empty() &&
+                                (m_rng.getFloat() < mapProbability) &&
+                                (sysMemUsage+m_memoryMappingSysMemSize <= (VkDeviceSize)m_memoryLimits.totalSystemMemory))
+               {
+                       // Map memory object
+                       MemoryObject* const             object  = m_rng.choose<MemoryObject*>(m_nonMappedMemoryObjects.begin(), m_nonMappedMemoryObjects.end());
+                       MemoryMapping*                  mapping = object->mapRandom(vkd, device, m_rng);
+
+                       m_memoryMappings.push_back(mapping);
+                       m_mappedMemoryObjects.push_back(object);
+                       removeFirstEqual(m_nonMappedMemoryObjects, object);
+
+                       m_totalMemTracker.allocate(MEMORY_CLASS_SYSTEM, (VkDeviceSize)m_memoryMappingSysMemSize);
+               }
+               else
+               {
+                       // Sort heaps based on capacity (full or not)
+                       vector<MemoryHeap*>             nonFullHeaps;
+                       vector<MemoryHeap*>             nonEmptyHeaps;
+
+                       if (sysMemUsage+m_memoryObjectSysMemSize <= (VkDeviceSize)m_memoryLimits.totalSystemMemory)
                        {
-                               // Map memory object
-                               MemoryObject* const             object  = m_rng.choose<MemoryObject*>(m_nonMappedMemoryObjects.begin(), m_nonMappedMemoryObjects.end());
-                               MemoryMapping*                  mapping = object->mapRandom(vkd, device, m_rng);
+                               // For the duration of sorting reserve MemoryObject space from system memory
+                               m_totalMemTracker.allocate(MEMORY_CLASS_SYSTEM, (VkDeviceSize)m_memoryObjectSysMemSize);
 
-                               m_memoryMappings.push_back(mapping);
-                               m_mappedMemoryObjects.push_back(object);
-                               removeFirstEqual(m_nonMappedMemoryObjects, object);
+                               for (vector<de::SharedPtr<MemoryHeap> >::const_iterator heapIter = m_memoryHeaps.begin();
+                                        heapIter != m_memoryHeaps.end();
+                                        ++heapIter)
+                               {
+                                       if (!(*heapIter)->full())
+                                               nonFullHeaps.push_back(heapIter->get());
+
+                                       if (!(*heapIter)->empty())
+                                               nonEmptyHeaps.push_back(heapIter->get());
+                               }
+
+                               m_totalMemTracker.free(MEMORY_CLASS_SYSTEM, (VkDeviceSize)m_memoryObjectSysMemSize);
                        }
                        else
                        {
-                               if (!m_nonFullHeaps.empty() && (m_nonEmptyHeaps.empty() || m_rng.getFloat() < allocProbability))
+                               // Not possible to even allocate MemoryObject from system memory, look for non-empty heaps
+                               for (vector<de::SharedPtr<MemoryHeap> >::const_iterator heapIter = m_memoryHeaps.begin();
+                                        heapIter != m_memoryHeaps.end();
+                                        ++heapIter)
                                {
-                                       // Allocate more memory objects
-                                       de::SharedPtr<MemoryHeap> const heap = m_rng.choose<de::SharedPtr<MemoryHeap> >(m_nonFullHeaps.begin(), m_nonFullHeaps.end());
+                                       if (!(*heapIter)->empty())
+                                               nonEmptyHeaps.push_back(heapIter->get());
+                               }
+                       }
 
-                                       if (heap->empty())
-                                               m_nonEmptyHeaps.push_back(heap);
+                       if (!nonFullHeaps.empty() && (nonEmptyHeaps.empty() || m_rng.getFloat() < allocProbability))
+                       {
+                               // Reserve MemoryObject from sys mem first
+                               m_totalMemTracker.allocate(MEMORY_CLASS_SYSTEM, (VkDeviceSize)m_memoryObjectSysMemSize);
 
-                                       {
-                                               MemoryObject* const     object = heap->allocateRandom(vkd, device, m_rng);
+                               // Allocate more memory objects
+                               MemoryHeap* const       heap    = m_rng.choose<MemoryHeap*>(nonFullHeaps.begin(), nonFullHeaps.end());
+                               MemoryObject* const     object  = heap->allocateRandom(vkd, device, m_rng);
 
-                                               if (heap->full())
-                                                       removeFirstEqual(m_nonFullHeaps, heap);
+                               m_nonMappedMemoryObjects.push_back(object);
+                       }
+                       else
+                       {
+                               // Free memory objects
+                               MemoryHeap* const               heap    = m_rng.choose<MemoryHeap*>(nonEmptyHeaps.begin(), nonEmptyHeaps.end());
+                               MemoryObject* const             object  = heap->getRandomObject(m_rng);
 
-                                               m_nonMappedMemoryObjects.push_back(object);
-                                       }
-                               }
-                               else
+                               // Remove mapping
+                               if (object->getMapping())
                                {
-                                       // Free memory objects
-                                       de::SharedPtr<MemoryHeap> const         heap    = m_rng.choose<de::SharedPtr<MemoryHeap> >(m_nonEmptyHeaps.begin(), m_nonEmptyHeaps.end());
-                                       MemoryObject* const                                     object  = heap->getRandomObject(m_rng);
-
-                                       // Remove mapping
-                                       if (object->getMapping())
-                                               removeFirstEqual(m_memoryMappings, object->getMapping());
-
-                                       removeFirstEqual(m_mappedMemoryObjects, object);
-                                       removeFirstEqual(m_nonMappedMemoryObjects, object);
-
-                                       if (heap->full())
-                                               m_nonFullHeaps.push_back(heap);
+                                       removeFirstEqual(m_memoryMappings, object->getMapping());
+                                       m_totalMemTracker.free(MEMORY_CLASS_SYSTEM, m_memoryMappingSysMemSize);
+                               }
 
-                                       heap->free(object);
+                               removeFirstEqual(m_mappedMemoryObjects, object);
+                               removeFirstEqual(m_nonMappedMemoryObjects, object);
 
-                                       if (heap->empty())
-                                               removeFirstEqual(m_nonEmptyHeaps, heap);
-                               }
+                               heap->free(object);
+                               m_totalMemTracker.free(MEMORY_CLASS_SYSTEM, (VkDeviceSize)m_memoryObjectSysMemSize);
                        }
-
-                       m_opNdx++;
-                       return tcu::TestStatus::incomplete();
                }
-               else
+
+               m_opNdx += 1;
+               if (m_opNdx == opCount)
                        return tcu::TestStatus::pass("Pass");
+               else
+                       return tcu::TestStatus::incomplete();
        }
 
 private:
+       const size_t                                            m_memoryObjectSysMemSize;
+       const size_t                                            m_memoryMappingSysMemSize;
+       const PlatformMemoryLimits                      m_memoryLimits;
+
        de::Random                                                      m_rng;
        size_t                                                          m_opNdx;
 
-       vector<de::SharedPtr<MemoryHeap> >      m_nonEmptyHeaps;
-       vector<de::SharedPtr<MemoryHeap> >      m_nonFullHeaps;
+       TotalMemoryTracker                                      m_totalMemTracker;
+       vector<de::SharedPtr<MemoryHeap> >      m_memoryHeaps;
 
        vector<MemoryObject*>                           m_mappedMemoryObjects;
        vector<MemoryObject*>                           m_nonMappedMemoryObjects;