/* * Copyright (C) 2023 The Android Open Source Project * * 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 "GraphicsDetectorVkExternalMemoryHost.h" #include "Vulkan.h" #if defined(__linux__) #include #include #include #endif namespace gfxstream { namespace { #if defined(__linux__) class ScopedFd { public: explicit ScopedFd(int fd) { Reset(fd); } ~ScopedFd() { Reset(); } ScopedFd(const ScopedFd&) = delete; ScopedFd& operator=(const ScopedFd&) = delete; ScopedFd(ScopedFd&& rhs) { Reset(rhs.Release()); rhs.mFd = -1; } ScopedFd& operator=(ScopedFd&& rhs) { Reset(rhs.Release()); return *this; } int Get() const { return mFd; } int Release() { int localFd = mFd; mFd = -1; return localFd; } void* Map(size_t size) { if (mFd == -1) { return nullptr; } mMappedAddr = mmap(nullptr, size, PROT_WRITE | PROT_READ, MAP_SHARED, mFd, 0); if (mMappedAddr == nullptr) { return nullptr; } mMappedSize = size; return mMappedAddr; } void Unmap() { if (mMappedAddr != nullptr) { munmap(mMappedAddr, mMappedSize); mMappedAddr = nullptr; mMappedSize = 0; } } private: void Reset(int newFd = -1) { if (mFd != -1) { //Unmap(); close(mFd); } mFd = newFd; } int mFd = -1; void* mMappedAddr = nullptr; size_t mMappedSize = 0; }; gfxstream::expected CreateSharedMemory(vkhpp::DeviceSize size) { int fd = -1; #if !defined(ANDROID) fd = memfd_create("unused-name", MFD_CLOEXEC); #elif defined(__NR_memfd_create) // Android host toolchain using a really old glibc ?_? fd = syscall(__NR_memfd_create, "unused-name", MFD_CLOEXEC); #else return gfxstream::unexpected("Failed to create shared memory: memfd_create unavailable."); #endif if (fd == -1) { return gfxstream::unexpected("Failed to create shared memory: " + std::string(strerror(errno))); } if (ftruncate(fd, static_cast(size))) { const std::string error = std::string(strerror(errno)); close(fd); return gfxstream::unexpected("Failed to resize shared memory: " + error); } return ScopedFd(fd); } gfxstream::expected CheckImportingSharedMemory( vkhpp::PhysicalDevice& physicalDevice) { const vkhpp::PhysicalDeviceMemoryProperties physicalDeviceMemoryProperties = physicalDevice.getMemoryProperties(); const auto properties2 = physicalDevice .getProperties2(); const auto& physicalDeviceExternalMemoryHostProperties = properties2.get(); const float queuePriority = 1.0f; const vkhpp::DeviceQueueCreateInfo deviceQueueCreateInfo = { .queueFamilyIndex = 0, .queueCount = 1, .pQueuePriorities = &queuePriority, }; const std::vector requestedDeviceExtensions = { VK_EXT_EXTERNAL_MEMORY_HOST_EXTENSION_NAME, }; const vkhpp::DeviceCreateInfo deviceCreateInfo = { .queueCreateInfoCount = 1, .pQueueCreateInfos = &deviceQueueCreateInfo, .enabledExtensionCount = static_cast(requestedDeviceExtensions.size()), .ppEnabledExtensionNames = requestedDeviceExtensions.data(), }; auto device = VK_EXPECT_RV_OR_STRING(physicalDevice.createDeviceUnique(deviceCreateInfo)); constexpr const vkhpp::DeviceSize kSize = 16384; auto shm = GFXSTREAM_EXPECT(CreateSharedMemory(kSize)); void* mappedShm = shm.Map(kSize); if (mappedShm == MAP_FAILED) { return gfxstream::unexpected("Failed to mmap shared memory: " + std::string(strerror(errno))); } const vkhpp::MemoryHostPointerPropertiesEXT memoryHostPointerProps = VK_EXPECT_RV_OR_STRING(device->getMemoryHostPointerPropertiesEXT( vkhpp::ExternalMemoryHandleTypeFlagBits::eHostAllocationEXT, mappedShm)); uint32_t memoryTypeIndex = -1; for (uint32_t i = 0; i < physicalDeviceMemoryProperties.memoryTypeCount; i++) { if (memoryHostPointerProps.memoryTypeBits & (1 << i)) { memoryTypeIndex = i; break; } } if (memoryTypeIndex == -1) { return gfxstream::unexpected("Failed to find memory type compatible with shm."); } const vkhpp::ImportMemoryHostPointerInfoEXT memoryHostPointerInto = { .handleType = vkhpp::ExternalMemoryHandleTypeFlagBits::eHostAllocationEXT, .pHostPointer = mappedShm, }; const vkhpp::MemoryAllocateInfo memoryAllocateInfo = { .pNext = &memoryHostPointerInto, .allocationSize = kSize, .memoryTypeIndex = memoryTypeIndex, }; auto memory = VK_EXPECT_RV_OR_STRING(device->allocateMemoryUnique(memoryAllocateInfo)); return Ok{}; } #endif // defined(__linux__) gfxstream::expected PopulateVulkanExternalMemoryHostQuirkImpl( ::gfxstream::proto::GraphicsAvailability* availability) { auto vk = GFXSTREAM_EXPECT(Vk::Load()); ::gfxstream::proto::VulkanAvailability* vulkanAvailability = availability->mutable_vulkan(); auto physicalDevices = VK_EXPECT_RV(vk.instance().enumeratePhysicalDevices()); for (uint32_t i = 0; i < physicalDevices.size(); i++) { auto& physicalDevice = physicalDevices[i]; auto* outPhysicalDevice = vulkanAvailability->mutable_physical_devices(i); const auto exts = VK_EXPECT_RV(physicalDevice.enumerateDeviceExtensionProperties()); std::unordered_set extensions; for (const auto& ext : exts) { extensions.insert(std::string(ext.extensionName)); } if (extensions.find(VK_EXT_EXTERNAL_MEMORY_HOST_EXTENSION_NAME) == extensions.end()) { continue; } auto* quirks = outPhysicalDevice->mutable_quirks() ->mutable_external_memory_host_quirks(); #if defined(__linux__) const auto shmResult = CheckImportingSharedMemory(physicalDevice); if (shmResult.ok()) { quirks->set_can_import_shm(true); } else { quirks->add_errors("can_import_shm error: " + shmResult.error()); quirks->set_can_import_shm(false); } #endif // defined(__linux__) } return Ok{}; } } // namespace gfxstream::expected PopulateVulkanExternalMemoryHostQuirk( ::gfxstream::proto::GraphicsAvailability* availability) { return PopulateVulkanExternalMemoryHostQuirkImpl(availability) .transform_error([](vkhpp::Result r) { return vkhpp::to_string(r); }); } } // namespace gfxstream