• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2023 The Android Open Source Project
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  */
16 
17 #include "GraphicsDetectorVkExternalMemoryHost.h"
18 
19 #include "Vulkan.h"
20 
21 #if defined(__linux__)
22 #include <sys/mman.h>
23 #include <syscall.h>
24 #include <unistd.h>
25 #endif
26 
27 namespace gfxstream {
28 namespace {
29 
30 #if defined(__linux__)
31 class ScopedFd {
32   public:
ScopedFd(int fd)33     explicit ScopedFd(int fd) { Reset(fd); }
~ScopedFd()34     ~ScopedFd() { Reset(); }
35 
36     ScopedFd(const ScopedFd&) = delete;
37     ScopedFd& operator=(const ScopedFd&) = delete;
38 
ScopedFd(ScopedFd && rhs)39     ScopedFd(ScopedFd&& rhs) {
40         Reset(rhs.Release());
41         rhs.mFd = -1;
42     }
43 
operator =(ScopedFd && rhs)44     ScopedFd& operator=(ScopedFd&& rhs) {
45         Reset(rhs.Release());
46         return *this;
47     }
48 
Get() const49     int Get() const {
50         return mFd;
51     }
52 
Release()53     int Release() {
54         int localFd = mFd;
55         mFd = -1;
56         return localFd;
57     }
58 
Map(size_t size)59     void* Map(size_t size) {
60         if (mFd == -1) {
61             return nullptr;
62         }
63 
64         mMappedAddr = mmap(nullptr, size,  PROT_WRITE | PROT_READ, MAP_SHARED, mFd, 0);
65         if (mMappedAddr == nullptr) {
66             return nullptr;
67         }
68 
69         mMappedSize = size;
70         return mMappedAddr;
71     }
72 
Unmap()73     void Unmap() {
74         if (mMappedAddr != nullptr) {
75             munmap(mMappedAddr, mMappedSize);
76             mMappedAddr = nullptr;
77             mMappedSize = 0;
78         }
79     }
80 
81   private:
Reset(int newFd=-1)82     void Reset(int newFd = -1) {
83         if (mFd != -1) {
84             //Unmap();
85             close(mFd);
86         }
87         mFd = newFd;
88     }
89 
90     int mFd = -1;
91     void* mMappedAddr = nullptr;
92     size_t mMappedSize = 0;
93 };
94 
CreateSharedMemory(vkhpp::DeviceSize size)95 gfxstream::expected<ScopedFd, std::string> CreateSharedMemory(vkhpp::DeviceSize size) {
96     int fd = -1;
97 
98 #if !defined(ANDROID)
99     fd = memfd_create("unused-name", MFD_CLOEXEC);
100 #elif defined(__NR_memfd_create)
101     // Android host toolchain using a really old glibc ?_?
102     fd = syscall(__NR_memfd_create, "unused-name", MFD_CLOEXEC);
103 #else
104     return gfxstream::unexpected("Failed to create shared memory: memfd_create unavailable.");
105 #endif
106 
107     if (fd == -1) {
108         return gfxstream::unexpected("Failed to create shared memory: " +
109                                      std::string(strerror(errno)));
110     }
111     if (ftruncate(fd, static_cast<off_t>(size))) {
112         const std::string error = std::string(strerror(errno));
113         close(fd);
114         return gfxstream::unexpected("Failed to resize shared memory: " + error);
115     }
116     return ScopedFd(fd);
117 }
118 
CheckImportingSharedMemory(vkhpp::PhysicalDevice & physicalDevice)119 gfxstream::expected<Ok, std::string> CheckImportingSharedMemory(
120         vkhpp::PhysicalDevice& physicalDevice) {
121     const vkhpp::PhysicalDeviceMemoryProperties physicalDeviceMemoryProperties =
122         physicalDevice.getMemoryProperties();
123 
124     const auto properties2 =
125         physicalDevice
126             .getProperties2<vkhpp::PhysicalDeviceProperties2,  //
127                             vkhpp::PhysicalDeviceExternalMemoryHostPropertiesEXT>();
128 
129     const auto& physicalDeviceExternalMemoryHostProperties =
130         properties2.get<vkhpp::PhysicalDeviceExternalMemoryHostPropertiesEXT>();
131 
132     const float queuePriority = 1.0f;
133     const vkhpp::DeviceQueueCreateInfo deviceQueueCreateInfo = {
134         .queueFamilyIndex = 0,
135         .queueCount = 1,
136         .pQueuePriorities = &queuePriority,
137     };
138     const std::vector<const char*> requestedDeviceExtensions = {
139         VK_EXT_EXTERNAL_MEMORY_HOST_EXTENSION_NAME,
140     };
141     const vkhpp::DeviceCreateInfo deviceCreateInfo = {
142         .queueCreateInfoCount = 1,
143         .pQueueCreateInfos = &deviceQueueCreateInfo,
144         .enabledExtensionCount = static_cast<uint32_t>(requestedDeviceExtensions.size()),
145         .ppEnabledExtensionNames = requestedDeviceExtensions.data(),
146     };
147     auto device = VK_EXPECT_RV_OR_STRING(physicalDevice.createDeviceUnique(deviceCreateInfo));
148 
149     constexpr const vkhpp::DeviceSize kSize = 16384;
150 
151     auto shm = GFXSTREAM_EXPECT(CreateSharedMemory(kSize));
152 
153     void* mappedShm = shm.Map(kSize);
154     if (mappedShm == MAP_FAILED) {
155         return gfxstream::unexpected("Failed to mmap shared memory: " +
156                                      std::string(strerror(errno)));
157     }
158 
159     const vkhpp::MemoryHostPointerPropertiesEXT memoryHostPointerProps =
160         VK_EXPECT_RV_OR_STRING(device->getMemoryHostPointerPropertiesEXT(
161             vkhpp::ExternalMemoryHandleTypeFlagBits::eHostAllocationEXT, mappedShm));
162 
163     uint32_t memoryTypeIndex = -1;
164     for (uint32_t i = 0; i < physicalDeviceMemoryProperties.memoryTypeCount; i++) {
165         if (memoryHostPointerProps.memoryTypeBits & (1 << i)) {
166             memoryTypeIndex = i;
167             break;
168         }
169     }
170     if (memoryTypeIndex == -1) {
171         return gfxstream::unexpected("Failed to find memory type compatible with shm.");
172     }
173 
174     const vkhpp::ImportMemoryHostPointerInfoEXT memoryHostPointerInto = {
175         .handleType = vkhpp::ExternalMemoryHandleTypeFlagBits::eHostAllocationEXT,
176         .pHostPointer = mappedShm,
177     };
178 
179     const vkhpp::MemoryAllocateInfo memoryAllocateInfo = {
180         .pNext = &memoryHostPointerInto,
181         .allocationSize = kSize,
182         .memoryTypeIndex = memoryTypeIndex,
183     };
184 
185     auto memory = VK_EXPECT_RV_OR_STRING(device->allocateMemoryUnique(memoryAllocateInfo));
186     return Ok{};
187 }
188 #endif  // defined(__linux__)
189 
PopulateVulkanExternalMemoryHostQuirkImpl(::gfxstream::proto::GraphicsAvailability * availability)190 gfxstream::expected<Ok, vkhpp::Result> PopulateVulkanExternalMemoryHostQuirkImpl(
191         ::gfxstream::proto::GraphicsAvailability* availability) {
192     auto vk = GFXSTREAM_EXPECT(Vk::Load());
193 
194     ::gfxstream::proto::VulkanAvailability* vulkanAvailability = availability->mutable_vulkan();
195 
196     auto physicalDevices = VK_EXPECT_RV(vk.instance().enumeratePhysicalDevices());
197     for (uint32_t i = 0; i < physicalDevices.size(); i++) {
198         auto& physicalDevice = physicalDevices[i];
199         auto* outPhysicalDevice = vulkanAvailability->mutable_physical_devices(i);
200 
201         const auto exts = VK_EXPECT_RV(physicalDevice.enumerateDeviceExtensionProperties());
202 
203         std::unordered_set<std::string> extensions;
204         for (const auto& ext : exts) {
205             extensions.insert(std::string(ext.extensionName));
206         }
207         if (extensions.find(VK_EXT_EXTERNAL_MEMORY_HOST_EXTENSION_NAME) == extensions.end()) {
208             continue;
209         }
210 
211         auto* quirks = outPhysicalDevice->mutable_quirks()
212                                         ->mutable_external_memory_host_quirks();
213 
214 #if defined(__linux__)
215         const auto shmResult = CheckImportingSharedMemory(physicalDevice);
216         if (shmResult.ok()) {
217             quirks->set_can_import_shm(true);
218         } else {
219             quirks->add_errors("can_import_shm error: " + shmResult.error());
220             quirks->set_can_import_shm(false);
221         }
222 #endif  // defined(__linux__)
223     }
224 
225     return Ok{};
226 }
227 
228 }  // namespace
229 
PopulateVulkanExternalMemoryHostQuirk(::gfxstream::proto::GraphicsAvailability * availability)230 gfxstream::expected<Ok, std::string> PopulateVulkanExternalMemoryHostQuirk(
231         ::gfxstream::proto::GraphicsAvailability* availability) {
232     return PopulateVulkanExternalMemoryHostQuirkImpl(availability)
233         .transform_error([](vkhpp::Result r) { return vkhpp::to_string(r); });
234 }
235 
236 }  // namespace gfxstream
237