| // 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 "ResourceTracker.h" |
| |
| #include "Resources.h" |
| #include "VkEncoder.h" |
| |
| #include "android/base/AlignedBuf.h" |
| #include "android/base/synchronization/AndroidLock.h" |
| |
| #include <unordered_map> |
| |
| #include <log/log.h> |
| |
| #define RESOURCE_TRACKER_DEBUG 0 |
| |
| #if RESOURCE_TRACKER_DEBUG |
| #define D(fmt,...) ALOGD("%s: " fmt, __func__, ##__VA_ARGS__); |
| #else |
| #ifndef D |
| #define D(fmt,...) |
| #endif |
| #endif |
| |
| using android::aligned_buf_alloc; |
| using android::aligned_buf_free; |
| using android::base::guest::AutoLock; |
| using android::base::guest::Lock; |
| |
| namespace goldfish_vk { |
| |
| #define MAKE_HANDLE_MAPPING_FOREACH(type_name, map_impl, map_to_u64_impl, map_from_u64_impl) \ |
| void mapHandles_##type_name(type_name* handles, size_t count) override { \ |
| for (size_t i = 0; i < count; ++i) { \ |
| map_impl; \ |
| } \ |
| } \ |
| void mapHandles_##type_name##_u64(const type_name* handles, uint64_t* handle_u64s, size_t count) override { \ |
| for (size_t i = 0; i < count; ++i) { \ |
| map_to_u64_impl; \ |
| } \ |
| } \ |
| void mapHandles_u64_##type_name(const uint64_t* handle_u64s, type_name* handles, size_t count) override { \ |
| for (size_t i = 0; i < count; ++i) { \ |
| map_from_u64_impl; \ |
| } \ |
| } \ |
| |
| #define DEFINE_RESOURCE_TRACKING_CLASS(class_name, impl) \ |
| class class_name : public VulkanHandleMapping { \ |
| public: \ |
| virtual ~class_name() { } \ |
| GOLDFISH_VK_LIST_HANDLE_TYPES(impl) \ |
| }; \ |
| |
| #define CREATE_MAPPING_IMPL_FOR_TYPE(type_name) \ |
| MAKE_HANDLE_MAPPING_FOREACH(type_name, \ |
| handles[i] = new_from_host_##type_name(handles[i]); ResourceTracker::get()->register_##type_name(handles[i]);, \ |
| handle_u64s[i] = (uint64_t)(uintptr_t)new_from_host_##type_name(handles[i]), \ |
| handles[i] = (type_name)(uintptr_t)new_from_host_u64_##type_name(handle_u64s[i]); ResourceTracker::get()->register_##type_name(handles[i]);) |
| |
| #define UNWRAP_MAPPING_IMPL_FOR_TYPE(type_name) \ |
| MAKE_HANDLE_MAPPING_FOREACH(type_name, \ |
| handles[i] = get_host_##type_name(handles[i]), \ |
| handle_u64s[i] = (uint64_t)(uintptr_t)get_host_u64_##type_name(handles[i]), \ |
| handles[i] = (type_name)(uintptr_t)get_host_##type_name((type_name)(uintptr_t)handle_u64s[i])) |
| |
| #define DESTROY_MAPPING_IMPL_FOR_TYPE(type_name) \ |
| MAKE_HANDLE_MAPPING_FOREACH(type_name, \ |
| ResourceTracker::get()->unregister_##type_name(handles[i]); delete_goldfish_##type_name(handles[i]), \ |
| (void)handle_u64s[i]; delete_goldfish_##type_name(handles[i]), \ |
| (void)handles[i]; delete_goldfish_##type_name((type_name)(uintptr_t)handle_u64s[i])) |
| |
| DEFINE_RESOURCE_TRACKING_CLASS(CreateMapping, CREATE_MAPPING_IMPL_FOR_TYPE) |
| DEFINE_RESOURCE_TRACKING_CLASS(UnwrapMapping, UNWRAP_MAPPING_IMPL_FOR_TYPE) |
| DEFINE_RESOURCE_TRACKING_CLASS(DestroyMapping, DESTROY_MAPPING_IMPL_FOR_TYPE) |
| |
| class ResourceTracker::Impl { |
| public: |
| Impl() = default; |
| CreateMapping createMapping; |
| UnwrapMapping unwrapMapping; |
| DestroyMapping destroyMapping; |
| DefaultHandleMapping defaultMapping; |
| |
| #define HANDLE_DEFINE_TRIVIAL_INFO_STRUCT(type) \ |
| struct type##_Info { \ |
| uint32_t unused; \ |
| }; \ |
| |
| GOLDFISH_VK_LIST_TRIVIAL_HANDLE_TYPES(HANDLE_DEFINE_TRIVIAL_INFO_STRUCT) |
| |
| struct VkDevice_Info { |
| VkPhysicalDevice physdev; |
| VkPhysicalDeviceProperties props; |
| VkPhysicalDeviceMemoryProperties memProps; |
| }; |
| |
| struct VkDeviceMemory_Info { |
| VkDeviceSize allocationSize; |
| uint32_t memoryTypeIndex; |
| }; |
| |
| #define HANDLE_REGISTER_IMPL_IMPL(type) \ |
| std::unordered_map<type, type##_Info> info_##type; \ |
| void register_##type(type obj) { \ |
| AutoLock lock(mLock); \ |
| info_##type[obj] = type##_Info(); \ |
| } \ |
| void unregister_##type(type obj) { \ |
| AutoLock lock(mLock); \ |
| info_##type.erase(obj); \ |
| } \ |
| |
| GOLDFISH_VK_LIST_HANDLE_TYPES(HANDLE_REGISTER_IMPL_IMPL) |
| |
| void setDeviceInfo(VkDevice device, |
| VkPhysicalDevice physdev, |
| VkPhysicalDeviceProperties props, |
| VkPhysicalDeviceMemoryProperties memProps) { |
| AutoLock lock(mLock); |
| auto& info = info_VkDevice[device]; |
| info.physdev = physdev; |
| info.props = props; |
| info.memProps = memProps; |
| } |
| |
| bool isMemoryTypeHostVisible(VkDevice device, uint32_t typeIndex) const { |
| AutoLock lock(mLock); |
| const auto it = info_VkDevice.find(device); |
| |
| if (it == info_VkDevice.end()) return false; |
| |
| const auto& info = it->second; |
| return info.memProps.memoryTypes[typeIndex].propertyFlags & |
| VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT; |
| } |
| |
| VkDeviceSize getNonCoherentExtendedSize(VkDevice device, VkDeviceSize basicSize) const { |
| AutoLock lock(mLock); |
| const auto it = info_VkDevice.find(device); |
| if (it == info_VkDevice.end()) return basicSize; |
| const auto& info = it->second; |
| |
| VkDeviceSize nonCoherentAtomSize = |
| info.props.limits.nonCoherentAtomSize; |
| VkDeviceSize atoms = |
| (basicSize + nonCoherentAtomSize - 1) / nonCoherentAtomSize; |
| return atoms * nonCoherentAtomSize; |
| } |
| |
| VkResult on_vkCreateDevice( |
| void* context, |
| VkResult input_result, |
| VkPhysicalDevice physicalDevice, |
| const VkDeviceCreateInfo*, |
| const VkAllocationCallbacks*, |
| VkDevice* pDevice) { |
| |
| if (input_result != VK_SUCCESS) return input_result; |
| |
| VkEncoder* enc = (VkEncoder*)context; |
| |
| VkPhysicalDeviceProperties props; |
| VkPhysicalDeviceMemoryProperties memProps; |
| enc->vkGetPhysicalDeviceProperties(physicalDevice, &props); |
| enc->vkGetPhysicalDeviceMemoryProperties(physicalDevice, &memProps); |
| |
| setDeviceInfo(*pDevice, physicalDevice, props, memProps); |
| |
| return input_result; |
| } |
| |
| VkResult on_vkAllocateMemory( |
| void*, |
| VkResult input_result, |
| VkDevice device, |
| const VkMemoryAllocateInfo* pAllocateInfo, |
| const VkAllocationCallbacks*, |
| VkDeviceMemory* pMemory) { |
| |
| if (input_result != VK_SUCCESS) return input_result; |
| |
| // Assumes pMemory has already been allocated. |
| goldfish_VkDeviceMemory* mem = as_goldfish_VkDeviceMemory(*pMemory); |
| VkDeviceSize size = pAllocateInfo->allocationSize; |
| |
| // assume memory is not host visible. |
| mem->ptr = nullptr; |
| mem->size = size; |
| mem->mappedSize = getNonCoherentExtendedSize(device, size); |
| |
| if (!isMemoryTypeHostVisible(device, pAllocateInfo->memoryTypeIndex)) { |
| return input_result; |
| } |
| |
| // This is a strict alignment; we do not expect any |
| // actual device to have more stringent requirements |
| // than this. |
| mem->ptr = (uint8_t*)aligned_buf_alloc(4096, mem->mappedSize); |
| D("host visible alloc: size 0x%llx host ptr %p mapped size 0x%llx", |
| (unsigned long long)size, mem->ptr, |
| (unsigned long long)mem->mappedSize); |
| |
| return input_result; |
| } |
| |
| private: |
| mutable Lock mLock; |
| |
| }; |
| ResourceTracker::ResourceTracker() : mImpl(new ResourceTracker::Impl()) { } |
| ResourceTracker::~ResourceTracker() { } |
| VulkanHandleMapping* ResourceTracker::createMapping() { |
| return &mImpl->createMapping; |
| } |
| VulkanHandleMapping* ResourceTracker::unwrapMapping() { |
| return &mImpl->unwrapMapping; |
| } |
| VulkanHandleMapping* ResourceTracker::destroyMapping() { |
| return &mImpl->destroyMapping; |
| } |
| VulkanHandleMapping* ResourceTracker::defaultMapping() { |
| return &mImpl->defaultMapping; |
| } |
| static ResourceTracker* sTracker = nullptr; |
| // static |
| ResourceTracker* ResourceTracker::get() { |
| if (!sTracker) { |
| // To be initialized once on vulkan device open. |
| sTracker = new ResourceTracker; |
| } |
| return sTracker; |
| } |
| |
| #define HANDLE_REGISTER_IMPL(type) \ |
| void ResourceTracker::register_##type(type obj) { \ |
| mImpl->register_##type(obj); \ |
| } \ |
| void ResourceTracker::unregister_##type(type obj) { \ |
| mImpl->unregister_##type(obj); \ |
| } \ |
| |
| GOLDFISH_VK_LIST_HANDLE_TYPES(HANDLE_REGISTER_IMPL) |
| |
| void ResourceTracker::setDeviceInfo( |
| VkDevice device, |
| VkPhysicalDevice physdev, |
| VkPhysicalDeviceProperties props, |
| VkPhysicalDeviceMemoryProperties memProps) { |
| mImpl->setDeviceInfo(device, physdev, props, memProps); |
| } |
| |
| bool ResourceTracker::isMemoryTypeHostVisible( |
| VkDevice device, uint32_t typeIndex) const { |
| return mImpl->isMemoryTypeHostVisible(device, typeIndex); |
| } |
| |
| VkDeviceSize ResourceTracker::getNonCoherentExtendedSize(VkDevice device, VkDeviceSize basicSize) const { |
| return mImpl->getNonCoherentExtendedSize(device, basicSize); |
| } |
| |
| VkResult ResourceTracker::on_vkCreateDevice( |
| void* context, |
| VkResult input_result, |
| VkPhysicalDevice physicalDevice, |
| const VkDeviceCreateInfo* pCreateInfo, |
| const VkAllocationCallbacks* pAllocator, |
| VkDevice* pDevice) { |
| return mImpl->on_vkCreateDevice( |
| context, input_result, physicalDevice, pCreateInfo, pAllocator, pDevice); |
| } |
| |
| VkResult ResourceTracker::on_vkAllocateMemory( |
| void* context, |
| VkResult input_result, |
| VkDevice device, |
| const VkMemoryAllocateInfo* pAllocateInfo, |
| const VkAllocationCallbacks* pAllocator, |
| VkDeviceMemory* pMemory) { |
| return mImpl->on_vkAllocateMemory( |
| context, input_result, device, pAllocateInfo, pAllocator, pMemory); |
| } |
| |
| } // namespace goldfish_vk |