1 /*-------------------------------------------------------------------------
2 * Vulkan Conformance Tests
3 * ------------------------
5 * Copyright (c) 2015 Google Inc.
7 * Permission is hereby granted, free of charge, to any person obtaining a
8 * copy of this software and/or associated documentation files (the
9 * "Materials"), to deal in the Materials without restriction, including
10 * without limitation the rights to use, copy, modify, merge, publish,
11 * distribute, sublicense, and/or sell copies of the Materials, and to
12 * permit persons to whom the Materials are furnished to do so, subject to
13 * the following conditions:
15 * The above copyright notice(s) and this permission notice shall be
16 * included in all copies or substantial portions of the Materials.
18 * The Materials are Confidential Information as defined by the
19 * Khronos Membership Agreement until designated non-confidential by
20 * Khronos, at which point this condition clause shall be removed.
22 * THE MATERIALS ARE PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
23 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
24 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
25 * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
26 * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
27 * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
28 * MATERIALS OR THE USE OR OTHER DEALINGS IN THE MATERIALS.
32 * \brief Simple memory allocation tests.
33 *//*--------------------------------------------------------------------*/
35 #include "vktMemoryAllocationTests.hpp"
37 #include "vktTestCaseUtil.hpp"
39 #include "tcuMaybe.hpp"
40 #include "tcuResultCollector.hpp"
41 #include "tcuTestLog.hpp"
43 #include "vkPlatform.hpp"
44 #include "vkStrUtil.hpp"
46 #include "vkDeviceUtil.hpp"
47 #include "vkQueryUtil.hpp"
49 #include "deUniquePtr.hpp"
50 #include "deStringUtil.hpp"
51 #include "deRandom.hpp"
78 Maybe<VkDeviceSize> memorySize;
79 Maybe<float> memoryPercentage;
80 deUint32 memoryAllocationCount;
84 : memoryAllocationCount ((deUint32)-1)
90 class AllocateFreeTestInstance : public TestInstance
93 AllocateFreeTestInstance (Context& context, const TestConfig config)
94 : TestInstance (context)
96 , m_result (m_context.getTestContext().getLog())
97 , m_memoryTypeIndex (0)
98 , m_memoryProperties (getPhysicalDeviceMemoryProperties(context.getInstanceInterface(), context.getPhysicalDevice()))
100 DE_ASSERT(!!m_config.memorySize != !!m_config.memoryPercentage);
103 tcu::TestStatus iterate (void);
106 const TestConfig m_config;
107 tcu::ResultCollector m_result;
108 deUint32 m_memoryTypeIndex;
109 const VkPhysicalDeviceMemoryProperties m_memoryProperties;
112 tcu::TestStatus AllocateFreeTestInstance::iterate (void)
114 TestLog& log = m_context.getTestContext().getLog();
115 const VkDevice device = m_context.getDevice();
116 const DeviceInterface& vkd = m_context.getDeviceInterface();
118 if (m_memoryTypeIndex == 0)
120 log << TestLog::Message << "Memory allocation count: " << m_config.memoryAllocationCount << TestLog::EndMessage;
121 log << TestLog::Message << "Single allocation size: " << (m_config.memorySize ? de::toString(*m_config.memorySize) : de::toString(100.0f * (*m_config.memoryPercentage)) + " percent of the heap size.") << TestLog::EndMessage;
123 if (m_config.order == TestConfig::ALLOC_REVERSE_FREE)
124 log << TestLog::Message << "Memory is freed in reversed order. " << TestLog::EndMessage;
125 else if (m_config.order == TestConfig::ALLOC_FREE)
126 log << TestLog::Message << "Memory is freed in same order as allocated. " << TestLog::EndMessage;
127 else if (m_config.order == TestConfig::MIXED_ALLOC_FREE)
128 log << TestLog::Message << "Memory is freed right after allocation. " << TestLog::EndMessage;
130 DE_FATAL("Unknown allocation order");
135 const VkMemoryType memoryType = m_memoryProperties.memoryTypes[m_memoryTypeIndex];
136 const VkMemoryHeap memoryHeap = m_memoryProperties.memoryHeaps[memoryType.heapIndex];
138 const VkDeviceSize allocationSize = (m_config.memorySize ? *m_config.memorySize : (VkDeviceSize)(*m_config.memoryPercentage * (float)memoryHeap.size));
139 vector<VkDeviceMemory> memoryObjects (m_config.memoryAllocationCount, (VkDeviceMemory)0);
141 log << TestLog::Message << "Memory type index: " << m_memoryTypeIndex << TestLog::EndMessage;
143 if (memoryType.heapIndex >= m_memoryProperties.memoryHeapCount)
144 m_result.fail("Invalid heap index defined for memory type.");
147 log << TestLog::Message << "Memory type: " << memoryType << TestLog::EndMessage;
148 log << TestLog::Message << "Memory heap: " << memoryHeap << TestLog::EndMessage;
150 if (allocationSize * m_config.memoryAllocationCount * 8 > memoryHeap.size)
151 TCU_THROW(NotSupportedError, "Memory heap doesn't have enough memory.");
155 if (m_config.order == TestConfig::ALLOC_FREE || m_config.order == TestConfig::ALLOC_REVERSE_FREE)
157 for (size_t ndx = 0; ndx < m_config.memoryAllocationCount; ndx++)
159 const VkMemoryAllocateInfo alloc =
161 VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO, // sType
163 allocationSize, // allocationSize
164 m_memoryTypeIndex // memoryTypeIndex;
167 VK_CHECK(vkd.allocateMemory(device, &alloc, (const VkAllocationCallbacks*)DE_NULL, &memoryObjects[ndx]));
169 TCU_CHECK(!!memoryObjects[ndx]);
172 if (m_config.order == TestConfig::ALLOC_FREE)
174 for (size_t ndx = 0; ndx < m_config.memoryAllocationCount; ndx++)
176 const VkDeviceMemory mem = memoryObjects[memoryObjects.size() - 1 - ndx];
178 vkd.freeMemory(device, mem, (const VkAllocationCallbacks*)DE_NULL);
179 memoryObjects[memoryObjects.size() - 1 - ndx] = (VkDeviceMemory)0;
184 for (size_t ndx = 0; ndx < m_config.memoryAllocationCount; ndx++)
186 const VkDeviceMemory mem = memoryObjects[ndx];
188 vkd.freeMemory(device, mem, (const VkAllocationCallbacks*)DE_NULL);
189 memoryObjects[ndx] = (VkDeviceMemory)0;
195 for (size_t ndx = 0; ndx < m_config.memoryAllocationCount; ndx++)
197 const VkMemoryAllocateInfo alloc =
199 VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO, // sType
201 allocationSize, // allocationSize
202 m_memoryTypeIndex // memoryTypeIndex;
205 VK_CHECK(vkd.allocateMemory(device, &alloc, (const VkAllocationCallbacks*)DE_NULL, &memoryObjects[ndx]));
206 TCU_CHECK(!!memoryObjects[ndx]);
208 vkd.freeMemory(device, memoryObjects[ndx], (const VkAllocationCallbacks*)DE_NULL);
209 memoryObjects[ndx] = (VkDeviceMemory)0;
215 for (size_t ndx = 0; ndx < m_config.memoryAllocationCount; ndx++)
217 const VkDeviceMemory mem = memoryObjects[ndx];
221 vkd.freeMemory(device, mem, (const VkAllocationCallbacks*)DE_NULL);
222 memoryObjects[ndx] = (VkDeviceMemory)0;
230 catch (const tcu::TestError& error)
232 m_result.fail(error.getMessage());
237 if (m_memoryTypeIndex < m_memoryProperties.memoryTypeCount)
238 return tcu::TestStatus::incomplete();
240 return tcu::TestStatus(m_result.getResult(), m_result.getMessage());
251 VkDeviceMemory memory;
258 VkDeviceSize memoryUsage;
259 VkDeviceSize maxMemoryUsage;
260 vector<MemoryType> types;
261 vector<MemoryObject> objects;
264 class RandomAllocFreeTestInstance : public TestInstance
267 RandomAllocFreeTestInstance (Context& context, deUint32 seed);
268 ~RandomAllocFreeTestInstance (void);
270 tcu::TestStatus iterate (void);
273 const size_t m_opCount;
276 vector<Heap> m_heaps;
277 vector<size_t> m_nonFullHeaps;
278 vector<size_t> m_nonEmptyHeaps;
281 RandomAllocFreeTestInstance::RandomAllocFreeTestInstance (Context& context, deUint32 seed)
282 : TestInstance (context)
287 const VkPhysicalDevice physicalDevice = context.getPhysicalDevice();
288 const InstanceInterface& vki = context.getInstanceInterface();
289 const VkPhysicalDeviceMemoryProperties memoryProperties = getPhysicalDeviceMemoryProperties(vki, physicalDevice);
291 TCU_CHECK(memoryProperties.memoryHeapCount <= 32);
292 TCU_CHECK(memoryProperties.memoryTypeCount <= 32);
294 m_heaps.resize(memoryProperties.memoryHeapCount);
296 m_nonFullHeaps.reserve(m_heaps.size());
297 m_nonEmptyHeaps.reserve(m_heaps.size());
299 for (deUint32 heapNdx = 0; heapNdx < memoryProperties.memoryHeapCount; heapNdx++)
301 m_heaps[heapNdx].heap = memoryProperties.memoryHeaps[heapNdx];
302 m_heaps[heapNdx].memoryUsage = 0;
303 m_heaps[heapNdx].maxMemoryUsage = m_heaps[heapNdx].heap.size / 8;
305 m_heaps[heapNdx].objects.reserve(100);
307 m_nonFullHeaps.push_back((size_t)heapNdx);
310 for (deUint32 memoryTypeNdx = 0; memoryTypeNdx < memoryProperties.memoryTypeCount; memoryTypeNdx++)
312 const MemoryType type =
315 memoryProperties.memoryTypes[memoryTypeNdx]
318 TCU_CHECK(type.type.heapIndex < memoryProperties.memoryHeapCount);
320 m_heaps[type.type.heapIndex].types.push_back(type);
324 RandomAllocFreeTestInstance::~RandomAllocFreeTestInstance (void)
326 const VkDevice device = m_context.getDevice();
327 const DeviceInterface& vkd = m_context.getDeviceInterface();
329 for (deUint32 heapNdx = 0; heapNdx < (deUint32)m_heaps.size(); heapNdx++)
331 const Heap& heap = m_heaps[heapNdx];
333 for (size_t objectNdx = 0; objectNdx < heap.objects.size(); objectNdx++)
335 if (!!heap.objects[objectNdx].memory)
336 vkd.freeMemory(device, heap.objects[objectNdx].memory, (const VkAllocationCallbacks*)DE_NULL);
341 tcu::TestStatus RandomAllocFreeTestInstance::iterate (void)
343 const VkDevice device = m_context.getDevice();
344 const DeviceInterface& vkd = m_context.getDeviceInterface();
345 TestLog& log = m_context.getTestContext().getLog();
350 log << TestLog::Message << "Performing " << m_opCount << " random VkAllocMemory() / VkFreeMemory() calls before freeing all memory." << TestLog::EndMessage;
351 log << TestLog::Message << "Using max 1/8 of the memory in each memory heap." << TestLog::EndMessage;
354 if (m_opNdx >= m_opCount)
356 if (m_nonEmptyHeaps.empty())
357 return tcu::TestStatus::pass("Pass");
359 allocateMore = false;
361 else if (!m_nonEmptyHeaps.empty() && !m_nonFullHeaps.empty())
362 allocateMore = m_rng.getBool(); // Randomize if both operations are doable.
363 else if (m_nonEmptyHeaps.empty())
364 allocateMore = true; // Allocate more if there are no objects to free.
365 else if (m_nonFullHeaps.empty())
366 allocateMore = false; // Free objects if there is no free space for new objects.
369 allocateMore = false;
375 const size_t nonFullHeapNdx = (size_t)(m_rng.getUint32() % (deUint32)m_nonFullHeaps.size());
376 const size_t heapNdx = m_nonFullHeaps[nonFullHeapNdx];
377 Heap& heap = m_heaps[heapNdx];
378 const MemoryType& memoryType = m_rng.choose<MemoryType>(heap.types.begin(), heap.types.end());
379 const VkDeviceSize allocationSize = 1 + (m_rng.getUint64() % (deUint64)(heap.maxMemoryUsage - heap.memoryUsage));
381 const MemoryObject object =
387 heap.objects.push_back(object);
389 const VkMemoryAllocateInfo alloc =
391 VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO, // sType
393 object.size, // allocationSize
394 memoryType.index // memoryTypeIndex;
397 VK_CHECK(vkd.allocateMemory(device, &alloc, (const VkAllocationCallbacks*)DE_NULL, &heap.objects.back().memory));
398 TCU_CHECK(!!heap.objects.back().memory);
400 // If heap was empty add to the non empty heaps.
401 if (heap.memoryUsage == 0)
403 DE_ASSERT(heap.objects.size() == 1);
404 m_nonEmptyHeaps.push_back(heapNdx);
407 DE_ASSERT(heap.objects.size() > 1);
409 heap.memoryUsage += allocationSize;
411 // If heap became full, remove from non full heaps.
412 if (heap.memoryUsage >= heap.maxMemoryUsage)
414 m_nonFullHeaps[nonFullHeapNdx] = m_nonFullHeaps.back();
415 m_nonFullHeaps.pop_back();
420 const size_t nonEmptyHeapNdx = (size_t)(m_rng.getUint32() % (deUint32)m_nonEmptyHeaps.size());
421 const size_t heapNdx = m_nonEmptyHeaps[nonEmptyHeapNdx];
422 Heap& heap = m_heaps[heapNdx];
423 const size_t memoryObjectNdx = m_rng.getUint32() % heap.objects.size();
424 MemoryObject& memoryObject = heap.objects[memoryObjectNdx];
426 vkd.freeMemory(device, memoryObject.memory, (const VkAllocationCallbacks*)DE_NULL);
427 memoryObject.memory = (VkDeviceMemory)0;
429 if (heap.memoryUsage >= heap.maxMemoryUsage && heap.memoryUsage - memoryObject.size < heap.maxMemoryUsage)
430 m_nonFullHeaps.push_back(heapNdx);
432 heap.memoryUsage -= memoryObject.size;
434 heap.objects[memoryObjectNdx] = heap.objects.back();
435 heap.objects.pop_back();
437 if (heap.memoryUsage == 0)
439 DE_ASSERT(heap.objects.empty());
441 m_nonEmptyHeaps[nonEmptyHeapNdx] = m_nonEmptyHeaps.back();
442 m_nonEmptyHeaps.pop_back();
445 DE_ASSERT(!heap.objects.empty());
449 return tcu::TestStatus::incomplete();
455 tcu::TestCaseGroup* createAllocationTests (tcu::TestContext& testCtx)
457 de::MovePtr<tcu::TestCaseGroup> group (new tcu::TestCaseGroup(testCtx, "allocation", "Memory allocation tests."));
459 const VkDeviceSize KiB = 1024;
460 const VkDeviceSize MiB = 1024 * KiB;
464 const char* const str;
466 } allocationSizes[] =
478 const int allocationPercents[] =
483 const int allocationCounts[] =
490 const char* const str;
491 const TestConfig::Order order;
494 { "forward", TestConfig::ALLOC_FREE },
495 { "reverse", TestConfig::ALLOC_REVERSE_FREE },
496 { "mixed", TestConfig::MIXED_ALLOC_FREE }
500 de::MovePtr<tcu::TestCaseGroup> basicGroup (new tcu::TestCaseGroup(testCtx, "basic", "Basic memory allocation and free tests"));
502 for (size_t allocationSizeNdx = 0; allocationSizeNdx < DE_LENGTH_OF_ARRAY(allocationSizes); allocationSizeNdx++)
504 const VkDeviceSize allocationSize = allocationSizes[allocationSizeNdx].size;
505 const char* const allocationSizeName = allocationSizes[allocationSizeNdx].str;
506 de::MovePtr<tcu::TestCaseGroup> sizeGroup (new tcu::TestCaseGroup(testCtx, ("size_" + string(allocationSizeName)).c_str(), ("Test different allocation sizes " + de::toString(allocationSize)).c_str()));
508 for (size_t orderNdx = 0; orderNdx < DE_LENGTH_OF_ARRAY(orders); orderNdx++)
510 const TestConfig::Order order = orders[orderNdx].order;
511 const char* const orderName = orders[orderNdx].str;
512 const char* const orderDescription = orderName;
513 de::MovePtr<tcu::TestCaseGroup> orderGroup (new tcu::TestCaseGroup(testCtx, orderName, orderDescription));
515 for (size_t allocationCountNdx = 0; allocationCountNdx < DE_LENGTH_OF_ARRAY(allocationCounts); allocationCountNdx++)
517 const int allocationCount = allocationCounts[allocationCountNdx];
519 if (allocationCount != -1 && allocationCount * allocationSize > 50 * MiB)
524 config.memorySize = allocationSize;
525 config.order = order;
527 if (allocationCount == -1)
529 if (allocationSize < 4096)
532 config.memoryAllocationCount = (deUint32)(50 * MiB / allocationSize);
534 if (config.memoryAllocationCount == 0
535 || config.memoryAllocationCount == 1
536 || config.memoryAllocationCount == 10
537 || config.memoryAllocationCount == 100
538 || config.memoryAllocationCount == 1000)
542 config.memoryAllocationCount = allocationCount;
544 orderGroup->addChild(new InstanceFactory1<AllocateFreeTestInstance, TestConfig>(testCtx, tcu::NODETYPE_SELF_VALIDATE, "count_" + de::toString(config.memoryAllocationCount), "", config));
547 sizeGroup->addChild(orderGroup.release());
550 basicGroup->addChild(sizeGroup.release());
553 for (size_t allocationPercentNdx = 0; allocationPercentNdx < DE_LENGTH_OF_ARRAY(allocationPercents); allocationPercentNdx++)
555 const int allocationPercent = allocationPercents[allocationPercentNdx];
556 de::MovePtr<tcu::TestCaseGroup> percentGroup (new tcu::TestCaseGroup(testCtx, ("percent_" + de::toString(allocationPercent)).c_str(), ("Test different allocation percents " + de::toString(allocationPercent)).c_str()));
558 for (size_t orderNdx = 0; orderNdx < DE_LENGTH_OF_ARRAY(orders); orderNdx++)
560 const TestConfig::Order order = orders[orderNdx].order;
561 const char* const orderName = orders[orderNdx].str;
562 const char* const orderDescription = orderName;
563 de::MovePtr<tcu::TestCaseGroup> orderGroup (new tcu::TestCaseGroup(testCtx, orderName, orderDescription));
565 for (size_t allocationCountNdx = 0; allocationCountNdx < DE_LENGTH_OF_ARRAY(allocationCounts); allocationCountNdx++)
567 const int allocationCount = allocationCounts[allocationCountNdx];
569 if ((allocationCount != -1) && ((float)allocationCount * (float)allocationPercent >= 1.00f / 8.00f))
574 config.memoryPercentage = (float)allocationPercent / 100.0f;
575 config.order = order;
577 if (allocationCount == -1)
579 config.memoryAllocationCount = (int)((1.00f / 8.00f) / ((float)allocationPercent / 100.0f));
581 if (config.memoryAllocationCount == 0
582 || config.memoryAllocationCount == 1
583 || config.memoryAllocationCount == 10
584 || config.memoryAllocationCount == 100
585 || config.memoryAllocationCount == 1000)
589 config.memoryAllocationCount = allocationCount;
591 orderGroup->addChild(new InstanceFactory1<AllocateFreeTestInstance, TestConfig>(testCtx, tcu::NODETYPE_SELF_VALIDATE, "count_" + de::toString(config.memoryAllocationCount), "", config));
594 percentGroup->addChild(orderGroup.release());
597 basicGroup->addChild(percentGroup.release());
600 group->addChild(basicGroup.release());
604 const deUint32 caseCount = 100;
605 de::MovePtr<tcu::TestCaseGroup> randomGroup (new tcu::TestCaseGroup(testCtx, "random", "Random memory allocation tests."));
607 for (deUint32 caseNdx = 0; caseNdx < caseCount; caseNdx++)
609 const deUint32 seed = deInt32Hash(caseNdx ^ 32480);
611 randomGroup->addChild(new InstanceFactory1<RandomAllocFreeTestInstance, deUint32>(testCtx, tcu::NODETYPE_SELF_VALIDATE, de::toString(caseNdx), "Random case", seed));
614 group->addChild(randomGroup.release());
617 return group.release();