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