blob: 5b3fad5ddbc650fa65f1697cccac709dec460876 [file] [log] [blame]
/*
* 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 <sys/mman.h>
#include <syscall.h>
#include <unistd.h>
#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<ScopedFd, std::string> 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<off_t>(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<Ok, std::string> CheckImportingSharedMemory(
vkhpp::PhysicalDevice& physicalDevice) {
const vkhpp::PhysicalDeviceMemoryProperties physicalDeviceMemoryProperties =
physicalDevice.getMemoryProperties();
const auto properties2 =
physicalDevice
.getProperties2<vkhpp::PhysicalDeviceProperties2, //
vkhpp::PhysicalDeviceExternalMemoryHostPropertiesEXT>();
const auto& physicalDeviceExternalMemoryHostProperties =
properties2.get<vkhpp::PhysicalDeviceExternalMemoryHostPropertiesEXT>();
const float queuePriority = 1.0f;
const vkhpp::DeviceQueueCreateInfo deviceQueueCreateInfo = {
.queueFamilyIndex = 0,
.queueCount = 1,
.pQueuePriorities = &queuePriority,
};
const std::vector<const char*> requestedDeviceExtensions = {
VK_EXT_EXTERNAL_MEMORY_HOST_EXTENSION_NAME,
};
const vkhpp::DeviceCreateInfo deviceCreateInfo = {
.queueCreateInfoCount = 1,
.pQueueCreateInfos = &deviceQueueCreateInfo,
.enabledExtensionCount = static_cast<uint32_t>(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<Ok, vkhpp::Result> 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<std::string> 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<Ok, std::string> PopulateVulkanExternalMemoryHostQuirk(
::gfxstream::proto::GraphicsAvailability* availability) {
return PopulateVulkanExternalMemoryHostQuirkImpl(availability)
.transform_error([](vkhpp::Result r) { return vkhpp::to_string(r); });
}
} // namespace gfxstream