• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 #ifdef ANDROID
29 #include <unistd.h>
30 #include <errno.h>
31 #endif
32 #include <sys/mman.h>
33 
34 using android::base::guest::SubAllocator;
35 
36 namespace goldfish_vk {
37 
canFitVirtualHostVisibleMemoryInfo(const VkPhysicalDeviceMemoryProperties * memoryProperties)38 bool canFitVirtualHostVisibleMemoryInfo(
39     const VkPhysicalDeviceMemoryProperties* memoryProperties) {
40     uint32_t typeCount =
41         memoryProperties->memoryTypeCount;
42     uint32_t heapCount =
43         memoryProperties->memoryHeapCount;
44 
45     bool canFit = true;
46 
47     if (typeCount == VK_MAX_MEMORY_TYPES) {
48         canFit = false;
49         ALOGE("Underlying device has no free memory types");
50     }
51 
52     if (heapCount == VK_MAX_MEMORY_HEAPS) {
53         canFit = false;
54         ALOGE("Underlying device has no free memory heaps");
55     }
56 
57     uint32_t numFreeMemoryTypes = VK_MAX_MEMORY_TYPES - typeCount;
58     uint32_t hostVisibleMemoryTypeCount = 0;
59 
60     if (hostVisibleMemoryTypeCount > numFreeMemoryTypes) {
61         ALOGE("Underlying device has too many host visible memory types (%u)"
62               "and not enough free types (%u)",
63               hostVisibleMemoryTypeCount, numFreeMemoryTypes);
64         canFit = false;
65     }
66 
67     return canFit;
68 }
69 
initHostVisibleMemoryVirtualizationInfo(VkPhysicalDevice physicalDevice,const VkPhysicalDeviceMemoryProperties * memoryProperties,const EmulatorFeatureInfo * featureInfo,HostVisibleMemoryVirtualizationInfo * info_out)70 void initHostVisibleMemoryVirtualizationInfo(
71     VkPhysicalDevice physicalDevice,
72     const VkPhysicalDeviceMemoryProperties* memoryProperties,
73     const EmulatorFeatureInfo* featureInfo,
74     HostVisibleMemoryVirtualizationInfo* info_out) {
75 
76     if (info_out->initialized) return;
77 
78     info_out->hostMemoryProperties = *memoryProperties;
79     info_out->initialized = true;
80 
81     info_out->memoryPropertiesSupported =
82         canFitVirtualHostVisibleMemoryInfo(memoryProperties);
83 
84     info_out->directMemSupported = featureInfo->hasDirectMem;
85     info_out->virtioGpuNextSupported = featureInfo->hasVirtioGpuNext;
86 
87     if (!info_out->memoryPropertiesSupported ||
88         (!info_out->directMemSupported &&
89          !info_out->virtioGpuNextSupported)) {
90         info_out->virtualizationSupported = false;
91         return;
92     }
93 
94     info_out->virtualizationSupported = true;
95 
96     info_out->physicalDevice = physicalDevice;
97     info_out->guestMemoryProperties = *memoryProperties;
98 
99     uint32_t typeCount =
100         memoryProperties->memoryTypeCount;
101     uint32_t heapCount =
102         memoryProperties->memoryHeapCount;
103 
104     uint32_t firstFreeTypeIndex = typeCount;
105     uint32_t firstFreeHeapIndex = heapCount;
106 
107     for (uint32_t i = 0; i < typeCount; ++i) {
108 
109         // Set up identity mapping and not-both
110         // by default, to be edited later.
111         info_out->memoryTypeIndexMappingToHost[i] = i;
112         info_out->memoryHeapIndexMappingToHost[i] = i;
113 
114         info_out->memoryTypeIndexMappingFromHost[i] = i;
115         info_out->memoryHeapIndexMappingFromHost[i] = i;
116 
117         info_out->memoryTypeBitsShouldAdvertiseBoth[i] = false;
118 
119         const auto& type = memoryProperties->memoryTypes[i];
120 
121         if (type.propertyFlags & VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT) {
122             uint32_t heapIndex = type.heapIndex;
123 
124             auto& guestMemoryType =
125                 info_out->guestMemoryProperties.memoryTypes[i];
126 
127             auto& newVirtualMemoryType =
128                 info_out->guestMemoryProperties.memoryTypes[firstFreeTypeIndex];
129 
130             auto& newVirtualMemoryHeap =
131                 info_out->guestMemoryProperties.memoryHeaps[firstFreeHeapIndex];
132 
133             // Remove all references to host visible in the guest memory type at
134             // index i, while transferring them to the new virtual memory type.
135             newVirtualMemoryType = type;
136 
137             // Set this memory type to have a separate heap.
138             newVirtualMemoryType.heapIndex = firstFreeHeapIndex;
139 
140             newVirtualMemoryType.propertyFlags =
141                 type.propertyFlags &
142                 ~(VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT);
143 
144             guestMemoryType.propertyFlags =
145                 type.propertyFlags & \
146                 ~(VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT |
147                   VK_MEMORY_PROPERTY_HOST_COHERENT_BIT |
148                   VK_MEMORY_PROPERTY_HOST_CACHED_BIT);
149 
150             // In the corresponding new memory heap, copy the information over,
151             // remove device local flags, and resize it based on what is
152             // supported by the PCI device.
153             newVirtualMemoryHeap =
154                 memoryProperties->memoryHeaps[heapIndex];
155             newVirtualMemoryHeap.flags =
156                 newVirtualMemoryHeap.flags &
157                 ~(VK_MEMORY_HEAP_DEVICE_LOCAL_BIT);
158 
159             // TODO: Figure out how to support bigger sizes
160             newVirtualMemoryHeap.size = VIRTUAL_HOST_VISIBLE_HEAP_SIZE;
161 
162             info_out->memoryTypeIndexMappingToHost[firstFreeTypeIndex] = i;
163             info_out->memoryHeapIndexMappingToHost[firstFreeHeapIndex] = i;
164 
165             info_out->memoryTypeIndexMappingFromHost[i] = firstFreeTypeIndex;
166             info_out->memoryHeapIndexMappingFromHost[i] = firstFreeHeapIndex;
167 
168             // Was the original memory type also a device local type? If so,
169             // advertise both types in resulting type bits.
170             info_out->memoryTypeBitsShouldAdvertiseBoth[i] =
171                 type.propertyFlags & VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT ||
172                 type.propertyFlags == 0;
173 
174             ++firstFreeTypeIndex;
175 
176             // Explicitly only create one new heap.
177             // ++firstFreeHeapIndex;
178         }
179     }
180 
181     info_out->guestMemoryProperties.memoryTypeCount = firstFreeTypeIndex;
182     info_out->guestMemoryProperties.memoryHeapCount = firstFreeHeapIndex + 1;
183 
184     for (uint32_t i = info_out->guestMemoryProperties.memoryTypeCount; i < VK_MAX_MEMORY_TYPES; ++i) {
185         memset(&info_out->guestMemoryProperties.memoryTypes[i],
186                0x0, sizeof(VkMemoryType));
187     }
188 }
189 
isHostVisibleMemoryTypeIndexForGuest(const HostVisibleMemoryVirtualizationInfo * info,uint32_t index)190 bool isHostVisibleMemoryTypeIndexForGuest(
191     const HostVisibleMemoryVirtualizationInfo* info,
192     uint32_t index) {
193 
194     const auto& props =
195         info->virtualizationSupported ?
196         info->guestMemoryProperties :
197         info->hostMemoryProperties;
198 
199     return props.memoryTypes[index].propertyFlags & VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT;
200 }
201 
isDeviceLocalMemoryTypeIndexForGuest(const HostVisibleMemoryVirtualizationInfo * info,uint32_t index)202 bool isDeviceLocalMemoryTypeIndexForGuest(
203     const HostVisibleMemoryVirtualizationInfo* info,
204     uint32_t index) {
205 
206     const auto& props =
207         info->virtualizationSupported ?
208         info->guestMemoryProperties :
209         info->hostMemoryProperties;
210 
211     return props.memoryTypes[index].propertyFlags & VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT;
212 }
213 
finishHostMemAllocInit(VkEncoder *,VkDevice device,uint32_t memoryTypeIndex,VkDeviceSize nonCoherentAtomSize,VkDeviceSize allocSize,VkDeviceSize mappedSize,uint8_t * mappedPtr,HostMemAlloc * out)214 VkResult finishHostMemAllocInit(
215     VkEncoder*,
216     VkDevice device,
217     uint32_t memoryTypeIndex,
218     VkDeviceSize nonCoherentAtomSize,
219     VkDeviceSize allocSize,
220     VkDeviceSize mappedSize,
221     uint8_t* mappedPtr,
222     HostMemAlloc* out) {
223 
224     out->device = device;
225     out->memoryTypeIndex = memoryTypeIndex;
226     out->nonCoherentAtomSize = nonCoherentAtomSize;
227     out->allocSize = allocSize;
228     out->mappedSize = mappedSize;
229     out->mappedPtr = mappedPtr;
230 
231     // because it's not just nonCoherentAtomSize granularity,
232     // people will also use it for uniform buffers, images, etc.
233     // that need some bigger alignment
234 // #define HIGHEST_BUFFER_OR_IMAGE_ALIGNMENT 1024
235 // bug: 145153816
236 // HACK: Make it 65k so yuv images are happy on vk cts 1.2.1
237 // TODO: Use a munmap/mmap MAP_FIXED scheme to realign memories
238 // if it's found that the buffer or image bind alignment will be violated
239 #define HIGHEST_BUFFER_OR_IMAGE_ALIGNMENT 65536
240 
241     uint64_t neededPageSize = out->nonCoherentAtomSize;
242     if (HIGHEST_BUFFER_OR_IMAGE_ALIGNMENT >
243         neededPageSize) {
244         neededPageSize = HIGHEST_BUFFER_OR_IMAGE_ALIGNMENT;
245     }
246 
247     out->subAlloc = new
248         SubAllocator(
249             out->mappedPtr,
250             out->mappedSize,
251             neededPageSize);
252 
253     out->initialized = true;
254     out->initResult = VK_SUCCESS;
255     return VK_SUCCESS;
256 }
257 
destroyHostMemAlloc(bool freeMemorySyncSupported,VkEncoder * enc,VkDevice device,HostMemAlloc * toDestroy)258 void destroyHostMemAlloc(
259     bool freeMemorySyncSupported,
260     VkEncoder* enc,
261     VkDevice device,
262     HostMemAlloc* toDestroy) {
263 
264     if (toDestroy->initResult != VK_SUCCESS) return;
265     if (!toDestroy->initialized) return;
266 
267 #ifdef ANDROID
268     if (toDestroy->fd > 0) {
269 
270         if (toDestroy->memoryAddr) {
271             int ret = munmap((void*)toDestroy->memoryAddr, toDestroy->memorySize);
272             ALOGE("%s: trying to unmap addr = 0x%" PRIx64", size = %d, ret = %d, errno = %d\n", __func__, toDestroy->memoryAddr, (int32_t)toDestroy->memorySize, ret, errno);
273         }
274 
275         ALOGE("%s: trying to close fd = %d\n", __func__, toDestroy->fd);
276         int ret = close(toDestroy->fd);
277         if (ret != 0) {
278             ALOGE("%s: fail to close fd = %d, ret = %d, errno = %d\n", __func__, toDestroy->fd, ret, errno);
279         } else {
280             ALOGE("%s: successfully close fd = %d, ret = %d\n", __func__, toDestroy->fd, ret);
281         }
282     }
283 #endif
284 
285     if (freeMemorySyncSupported) {
286         enc->vkFreeMemorySyncGOOGLE(device, toDestroy->memory, nullptr, false /* no lock */);
287     } else {
288         enc->vkFreeMemory(device, toDestroy->memory, nullptr, false /* no lock */);
289     }
290 
291     delete toDestroy->subAlloc;
292 }
293 
subAllocHostMemory(HostMemAlloc * alloc,const VkMemoryAllocateInfo * pAllocateInfo,SubAlloc * out)294 void subAllocHostMemory(
295     HostMemAlloc* alloc,
296     const VkMemoryAllocateInfo* pAllocateInfo,
297     SubAlloc* out) {
298 
299     VkDeviceSize mappedSize =
300         alloc->nonCoherentAtomSize * (
301             (pAllocateInfo->allocationSize +
302              alloc->nonCoherentAtomSize - 1) /
303             alloc->nonCoherentAtomSize);
304 
305     ALOGV("%s: alloc size %u mapped size %u ncaSize %u\n", __func__,
306             (unsigned int)pAllocateInfo->allocationSize,
307             (unsigned int)mappedSize,
308             (unsigned int)alloc->nonCoherentAtomSize);
309 
310     void* subMapped = alloc->subAlloc->alloc(mappedSize);
311     out->mappedPtr = (uint8_t*)subMapped;
312 
313     out->subAllocSize = pAllocateInfo->allocationSize;
314     out->subMappedSize = mappedSize;
315 
316     out->baseMemory = alloc->memory;
317     out->baseOffset = alloc->subAlloc->getOffset(subMapped);
318 
319     out->subMemory = new_from_host_VkDeviceMemory(VK_NULL_HANDLE);
320     out->subAlloc = alloc->subAlloc;
321 }
322 
subFreeHostMemory(SubAlloc * toFree)323 void subFreeHostMemory(SubAlloc* toFree) {
324     delete_goldfish_VkDeviceMemory(toFree->subMemory);
325     toFree->subAlloc->free(toFree->mappedPtr);
326     memset(toFree, 0x0, sizeof(SubAlloc));
327 }
328 
canSubAlloc(android::base::guest::SubAllocator * subAlloc,VkDeviceSize size)329 bool canSubAlloc(android::base::guest::SubAllocator* subAlloc, VkDeviceSize size) {
330     auto ptr = subAlloc->alloc(size);
331     if (!ptr) return false;
332     subAlloc->free(ptr);
333     return true;
334 }
335 
isNoFlagsMemoryTypeIndexForGuest(const HostVisibleMemoryVirtualizationInfo * info,uint32_t index)336 bool isNoFlagsMemoryTypeIndexForGuest(
337     const HostVisibleMemoryVirtualizationInfo* info,
338     uint32_t index) {
339     const auto& props =
340         info->virtualizationSupported ?
341         info->guestMemoryProperties :
342         info->hostMemoryProperties;
343     return props.memoryTypes[index].propertyFlags == 0;
344 }
345 
346 
347 } // namespace goldfish_vk
348