1 // Copyright (C) 2018 The Android Open Source Project
2 // Copyright (C) 2018 Google Inc.
3 //
4 // Licensed under the Apache License, Version 2.0 (the "License");
5 // you may not use this file except in compliance with the License.
6 // You may obtain a copy of the License at
7 //
8 // http://www.apache.org/licenses/LICENSE-2.0
9 //
10 // Unless required by applicable law or agreed to in writing, software
11 // distributed under the License is distributed on an "AS IS" BASIS,
12 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 // See the License for the specific language governing permissions and
14 // limitations under the License.
15 #include "HostVisibleMemoryVirtualization.h"
16 
17 #include "android/base/AndroidSubAllocator.h"
18 
19 #include "Resources.h"
20 #include "VkEncoder.h"
21 
22 #include "../OpenglSystemCommon/EmulatorFeatureInfo.h"
23 
24 #include <log/log.h>
25 
26 #include <set>
27 
28 using android::base::guest::SubAllocator;
29 
30 namespace goldfish_vk {
31 
canFitVirtualHostVisibleMemoryInfo(const VkPhysicalDeviceMemoryProperties * memoryProperties)32 bool canFitVirtualHostVisibleMemoryInfo(
33     const VkPhysicalDeviceMemoryProperties* memoryProperties) {
34     uint32_t typeCount =
35         memoryProperties->memoryTypeCount;
36     uint32_t heapCount =
37         memoryProperties->memoryHeapCount;
38 
39     bool canFit = true;
40 
41     if (typeCount == VK_MAX_MEMORY_TYPES) {
42         canFit = false;
43         ALOGE("Underlying device has no free memory types");
44     }
45 
46     if (heapCount == VK_MAX_MEMORY_HEAPS) {
47         canFit = false;
48         ALOGE("Underlying device has no free memory heaps");
49     }
50 
51     uint32_t numFreeMemoryTypes = VK_MAX_MEMORY_TYPES - typeCount;
52     uint32_t hostVisibleMemoryTypeCount = 0;
53 
54     if (hostVisibleMemoryTypeCount > numFreeMemoryTypes) {
55         ALOGE("Underlying device has too many host visible memory types (%u)"
56               "and not enough free types (%u)",
57               hostVisibleMemoryTypeCount, numFreeMemoryTypes);
58         canFit = false;
59     }
60 
61     return canFit;
62 }
63 
initHostVisibleMemoryVirtualizationInfo(VkPhysicalDevice physicalDevice,const VkPhysicalDeviceMemoryProperties * memoryProperties,const EmulatorFeatureInfo * featureInfo,HostVisibleMemoryVirtualizationInfo * info_out)64 void initHostVisibleMemoryVirtualizationInfo(
65     VkPhysicalDevice physicalDevice,
66     const VkPhysicalDeviceMemoryProperties* memoryProperties,
67     const EmulatorFeatureInfo* featureInfo,
68     HostVisibleMemoryVirtualizationInfo* info_out) {
69 
70     if (info_out->initialized) return;
71 
72     info_out->hostMemoryProperties = *memoryProperties;
73     info_out->initialized = true;
74 
75     info_out->memoryPropertiesSupported =
76         canFitVirtualHostVisibleMemoryInfo(memoryProperties);
77 
78     info_out->directMemSupported = featureInfo->hasDirectMem;
79     info_out->virtioGpuNextSupported = featureInfo->hasVirtioGpuNext;
80 
81     if (!info_out->memoryPropertiesSupported ||
82         (!info_out->directMemSupported &&
83          !info_out->virtioGpuNextSupported)) {
84         info_out->virtualizationSupported = false;
85         return;
86     }
87 
88     info_out->virtualizationSupported = true;
89 
90     info_out->physicalDevice = physicalDevice;
91     info_out->guestMemoryProperties = *memoryProperties;
92 
93     uint32_t typeCount =
94         memoryProperties->memoryTypeCount;
95     uint32_t heapCount =
96         memoryProperties->memoryHeapCount;
97 
98     uint32_t firstFreeTypeIndex = typeCount;
99     uint32_t firstFreeHeapIndex = heapCount;
100 
101     for (uint32_t i = 0; i < typeCount; ++i) {
102 
103         // Set up identity mapping and not-both
104         // by default, to be edited later.
105         info_out->memoryTypeIndexMappingToHost[i] = i;
106         info_out->memoryHeapIndexMappingToHost[i] = i;
107 
108         info_out->memoryTypeIndexMappingFromHost[i] = i;
109         info_out->memoryHeapIndexMappingFromHost[i] = i;
110 
111         info_out->memoryTypeBitsShouldAdvertiseBoth[i] = false;
112 
113         const auto& type = memoryProperties->memoryTypes[i];
114 
115         if (type.propertyFlags & VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT) {
116             uint32_t heapIndex = type.heapIndex;
117 
118             auto& guestMemoryType =
119                 info_out->guestMemoryProperties.memoryTypes[i];
120 
121             auto& newVirtualMemoryType =
122                 info_out->guestMemoryProperties.memoryTypes[firstFreeTypeIndex];
123 
124             auto& newVirtualMemoryHeap =
125                 info_out->guestMemoryProperties.memoryHeaps[firstFreeHeapIndex];
126 
127             // Remove all references to host visible in the guest memory type at
128             // index i, while transferring them to the new virtual memory type.
129             newVirtualMemoryType = type;
130 
131             // Set this memory type to have a separate heap.
132             newVirtualMemoryType.heapIndex = firstFreeHeapIndex;
133 
134             newVirtualMemoryType.propertyFlags =
135                 type.propertyFlags &
136                 ~(VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT);
137 
138             guestMemoryType.propertyFlags =
139                 type.propertyFlags & \
140                 ~(VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT |
141                   VK_MEMORY_PROPERTY_HOST_COHERENT_BIT |
142                   VK_MEMORY_PROPERTY_HOST_CACHED_BIT);
143 
144             // In the corresponding new memory heap, copy the information over,
145             // remove device local flags, and resize it based on what is
146             // supported by the PCI device.
147             newVirtualMemoryHeap =
148                 memoryProperties->memoryHeaps[heapIndex];
149             newVirtualMemoryHeap.flags =
150                 newVirtualMemoryHeap.flags &
151                 ~(VK_MEMORY_HEAP_DEVICE_LOCAL_BIT);
152 
153             // TODO: Figure out how to support bigger sizes
154             newVirtualMemoryHeap.size = VIRTUAL_HOST_VISIBLE_HEAP_SIZE;
155 
156             info_out->memoryTypeIndexMappingToHost[firstFreeTypeIndex] = i;
157             info_out->memoryHeapIndexMappingToHost[firstFreeHeapIndex] = i;
158 
159             info_out->memoryTypeIndexMappingFromHost[i] = firstFreeTypeIndex;
160             info_out->memoryHeapIndexMappingFromHost[i] = firstFreeHeapIndex;
161 
162             // Was the original memory type also a device local type? If so,
163             // advertise both types in resulting type bits.
164             info_out->memoryTypeBitsShouldAdvertiseBoth[i] =
165                 type.propertyFlags & VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT ||
166                 type.propertyFlags == 0;
167 
168             ++firstFreeTypeIndex;
169 
170             // Explicitly only create one new heap.
171             // ++firstFreeHeapIndex;
172         }
173     }
174 
175     info_out->guestMemoryProperties.memoryTypeCount = firstFreeTypeIndex;
176     info_out->guestMemoryProperties.memoryHeapCount = firstFreeHeapIndex + 1;
177 
178     for (uint32_t i = info_out->guestMemoryProperties.memoryTypeCount; i < VK_MAX_MEMORY_TYPES; ++i) {
179         memset(&info_out->guestMemoryProperties.memoryTypes[i],
180                0x0, sizeof(VkMemoryType));
181     }
182 }
183 
isHostVisibleMemoryTypeIndexForGuest(const HostVisibleMemoryVirtualizationInfo * info,uint32_t index)184 bool isHostVisibleMemoryTypeIndexForGuest(
185     const HostVisibleMemoryVirtualizationInfo* info,
186     uint32_t index) {
187 
188     const auto& props =
189         info->virtualizationSupported ?
190         info->guestMemoryProperties :
191         info->hostMemoryProperties;
192 
193     return props.memoryTypes[index].propertyFlags & VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT;
194 }
195 
isDeviceLocalMemoryTypeIndexForGuest(const HostVisibleMemoryVirtualizationInfo * info,uint32_t index)196 bool isDeviceLocalMemoryTypeIndexForGuest(
197     const HostVisibleMemoryVirtualizationInfo* info,
198     uint32_t index) {
199 
200     const auto& props =
201         info->virtualizationSupported ?
202         info->guestMemoryProperties :
203         info->hostMemoryProperties;
204 
205     return props.memoryTypes[index].propertyFlags & VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT;
206 }
207 
finishHostMemAllocInit(VkEncoder *,VkDevice device,uint32_t memoryTypeIndex,VkDeviceSize nonCoherentAtomSize,VkDeviceSize allocSize,VkDeviceSize mappedSize,uint8_t * mappedPtr,HostMemAlloc * out)208 VkResult finishHostMemAllocInit(
209     VkEncoder*,
210     VkDevice device,
211     uint32_t memoryTypeIndex,
212     VkDeviceSize nonCoherentAtomSize,
213     VkDeviceSize allocSize,
214     VkDeviceSize mappedSize,
215     uint8_t* mappedPtr,
216     HostMemAlloc* out) {
217 
218     out->device = device;
219     out->memoryTypeIndex = memoryTypeIndex;
220     out->nonCoherentAtomSize = nonCoherentAtomSize;
221     out->allocSize = allocSize;
222     out->mappedSize = mappedSize;
223     out->mappedPtr = mappedPtr;
224 
225     // because it's not just nonCoherentAtomSize granularity,
226     // people will also use it for uniform buffers, images, etc.
227     // that need some bigger alignment
228 // #define HIGHEST_BUFFER_OR_IMAGE_ALIGNMENT 1024
229 // bug: 145153816
230 // HACK: Make it 65k so yuv images are happy on vk cts 1.2.1
231 // TODO: Use a munmap/mmap MAP_FIXED scheme to realign memories
232 // if it's found that the buffer or image bind alignment will be violated
233 #define HIGHEST_BUFFER_OR_IMAGE_ALIGNMENT 65536
234 
235     uint64_t neededPageSize = out->nonCoherentAtomSize;
236     if (HIGHEST_BUFFER_OR_IMAGE_ALIGNMENT >
237         neededPageSize) {
238         neededPageSize = HIGHEST_BUFFER_OR_IMAGE_ALIGNMENT;
239     }
240 
241     out->subAlloc = new
242         SubAllocator(
243             out->mappedPtr,
244             out->mappedSize,
245             neededPageSize);
246 
247     out->initialized = true;
248     out->initResult = VK_SUCCESS;
249     return VK_SUCCESS;
250 }
251 
destroyHostMemAlloc(bool freeMemorySyncSupported,VkEncoder * enc,VkDevice device,HostMemAlloc * toDestroy)252 void destroyHostMemAlloc(
253     bool freeMemorySyncSupported,
254     VkEncoder* enc,
255     VkDevice device,
256     HostMemAlloc* toDestroy) {
257 
258     if (toDestroy->initResult != VK_SUCCESS) return;
259     if (!toDestroy->initialized) return;
260 
261     if (freeMemorySyncSupported) {
262         enc->vkFreeMemorySyncGOOGLE(device, toDestroy->memory, nullptr);
263     } else {
264         enc->vkFreeMemory(device, toDestroy->memory, nullptr);
265     }
266 
267     delete toDestroy->subAlloc;
268 }
269 
subAllocHostMemory(HostMemAlloc * alloc,const VkMemoryAllocateInfo * pAllocateInfo,SubAlloc * out)270 void subAllocHostMemory(
271     HostMemAlloc* alloc,
272     const VkMemoryAllocateInfo* pAllocateInfo,
273     SubAlloc* out) {
274 
275     VkDeviceSize mappedSize =
276         alloc->nonCoherentAtomSize * (
277             (pAllocateInfo->allocationSize +
278              alloc->nonCoherentAtomSize - 1) /
279             alloc->nonCoherentAtomSize);
280 
281     ALOGV("%s: alloc size %u mapped size %u ncaSize %u\n", __func__,
282             (unsigned int)pAllocateInfo->allocationSize,
283             (unsigned int)mappedSize,
284             (unsigned int)alloc->nonCoherentAtomSize);
285 
286     void* subMapped = alloc->subAlloc->alloc(mappedSize);
287     out->mappedPtr = (uint8_t*)subMapped;
288 
289     out->subAllocSize = pAllocateInfo->allocationSize;
290     out->subMappedSize = mappedSize;
291 
292     out->baseMemory = alloc->memory;
293     out->baseOffset = alloc->subAlloc->getOffset(subMapped);
294 
295     out->subMemory = new_from_host_VkDeviceMemory(VK_NULL_HANDLE);
296     out->subAlloc = alloc->subAlloc;
297 }
298 
subFreeHostMemory(SubAlloc * toFree)299 void subFreeHostMemory(SubAlloc* toFree) {
300     delete_goldfish_VkDeviceMemory(toFree->subMemory);
301     toFree->subAlloc->free(toFree->mappedPtr);
302     memset(toFree, 0x0, sizeof(SubAlloc));
303 }
304 
canSubAlloc(android::base::guest::SubAllocator * subAlloc,VkDeviceSize size)305 bool canSubAlloc(android::base::guest::SubAllocator* subAlloc, VkDeviceSize size) {
306     auto ptr = subAlloc->alloc(size);
307     if (!ptr) return false;
308     subAlloc->free(ptr);
309     return true;
310 }
311 
isNoFlagsMemoryTypeIndexForGuest(const HostVisibleMemoryVirtualizationInfo * info,uint32_t index)312 bool isNoFlagsMemoryTypeIndexForGuest(
313     const HostVisibleMemoryVirtualizationInfo* info,
314     uint32_t index) {
315     const auto& props =
316         info->virtualizationSupported ?
317         info->guestMemoryProperties :
318         info->hostMemoryProperties;
319     return props.memoryTypes[index].propertyFlags == 0;
320 }
321 
322 
323 } // namespace goldfish_vk
324