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/SubAllocator.h"
18
19 #include "Resources.h"
20 #include "VkEncoder.h"
21
22 #include <log/log.h>
23
24 #include <set>
25
26 using android::base::SubAllocator;
27
28 namespace goldfish_vk {
29
canFitVirtualHostVisibleMemoryInfo(const VkPhysicalDeviceMemoryProperties * memoryProperties)30 bool canFitVirtualHostVisibleMemoryInfo(
31 const VkPhysicalDeviceMemoryProperties* memoryProperties) {
32 uint32_t typeCount =
33 memoryProperties->memoryTypeCount;
34 uint32_t heapCount =
35 memoryProperties->memoryHeapCount;
36
37 bool canFit = true;
38
39 if (typeCount == VK_MAX_MEMORY_TYPES) {
40 canFit = false;
41 ALOGE("Underlying device has no free memory types");
42 }
43
44 if (heapCount == VK_MAX_MEMORY_HEAPS) {
45 canFit = false;
46 ALOGE("Underlying device has no free memory heaps");
47 }
48
49 uint32_t numFreeMemoryTypes = VK_MAX_MEMORY_TYPES - typeCount;
50 uint32_t hostVisibleMemoryTypeCount = 0;
51
52 if (hostVisibleMemoryTypeCount > numFreeMemoryTypes) {
53 ALOGE("Underlying device has too many host visible memory types (%u)"
54 "and not enough free types (%u)",
55 hostVisibleMemoryTypeCount, numFreeMemoryTypes);
56 canFit = false;
57 }
58
59 return canFit;
60 }
61
initHostVisibleMemoryVirtualizationInfo(VkPhysicalDevice physicalDevice,const VkPhysicalDeviceMemoryProperties * memoryProperties,bool hasDirectMem,HostVisibleMemoryVirtualizationInfo * info_out)62 void initHostVisibleMemoryVirtualizationInfo(
63 VkPhysicalDevice physicalDevice,
64 const VkPhysicalDeviceMemoryProperties* memoryProperties,
65 bool hasDirectMem,
66 HostVisibleMemoryVirtualizationInfo* info_out) {
67
68 if (info_out->initialized) return;
69
70 info_out->hostMemoryProperties = *memoryProperties;
71 info_out->initialized = true;
72
73 info_out->memoryPropertiesSupported =
74 canFitVirtualHostVisibleMemoryInfo(memoryProperties);
75
76 info_out->directMemSupported = hasDirectMem;
77
78 if (!info_out->memoryPropertiesSupported ||
79 !info_out->directMemSupported) {
80 info_out->virtualizationSupported = false;
81 return;
82 }
83
84 info_out->virtualizationSupported = true;
85
86 info_out->physicalDevice = physicalDevice;
87 info_out->guestMemoryProperties = *memoryProperties;
88
89 uint32_t typeCount =
90 memoryProperties->memoryTypeCount;
91 uint32_t heapCount =
92 memoryProperties->memoryHeapCount;
93
94 uint32_t firstFreeTypeIndex = typeCount;
95 uint32_t firstFreeHeapIndex = heapCount;
96
97 for (uint32_t i = 0; i < typeCount; ++i) {
98
99 // Set up identity mapping and not-both
100 // by default, to be edited later.
101 info_out->memoryTypeIndexMappingToHost[i] = i;
102 info_out->memoryHeapIndexMappingToHost[i] = i;
103
104 info_out->memoryTypeIndexMappingFromHost[i] = i;
105 info_out->memoryHeapIndexMappingFromHost[i] = i;
106
107 info_out->memoryTypeBitsShouldAdvertiseBoth[i] = false;
108
109 const auto& type = memoryProperties->memoryTypes[i];
110
111 if (type.propertyFlags & VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT) {
112 uint32_t heapIndex = type.heapIndex;
113
114 auto& guestMemoryType =
115 info_out->guestMemoryProperties.memoryTypes[i];
116
117 auto& newVirtualMemoryType =
118 info_out->guestMemoryProperties.memoryTypes[firstFreeTypeIndex];
119
120 auto& newVirtualMemoryHeap =
121 info_out->guestMemoryProperties.memoryHeaps[firstFreeHeapIndex];
122
123 // Remove all references to host visible in the guest memory type at
124 // index i, while transferring them to the new virtual memory type.
125 newVirtualMemoryType = type;
126
127 // Set this memory type to have a separate heap.
128 newVirtualMemoryType.heapIndex = firstFreeHeapIndex;
129
130 newVirtualMemoryType.propertyFlags =
131 type.propertyFlags &
132 ~(VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT);
133
134 guestMemoryType.propertyFlags =
135 type.propertyFlags & \
136 ~(VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT |
137 VK_MEMORY_PROPERTY_HOST_COHERENT_BIT |
138 VK_MEMORY_PROPERTY_HOST_CACHED_BIT);
139
140 // In the corresponding new memory heap, copy the information over,
141 // remove device local flags, and resize it based on what is
142 // supported by the PCI device.
143 newVirtualMemoryHeap =
144 memoryProperties->memoryHeaps[heapIndex];
145 newVirtualMemoryHeap.flags =
146 newVirtualMemoryHeap.flags &
147 ~(VK_MEMORY_HEAP_DEVICE_LOCAL_BIT);
148
149 // TODO: Figure out how to support bigger sizes
150 newVirtualMemoryHeap.size = VIRTUAL_HOST_VISIBLE_HEAP_SIZE;
151
152 info_out->memoryTypeIndexMappingToHost[firstFreeTypeIndex] = i;
153 info_out->memoryHeapIndexMappingToHost[firstFreeHeapIndex] = i;
154
155 info_out->memoryTypeIndexMappingFromHost[i] = firstFreeTypeIndex;
156 info_out->memoryHeapIndexMappingFromHost[i] = firstFreeHeapIndex;
157
158 // Was the original memory type also a device local type? If so,
159 // advertise both types in resulting type bits.
160 info_out->memoryTypeBitsShouldAdvertiseBoth[i] =
161 type.propertyFlags & VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT;
162
163 ++firstFreeTypeIndex;
164
165 // Explicitly only create one new heap.
166 // ++firstFreeHeapIndex;
167 }
168 }
169
170 info_out->guestMemoryProperties.memoryTypeCount = firstFreeTypeIndex;
171 info_out->guestMemoryProperties.memoryHeapCount = firstFreeHeapIndex + 1;
172
173 for (uint32_t i = info_out->guestMemoryProperties.memoryTypeCount; i < VK_MAX_MEMORY_TYPES; ++i) {
174 memset(&info_out->guestMemoryProperties.memoryTypes[i],
175 0x0, sizeof(VkMemoryType));
176 }
177 }
178
isHostVisibleMemoryTypeIndexForGuest(const HostVisibleMemoryVirtualizationInfo * info,uint32_t index)179 bool isHostVisibleMemoryTypeIndexForGuest(
180 const HostVisibleMemoryVirtualizationInfo* info,
181 uint32_t index) {
182
183 const auto& props =
184 info->virtualizationSupported ?
185 info->guestMemoryProperties :
186 info->hostMemoryProperties;
187
188 return props.memoryTypes[index].propertyFlags & VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT;
189 }
190
isDeviceLocalMemoryTypeIndexForGuest(const HostVisibleMemoryVirtualizationInfo * info,uint32_t index)191 bool isDeviceLocalMemoryTypeIndexForGuest(
192 const HostVisibleMemoryVirtualizationInfo* info,
193 uint32_t index) {
194
195 const auto& props =
196 info->virtualizationSupported ?
197 info->guestMemoryProperties :
198 info->hostMemoryProperties;
199
200 return props.memoryTypes[index].propertyFlags & VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT;
201 }
202
isNoFlagsMemoryTypeIndexForGuest(const HostVisibleMemoryVirtualizationInfo * info,uint32_t index)203 bool isNoFlagsMemoryTypeIndexForGuest(
204 const HostVisibleMemoryVirtualizationInfo* info,
205 uint32_t index) {
206 const auto& props =
207 info->virtualizationSupported ?
208 info->guestMemoryProperties :
209 info->hostMemoryProperties;
210 return props.memoryTypes[index].propertyFlags == 0;
211 }
212
finishHostMemAllocInit(VkEncoder *,VkDevice device,uint32_t memoryTypeIndex,VkDeviceSize nonCoherentAtomSize,VkDeviceSize allocSize,VkDeviceSize mappedSize,uint8_t * mappedPtr,HostMemAlloc * out)213 VkResult finishHostMemAllocInit(
214 VkEncoder*,
215 VkDevice device,
216 uint32_t memoryTypeIndex,
217 VkDeviceSize nonCoherentAtomSize,
218 VkDeviceSize allocSize,
219 VkDeviceSize mappedSize,
220 uint8_t* mappedPtr,
221 HostMemAlloc* out) {
222
223 out->device = device;
224 out->memoryTypeIndex = memoryTypeIndex;
225 out->nonCoherentAtomSize = nonCoherentAtomSize;
226 out->allocSize = allocSize;
227 out->mappedSize = mappedSize;
228 out->mappedPtr = mappedPtr;
229
230 // because it's not just nonCoherentAtomSize granularity,
231 // people will also use it for uniform buffers, images, etc.
232 // that need some bigger alignment
233 #define HIGHEST_BUFFER_OR_IMAGE_ALIGNMENT 1024
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(VkEncoder * enc,VkDevice device,HostMemAlloc * toDestroy)252 void destroyHostMemAlloc(
253 VkEncoder* enc,
254 VkDevice device,
255 HostMemAlloc* toDestroy) {
256
257 if (toDestroy->initResult != VK_SUCCESS) return;
258 if (!toDestroy->initialized) return;
259
260 enc->vkFreeMemory(device, toDestroy->memory, nullptr);
261 delete toDestroy->subAlloc;
262 }
263
subAllocHostMemory(HostMemAlloc * alloc,const VkMemoryAllocateInfo * pAllocateInfo,SubAlloc * out)264 void subAllocHostMemory(
265 HostMemAlloc* alloc,
266 const VkMemoryAllocateInfo* pAllocateInfo,
267 SubAlloc* out) {
268
269 VkDeviceSize mappedSize =
270 alloc->nonCoherentAtomSize * (
271 (pAllocateInfo->allocationSize +
272 alloc->nonCoherentAtomSize - 1) /
273 alloc->nonCoherentAtomSize);
274
275 ALOGV("%s: alloc size %u mapped size %u ncaSize %u\n", __func__,
276 (unsigned int)pAllocateInfo->allocationSize,
277 (unsigned int)mappedSize,
278 (unsigned int)alloc->nonCoherentAtomSize);
279
280 void* subMapped = alloc->subAlloc->alloc(mappedSize);
281 out->mappedPtr = (uint8_t*)subMapped;
282
283 out->subAllocSize = pAllocateInfo->allocationSize;
284 out->subMappedSize = mappedSize;
285
286 out->baseMemory = alloc->memory;
287 out->baseOffset = alloc->subAlloc->getOffset(subMapped);
288
289 out->subMemory = new_from_host_VkDeviceMemory(VK_NULL_HANDLE);
290 out->subAlloc = alloc->subAlloc;
291 }
292
subFreeHostMemory(SubAlloc * toFree)293 void subFreeHostMemory(SubAlloc* toFree) {
294 delete_goldfish_VkDeviceMemory(toFree->subMemory);
295 toFree->subAlloc->free(toFree->mappedPtr);
296 memset(toFree, 0x0, sizeof(SubAlloc));
297 }
298
canSubAlloc(android::base::SubAllocator * subAlloc,VkDeviceSize size)299 bool canSubAlloc(android::base::SubAllocator* subAlloc, VkDeviceSize size) {
300 auto ptr = subAlloc->alloc(size);
301 if (!ptr) return false;
302 subAlloc->free(ptr);
303 return true;
304 }
305
306 } // namespace goldfish_vk
307