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));
382 if ((allocationSize > (deUint64)(heap.maxMemoryUsage - heap.memoryUsage)) && (allocationSize != 1))
383 TCU_THROW(InternalError, "Test Error: trying to allocate memory more than the available heap size.");
386 const MemoryObject object =
392 heap.objects.push_back(object);
394 const VkMemoryAllocateInfo alloc =
396 VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO, // sType
398 object.size, // allocationSize
399 memoryType.index // memoryTypeIndex;
402 VK_CHECK(vkd.allocateMemory(device, &alloc, (const VkAllocationCallbacks*)DE_NULL, &heap.objects.back().memory));
403 TCU_CHECK(!!heap.objects.back().memory);
405 // If heap was empty add to the non empty heaps.
406 if (heap.memoryUsage == 0)
408 DE_ASSERT(heap.objects.size() == 1);
409 m_nonEmptyHeaps.push_back(heapNdx);
412 DE_ASSERT(heap.objects.size() > 1);
414 heap.memoryUsage += allocationSize;
416 // If heap became full, remove from non full heaps.
417 if (heap.memoryUsage >= heap.maxMemoryUsage)
419 m_nonFullHeaps[nonFullHeapNdx] = m_nonFullHeaps.back();
420 m_nonFullHeaps.pop_back();
425 const size_t nonEmptyHeapNdx = (size_t)(m_rng.getUint32() % (deUint32)m_nonEmptyHeaps.size());
426 const size_t heapNdx = m_nonEmptyHeaps[nonEmptyHeapNdx];
427 Heap& heap = m_heaps[heapNdx];
428 const size_t memoryObjectNdx = m_rng.getUint32() % heap.objects.size();
429 MemoryObject& memoryObject = heap.objects[memoryObjectNdx];
431 vkd.freeMemory(device, memoryObject.memory, (const VkAllocationCallbacks*)DE_NULL);
432 memoryObject.memory = (VkDeviceMemory)0;
434 if (heap.memoryUsage >= heap.maxMemoryUsage && heap.memoryUsage - memoryObject.size < heap.maxMemoryUsage)
435 m_nonFullHeaps.push_back(heapNdx);
437 heap.memoryUsage -= memoryObject.size;
439 heap.objects[memoryObjectNdx] = heap.objects.back();
440 heap.objects.pop_back();
442 if (heap.memoryUsage == 0)
444 DE_ASSERT(heap.objects.empty());
446 m_nonEmptyHeaps[nonEmptyHeapNdx] = m_nonEmptyHeaps.back();
447 m_nonEmptyHeaps.pop_back();
450 DE_ASSERT(!heap.objects.empty());
454 return tcu::TestStatus::incomplete();
460 tcu::TestCaseGroup* createAllocationTests (tcu::TestContext& testCtx)
462 de::MovePtr<tcu::TestCaseGroup> group (new tcu::TestCaseGroup(testCtx, "allocation", "Memory allocation tests."));
464 const VkDeviceSize KiB = 1024;
465 const VkDeviceSize MiB = 1024 * KiB;
469 const char* const str;
471 } allocationSizes[] =
483 const int allocationPercents[] =
488 const int allocationCounts[] =
495 const char* const str;
496 const TestConfig::Order order;
499 { "forward", TestConfig::ALLOC_FREE },
500 { "reverse", TestConfig::ALLOC_REVERSE_FREE },
501 { "mixed", TestConfig::MIXED_ALLOC_FREE }
505 de::MovePtr<tcu::TestCaseGroup> basicGroup (new tcu::TestCaseGroup(testCtx, "basic", "Basic memory allocation and free tests"));
507 for (size_t allocationSizeNdx = 0; allocationSizeNdx < DE_LENGTH_OF_ARRAY(allocationSizes); allocationSizeNdx++)
509 const VkDeviceSize allocationSize = allocationSizes[allocationSizeNdx].size;
510 const char* const allocationSizeName = allocationSizes[allocationSizeNdx].str;
511 de::MovePtr<tcu::TestCaseGroup> sizeGroup (new tcu::TestCaseGroup(testCtx, ("size_" + string(allocationSizeName)).c_str(), ("Test different allocation sizes " + de::toString(allocationSize)).c_str()));
513 for (size_t orderNdx = 0; orderNdx < DE_LENGTH_OF_ARRAY(orders); orderNdx++)
515 const TestConfig::Order order = orders[orderNdx].order;
516 const char* const orderName = orders[orderNdx].str;
517 const char* const orderDescription = orderName;
518 de::MovePtr<tcu::TestCaseGroup> orderGroup (new tcu::TestCaseGroup(testCtx, orderName, orderDescription));
520 for (size_t allocationCountNdx = 0; allocationCountNdx < DE_LENGTH_OF_ARRAY(allocationCounts); allocationCountNdx++)
522 const int allocationCount = allocationCounts[allocationCountNdx];
524 if (allocationCount != -1 && allocationCount * allocationSize > 50 * MiB)
529 config.memorySize = allocationSize;
530 config.order = order;
532 if (allocationCount == -1)
534 if (allocationSize < 4096)
537 config.memoryAllocationCount = (deUint32)(50 * MiB / allocationSize);
539 if (config.memoryAllocationCount == 0
540 || config.memoryAllocationCount == 1
541 || config.memoryAllocationCount == 10
542 || config.memoryAllocationCount == 100
543 || config.memoryAllocationCount == 1000)
547 config.memoryAllocationCount = allocationCount;
549 orderGroup->addChild(new InstanceFactory1<AllocateFreeTestInstance, TestConfig>(testCtx, tcu::NODETYPE_SELF_VALIDATE, "count_" + de::toString(config.memoryAllocationCount), "", config));
552 sizeGroup->addChild(orderGroup.release());
555 basicGroup->addChild(sizeGroup.release());
558 for (size_t allocationPercentNdx = 0; allocationPercentNdx < DE_LENGTH_OF_ARRAY(allocationPercents); allocationPercentNdx++)
560 const int allocationPercent = allocationPercents[allocationPercentNdx];
561 de::MovePtr<tcu::TestCaseGroup> percentGroup (new tcu::TestCaseGroup(testCtx, ("percent_" + de::toString(allocationPercent)).c_str(), ("Test different allocation percents " + de::toString(allocationPercent)).c_str()));
563 for (size_t orderNdx = 0; orderNdx < DE_LENGTH_OF_ARRAY(orders); orderNdx++)
565 const TestConfig::Order order = orders[orderNdx].order;
566 const char* const orderName = orders[orderNdx].str;
567 const char* const orderDescription = orderName;
568 de::MovePtr<tcu::TestCaseGroup> orderGroup (new tcu::TestCaseGroup(testCtx, orderName, orderDescription));
570 for (size_t allocationCountNdx = 0; allocationCountNdx < DE_LENGTH_OF_ARRAY(allocationCounts); allocationCountNdx++)
572 const int allocationCount = allocationCounts[allocationCountNdx];
574 if ((allocationCount != -1) && ((float)allocationCount * (float)allocationPercent >= 1.00f / 8.00f))
579 config.memoryPercentage = (float)allocationPercent / 100.0f;
580 config.order = order;
582 if (allocationCount == -1)
584 config.memoryAllocationCount = (int)((1.00f / 8.00f) / ((float)allocationPercent / 100.0f));
586 if (config.memoryAllocationCount == 0
587 || config.memoryAllocationCount == 1
588 || config.memoryAllocationCount == 10
589 || config.memoryAllocationCount == 100
590 || config.memoryAllocationCount == 1000)
594 config.memoryAllocationCount = allocationCount;
596 orderGroup->addChild(new InstanceFactory1<AllocateFreeTestInstance, TestConfig>(testCtx, tcu::NODETYPE_SELF_VALIDATE, "count_" + de::toString(config.memoryAllocationCount), "", config));
599 percentGroup->addChild(orderGroup.release());
602 basicGroup->addChild(percentGroup.release());
605 group->addChild(basicGroup.release());
609 const deUint32 caseCount = 100;
610 de::MovePtr<tcu::TestCaseGroup> randomGroup (new tcu::TestCaseGroup(testCtx, "random", "Random memory allocation tests."));
612 for (deUint32 caseNdx = 0; caseNdx < caseCount; caseNdx++)
614 const deUint32 seed = deInt32Hash(caseNdx ^ 32480);
616 randomGroup->addChild(new InstanceFactory1<RandomAllocFreeTestInstance, deUint32>(testCtx, tcu::NODETYPE_SELF_VALIDATE, de::toString(caseNdx), "Random case", seed));
619 group->addChild(randomGroup.release());
622 return group.release();