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