xref: /aosp_15_r20/external/deqp/external/vulkancts/modules/vulkan/memory/vktMemoryMappingTests.cpp (revision 35238bce31c2a825756842865a792f8cf7f89930)
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