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