1 /*-------------------------------------------------------------------------
2 * Vulkan Conformance Tests
3 * ------------------------
4 *
5 * Copyright (c) 2015 Google Inc.
6 *
7 * Licensed under the Apache License, Version 2.0 (the "License");
8 * you may not use this file except in compliance with the License.
9 * You may obtain a copy of the License at
10 *
11 * http://www.apache.org/licenses/LICENSE-2.0
12 *
13 * Unless required by applicable law or agreed to in writing, software
14 * distributed under the License is distributed on an "AS IS" BASIS,
15 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16 * See the License for the specific language governing permissions and
17 * limitations under the License.
18 *
19 *//*!
20 * \file
21 * \brief Simple memory mapping tests.
22 *//*--------------------------------------------------------------------*/
23
24 #include "vktMemoryMappingTests.hpp"
25
26 #include "vktTestCaseUtil.hpp"
27 #include "vktCustomInstancesDevices.hpp"
28
29 #include "tcuMaybe.hpp"
30 #include "tcuResultCollector.hpp"
31 #include "tcuTestLog.hpp"
32 #include "tcuPlatform.hpp"
33 #include "tcuTextureUtil.hpp"
34 #include "tcuCommandLine.hpp"
35
36 #include "vkDeviceUtil.hpp"
37 #include "vkPlatform.hpp"
38 #include "vkQueryUtil.hpp"
39 #include "vkRef.hpp"
40 #include "vkRefUtil.hpp"
41 #include "vkStrUtil.hpp"
42 #include "vkAllocationCallbackUtil.hpp"
43 #include "vkImageUtil.hpp"
44
45 #include "deRandom.hpp"
46 #include "deSharedPtr.hpp"
47 #include "deStringUtil.hpp"
48 #include "deUniquePtr.hpp"
49 #include "deSTLUtil.hpp"
50 #include "deMath.h"
51
52 #include <string>
53 #include <vector>
54 #include <algorithm>
55
56 using tcu::Maybe;
57 using tcu::TestLog;
58
59 using de::SharedPtr;
60
61 using std::string;
62 using std::vector;
63 using std::pair;
64
65 using namespace vk;
66
67 namespace vkt
68 {
69 namespace memory
70 {
71 namespace
72 {
73 template<typename T>
divRoundUp(const T & a,const T & b)74 T divRoundUp (const T& a, const T& b)
75 {
76 return (a / b) + (a % b == 0 ? 0 : 1);
77 }
78
79 template<typename T>
roundDownToMultiple(const T & a,const T & b)80 T roundDownToMultiple (const T& a, const T& b)
81 {
82 return b * (a / b);
83 }
84
85 template<typename T>
roundUpToMultiple(const T & a,const T & b)86 T roundUpToMultiple (const T& a, const T& b)
87 {
88 return b * (a / b + (a % b != 0 ? 1 : 0));
89 }
90
91 enum AllocationKind
92 {
93 ALLOCATION_KIND_SUBALLOCATED = 0,
94 ALLOCATION_KIND_DEDICATED_BUFFER = 1,
95 ALLOCATION_KIND_DEDICATED_IMAGE = 2,
96 ALLOCATION_KIND_LAST
97 };
98
99 // \note Bit vector that guarantees that each value takes only one bit.
100 // std::vector<bool> is often optimized to only take one bit for each bool, but
101 // that is implementation detail and in this case we really need to known how much
102 // memory is used.
103 class BitVector
104 {
105 public:
106 enum
107 {
108 BLOCK_BIT_SIZE = 8 * sizeof(deUint32)
109 };
110
BitVector(size_t size,bool value=false)111 BitVector (size_t size, bool value = false)
112 : m_data(divRoundUp<size_t>(size, (size_t)BLOCK_BIT_SIZE), value ? ~0x0u : 0x0u)
113 {
114 }
115
get(size_t ndx) const116 bool get (size_t ndx) const
117 {
118 return (m_data[ndx / BLOCK_BIT_SIZE] & (0x1u << (deUint32)(ndx % BLOCK_BIT_SIZE))) != 0;
119 }
120
set(size_t ndx,bool value)121 void set (size_t ndx, bool value)
122 {
123 if (value)
124 m_data[ndx / BLOCK_BIT_SIZE] |= 0x1u << (deUint32)(ndx % BLOCK_BIT_SIZE);
125 else
126 m_data[ndx / BLOCK_BIT_SIZE] &= ~(0x1u << (deUint32)(ndx % BLOCK_BIT_SIZE));
127 }
128
setRange(size_t offset,size_t count,bool value)129 void setRange (size_t offset, size_t count, bool value)
130 {
131 size_t ndx = offset;
132
133 for (; (ndx < offset + count) && ((ndx % BLOCK_BIT_SIZE) != 0); ndx++)
134 {
135 DE_ASSERT(ndx >= offset);
136 DE_ASSERT(ndx < offset + count);
137 set(ndx, value);
138 }
139
140 {
141 const size_t endOfFullBlockNdx = roundDownToMultiple<size_t>(offset + count, BLOCK_BIT_SIZE);
142
143 if (ndx < endOfFullBlockNdx)
144 {
145 deMemset(&m_data[ndx / BLOCK_BIT_SIZE], (value ? 0xFF : 0x0), (endOfFullBlockNdx - ndx) / 8);
146 ndx = endOfFullBlockNdx;
147 }
148 }
149
150 for (; ndx < offset + count; ndx++)
151 {
152 DE_ASSERT(ndx >= offset);
153 DE_ASSERT(ndx < offset + count);
154 set(ndx, value);
155 }
156 }
157
vectorAnd(const BitVector & other,size_t offset,size_t count)158 void vectorAnd (const BitVector& other, size_t offset, size_t count)
159 {
160 size_t ndx = offset;
161
162 for (; ndx < offset + count && (ndx % BLOCK_BIT_SIZE) != 0; ndx++)
163 {
164 DE_ASSERT(ndx >= offset);
165 DE_ASSERT(ndx < offset + count);
166 set(ndx, other.get(ndx) && get(ndx));
167 }
168
169 for (; ndx < roundDownToMultiple<size_t>(offset + count, BLOCK_BIT_SIZE); ndx += BLOCK_BIT_SIZE)
170 {
171 DE_ASSERT(ndx >= offset);
172 DE_ASSERT(ndx < offset + count);
173 DE_ASSERT(ndx % BLOCK_BIT_SIZE == 0);
174 DE_ASSERT(ndx + BLOCK_BIT_SIZE <= offset + count);
175 m_data[ndx / BLOCK_BIT_SIZE] &= other.m_data[ndx / BLOCK_BIT_SIZE];
176 }
177
178 for (; ndx < offset + count; ndx++)
179 {
180 DE_ASSERT(ndx >= offset);
181 DE_ASSERT(ndx < offset + count);
182 set(ndx, other.get(ndx) && get(ndx));
183 }
184 }
185
186 private:
187 vector<deUint32> m_data;
188 };
189
190 class ReferenceMemory
191 {
192 public:
ReferenceMemory(size_t size,size_t atomSize)193 ReferenceMemory (size_t size, size_t atomSize)
194 : m_atomSize (atomSize)
195 , m_bytes (size, 0xDEu)
196 , m_defined (size, false)
197 , m_flushed (size / atomSize, false)
198 {
199 DE_ASSERT(size % m_atomSize == 0);
200 }
201
write(size_t pos,deUint8 value)202 void write (size_t pos, deUint8 value)
203 {
204 m_bytes[pos] = value;
205 m_defined.set(pos, true);
206 m_flushed.set(pos / m_atomSize, false);
207 }
208
read(size_t pos,deUint8 value)209 bool read (size_t pos, deUint8 value)
210 {
211 const bool isOk = !m_defined.get(pos)
212 || m_bytes[pos] == value;
213
214 m_bytes[pos] = value;
215 m_defined.set(pos, true);
216
217 return isOk;
218 }
219
modifyXor(size_t pos,deUint8 value,deUint8 mask)220 bool modifyXor (size_t pos, deUint8 value, deUint8 mask)
221 {
222 const bool isOk = !m_defined.get(pos)
223 || m_bytes[pos] == value;
224
225 m_bytes[pos] = value ^ mask;
226 m_defined.set(pos, true);
227 m_flushed.set(pos / m_atomSize, false);
228
229 return isOk;
230 }
231
flush(size_t offset,size_t size)232 void flush (size_t offset, size_t size)
233 {
234 DE_ASSERT((offset % m_atomSize) == 0);
235 DE_ASSERT((size % m_atomSize) == 0);
236
237 m_flushed.setRange(offset / m_atomSize, size / m_atomSize, true);
238 }
239
invalidate(size_t offset,size_t size)240 void invalidate (size_t offset, size_t size)
241 {
242 DE_ASSERT((offset % m_atomSize) == 0);
243 DE_ASSERT((size % m_atomSize) == 0);
244
245 if (m_atomSize == 1)
246 {
247 m_defined.vectorAnd(m_flushed, offset, size);
248 }
249 else
250 {
251 for (size_t ndx = 0; ndx < size / m_atomSize; ndx++)
252 {
253 if (!m_flushed.get((offset / m_atomSize) + ndx))
254 m_defined.setRange(offset + ndx * m_atomSize, m_atomSize, false);
255 }
256 }
257 }
258
259
260 private:
261 const size_t m_atomSize;
262 vector<deUint8> m_bytes;
263 BitVector m_defined;
264 BitVector m_flushed;
265 };
266
267 struct MemoryType
268 {
MemoryTypevkt::memory::__anon66ba95590111::MemoryType269 MemoryType (deUint32 index_, const VkMemoryType& type_)
270 : index (index_)
271 , type (type_)
272 {
273 }
274
MemoryTypevkt::memory::__anon66ba95590111::MemoryType275 MemoryType (void)
276 : index (~0u)
277 {
278 }
279
280 deUint32 index;
281 VkMemoryType type;
282 };
283
computeDeviceMemorySystemMemFootprint(const DeviceInterface & vk,VkDevice device)284 size_t computeDeviceMemorySystemMemFootprint (const DeviceInterface& vk, VkDevice device)
285 {
286 AllocationCallbackRecorder callbackRecorder (getSystemAllocator());
287
288 {
289 // 1 B allocation from memory type 0
290 const VkMemoryAllocateInfo allocInfo =
291 {
292 VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO,
293 DE_NULL,
294 1u,
295 0u,
296 };
297 const Unique<VkDeviceMemory> memory (allocateMemory(vk, device, &allocInfo, callbackRecorder.getCallbacks()));
298 AllocationCallbackValidationResults validateRes;
299
300 validateAllocationCallbacks(callbackRecorder, &validateRes);
301
302 TCU_CHECK(validateRes.violations.empty());
303
304 return getLiveSystemAllocationTotal(validateRes)
305 + sizeof(void*)*validateRes.liveAllocations.size(); // allocation overhead
306 }
307 }
308
makeImage(const DeviceInterface & vk,VkDevice device,VkDeviceSize size,deUint32 queueFamilyIndex)309 Move<VkImage> makeImage (const DeviceInterface& vk, VkDevice device, VkDeviceSize size, deUint32 queueFamilyIndex)
310 {
311 const VkFormat formats[] =
312 {
313 VK_FORMAT_R8G8B8A8_UINT,
314 VK_FORMAT_R16G16B16A16_UINT,
315 VK_FORMAT_R32G32B32A32_UINT,
316 };
317
318 VkFormat format = VK_FORMAT_UNDEFINED;
319 deUint32 powerOfTwoSize = 0;
320
321 for (const VkFormat f : formats)
322 {
323 const int pixelSize = vk::mapVkFormat(f).getPixelSize();
324 const VkDeviceSize sizeInPixels = (size + 3u) / pixelSize;
325 const deUint32 sqrtSize = static_cast<deUint32>(deFloatCeil(deFloatSqrt(static_cast<float>(sizeInPixels))));
326
327 format = f;
328 powerOfTwoSize = deSmallestGreaterOrEquallPowerOfTwoU32(sqrtSize);
329
330 // maxImageDimension2D
331 if (powerOfTwoSize < 4096)
332 break;
333 }
334
335 const VkImageCreateInfo colorImageParams =
336 {
337 VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO, // VkStructureType sType;
338 DE_NULL, // const void* pNext;
339 0u, // VkImageCreateFlags flags;
340 VK_IMAGE_TYPE_2D, // VkImageType imageType;
341 format, // VkFormat format;
342 {
343 powerOfTwoSize,
344 powerOfTwoSize,
345 1u
346 }, // VkExtent3D extent;
347 1u, // deUint32 mipLevels;
348 1u, // deUint32 arraySize;
349 VK_SAMPLE_COUNT_1_BIT, // deUint32 samples;
350 VK_IMAGE_TILING_LINEAR, // VkImageTiling tiling;
351 VK_IMAGE_USAGE_TRANSFER_SRC_BIT | VK_IMAGE_USAGE_TRANSFER_DST_BIT, // VkImageUsageFlags usage;
352 VK_SHARING_MODE_EXCLUSIVE, // VkSharingMode sharingMode;
353 1u, // deUint32 queueFamilyCount;
354 &queueFamilyIndex, // const deUint32* pQueueFamilyIndices;
355 VK_IMAGE_LAYOUT_UNDEFINED, // VkImageLayout initialLayout;
356 };
357
358 return createImage(vk, device, &colorImageParams);
359 }
360
makeBuffer(const DeviceInterface & vk,VkDevice device,VkDeviceSize size,deUint32 queueFamilyIndex)361 Move<VkBuffer> makeBuffer(const DeviceInterface& vk, VkDevice device, VkDeviceSize size, deUint32 queueFamilyIndex)
362 {
363 const VkBufferCreateInfo bufferParams =
364 {
365 VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO, // VkStructureType sType;
366 DE_NULL, // const void* pNext;
367 0u, // VkBufferCreateFlags flags;
368 size, // VkDeviceSize size;
369 VK_BUFFER_USAGE_TRANSFER_SRC_BIT | VK_BUFFER_USAGE_TRANSFER_DST_BIT, // VkBufferUsageFlags usage;
370 VK_SHARING_MODE_EXCLUSIVE, // VkSharingMode sharingMode;
371 1u, // deUint32 queueFamilyCount;
372 &queueFamilyIndex, // const deUint32* pQueueFamilyIndices;
373 };
374 return vk::createBuffer(vk, device, &bufferParams, (const VkAllocationCallbacks*)DE_NULL);
375 }
376
getImageMemoryRequirements(const DeviceInterface & vk,VkDevice device,Move<VkImage> & image)377 VkMemoryRequirements getImageMemoryRequirements(const DeviceInterface& vk, VkDevice device, Move<VkImage>& image)
378 {
379 VkImageMemoryRequirementsInfo2 info =
380 {
381 VK_STRUCTURE_TYPE_IMAGE_MEMORY_REQUIREMENTS_INFO_2, // VkStructureType sType
382 DE_NULL, // const void* pNext
383 *image // VkImage image
384 };
385 VkMemoryDedicatedRequirements dedicatedRequirements =
386 {
387 VK_STRUCTURE_TYPE_MEMORY_DEDICATED_REQUIREMENTS, // VkStructureType sType
388 DE_NULL, // const void* pNext
389 VK_FALSE, // VkBool32 prefersDedicatedAllocation
390 VK_FALSE // VkBool32 requiresDedicatedAllocation
391 };
392 VkMemoryRequirements2 req2 =
393 {
394 VK_STRUCTURE_TYPE_MEMORY_REQUIREMENTS_2, // VkStructureType sType
395 &dedicatedRequirements, // void* pNext
396 {0, 0, 0} // VkMemoryRequirements memoryRequirements
397 };
398
399 vk.getImageMemoryRequirements2(device, &info, &req2);
400
401 return req2.memoryRequirements;
402 }
403
getBufferMemoryRequirements(const DeviceInterface & vk,VkDevice device,Move<VkBuffer> & buffer)404 VkMemoryRequirements getBufferMemoryRequirements(const DeviceInterface& vk, VkDevice device, Move<VkBuffer>& buffer)
405 {
406 VkBufferMemoryRequirementsInfo2 info =
407 {
408 VK_STRUCTURE_TYPE_BUFFER_MEMORY_REQUIREMENTS_INFO_2, // VkStructureType sType
409 DE_NULL, // const void* pNext
410 *buffer // VkImage image
411 };
412 VkMemoryDedicatedRequirements dedicatedRequirements =
413 {
414 VK_STRUCTURE_TYPE_MEMORY_DEDICATED_REQUIREMENTS, // VkStructureType sType
415 DE_NULL, // const void* pNext
416 VK_FALSE, // VkBool32 prefersDedicatedAllocation
417 VK_FALSE // VkBool32 requiresDedicatedAllocation
418 };
419 VkMemoryRequirements2 req2 =
420 {
421 VK_STRUCTURE_TYPE_MEMORY_REQUIREMENTS_2, // VkStructureType sType
422 &dedicatedRequirements, // void* pNext
423 {0, 0, 0} // VkMemoryRequirements memoryRequirements
424 };
425
426 vk.getBufferMemoryRequirements2(device, &info, &req2);
427
428 return req2.memoryRequirements;
429 }
430
allocMemory(const DeviceInterface & vk,VkDevice device,VkDeviceSize pAllocInfo_allocationSize,deUint32 pAllocInfo_memoryTypeIndex)431 Move<VkDeviceMemory> allocMemory (const DeviceInterface& vk, VkDevice device, VkDeviceSize pAllocInfo_allocationSize, deUint32 pAllocInfo_memoryTypeIndex)
432 {
433 const VkMemoryAllocateInfo pAllocInfo =
434 {
435 VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO,
436 DE_NULL,
437 pAllocInfo_allocationSize,
438 pAllocInfo_memoryTypeIndex,
439 };
440 return allocateMemory(vk, device, &pAllocInfo);
441 }
442
findLargeAllocationSize(const DeviceInterface & vk,VkDevice device,VkDeviceSize max,deUint32 memoryTypeIndex)443 VkDeviceSize findLargeAllocationSize (const DeviceInterface& vk, VkDevice device, VkDeviceSize max, deUint32 memoryTypeIndex)
444 {
445 // max must be power of two
446 DE_ASSERT((max & (max - 1)) == 0);
447
448 for (VkDeviceSize size = max; size > 0; size >>= 1)
449 {
450 const VkMemoryAllocateInfo allocInfo =
451 {
452 VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO,
453 DE_NULL,
454 size,
455 memoryTypeIndex,
456 };
457
458 VkDeviceMemory memory;
459 VkResult result = vk.allocateMemory(device, &allocInfo, NULL, &memory);
460
461 if (result == VK_SUCCESS)
462 {
463 vk.freeMemory(device, memory, NULL);
464 return size;
465 }
466 }
467
468 return 0;
469 }
470
allocMemory(const DeviceInterface & vk,VkDevice device,VkDeviceSize pAllocInfo_allocationSize,deUint32 pAllocInfo_memoryTypeIndex,Move<VkImage> & image,Move<VkBuffer> & buffer,const VkAllocationCallbacks * allocator=DE_NULL)471 Move<VkDeviceMemory> allocMemory (const DeviceInterface& vk, VkDevice device, VkDeviceSize pAllocInfo_allocationSize, deUint32 pAllocInfo_memoryTypeIndex, Move<VkImage>& image, Move<VkBuffer>& buffer, const VkAllocationCallbacks* allocator = DE_NULL)
472 {
473 DE_ASSERT((!image) || (!buffer));
474
475 const VkMemoryDedicatedAllocateInfo
476 dedicatedAllocateInfo =
477 {
478 VK_STRUCTURE_TYPE_MEMORY_DEDICATED_ALLOCATE_INFO, // VkStructureType sType
479 DE_NULL, // const void* pNext
480 *image, // VkImage image
481 *buffer // VkBuffer buffer
482 };
483
484 const VkMemoryAllocateInfo pAllocInfo =
485 {
486 VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO,
487 !image && !buffer ? DE_NULL : &dedicatedAllocateInfo,
488 pAllocInfo_allocationSize,
489 pAllocInfo_memoryTypeIndex,
490 };
491 return allocateMemory(vk, device, &pAllocInfo, allocator);
492 }
493
494 struct MemoryRange
495 {
MemoryRangevkt::memory::__anon66ba95590111::MemoryRange496 MemoryRange (VkDeviceSize offset_ = ~(VkDeviceSize)0, VkDeviceSize size_ = ~(VkDeviceSize)0)
497 : offset (offset_)
498 , size (size_)
499 {
500 }
501
502 VkDeviceSize offset;
503 VkDeviceSize size;
504 };
505
506 struct TestConfig
507 {
TestConfigvkt::memory::__anon66ba95590111::TestConfig508 TestConfig (void)
509 : allocationSize (~(VkDeviceSize)0)
510 , allocationKind (ALLOCATION_KIND_SUBALLOCATED)
511 {
512 }
513
514 VkDeviceSize allocationSize;
515 deUint32 seed;
516
517 MemoryRange mapping;
518 vector<MemoryRange> flushMappings;
519 vector<MemoryRange> invalidateMappings;
520 bool remap;
521 bool implicitUnmap;
522 AllocationKind allocationKind;
523 };
524
compareAndLogBuffer(TestLog & log,size_t size,size_t referenceSize,const deUint8 * result,const deUint8 * reference)525 bool compareAndLogBuffer (TestLog& log, size_t size, size_t referenceSize, const deUint8* result, const deUint8* reference)
526 {
527 size_t stride = size / referenceSize;
528 size_t failedBytes = 0;
529 size_t firstFailed = (size_t)-1;
530
531 DE_ASSERT(referenceSize <= size);
532
533 for (size_t ndx = 0; ndx < referenceSize; ndx += stride)
534 {
535 if (result[ndx * stride] != reference[ndx])
536 {
537 failedBytes++;
538
539 if (firstFailed == (size_t)-1)
540 firstFailed = ndx;
541 }
542 }
543
544 if (failedBytes > 0)
545 {
546 log << TestLog::Message << "Comparison failed. Failed bytes " << failedBytes << ". First failed at offset " << firstFailed << "." << TestLog::EndMessage;
547
548 std::ostringstream expectedValues;
549 std::ostringstream resultValues;
550
551 for (size_t ndx = firstFailed; ndx < firstFailed + 10 && ndx < referenceSize; ndx++)
552 {
553 if (ndx != firstFailed)
554 {
555 expectedValues << ", ";
556 resultValues << ", ";
557 }
558
559 expectedValues << reference[ndx];
560 resultValues << result[ndx * stride];
561 }
562
563 if (firstFailed + 10 < size)
564 {
565 expectedValues << "...";
566 resultValues << "...";
567 }
568
569 log << TestLog::Message << "Expected values at offset: " << firstFailed << ", " << expectedValues.str() << TestLog::EndMessage;
570 log << TestLog::Message << "Result values at offset: " << firstFailed << ", " << resultValues.str() << TestLog::EndMessage;
571
572 return false;
573 }
574 else
575 return true;
576 }
577
createProtectedMemoryDevice(const Context & context,const VkPhysicalDeviceFeatures2 & features2)578 static Move<VkDevice> createProtectedMemoryDevice(const Context& context, const VkPhysicalDeviceFeatures2& features2)
579 {
580 auto& cmdLine = context.getTestContext().getCommandLine();
581 const InstanceInterface& vki = context.getInstanceInterface();
582 const float queuePriority = 1.0f;
583 deUint32 queueFamilyIndex = context.getUniversalQueueFamilyIndex();
584
585 VkDeviceQueueCreateInfo queueInfo =
586 {
587 VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO, // VkStructureType sType;
588 DE_NULL, // const void* pNext;
589 vk::VK_DEVICE_QUEUE_CREATE_PROTECTED_BIT, // VkDeviceQueueCreateFlags flags;
590 queueFamilyIndex, // deUint32 queueFamilyIndex;
591 1u, // deUint32 queueCount;
592 &queuePriority // const float* pQueuePriorities;
593 };
594
595 const VkDeviceCreateInfo deviceInfo =
596 {
597 VK_STRUCTURE_TYPE_DEVICE_CREATE_INFO, // VkStructureType sType;
598 &features2, // const void* pNext;
599 (VkDeviceCreateFlags)0, // VkDeviceCreateFlags flags;
600 1u, // uint32_t queueCreateInfoCount;
601 &queueInfo, // const VkDeviceQueueCreateInfo* pQueueCreateInfos;
602 0u, // uint32_t enabledLayerCount;
603 DE_NULL, // const char* const* ppEnabledLayerNames;
604 0u, // uint32_t enabledExtensionCount;
605 DE_NULL, // const char* const* ppEnabledExtensionNames;
606 DE_NULL // const VkPhysicalDeviceFeatures* pEnabledFeatures;
607 };
608
609 return createCustomDevice(cmdLine.isValidationEnabled(), context.getPlatformInterface(), context.getInstance(), vki, context.getPhysicalDevice(), &deviceInfo);
610 }
611
testMemoryMapping(Context & context,const TestConfig config)612 tcu::TestStatus testMemoryMapping (Context& context, const TestConfig config)
613 {
614 TestLog& log = context.getTestContext().getLog();
615 tcu::ResultCollector result (log);
616 bool atLeastOneTestPerformed = false;
617 const VkPhysicalDevice physicalDevice = context.getPhysicalDevice();
618 const InstanceInterface& vki = context.getInstanceInterface();
619 const DeviceInterface& vkd = context.getDeviceInterface();
620 const VkPhysicalDeviceMemoryProperties memoryProperties = getPhysicalDeviceMemoryProperties(vki, physicalDevice);
621 const VkDeviceSize nonCoherentAtomSize = context.getDeviceProperties().limits.nonCoherentAtomSize;
622 const deUint32 queueFamilyIndex = context.getUniversalQueueFamilyIndex();
623
624 //Create protected memory device if protected memory is supported
625 //otherwise use the default device
626 Move<VkDevice> protectMemoryDevice;
627 VkDevice device;
628 {
629 VkPhysicalDeviceProtectedMemoryFeatures protectedFeatures;
630 protectedFeatures.sType = vk::VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PROTECTED_MEMORY_FEATURES;
631 protectedFeatures.pNext = DE_NULL;
632 protectedFeatures.protectedMemory = VK_FALSE;
633
634 VkPhysicalDeviceFeatures2 deviceFeatures2;
635 deviceFeatures2.sType = vk::VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_FEATURES_2;
636 deviceFeatures2.pNext = &protectedFeatures;
637
638 vki.getPhysicalDeviceFeatures2(context.getPhysicalDevice(), &deviceFeatures2);
639 if(protectedFeatures.protectedMemory && config.implicitUnmap)
640 {
641 protectMemoryDevice = createProtectedMemoryDevice(context, deviceFeatures2);
642 device = *protectMemoryDevice;
643 }
644 else
645 {
646 device = context.getDevice();
647 }
648 }
649
650 {
651 const tcu::ScopedLogSection section (log, "TestCaseInfo", "TestCaseInfo");
652
653 log << TestLog::Message << "Seed: " << config.seed << TestLog::EndMessage;
654 log << TestLog::Message << "Allocation size: " << config.allocationSize << TestLog::EndMessage;
655 log << TestLog::Message << "Mapping, offset: " << config.mapping.offset << ", size: " << config.mapping.size << TestLog::EndMessage;
656
657 if (!config.flushMappings.empty())
658 {
659 log << TestLog::Message << "Invalidating following ranges:" << TestLog::EndMessage;
660
661 for (size_t ndx = 0; ndx < config.flushMappings.size(); ndx++)
662 log << TestLog::Message << "\tOffset: " << config.flushMappings[ndx].offset << ", Size: " << config.flushMappings[ndx].size << TestLog::EndMessage;
663 }
664
665 if (config.remap)
666 log << TestLog::Message << "Remapping memory between flush and invalidation." << TestLog::EndMessage;
667
668 if (!config.invalidateMappings.empty())
669 {
670 log << TestLog::Message << "Flushing following ranges:" << TestLog::EndMessage;
671
672 for (size_t ndx = 0; ndx < config.invalidateMappings.size(); ndx++)
673 log << TestLog::Message << "\tOffset: " << config.invalidateMappings[ndx].offset << ", Size: " << config.invalidateMappings[ndx].size << TestLog::EndMessage;
674 }
675 }
676
677 for (deUint32 memoryTypeIndex = 0; memoryTypeIndex < memoryProperties.memoryTypeCount; memoryTypeIndex++)
678 {
679 try
680 {
681 const tcu::ScopedLogSection section (log, "MemoryType" + de::toString(memoryTypeIndex), "MemoryType" + de::toString(memoryTypeIndex));
682 const vk::VkMemoryType& memoryType = memoryProperties.memoryTypes[memoryTypeIndex];
683 const VkMemoryHeap& memoryHeap = memoryProperties.memoryHeaps[memoryType.heapIndex];
684 const VkDeviceSize atomSize = nonCoherentAtomSize;
685 const VkDeviceSize stride = config.implicitUnmap ? 1024 : 1;
686 const deUint32 iterations = config.implicitUnmap ? 128 : 1;
687
688 VkDeviceSize allocationSize = (config.allocationSize % atomSize == 0) ? config.allocationSize : config.allocationSize + (atomSize - (config.allocationSize % atomSize));
689 size_t referenceSize = 0;
690 vector<deUint8> reference;
691
692 if ((memoryType.propertyFlags & VK_MEMORY_PROPERTY_DEVICE_COHERENT_BIT_AMD) != 0 && !context.getCoherentMemoryFeaturesAMD().deviceCoherentMemory)
693 continue;
694
695 if (config.implicitUnmap)
696 {
697 VkDeviceSize max = 0x10000000; // 256MiB
698
699 while (memoryHeap.size <= 4 * max)
700 max >>= 1;
701
702 allocationSize = findLargeAllocationSize(vkd, device, max, memoryTypeIndex);
703 }
704
705 vk::VkMemoryRequirements req =
706 {
707 (VkDeviceSize)allocationSize,
708 (VkDeviceSize)0,
709 ~(deUint32)0u
710 };
711 Move<VkImage> image;
712 Move<VkBuffer> buffer;
713
714 if (config.allocationKind == ALLOCATION_KIND_DEDICATED_IMAGE)
715 {
716 image = makeImage(vkd, device, allocationSize, queueFamilyIndex);
717 req = getImageMemoryRequirements(vkd, device, image);
718 }
719 else if (config.allocationKind == ALLOCATION_KIND_DEDICATED_BUFFER)
720 {
721 buffer = makeBuffer(vkd, device, allocationSize, queueFamilyIndex);
722 req = getBufferMemoryRequirements(vkd, device, buffer);
723 }
724 allocationSize = req.size;
725 VkDeviceSize mappingSize = (config.mapping.size % atomSize == 0) ? config.mapping.size : config.mapping.size + (atomSize - (config.mapping.size % atomSize));
726 VkDeviceSize mappingOffset = (config.mapping.offset % atomSize == 0) ? config.mapping.offset : config.mapping.offset - (config.mapping.offset % atomSize);
727 if (config.mapping.size == config.allocationSize && config.mapping.offset == 0u)
728 {
729 mappingSize = allocationSize;
730 }
731
732 referenceSize = static_cast<size_t>(mappingSize / stride);
733 reference.resize(static_cast<size_t>(mappingOffset) + referenceSize);
734
735 log << TestLog::Message << "MemoryType: " << memoryType << TestLog::EndMessage;
736 log << TestLog::Message << "MemoryHeap: " << memoryHeap << TestLog::EndMessage;
737 log << TestLog::Message << "AtomSize: " << atomSize << TestLog::EndMessage;
738 log << TestLog::Message << "AllocationSize: " << allocationSize << TestLog::EndMessage;
739 log << TestLog::Message << "Mapping, offset: " << mappingOffset << ", size: " << mappingSize << TestLog::EndMessage;
740
741 if ((req.memoryTypeBits & (1u << memoryTypeIndex)) == 0)
742 {
743 static const char* const allocationKindName[] =
744 {
745 "suballocation",
746 "dedicated allocation of buffers",
747 "dedicated allocation of images"
748 };
749 log << TestLog::Message << "Memory type does not support " << allocationKindName[static_cast<deUint32>(config.allocationKind)] << '.' << TestLog::EndMessage;
750 continue;
751 }
752
753 if (!config.flushMappings.empty())
754 {
755 log << TestLog::Message << "Invalidating following ranges:" << TestLog::EndMessage;
756
757 for (size_t ndx = 0; ndx < config.flushMappings.size(); ndx++)
758 {
759 const VkDeviceSize offset = (config.flushMappings[ndx].offset % atomSize == 0) ? config.flushMappings[ndx].offset : config.flushMappings[ndx].offset - (config.flushMappings[ndx].offset % atomSize);
760 const VkDeviceSize size = (config.flushMappings[ndx].size % atomSize == 0) ? config.flushMappings[ndx].size : config.flushMappings[ndx].size + (atomSize - (config.flushMappings[ndx].size % atomSize));
761 log << TestLog::Message << "\tOffset: " << offset << ", Size: " << size << TestLog::EndMessage;
762 }
763 }
764
765 if (!config.invalidateMappings.empty())
766 {
767 log << TestLog::Message << "Flushing following ranges:" << TestLog::EndMessage;
768
769 for (size_t ndx = 0; ndx < config.invalidateMappings.size(); ndx++)
770 {
771 const VkDeviceSize offset = (config.invalidateMappings[ndx].offset % atomSize == 0) ? config.invalidateMappings[ndx].offset : config.invalidateMappings[ndx].offset - (config.invalidateMappings[ndx].offset % atomSize);
772 const VkDeviceSize size = (config.invalidateMappings[ndx].size % atomSize == 0) ? config.invalidateMappings[ndx].size : config.invalidateMappings[ndx].size + (atomSize - (config.invalidateMappings[ndx].size % atomSize));
773 log << TestLog::Message << "\tOffset: " << offset << ", Size: " << size << TestLog::EndMessage;
774 }
775 }
776
777 if ((memoryType.propertyFlags & VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT) == 0)
778 {
779 log << TestLog::Message << "Memory type doesn't support mapping." << TestLog::EndMessage;
780 }
781 else if (memoryHeap.size <= 4 * allocationSize)
782 {
783 log << TestLog::Message << "Memory type's heap is too small." << TestLog::EndMessage;
784 }
785 else for (deUint32 iteration = 0; iteration < iterations; iteration++)
786 {
787 atLeastOneTestPerformed = true;
788 AllocationCallbackRecorder recorder (getSystemAllocator());
789 const VkAllocationCallbacks* allocator = config.implicitUnmap ? recorder.getCallbacks() : DE_NULL;
790 Move<VkDeviceMemory> memory (allocMemory(vkd, device, allocationSize, memoryTypeIndex, image, buffer, allocator));
791 de::Random rng (config.seed);
792 deUint8* mapping = DE_NULL;
793
794 {
795 void* ptr;
796 VK_CHECK(vkd.mapMemory(device, *memory, mappingOffset, mappingSize, 0u, &ptr));
797 TCU_CHECK(ptr);
798
799 mapping = (deUint8*)ptr;
800 }
801
802 for (VkDeviceSize ndx = 0; ndx < referenceSize; ndx += stride)
803 {
804 const deUint8 val = rng.getUint8();
805
806 mapping[ndx * stride] = val;
807 reference[(size_t)(mappingOffset + ndx)] = val;
808 }
809
810 if (!config.flushMappings.empty())
811 {
812 vector<VkMappedMemoryRange> ranges;
813
814 for (size_t ndx = 0; ndx < config.flushMappings.size(); ndx++)
815 {
816 const VkMappedMemoryRange range =
817 {
818 VK_STRUCTURE_TYPE_MAPPED_MEMORY_RANGE,
819 DE_NULL,
820
821 *memory,
822 (config.flushMappings[ndx].offset % atomSize == 0) ? config.flushMappings[ndx].offset : config.flushMappings[ndx].offset - (config.flushMappings[ndx].offset % atomSize),
823 (config.flushMappings[ndx].size % atomSize == 0) ? config.flushMappings[ndx].size : config.flushMappings[ndx].size + (atomSize - (config.flushMappings[ndx].size % atomSize)),
824 };
825
826 ranges.push_back(range);
827 }
828
829 VK_CHECK(vkd.flushMappedMemoryRanges(device, (deUint32)ranges.size(), &ranges[0]));
830 }
831
832 if (config.remap)
833 {
834 void* ptr;
835 vkd.unmapMemory(device, *memory);
836 VK_CHECK(vkd.mapMemory(device, *memory, mappingOffset, mappingSize, 0u, &ptr));
837 TCU_CHECK(ptr);
838
839 mapping = (deUint8*)ptr;
840 }
841
842 if (!config.invalidateMappings.empty())
843 {
844 vector<VkMappedMemoryRange> ranges;
845
846 for (size_t ndx = 0; ndx < config.invalidateMappings.size(); ndx++)
847 {
848 const VkMappedMemoryRange range =
849 {
850 VK_STRUCTURE_TYPE_MAPPED_MEMORY_RANGE,
851 DE_NULL,
852
853 *memory,
854 (config.invalidateMappings[ndx].offset % atomSize == 0) ? config.invalidateMappings[ndx].offset : config.invalidateMappings[ndx].offset - (config.invalidateMappings[ndx].offset % atomSize),
855 (config.invalidateMappings[ndx].size % atomSize == 0) ? config.invalidateMappings[ndx].size : config.invalidateMappings[ndx].size + (atomSize - (config.invalidateMappings[ndx].size % atomSize)),
856 };
857
858 ranges.push_back(range);
859 }
860
861 VK_CHECK(vkd.invalidateMappedMemoryRanges(device, static_cast<deUint32>(ranges.size()), &ranges[0]));
862 }
863
864 if (!compareAndLogBuffer(log, static_cast<size_t>(mappingSize), referenceSize, mapping, &reference[static_cast<size_t>(mappingOffset)]))
865 result.fail("Unexpected values read from mapped memory.");
866
867 if (config.implicitUnmap)
868 {
869 AllocationCallbackValidationResults results;
870
871 vkd.freeMemory(device, memory.disown(), allocator);
872 validateAllocationCallbacks(recorder, &results);
873
874 if (!results.liveAllocations.empty())
875 result.fail("Live allocations remain after freeing mapped memory");
876 }
877 else
878 {
879 vkd.unmapMemory(device, *memory);
880 }
881
882 context.getTestContext().touchWatchdog();
883 }
884 }
885 catch (const tcu::TestError& error)
886 {
887 result.fail(error.getMessage());
888 }
889 }
890
891 if (!atLeastOneTestPerformed)
892 result.addResult(QP_TEST_RESULT_NOT_SUPPORTED, "No suitable memory kind found to perform test.");
893
894 return tcu::TestStatus(result.getResult(), result.getMessage());
895 }
896
897 class MemoryMapping
898 {
899 public:
900 MemoryMapping (const MemoryRange& range,
901 void* ptr,
902 ReferenceMemory& reference);
903
904 void randomRead (de::Random& rng);
905 void randomWrite (de::Random& rng);
906 void randomModify (de::Random& rng);
907
getRange(void) const908 const MemoryRange& getRange (void) const { return m_range; }
909
910 private:
911 MemoryRange m_range;
912 void* m_ptr;
913 ReferenceMemory& m_reference;
914 };
915
MemoryMapping(const MemoryRange & range,void * ptr,ReferenceMemory & reference)916 MemoryMapping::MemoryMapping (const MemoryRange& range,
917 void* ptr,
918 ReferenceMemory& reference)
919 : m_range (range)
920 , m_ptr (ptr)
921 , m_reference (reference)
922 {
923 DE_ASSERT(range.size > 0);
924 }
925
randomRead(de::Random & rng)926 void MemoryMapping::randomRead (de::Random& rng)
927 {
928 const size_t count = (size_t)rng.getInt(0, 100);
929
930 for (size_t ndx = 0; ndx < count; ndx++)
931 {
932 const size_t pos = (size_t)(rng.getUint64() % (deUint64)m_range.size);
933 const deUint8 val = ((deUint8*)m_ptr)[pos];
934
935 TCU_CHECK(m_reference.read((size_t)(m_range.offset + pos), val));
936 }
937 }
938
randomWrite(de::Random & rng)939 void MemoryMapping::randomWrite (de::Random& rng)
940 {
941 const size_t count = (size_t)rng.getInt(0, 100);
942
943 for (size_t ndx = 0; ndx < count; ndx++)
944 {
945 const size_t pos = (size_t)(rng.getUint64() % (deUint64)m_range.size);
946 const deUint8 val = rng.getUint8();
947
948 ((deUint8*)m_ptr)[pos] = val;
949 m_reference.write((size_t)(m_range.offset + pos), val);
950 }
951 }
952
randomModify(de::Random & rng)953 void MemoryMapping::randomModify (de::Random& rng)
954 {
955 const size_t count = (size_t)rng.getInt(0, 100);
956
957 for (size_t ndx = 0; ndx < count; ndx++)
958 {
959 const size_t pos = (size_t)(rng.getUint64() % (deUint64)m_range.size);
960 const deUint8 val = ((deUint8*)m_ptr)[pos];
961 const deUint8 mask = rng.getUint8();
962
963 ((deUint8*)m_ptr)[pos] = val ^ mask;
964 TCU_CHECK(m_reference.modifyXor((size_t)(m_range.offset + pos), val, mask));
965 }
966 }
967
randomSize(de::Random & rng,VkDeviceSize atomSize,VkDeviceSize maxSize)968 VkDeviceSize randomSize (de::Random& rng, VkDeviceSize atomSize, VkDeviceSize maxSize)
969 {
970 const VkDeviceSize maxSizeInAtoms = maxSize / atomSize;
971
972 DE_ASSERT(maxSizeInAtoms > 0);
973
974 return maxSizeInAtoms > 1
975 ? atomSize * (1 + (VkDeviceSize)(rng.getUint64() % (deUint64)maxSizeInAtoms))
976 : atomSize;
977 }
978
randomOffset(de::Random & rng,VkDeviceSize atomSize,VkDeviceSize maxOffset)979 VkDeviceSize randomOffset (de::Random& rng, VkDeviceSize atomSize, VkDeviceSize maxOffset)
980 {
981 const VkDeviceSize maxOffsetInAtoms = maxOffset / atomSize;
982
983 return maxOffsetInAtoms > 0
984 ? atomSize * (VkDeviceSize)(rng.getUint64() % (deUint64)(maxOffsetInAtoms + 1))
985 : 0;
986 }
987
randomRanges(de::Random & rng,vector<VkMappedMemoryRange> & ranges,size_t count,VkDeviceMemory memory,VkDeviceSize minOffset,VkDeviceSize maxSize,VkDeviceSize atomSize)988 void randomRanges (de::Random& rng, vector<VkMappedMemoryRange>& ranges, size_t count, VkDeviceMemory memory, VkDeviceSize minOffset, VkDeviceSize maxSize, VkDeviceSize atomSize)
989 {
990 ranges.resize(count);
991
992 for (size_t rangeNdx = 0; rangeNdx < count; rangeNdx++)
993 {
994 const VkDeviceSize size = randomSize(rng, atomSize, maxSize);
995 const VkDeviceSize offset = minOffset + randomOffset(rng, atomSize, maxSize - size);
996
997 const VkMappedMemoryRange range =
998 {
999 VK_STRUCTURE_TYPE_MAPPED_MEMORY_RANGE,
1000 DE_NULL,
1001
1002 memory,
1003 offset,
1004 size
1005 };
1006 ranges[rangeNdx] = range;
1007 }
1008 }
1009
1010 class MemoryObject
1011 {
1012 public:
1013 MemoryObject (const DeviceInterface& vkd,
1014 VkDevice device,
1015 VkDeviceSize size,
1016 deUint32 memoryTypeIndex,
1017 VkDeviceSize atomSize,
1018 VkDeviceSize memoryUsage,
1019 VkDeviceSize referenceMemoryUsage);
1020
1021 ~MemoryObject (void);
1022
1023 MemoryMapping* mapRandom (const DeviceInterface& vkd, VkDevice device, de::Random& rng);
1024 void unmap (void);
1025
1026 void randomFlush (const DeviceInterface& vkd, VkDevice device, de::Random& rng);
1027 void randomInvalidate (const DeviceInterface& vkd, VkDevice device, de::Random& rng);
1028
getSize(void) const1029 VkDeviceSize getSize (void) const { return m_size; }
getMapping(void)1030 MemoryMapping* getMapping (void) { return m_mapping; }
1031
getMemoryUsage(void) const1032 VkDeviceSize getMemoryUsage (void) const { return m_memoryUsage; }
getReferenceMemoryUsage(void) const1033 VkDeviceSize getReferenceMemoryUsage (void) const { return m_referenceMemoryUsage; }
1034 private:
1035 const DeviceInterface& m_vkd;
1036 const VkDevice m_device;
1037
1038 const deUint32 m_memoryTypeIndex;
1039 const VkDeviceSize m_size;
1040 const VkDeviceSize m_atomSize;
1041 const VkDeviceSize m_memoryUsage;
1042 const VkDeviceSize m_referenceMemoryUsage;
1043
1044 Move<VkDeviceMemory> m_memory;
1045
1046 MemoryMapping* m_mapping;
1047 ReferenceMemory m_referenceMemory;
1048 };
1049
MemoryObject(const DeviceInterface & vkd,VkDevice device,VkDeviceSize size,deUint32 memoryTypeIndex,VkDeviceSize atomSize,VkDeviceSize memoryUsage,VkDeviceSize referenceMemoryUsage)1050 MemoryObject::MemoryObject (const DeviceInterface& vkd,
1051 VkDevice device,
1052 VkDeviceSize size,
1053 deUint32 memoryTypeIndex,
1054 VkDeviceSize atomSize,
1055 VkDeviceSize memoryUsage,
1056 VkDeviceSize referenceMemoryUsage)
1057 : m_vkd (vkd)
1058 , m_device (device)
1059 , m_memoryTypeIndex (memoryTypeIndex)
1060 , m_size (size)
1061 , m_atomSize (atomSize)
1062 , m_memoryUsage (memoryUsage)
1063 , m_referenceMemoryUsage (referenceMemoryUsage)
1064 , m_mapping (DE_NULL)
1065 , m_referenceMemory ((size_t)size, (size_t)m_atomSize)
1066 {
1067 m_memory = allocMemory(m_vkd, m_device, m_size, m_memoryTypeIndex);
1068 }
1069
~MemoryObject(void)1070 MemoryObject::~MemoryObject (void)
1071 {
1072 delete m_mapping;
1073 }
1074
mapRandom(const DeviceInterface & vkd,VkDevice device,de::Random & rng)1075 MemoryMapping* MemoryObject::mapRandom (const DeviceInterface& vkd, VkDevice device, de::Random& rng)
1076 {
1077 const VkDeviceSize size = randomSize(rng, m_atomSize, m_size);
1078 const VkDeviceSize offset = randomOffset(rng, m_atomSize, m_size - size);
1079 void* ptr;
1080
1081 DE_ASSERT(!m_mapping);
1082
1083 VK_CHECK(vkd.mapMemory(device, *m_memory, offset, size, 0u, &ptr));
1084 TCU_CHECK(ptr);
1085 m_mapping = new MemoryMapping(MemoryRange(offset, size), ptr, m_referenceMemory);
1086
1087 return m_mapping;
1088 }
1089
unmap(void)1090 void MemoryObject::unmap (void)
1091 {
1092 m_vkd.unmapMemory(m_device, *m_memory);
1093
1094 delete m_mapping;
1095 m_mapping = DE_NULL;
1096 }
1097
randomFlush(const DeviceInterface & vkd,VkDevice device,de::Random & rng)1098 void MemoryObject::randomFlush (const DeviceInterface& vkd, VkDevice device, de::Random& rng)
1099 {
1100 const size_t rangeCount = (size_t)rng.getInt(1, 10);
1101 vector<VkMappedMemoryRange> ranges (rangeCount);
1102
1103 randomRanges(rng, ranges, rangeCount, *m_memory, m_mapping->getRange().offset, m_mapping->getRange().size, m_atomSize);
1104
1105 for (size_t rangeNdx = 0; rangeNdx < ranges.size(); rangeNdx++)
1106 m_referenceMemory.flush((size_t)ranges[rangeNdx].offset, (size_t)ranges[rangeNdx].size);
1107
1108 VK_CHECK(vkd.flushMappedMemoryRanges(device, (deUint32)ranges.size(), ranges.empty() ? DE_NULL : &ranges[0]));
1109 }
1110
randomInvalidate(const DeviceInterface & vkd,VkDevice device,de::Random & rng)1111 void MemoryObject::randomInvalidate (const DeviceInterface& vkd, VkDevice device, de::Random& rng)
1112 {
1113 const size_t rangeCount = (size_t)rng.getInt(1, 10);
1114 vector<VkMappedMemoryRange> ranges (rangeCount);
1115
1116 randomRanges(rng, ranges, rangeCount, *m_memory, m_mapping->getRange().offset, m_mapping->getRange().size, m_atomSize);
1117
1118 for (size_t rangeNdx = 0; rangeNdx < ranges.size(); rangeNdx++)
1119 m_referenceMemory.invalidate((size_t)ranges[rangeNdx].offset, (size_t)ranges[rangeNdx].size);
1120
1121 VK_CHECK(vkd.invalidateMappedMemoryRanges(device, (deUint32)ranges.size(), ranges.empty() ? DE_NULL : &ranges[0]));
1122 }
1123
1124 enum
1125 {
1126 MAX_MEMORY_USAGE_DIV = 2, // Use only 1/2 of each memory heap.
1127 MAX_MEMORY_ALLOC_DIV = 2, // Do not alloc more than 1/2 of available space.
1128 };
1129
1130 template<typename T>
removeFirstEqual(vector<T> & vec,const T & val)1131 void removeFirstEqual (vector<T>& vec, const T& val)
1132 {
1133 for (size_t ndx = 0; ndx < vec.size(); ndx++)
1134 {
1135 if (vec[ndx] == val)
1136 {
1137 vec[ndx] = vec.back();
1138 vec.pop_back();
1139 return;
1140 }
1141 }
1142 }
1143
1144 enum MemoryClass
1145 {
1146 MEMORY_CLASS_SYSTEM = 0,
1147 MEMORY_CLASS_DEVICE,
1148
1149 MEMORY_CLASS_LAST
1150 };
1151
1152 // \todo [2016-04-20 pyry] Consider estimating memory fragmentation
1153 class TotalMemoryTracker
1154 {
1155 public:
TotalMemoryTracker(void)1156 TotalMemoryTracker (void)
1157 {
1158 std::fill(DE_ARRAY_BEGIN(m_usage), DE_ARRAY_END(m_usage), 0);
1159 }
1160
allocate(MemoryClass memClass,VkDeviceSize size)1161 void allocate (MemoryClass memClass, VkDeviceSize size)
1162 {
1163 m_usage[memClass] += size;
1164 }
1165
free(MemoryClass memClass,VkDeviceSize size)1166 void free (MemoryClass memClass, VkDeviceSize size)
1167 {
1168 DE_ASSERT(size <= m_usage[memClass]);
1169 m_usage[memClass] -= size;
1170 }
1171
getUsage(MemoryClass memClass) const1172 VkDeviceSize getUsage (MemoryClass memClass) const
1173 {
1174 return m_usage[memClass];
1175 }
1176
getTotalUsage(void) const1177 VkDeviceSize getTotalUsage (void) const
1178 {
1179 VkDeviceSize total = 0;
1180 for (int ndx = 0; ndx < MEMORY_CLASS_LAST; ++ndx)
1181 total += getUsage((MemoryClass)ndx);
1182 return total;
1183 }
1184
1185 private:
1186 VkDeviceSize m_usage[MEMORY_CLASS_LAST];
1187 };
1188
getHostPageSize(void)1189 VkDeviceSize getHostPageSize (void)
1190 {
1191 return 4096;
1192 }
1193
1194 class MemoryHeap
1195 {
1196 public:
MemoryHeap(const VkMemoryHeap & heap,const vector<MemoryType> & memoryTypes,const tcu::PlatformMemoryLimits & memoryLimits,const VkDeviceSize nonCoherentAtomSize,TotalMemoryTracker & totalMemTracker)1197 MemoryHeap (const VkMemoryHeap& heap,
1198 const vector<MemoryType>& memoryTypes,
1199 const tcu::PlatformMemoryLimits& memoryLimits,
1200 const VkDeviceSize nonCoherentAtomSize,
1201 TotalMemoryTracker& totalMemTracker)
1202 : m_heap (heap)
1203 , m_memoryTypes (memoryTypes)
1204 , m_limits (memoryLimits)
1205 , m_nonCoherentAtomSize (nonCoherentAtomSize)
1206 , m_minAtomSize (nonCoherentAtomSize)
1207 , m_totalMemTracker (totalMemTracker)
1208 , m_usage (0)
1209 {
1210 }
1211
~MemoryHeap(void)1212 ~MemoryHeap (void)
1213 {
1214 for (vector<MemoryObject*>::iterator iter = m_objects.begin(); iter != m_objects.end(); ++iter)
1215 delete *iter;
1216 }
1217
1218 bool full (void) const;
empty(void) const1219 bool empty (void) const
1220 {
1221 return m_usage == 0 && !full();
1222 }
1223
1224 MemoryObject* allocateRandom (const DeviceInterface& vkd, VkDevice device, de::Random& rng);
1225
getRandomObject(de::Random & rng) const1226 MemoryObject* getRandomObject (de::Random& rng) const
1227 {
1228 return rng.choose<MemoryObject*>(m_objects.begin(), m_objects.end());
1229 }
1230
free(MemoryObject * object)1231 void free (MemoryObject* object)
1232 {
1233 removeFirstEqual(m_objects, object);
1234 m_usage -= object->getMemoryUsage();
1235 m_totalMemTracker.free(MEMORY_CLASS_SYSTEM, object->getReferenceMemoryUsage());
1236 m_totalMemTracker.free(getMemoryClass(), object->getMemoryUsage());
1237 delete object;
1238 }
1239
1240 private:
getMemoryClass(void) const1241 MemoryClass getMemoryClass (void) const
1242 {
1243 if ((m_heap.flags & VK_MEMORY_HEAP_DEVICE_LOCAL_BIT) != 0)
1244 return MEMORY_CLASS_DEVICE;
1245 else
1246 return MEMORY_CLASS_SYSTEM;
1247 }
1248
1249 const VkMemoryHeap m_heap;
1250 const vector<MemoryType> m_memoryTypes;
1251 const tcu::PlatformMemoryLimits& m_limits;
1252 const VkDeviceSize m_nonCoherentAtomSize;
1253 const VkDeviceSize m_minAtomSize;
1254 TotalMemoryTracker& m_totalMemTracker;
1255
1256 VkDeviceSize m_usage;
1257 vector<MemoryObject*> m_objects;
1258 };
1259
1260 // Heap is full if there is not enough memory to allocate minimal memory object.
full(void) const1261 bool MemoryHeap::full (void) const
1262 {
1263 DE_ASSERT(m_usage <= m_heap.size/MAX_MEMORY_USAGE_DIV);
1264
1265 const VkDeviceSize availableInHeap = m_heap.size/MAX_MEMORY_USAGE_DIV - m_usage;
1266 const bool isUMA = m_limits.totalDeviceLocalMemory == 0;
1267 const MemoryClass memClass = getMemoryClass();
1268 const VkDeviceSize minAllocationSize = de::max(m_minAtomSize, memClass == MEMORY_CLASS_DEVICE ? m_limits.devicePageSize : getHostPageSize());
1269 // Memory required for reference. One byte and one bit for each byte and one bit per each m_atomSize.
1270 const VkDeviceSize minReferenceSize = minAllocationSize
1271 + divRoundUp<VkDeviceSize>(minAllocationSize, 8)
1272 + divRoundUp<VkDeviceSize>(minAllocationSize, m_minAtomSize * 8);
1273
1274 if (isUMA)
1275 {
1276 const VkDeviceSize totalUsage = m_totalMemTracker.getTotalUsage();
1277 const VkDeviceSize totalSysMem = (VkDeviceSize)m_limits.totalSystemMemory;
1278
1279 DE_ASSERT(totalUsage <= totalSysMem);
1280
1281 return (minAllocationSize + minReferenceSize) > (totalSysMem - totalUsage)
1282 || minAllocationSize > availableInHeap;
1283 }
1284 else
1285 {
1286 const VkDeviceSize totalUsage = m_totalMemTracker.getTotalUsage();
1287 const VkDeviceSize totalSysMem = (VkDeviceSize)m_limits.totalSystemMemory;
1288
1289 const VkDeviceSize totalMemClass = memClass == MEMORY_CLASS_SYSTEM
1290 ? m_limits.totalSystemMemory
1291 : m_limits.totalDeviceLocalMemory;
1292 const VkDeviceSize usedMemClass = m_totalMemTracker.getUsage(memClass);
1293
1294 DE_ASSERT(usedMemClass <= totalMemClass);
1295
1296 return minAllocationSize > availableInHeap
1297 || minAllocationSize > (totalMemClass - usedMemClass)
1298 || minReferenceSize > (totalSysMem - totalUsage);
1299 }
1300 }
1301
allocateRandom(const DeviceInterface & vkd,VkDevice device,de::Random & rng)1302 MemoryObject* MemoryHeap::allocateRandom (const DeviceInterface& vkd, VkDevice device, de::Random& rng)
1303 {
1304 pair<MemoryType, VkDeviceSize> memoryTypeMaxSizePair;
1305
1306 // Pick random memory type
1307 {
1308 vector<pair<MemoryType, VkDeviceSize> > memoryTypes;
1309
1310 const VkDeviceSize availableInHeap = m_heap.size/MAX_MEMORY_USAGE_DIV - m_usage;
1311 const bool isUMA = m_limits.totalDeviceLocalMemory == 0;
1312 const MemoryClass memClass = getMemoryClass();
1313
1314 // Collect memory types that can be allocated and the maximum size of allocation.
1315 // Memory type can be only allocated if minimal memory allocation is less than available memory.
1316 for (size_t memoryTypeNdx = 0; memoryTypeNdx < m_memoryTypes.size(); memoryTypeNdx++)
1317 {
1318 const MemoryType type = m_memoryTypes[memoryTypeNdx];
1319 const VkDeviceSize atomSize = m_nonCoherentAtomSize;
1320 const VkDeviceSize allocationSizeGranularity = de::max(atomSize, memClass == MEMORY_CLASS_DEVICE ? m_limits.devicePageSize : getHostPageSize());
1321 const VkDeviceSize minAllocationSize = allocationSizeGranularity;
1322 const VkDeviceSize minReferenceSize = minAllocationSize
1323 + divRoundUp<VkDeviceSize>(minAllocationSize, 8)
1324 + divRoundUp<VkDeviceSize>(minAllocationSize, atomSize * 8);
1325
1326 if (isUMA)
1327 {
1328 // Max memory size calculation is little tricky since reference memory requires 1/n bits per byte.
1329 const VkDeviceSize totalUsage = m_totalMemTracker.getTotalUsage();
1330 const VkDeviceSize totalSysMem = (VkDeviceSize)m_limits.totalSystemMemory;
1331 const VkDeviceSize availableBits = (totalSysMem - totalUsage) * 8;
1332 // availableBits == maxAllocationSizeBits + maxAllocationReferenceSizeBits
1333 // maxAllocationReferenceSizeBits == maxAllocationSizeBits + (maxAllocationSizeBits / 8) + (maxAllocationSizeBits / atomSizeBits)
1334 // availableBits == maxAllocationSizeBits + maxAllocationSizeBits + (maxAllocationSizeBits / 8) + (maxAllocationSizeBits / atomSizeBits)
1335 // availableBits == 2 * maxAllocationSizeBits + (maxAllocationSizeBits / 8) + (maxAllocationSizeBits / atomSizeBits)
1336 // availableBits == (2 + 1/8 + 1/atomSizeBits) * maxAllocationSizeBits
1337 // 8 * availableBits == (16 + 1 + 8/atomSizeBits) * maxAllocationSizeBits
1338 // atomSizeBits * 8 * availableBits == (17 * atomSizeBits + 8) * maxAllocationSizeBits
1339 // maxAllocationSizeBits == atomSizeBits * 8 * availableBits / (17 * atomSizeBits + 8)
1340 // maxAllocationSizeBytes == maxAllocationSizeBits / 8
1341 // maxAllocationSizeBytes == atomSizeBits * availableBits / (17 * atomSizeBits + 8)
1342 // atomSizeBits = atomSize * 8
1343 // maxAllocationSizeBytes == atomSize * 8 * availableBits / (17 * atomSize * 8 + 8)
1344 // maxAllocationSizeBytes == atomSize * availableBits / (17 * atomSize + 1)
1345 //
1346 // Finally, the allocation size must be less than or equal to memory heap size
1347 const VkDeviceSize maxAllocationSize = roundDownToMultiple(de::min((atomSize * availableBits) / (17 * atomSize + 1), availableInHeap), allocationSizeGranularity);
1348
1349 DE_ASSERT(totalUsage <= totalSysMem);
1350 DE_ASSERT(maxAllocationSize <= totalSysMem);
1351
1352 if (minAllocationSize + minReferenceSize <= (totalSysMem - totalUsage) && minAllocationSize <= availableInHeap)
1353 {
1354 DE_ASSERT(maxAllocationSize >= minAllocationSize);
1355 memoryTypes.push_back(std::make_pair(type, maxAllocationSize));
1356 }
1357 }
1358 else
1359 {
1360 // Max memory size calculation is little tricky since reference memory requires 1/n bits per byte.
1361 const VkDeviceSize totalUsage = m_totalMemTracker.getTotalUsage();
1362 const VkDeviceSize totalSysMem = (VkDeviceSize)m_limits.totalSystemMemory;
1363
1364 const VkDeviceSize totalMemClass = memClass == MEMORY_CLASS_SYSTEM
1365 ? m_limits.totalSystemMemory
1366 : m_limits.totalDeviceLocalMemory;
1367 const VkDeviceSize usedMemClass = m_totalMemTracker.getUsage(memClass);
1368 // availableRefBits = maxRefBits + maxRefBits/8 + maxRefBits/atomSizeBits
1369 // availableRefBits = maxRefBits * (1 + 1/8 + 1/atomSizeBits)
1370 // 8 * availableRefBits = maxRefBits * (8 + 1 + 8/atomSizeBits)
1371 // 8 * atomSizeBits * availableRefBits = maxRefBits * (9 * atomSizeBits + 8)
1372 // maxRefBits = 8 * atomSizeBits * availableRefBits / (9 * atomSizeBits + 8)
1373 // atomSizeBits = atomSize * 8
1374 // maxRefBits = 8 * atomSize * 8 * availableRefBits / (9 * atomSize * 8 + 8)
1375 // maxRefBits = atomSize * 8 * availableRefBits / (9 * atomSize + 1)
1376 // maxRefBytes = atomSize * availableRefBits / (9 * atomSize + 1)
1377 //
1378 // Finally, the allocation size must be less than or equal to memory heap size
1379 const VkDeviceSize maxAllocationSize = roundDownToMultiple(de::min(de::min(totalMemClass - usedMemClass, (atomSize * 8 * (totalSysMem - totalUsage)) / (9 * atomSize + 1)), availableInHeap), allocationSizeGranularity);
1380
1381 DE_ASSERT(usedMemClass <= totalMemClass);
1382
1383 if (minAllocationSize <= availableInHeap
1384 && minAllocationSize <= (totalMemClass - usedMemClass)
1385 && minReferenceSize <= (totalSysMem - totalUsage))
1386 {
1387 DE_ASSERT(maxAllocationSize >= minAllocationSize);
1388 memoryTypes.push_back(std::make_pair(type, maxAllocationSize));
1389 }
1390
1391 }
1392 }
1393
1394 memoryTypeMaxSizePair = rng.choose<pair<MemoryType, VkDeviceSize> >(memoryTypes.begin(), memoryTypes.end());
1395 }
1396
1397 const MemoryType type = memoryTypeMaxSizePair.first;
1398 const VkDeviceSize maxAllocationSize = memoryTypeMaxSizePair.second / MAX_MEMORY_ALLOC_DIV;
1399 const VkDeviceSize atomSize = m_nonCoherentAtomSize;
1400 const VkDeviceSize allocationSizeGranularity = de::max(atomSize, getMemoryClass() == MEMORY_CLASS_DEVICE ? m_limits.devicePageSize : getHostPageSize());
1401 const VkDeviceSize size = randomSize(rng, atomSize, maxAllocationSize);
1402 const VkDeviceSize memoryUsage = roundUpToMultiple(size, allocationSizeGranularity);
1403 const VkDeviceSize referenceMemoryUsage = size + divRoundUp<VkDeviceSize>(size, 8) + divRoundUp<VkDeviceSize>(size / atomSize, 8);
1404
1405 DE_ASSERT(size <= maxAllocationSize);
1406
1407 MemoryObject* const object = new MemoryObject(vkd, device, size, type.index, atomSize, memoryUsage, referenceMemoryUsage);
1408
1409 m_usage += memoryUsage;
1410 m_totalMemTracker.allocate(getMemoryClass(), memoryUsage);
1411 m_totalMemTracker.allocate(MEMORY_CLASS_SYSTEM, referenceMemoryUsage);
1412 m_objects.push_back(object);
1413
1414 return object;
1415 }
1416
getMemoryObjectSystemSize(Context & context)1417 size_t getMemoryObjectSystemSize (Context& context)
1418 {
1419 return computeDeviceMemorySystemMemFootprint(context.getDeviceInterface(), context.getDevice())
1420 + sizeof(MemoryObject)
1421 + sizeof(de::SharedPtr<MemoryObject>);
1422 }
1423
getMemoryMappingSystemSize(void)1424 size_t getMemoryMappingSystemSize (void)
1425 {
1426 return sizeof(MemoryMapping) + sizeof(de::SharedPtr<MemoryMapping>);
1427 }
1428
1429 class RandomMemoryMappingInstance : public TestInstance
1430 {
1431 public:
RandomMemoryMappingInstance(Context & context,deUint32 seed)1432 RandomMemoryMappingInstance (Context& context, deUint32 seed)
1433 : TestInstance (context)
1434 , m_memoryObjectSysMemSize (getMemoryObjectSystemSize(context))
1435 , m_memoryMappingSysMemSize (getMemoryMappingSystemSize())
1436 , m_memoryLimits (tcu::getMemoryLimits(context.getTestContext().getPlatform()))
1437 , m_rng (seed)
1438 , m_opNdx (0)
1439 {
1440 const VkPhysicalDevice physicalDevice = context.getPhysicalDevice();
1441 const InstanceInterface& vki = context.getInstanceInterface();
1442 const VkPhysicalDeviceMemoryProperties memoryProperties = getPhysicalDeviceMemoryProperties(vki, physicalDevice);
1443 const VkDeviceSize nonCoherentAtomSize = context.getDeviceProperties().limits.nonCoherentAtomSize;
1444
1445 // Initialize heaps
1446 {
1447 vector<vector<MemoryType> > memoryTypes (memoryProperties.memoryHeapCount);
1448
1449 for (deUint32 memoryTypeNdx = 0; memoryTypeNdx < memoryProperties.memoryTypeCount; memoryTypeNdx++)
1450 {
1451 if (memoryProperties.memoryTypes[memoryTypeNdx].propertyFlags & VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT)
1452 memoryTypes[memoryProperties.memoryTypes[memoryTypeNdx].heapIndex].push_back(MemoryType(memoryTypeNdx, memoryProperties.memoryTypes[memoryTypeNdx]));
1453 }
1454
1455 for (deUint32 heapIndex = 0; heapIndex < memoryProperties.memoryHeapCount; heapIndex++)
1456 {
1457 const VkMemoryHeap heapInfo = memoryProperties.memoryHeaps[heapIndex];
1458
1459 if (!memoryTypes[heapIndex].empty())
1460 {
1461 const de::SharedPtr<MemoryHeap> heap (new MemoryHeap(heapInfo, memoryTypes[heapIndex], m_memoryLimits, nonCoherentAtomSize, m_totalMemTracker));
1462
1463 TCU_CHECK_INTERNAL(!heap->full());
1464
1465 m_memoryHeaps.push_back(heap);
1466 }
1467 }
1468 }
1469 }
1470
~RandomMemoryMappingInstance(void)1471 ~RandomMemoryMappingInstance (void)
1472 {
1473 }
1474
iterate(void)1475 tcu::TestStatus iterate (void)
1476 {
1477 const size_t opCount = 100;
1478 const float memoryOpProbability = 0.5f; // 0.50
1479 const float flushInvalidateProbability = 0.4f; // 0.20
1480 const float mapProbability = 0.50f; // 0.15
1481 const float unmapProbability = 0.25f; // 0.075
1482
1483 const float allocProbability = 0.75f; // Versun free
1484
1485 const VkDevice device = m_context.getDevice();
1486 const DeviceInterface& vkd = m_context.getDeviceInterface();
1487
1488 const VkDeviceSize sysMemUsage = (m_memoryLimits.totalDeviceLocalMemory == 0)
1489 ? m_totalMemTracker.getTotalUsage()
1490 : m_totalMemTracker.getUsage(MEMORY_CLASS_SYSTEM);
1491
1492 if (!m_memoryMappings.empty() && m_rng.getFloat() < memoryOpProbability)
1493 {
1494 // Perform operations on mapped memory
1495 MemoryMapping* const mapping = m_rng.choose<MemoryMapping*>(m_memoryMappings.begin(), m_memoryMappings.end());
1496
1497 enum Op
1498 {
1499 OP_READ = 0,
1500 OP_WRITE,
1501 OP_MODIFY,
1502 OP_LAST
1503 };
1504
1505 const Op op = (Op)(m_rng.getUint32() % OP_LAST);
1506
1507 switch (op)
1508 {
1509 case OP_READ:
1510 mapping->randomRead(m_rng);
1511 break;
1512
1513 case OP_WRITE:
1514 mapping->randomWrite(m_rng);
1515 break;
1516
1517 case OP_MODIFY:
1518 mapping->randomModify(m_rng);
1519 break;
1520
1521 default:
1522 DE_FATAL("Invalid operation");
1523 }
1524 }
1525 else if (!m_mappedMemoryObjects.empty() && m_rng.getFloat() < flushInvalidateProbability)
1526 {
1527 MemoryObject* const object = m_rng.choose<MemoryObject*>(m_mappedMemoryObjects.begin(), m_mappedMemoryObjects.end());
1528
1529 if (m_rng.getBool())
1530 object->randomFlush(vkd, device, m_rng);
1531 else
1532 object->randomInvalidate(vkd, device, m_rng);
1533 }
1534 else if (!m_mappedMemoryObjects.empty() && m_rng.getFloat() < unmapProbability)
1535 {
1536 // Unmap memory object
1537 MemoryObject* const object = m_rng.choose<MemoryObject*>(m_mappedMemoryObjects.begin(), m_mappedMemoryObjects.end());
1538
1539 // Remove mapping
1540 removeFirstEqual(m_memoryMappings, object->getMapping());
1541
1542 object->unmap();
1543 removeFirstEqual(m_mappedMemoryObjects, object);
1544 m_nonMappedMemoryObjects.push_back(object);
1545
1546 m_totalMemTracker.free(MEMORY_CLASS_SYSTEM, (VkDeviceSize)m_memoryMappingSysMemSize);
1547 }
1548 else if (!m_nonMappedMemoryObjects.empty() &&
1549 (m_rng.getFloat() < mapProbability) &&
1550 (sysMemUsage+m_memoryMappingSysMemSize <= (VkDeviceSize)m_memoryLimits.totalSystemMemory))
1551 {
1552 // Map memory object
1553 MemoryObject* const object = m_rng.choose<MemoryObject*>(m_nonMappedMemoryObjects.begin(), m_nonMappedMemoryObjects.end());
1554 MemoryMapping* mapping = object->mapRandom(vkd, device, m_rng);
1555
1556 m_memoryMappings.push_back(mapping);
1557 m_mappedMemoryObjects.push_back(object);
1558 removeFirstEqual(m_nonMappedMemoryObjects, object);
1559
1560 m_totalMemTracker.allocate(MEMORY_CLASS_SYSTEM, (VkDeviceSize)m_memoryMappingSysMemSize);
1561 }
1562 else
1563 {
1564 // Sort heaps based on capacity (full or not)
1565 vector<MemoryHeap*> nonFullHeaps;
1566 vector<MemoryHeap*> nonEmptyHeaps;
1567
1568 if (sysMemUsage+m_memoryObjectSysMemSize <= (VkDeviceSize)m_memoryLimits.totalSystemMemory)
1569 {
1570 // For the duration of sorting reserve MemoryObject space from system memory
1571 m_totalMemTracker.allocate(MEMORY_CLASS_SYSTEM, (VkDeviceSize)m_memoryObjectSysMemSize);
1572
1573 for (vector<de::SharedPtr<MemoryHeap> >::const_iterator heapIter = m_memoryHeaps.begin();
1574 heapIter != m_memoryHeaps.end();
1575 ++heapIter)
1576 {
1577 if (!(*heapIter)->full())
1578 nonFullHeaps.push_back(heapIter->get());
1579
1580 if (!(*heapIter)->empty())
1581 nonEmptyHeaps.push_back(heapIter->get());
1582 }
1583
1584 m_totalMemTracker.free(MEMORY_CLASS_SYSTEM, (VkDeviceSize)m_memoryObjectSysMemSize);
1585 }
1586 else
1587 {
1588 // Not possible to even allocate MemoryObject from system memory, look for non-empty heaps
1589 for (vector<de::SharedPtr<MemoryHeap> >::const_iterator heapIter = m_memoryHeaps.begin();
1590 heapIter != m_memoryHeaps.end();
1591 ++heapIter)
1592 {
1593 if (!(*heapIter)->empty())
1594 nonEmptyHeaps.push_back(heapIter->get());
1595 }
1596 }
1597
1598 if (!nonFullHeaps.empty() && (nonEmptyHeaps.empty() || m_rng.getFloat() < allocProbability))
1599 {
1600 // Reserve MemoryObject from sys mem first
1601 m_totalMemTracker.allocate(MEMORY_CLASS_SYSTEM, (VkDeviceSize)m_memoryObjectSysMemSize);
1602
1603 // Allocate more memory objects
1604 MemoryHeap* const heap = m_rng.choose<MemoryHeap*>(nonFullHeaps.begin(), nonFullHeaps.end());
1605 MemoryObject* const object = heap->allocateRandom(vkd, device, m_rng);
1606
1607 m_nonMappedMemoryObjects.push_back(object);
1608 }
1609 else
1610 {
1611 // Free memory objects
1612 MemoryHeap* const heap = m_rng.choose<MemoryHeap*>(nonEmptyHeaps.begin(), nonEmptyHeaps.end());
1613 MemoryObject* const object = heap->getRandomObject(m_rng);
1614
1615 // Remove mapping
1616 if (object->getMapping())
1617 {
1618 removeFirstEqual(m_memoryMappings, object->getMapping());
1619 m_totalMemTracker.free(MEMORY_CLASS_SYSTEM, m_memoryMappingSysMemSize);
1620 }
1621
1622 removeFirstEqual(m_mappedMemoryObjects, object);
1623 removeFirstEqual(m_nonMappedMemoryObjects, object);
1624
1625 heap->free(object);
1626 m_totalMemTracker.free(MEMORY_CLASS_SYSTEM, (VkDeviceSize)m_memoryObjectSysMemSize);
1627 }
1628 }
1629
1630 m_opNdx += 1;
1631 if (m_opNdx == opCount)
1632 return tcu::TestStatus::pass("Pass");
1633 else
1634 return tcu::TestStatus::incomplete();
1635 }
1636
1637 private:
1638 const size_t m_memoryObjectSysMemSize;
1639 const size_t m_memoryMappingSysMemSize;
1640 const tcu::PlatformMemoryLimits m_memoryLimits;
1641
1642 de::Random m_rng;
1643 size_t m_opNdx;
1644
1645 TotalMemoryTracker m_totalMemTracker;
1646 vector<de::SharedPtr<MemoryHeap> > m_memoryHeaps;
1647
1648 vector<MemoryObject*> m_mappedMemoryObjects;
1649 vector<MemoryObject*> m_nonMappedMemoryObjects;
1650 vector<MemoryMapping*> m_memoryMappings;
1651 };
1652
1653 enum Op
1654 {
1655 OP_NONE = 0,
1656
1657 OP_FLUSH,
1658 OP_SUB_FLUSH,
1659 OP_SUB_FLUSH_SEPARATE,
1660 OP_SUB_FLUSH_OVERLAPPING,
1661
1662 OP_INVALIDATE,
1663 OP_SUB_INVALIDATE,
1664 OP_SUB_INVALIDATE_SEPARATE,
1665 OP_SUB_INVALIDATE_OVERLAPPING,
1666
1667 OP_REMAP,
1668 OP_IMPLICIT_UNMAP,
1669
1670 OP_LAST
1671 };
1672
subMappedConfig(VkDeviceSize allocationSize,const MemoryRange & mapping,Op op,deUint32 seed,AllocationKind allocationKind)1673 TestConfig subMappedConfig (VkDeviceSize allocationSize,
1674 const MemoryRange& mapping,
1675 Op op,
1676 deUint32 seed,
1677 AllocationKind allocationKind)
1678 {
1679 TestConfig config;
1680
1681 config.allocationSize = allocationSize;
1682 config.seed = seed;
1683 config.mapping = mapping;
1684 config.remap = false;
1685 config.implicitUnmap = false;
1686 config.allocationKind = allocationKind;
1687
1688 switch (op)
1689 {
1690 case OP_NONE:
1691 break;
1692
1693 case OP_REMAP:
1694 config.remap = true;
1695 break;
1696
1697 case OP_IMPLICIT_UNMAP:
1698 config.implicitUnmap = true;
1699 break;
1700
1701 case OP_FLUSH:
1702 config.flushMappings = vector<MemoryRange>(1, MemoryRange(mapping.offset, mapping.size));
1703 break;
1704
1705 case OP_SUB_FLUSH:
1706 DE_ASSERT(mapping.size / 4 > 0);
1707
1708 config.flushMappings = vector<MemoryRange>(1, MemoryRange(mapping.offset + mapping.size / 4, mapping.size / 2));
1709 break;
1710
1711 case OP_SUB_FLUSH_SEPARATE:
1712 DE_ASSERT(mapping.size / 2 > 0);
1713
1714 config.flushMappings.push_back(MemoryRange(mapping.offset + mapping.size / 2, mapping.size - (mapping.size / 2)));
1715 config.flushMappings.push_back(MemoryRange(mapping.offset, mapping.size / 2));
1716
1717 break;
1718
1719 case OP_SUB_FLUSH_OVERLAPPING:
1720 DE_ASSERT((mapping.size / 3) > 0);
1721
1722 config.flushMappings.push_back(MemoryRange(mapping.offset + mapping.size / 3, mapping.size - (mapping.size / 2)));
1723 config.flushMappings.push_back(MemoryRange(mapping.offset, (2 * mapping.size) / 3));
1724
1725 break;
1726
1727 case OP_INVALIDATE:
1728 config.flushMappings = vector<MemoryRange>(1, MemoryRange(mapping.offset, mapping.size));
1729 config.invalidateMappings = vector<MemoryRange>(1, MemoryRange(mapping.offset, mapping.size));
1730 break;
1731
1732 case OP_SUB_INVALIDATE:
1733 DE_ASSERT(mapping.size / 4 > 0);
1734
1735 config.flushMappings = vector<MemoryRange>(1, MemoryRange(mapping.offset + mapping.size / 4, mapping.size / 2));
1736 config.invalidateMappings = vector<MemoryRange>(1, MemoryRange(mapping.offset + mapping.size / 4, mapping.size / 2));
1737 break;
1738
1739 case OP_SUB_INVALIDATE_SEPARATE:
1740 DE_ASSERT(mapping.size / 2 > 0);
1741
1742 config.flushMappings.push_back(MemoryRange(mapping.offset + mapping.size / 2, mapping.size - (mapping.size / 2)));
1743 config.flushMappings.push_back(MemoryRange(mapping.offset, mapping.size / 2));
1744
1745 config.invalidateMappings.push_back(MemoryRange(mapping.offset + mapping.size / 2, mapping.size - (mapping.size / 2)));
1746 config.invalidateMappings.push_back(MemoryRange(mapping.offset, mapping.size / 2));
1747
1748 break;
1749
1750 case OP_SUB_INVALIDATE_OVERLAPPING:
1751 DE_ASSERT((mapping.size / 3) > 0);
1752
1753 config.flushMappings.push_back(MemoryRange(mapping.offset + mapping.size / 3, mapping.size - (mapping.size / 2)));
1754 config.flushMappings.push_back(MemoryRange(mapping.offset, (2 * mapping.size) / 3));
1755
1756 config.invalidateMappings.push_back(MemoryRange(mapping.offset + mapping.size / 3, mapping.size - (mapping.size / 2)));
1757 config.invalidateMappings.push_back(MemoryRange(mapping.offset, (2 * mapping.size) / 3));
1758
1759 break;
1760
1761 default:
1762 DE_FATAL("Unknown Op");
1763 return TestConfig();
1764 }
1765 for (size_t ndx = 0; ndx < config.flushMappings.size(); ndx++)
1766 {
1767 if (config.flushMappings[ndx].offset + config.flushMappings[ndx].size > mapping.size) {
1768 config.flushMappings[ndx].size = VK_WHOLE_SIZE;
1769 }
1770 }
1771 for (size_t ndx = 0; ndx < config.invalidateMappings.size(); ndx++)
1772 {
1773 if (config.invalidateMappings[ndx].offset + config.invalidateMappings[ndx].size > mapping.size) {
1774 config.invalidateMappings[ndx].size = VK_WHOLE_SIZE;
1775 }
1776 }
1777 return config;
1778 }
1779
fullMappedConfig(VkDeviceSize allocationSize,Op op,deUint32 seed,AllocationKind allocationKind)1780 TestConfig fullMappedConfig (VkDeviceSize allocationSize,
1781 Op op,
1782 deUint32 seed,
1783 AllocationKind allocationKind)
1784 {
1785 return subMappedConfig(allocationSize, MemoryRange(0, allocationSize), op, seed, allocationKind);
1786 }
1787
checkSupport(Context & context,TestConfig config)1788 void checkSupport (Context& context, TestConfig config)
1789 {
1790 context.requireInstanceFunctionality("VK_KHR_get_physical_device_properties2");
1791
1792 if (config.allocationKind == ALLOCATION_KIND_DEDICATED_IMAGE
1793 || config.allocationKind == ALLOCATION_KIND_DEDICATED_BUFFER)
1794 {
1795 context.requireDeviceFunctionality("VK_KHR_dedicated_allocation");
1796 }
1797 }
1798
1799 } // anonymous
1800
createMappingTests(tcu::TestContext & testCtx)1801 tcu::TestCaseGroup* createMappingTests (tcu::TestContext& testCtx)
1802 {
1803 de::MovePtr<tcu::TestCaseGroup> group (new tcu::TestCaseGroup(testCtx, "mapping", "Memory mapping tests."));
1804 de::MovePtr<tcu::TestCaseGroup> dedicated (new tcu::TestCaseGroup(testCtx, "dedicated_alloc", "Dedicated memory mapping tests."));
1805 de::MovePtr<tcu::TestCaseGroup> sets[] =
1806 {
1807 de::MovePtr<tcu::TestCaseGroup> (new tcu::TestCaseGroup(testCtx, "suballocation", "Suballocated memory mapping tests.")),
1808 de::MovePtr<tcu::TestCaseGroup> (new tcu::TestCaseGroup(testCtx, "buffer", "Buffer dedicated memory mapping tests.")),
1809 de::MovePtr<tcu::TestCaseGroup> (new tcu::TestCaseGroup(testCtx, "image", "Image dedicated memory mapping tests."))
1810 };
1811
1812 const VkDeviceSize allocationSizes[] =
1813 {
1814 0, 33, 257, 4087, 8095, 1*1024*1024 + 1,
1815 };
1816
1817 const VkDeviceSize offsets[] =
1818 {
1819 0, 17, 129, 255, 1025, 32*1024+1
1820 };
1821
1822 const VkDeviceSize sizes[] =
1823 {
1824 31, 255, 1025, 4085, 1*1024*1024 - 1
1825 };
1826
1827 const struct
1828 {
1829 const Op op;
1830 const char* const name;
1831 } ops[] =
1832 {
1833 { OP_NONE, "simple" },
1834 { OP_REMAP, "remap" },
1835 #ifndef CTS_USES_VULKANSC
1836 // implicit_unmap tests use VkAllocationCallbacks forbidden in Vulkan SC
1837 { OP_IMPLICIT_UNMAP, "implicit_unmap" },
1838 #endif // CTS_USES_VULKANSC
1839 { OP_FLUSH, "flush" },
1840 { OP_SUB_FLUSH, "subflush" },
1841 { OP_SUB_FLUSH_SEPARATE, "subflush_separate" },
1842 { OP_SUB_FLUSH_SEPARATE, "subflush_overlapping" },
1843 { OP_INVALIDATE, "invalidate" },
1844 { OP_SUB_INVALIDATE, "subinvalidate" },
1845 { OP_SUB_INVALIDATE_SEPARATE, "subinvalidate_separate" },
1846 { OP_SUB_INVALIDATE_SEPARATE, "subinvalidate_overlapping" }
1847 };
1848
1849 // .full
1850 for (size_t allocationKindNdx = 0; allocationKindNdx < ALLOCATION_KIND_LAST; allocationKindNdx++)
1851 {
1852 de::MovePtr<tcu::TestCaseGroup> fullGroup (new tcu::TestCaseGroup(testCtx, "full", "Map memory completely."));
1853
1854 for (size_t allocationSizeNdx = 0; allocationSizeNdx < DE_LENGTH_OF_ARRAY(allocationSizes); allocationSizeNdx++)
1855 {
1856 const VkDeviceSize allocationSize = allocationSizes[allocationSizeNdx];
1857 const string sizeGroupName = (allocationSize == 0) ? "variable" : de::toString(allocationSize);
1858 de::MovePtr<tcu::TestCaseGroup> allocationSizeGroup (new tcu::TestCaseGroup(testCtx, sizeGroupName.c_str(), ""));
1859
1860 for (size_t opNdx = 0; opNdx < DE_LENGTH_OF_ARRAY(ops); opNdx++)
1861 {
1862 const Op op = ops[opNdx].op;
1863
1864 // implicit_unmap ignores allocationSize
1865 if (((allocationSize == 0) && (op != OP_IMPLICIT_UNMAP)) ||
1866 ((allocationSize != 0) && (op == OP_IMPLICIT_UNMAP)))
1867 continue;
1868
1869 const char* const name = ops[opNdx].name;
1870 const deUint32 seed = (deUint32)(opNdx * allocationSizeNdx);
1871 const TestConfig config = fullMappedConfig(allocationSize, op, seed, static_cast<AllocationKind>(allocationKindNdx));
1872
1873 addFunctionCase(allocationSizeGroup.get(), name, name, checkSupport, testMemoryMapping, config);
1874 }
1875
1876 fullGroup->addChild(allocationSizeGroup.release());
1877 }
1878
1879 sets[allocationKindNdx]->addChild(fullGroup.release());
1880 }
1881
1882 // .sub
1883 for (size_t allocationKindNdx = 0; allocationKindNdx < ALLOCATION_KIND_LAST; allocationKindNdx++)
1884 {
1885 de::MovePtr<tcu::TestCaseGroup> subGroup (new tcu::TestCaseGroup(testCtx, "sub", "Map part of the memory."));
1886
1887 for (size_t allocationSizeNdx = 0; allocationSizeNdx < DE_LENGTH_OF_ARRAY(allocationSizes); allocationSizeNdx++)
1888 {
1889 const VkDeviceSize allocationSize = allocationSizes[allocationSizeNdx];
1890 const string sizeGroupName = (allocationSize == 0) ? "variable" : de::toString(allocationSize);
1891 de::MovePtr<tcu::TestCaseGroup> allocationSizeGroup (new tcu::TestCaseGroup(testCtx, sizeGroupName.c_str(), ""));
1892
1893 for (size_t offsetNdx = 0; offsetNdx < DE_LENGTH_OF_ARRAY(offsets); offsetNdx++)
1894 {
1895 const VkDeviceSize offset = offsets[offsetNdx];
1896
1897 if (offset >= allocationSize)
1898 continue;
1899
1900 de::MovePtr<tcu::TestCaseGroup> offsetGroup (new tcu::TestCaseGroup(testCtx, ("offset_" + de::toString(offset)).c_str(), ""));
1901
1902 for (size_t sizeNdx = 0; sizeNdx < DE_LENGTH_OF_ARRAY(sizes); sizeNdx++)
1903 {
1904 const VkDeviceSize size = sizes[sizeNdx];
1905
1906 if (offset + size > allocationSize)
1907 continue;
1908
1909 if (offset == 0 && size == allocationSize)
1910 continue;
1911
1912 de::MovePtr<tcu::TestCaseGroup> sizeGroup (new tcu::TestCaseGroup(testCtx, ("size_" + de::toString(size)).c_str(), ""));
1913
1914 for (size_t opNdx = 0; opNdx < DE_LENGTH_OF_ARRAY(ops); opNdx++)
1915 {
1916 const Op op = ops[opNdx].op;
1917
1918 // implicit_unmap ignores allocationSize
1919 if (((allocationSize == 0) && (op != OP_IMPLICIT_UNMAP)) ||
1920 ((allocationSize != 0) && (op == OP_IMPLICIT_UNMAP)))
1921 continue;
1922
1923 const deUint32 seed = (deUint32)(opNdx * allocationSizeNdx);
1924 const char* const name = ops[opNdx].name;
1925 const TestConfig config = subMappedConfig(allocationSize, MemoryRange(offset, size), op, seed, static_cast<AllocationKind>(allocationKindNdx));
1926
1927 addFunctionCase(sizeGroup.get(), name, name, checkSupport, testMemoryMapping, config);
1928 }
1929
1930 offsetGroup->addChild(sizeGroup.release());
1931 }
1932
1933 allocationSizeGroup->addChild(offsetGroup.release());
1934 }
1935
1936 subGroup->addChild(allocationSizeGroup.release());
1937 }
1938
1939 sets[allocationKindNdx]->addChild(subGroup.release());
1940 }
1941
1942 // .random
1943 {
1944 de::MovePtr<tcu::TestCaseGroup> randomGroup (new tcu::TestCaseGroup(testCtx, "random", "Random memory mapping tests."));
1945 de::Random rng (3927960301u);
1946
1947 for (size_t ndx = 0; ndx < 100; ndx++)
1948 {
1949 const deUint32 seed = rng.getUint32();
1950 const std::string name = de::toString(ndx);
1951
1952 randomGroup->addChild(new InstanceFactory1<RandomMemoryMappingInstance, deUint32>(testCtx, tcu::NODETYPE_SELF_VALIDATE, de::toString(ndx), "Random case", seed));
1953 }
1954
1955 sets[static_cast<deUint32>(ALLOCATION_KIND_SUBALLOCATED)]->addChild(randomGroup.release());
1956 }
1957
1958 group->addChild(sets[0].release());
1959 dedicated->addChild(sets[1].release());
1960 dedicated->addChild(sets[2].release());
1961 group->addChild(dedicated.release());
1962
1963 return group.release();
1964 }
1965
1966 } // memory
1967 } // vkt
1968