blob: a5b02f863739d6033b439bbbe55829a4e50cc567 [file] [log] [blame]
// Copyright (C) 2018 The Android Open Source Project
// Copyright (C) 2018 Google Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#include "HostVisibleMemoryVirtualization.h"
#include "android/base/AndroidSubAllocator.h"
#include "Resources.h"
#include "VkEncoder.h"
#include "../OpenglSystemCommon/EmulatorFeatureInfo.h"
#include <log/log.h>
#include <set>
using android::base::guest::SubAllocator;
namespace goldfish_vk {
bool canFitVirtualHostVisibleMemoryInfo(
const VkPhysicalDeviceMemoryProperties* memoryProperties) {
uint32_t typeCount =
memoryProperties->memoryTypeCount;
uint32_t heapCount =
memoryProperties->memoryHeapCount;
bool canFit = true;
if (typeCount == VK_MAX_MEMORY_TYPES) {
canFit = false;
ALOGE("Underlying device has no free memory types");
}
if (heapCount == VK_MAX_MEMORY_HEAPS) {
canFit = false;
ALOGE("Underlying device has no free memory heaps");
}
uint32_t numFreeMemoryTypes = VK_MAX_MEMORY_TYPES - typeCount;
uint32_t hostVisibleMemoryTypeCount = 0;
if (hostVisibleMemoryTypeCount > numFreeMemoryTypes) {
ALOGE("Underlying device has too many host visible memory types (%u)"
"and not enough free types (%u)",
hostVisibleMemoryTypeCount, numFreeMemoryTypes);
canFit = false;
}
return canFit;
}
void initHostVisibleMemoryVirtualizationInfo(
VkPhysicalDevice physicalDevice,
const VkPhysicalDeviceMemoryProperties* memoryProperties,
const EmulatorFeatureInfo* featureInfo,
HostVisibleMemoryVirtualizationInfo* info_out) {
if (info_out->initialized) return;
info_out->hostMemoryProperties = *memoryProperties;
info_out->initialized = true;
info_out->memoryPropertiesSupported =
canFitVirtualHostVisibleMemoryInfo(memoryProperties);
info_out->directMemSupported = featureInfo->hasDirectMem;
info_out->virtioGpuNextSupported = featureInfo->hasVirtioGpuNext;
if (!info_out->memoryPropertiesSupported ||
(!info_out->directMemSupported &&
!info_out->virtioGpuNextSupported)) {
info_out->virtualizationSupported = false;
return;
}
info_out->virtualizationSupported = true;
info_out->physicalDevice = physicalDevice;
info_out->guestMemoryProperties = *memoryProperties;
uint32_t typeCount =
memoryProperties->memoryTypeCount;
uint32_t heapCount =
memoryProperties->memoryHeapCount;
uint32_t firstFreeTypeIndex = typeCount;
uint32_t firstFreeHeapIndex = heapCount;
for (uint32_t i = 0; i < typeCount; ++i) {
// Set up identity mapping and not-both
// by default, to be edited later.
info_out->memoryTypeIndexMappingToHost[i] = i;
info_out->memoryHeapIndexMappingToHost[i] = i;
info_out->memoryTypeIndexMappingFromHost[i] = i;
info_out->memoryHeapIndexMappingFromHost[i] = i;
info_out->memoryTypeBitsShouldAdvertiseBoth[i] = false;
const auto& type = memoryProperties->memoryTypes[i];
if (type.propertyFlags & VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT) {
uint32_t heapIndex = type.heapIndex;
auto& guestMemoryType =
info_out->guestMemoryProperties.memoryTypes[i];
auto& newVirtualMemoryType =
info_out->guestMemoryProperties.memoryTypes[firstFreeTypeIndex];
auto& newVirtualMemoryHeap =
info_out->guestMemoryProperties.memoryHeaps[firstFreeHeapIndex];
// Remove all references to host visible in the guest memory type at
// index i, while transferring them to the new virtual memory type.
newVirtualMemoryType = type;
// Set this memory type to have a separate heap.
newVirtualMemoryType.heapIndex = firstFreeHeapIndex;
newVirtualMemoryType.propertyFlags =
type.propertyFlags &
~(VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT);
guestMemoryType.propertyFlags =
type.propertyFlags & \
~(VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT |
VK_MEMORY_PROPERTY_HOST_COHERENT_BIT |
VK_MEMORY_PROPERTY_HOST_CACHED_BIT);
// In the corresponding new memory heap, copy the information over,
// remove device local flags, and resize it based on what is
// supported by the PCI device.
newVirtualMemoryHeap =
memoryProperties->memoryHeaps[heapIndex];
newVirtualMemoryHeap.flags =
newVirtualMemoryHeap.flags &
~(VK_MEMORY_HEAP_DEVICE_LOCAL_BIT);
// TODO: Figure out how to support bigger sizes
newVirtualMemoryHeap.size = VIRTUAL_HOST_VISIBLE_HEAP_SIZE;
info_out->memoryTypeIndexMappingToHost[firstFreeTypeIndex] = i;
info_out->memoryHeapIndexMappingToHost[firstFreeHeapIndex] = i;
info_out->memoryTypeIndexMappingFromHost[i] = firstFreeTypeIndex;
info_out->memoryHeapIndexMappingFromHost[i] = firstFreeHeapIndex;
// Was the original memory type also a device local type? If so,
// advertise both types in resulting type bits.
info_out->memoryTypeBitsShouldAdvertiseBoth[i] =
type.propertyFlags & VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT ||
type.propertyFlags == 0;
++firstFreeTypeIndex;
// Explicitly only create one new heap.
// ++firstFreeHeapIndex;
}
}
info_out->guestMemoryProperties.memoryTypeCount = firstFreeTypeIndex;
info_out->guestMemoryProperties.memoryHeapCount = firstFreeHeapIndex + 1;
for (uint32_t i = info_out->guestMemoryProperties.memoryTypeCount; i < VK_MAX_MEMORY_TYPES; ++i) {
memset(&info_out->guestMemoryProperties.memoryTypes[i],
0x0, sizeof(VkMemoryType));
}
}
bool isHostVisibleMemoryTypeIndexForGuest(
const HostVisibleMemoryVirtualizationInfo* info,
uint32_t index) {
const auto& props =
info->virtualizationSupported ?
info->guestMemoryProperties :
info->hostMemoryProperties;
return props.memoryTypes[index].propertyFlags & VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT;
}
bool isDeviceLocalMemoryTypeIndexForGuest(
const HostVisibleMemoryVirtualizationInfo* info,
uint32_t index) {
const auto& props =
info->virtualizationSupported ?
info->guestMemoryProperties :
info->hostMemoryProperties;
return props.memoryTypes[index].propertyFlags & VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT;
}
VkResult finishHostMemAllocInit(
VkEncoder*,
VkDevice device,
uint32_t memoryTypeIndex,
VkDeviceSize nonCoherentAtomSize,
VkDeviceSize allocSize,
VkDeviceSize mappedSize,
uint8_t* mappedPtr,
HostMemAlloc* out) {
out->device = device;
out->memoryTypeIndex = memoryTypeIndex;
out->nonCoherentAtomSize = nonCoherentAtomSize;
out->allocSize = allocSize;
out->mappedSize = mappedSize;
out->mappedPtr = mappedPtr;
// because it's not just nonCoherentAtomSize granularity,
// people will also use it for uniform buffers, images, etc.
// that need some bigger alignment
// #define HIGHEST_BUFFER_OR_IMAGE_ALIGNMENT 1024
// bug: 145153816
// HACK: Make it 65k so yuv images are happy on vk cts 1.2.1
// TODO: Use a munmap/mmap MAP_FIXED scheme to realign memories
// if it's found that the buffer or image bind alignment will be violated
#define HIGHEST_BUFFER_OR_IMAGE_ALIGNMENT 65536
uint64_t neededPageSize = out->nonCoherentAtomSize;
if (HIGHEST_BUFFER_OR_IMAGE_ALIGNMENT >
neededPageSize) {
neededPageSize = HIGHEST_BUFFER_OR_IMAGE_ALIGNMENT;
}
out->subAlloc = new
SubAllocator(
out->mappedPtr,
out->mappedSize,
neededPageSize);
out->initialized = true;
out->initResult = VK_SUCCESS;
return VK_SUCCESS;
}
void destroyHostMemAlloc(
bool freeMemorySyncSupported,
VkEncoder* enc,
VkDevice device,
HostMemAlloc* toDestroy) {
if (toDestroy->initResult != VK_SUCCESS) return;
if (!toDestroy->initialized) return;
if (freeMemorySyncSupported) {
enc->vkFreeMemorySyncGOOGLE(device, toDestroy->memory, nullptr);
} else {
enc->vkFreeMemory(device, toDestroy->memory, nullptr);
}
delete toDestroy->subAlloc;
}
void subAllocHostMemory(
HostMemAlloc* alloc,
const VkMemoryAllocateInfo* pAllocateInfo,
SubAlloc* out) {
VkDeviceSize mappedSize =
alloc->nonCoherentAtomSize * (
(pAllocateInfo->allocationSize +
alloc->nonCoherentAtomSize - 1) /
alloc->nonCoherentAtomSize);
ALOGV("%s: alloc size %u mapped size %u ncaSize %u\n", __func__,
(unsigned int)pAllocateInfo->allocationSize,
(unsigned int)mappedSize,
(unsigned int)alloc->nonCoherentAtomSize);
void* subMapped = alloc->subAlloc->alloc(mappedSize);
out->mappedPtr = (uint8_t*)subMapped;
out->subAllocSize = pAllocateInfo->allocationSize;
out->subMappedSize = mappedSize;
out->baseMemory = alloc->memory;
out->baseOffset = alloc->subAlloc->getOffset(subMapped);
out->subMemory = new_from_host_VkDeviceMemory(VK_NULL_HANDLE);
out->subAlloc = alloc->subAlloc;
}
void subFreeHostMemory(SubAlloc* toFree) {
delete_goldfish_VkDeviceMemory(toFree->subMemory);
toFree->subAlloc->free(toFree->mappedPtr);
memset(toFree, 0x0, sizeof(SubAlloc));
}
bool canSubAlloc(android::base::guest::SubAllocator* subAlloc, VkDeviceSize size) {
auto ptr = subAlloc->alloc(size);
if (!ptr) return false;
subAlloc->free(ptr);
return true;
}
bool isNoFlagsMemoryTypeIndexForGuest(
const HostVisibleMemoryVirtualizationInfo* info,
uint32_t index) {
const auto& props =
info->virtualizationSupported ?
info->guestMemoryProperties :
info->hostMemoryProperties;
return props.memoryTypes[index].propertyFlags == 0;
}
} // namespace goldfish_vk