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 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 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 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 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 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 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 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 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 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 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