• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright (C) 2018 The Android Open Source Project
2 //
3 // Licensed under the Apache License, Version 2.0 (the "License");
4 // you may not use this file except in compliance with the License.
5 // You may obtain a copy of the License at
6 //
7 // http://www.apache.org/licenses/LICENSE-2.0
8 //
9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS IS" BASIS,
11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 // See the License for the specific language governing permissions and
13 // limitations under the License.
14 
15 #include "VulkanDispatch.h"
16 
17 #include "aemu/base/SharedLibrary.h"
18 #include "aemu/base/files/PathUtils.h"
19 #include "aemu/base/synchronization/Lock.h"
20 #include "aemu/base/system/System.h"
21 #include "host-common/misc.h"
22 
23 using android::base::AutoLock;
24 using android::base::Lock;
25 using android::base::pj;
26 
27 #ifndef VERBOSE
28 #define VERBOSE INFO
29 #endif
30 
31 namespace gfxstream {
32 namespace vk {
33 
icdJsonNameToProgramAndLauncherPaths(const std::string & icdFilename)34 static std::string icdJsonNameToProgramAndLauncherPaths(const std::string& icdFilename) {
35     std::string suffix = pj({"lib64", "vulkan", icdFilename});
36 #if defined(_WIN32)
37     const char* sep = ";";
38 #else
39     const char* sep = ":";
40 #endif
41     return pj({android::base::getProgramDirectory(), suffix}) + sep +
42            pj({android::base::getLauncherDirectory(), suffix});
43 }
44 
setIcdPaths(const std::string & icdFilename)45 static void setIcdPaths(const std::string& icdFilename) {
46     const std::string paths = icdJsonNameToProgramAndLauncherPaths(icdFilename);
47     INFO("Setting ICD filenames for the loader = %s", paths.c_str());
48     // Set both for backwards compatibility
49     android::base::setEnvironmentVariable("VK_DRIVER_FILES", paths);
50     android::base::setEnvironmentVariable("VK_ICD_FILENAMES", paths);
51 }
52 
initIcdPaths(bool forTesting)53 static void initIcdPaths(bool forTesting) {
54     auto androidIcd = android::base::getEnvironmentVariable("ANDROID_EMU_VK_ICD");
55     if (androidIcd == "") {
56         // Rely on user to set VK_DRIVER_FILES
57         return;
58     }
59 
60     if (forTesting) {
61         const char* testingICD = "swiftshader";
62         INFO("%s: In test environment, enforcing %s ICD.", __func__, testingICD);
63         android::base::setEnvironmentVariable("ANDROID_EMU_VK_ICD", testingICD);
64         androidIcd = testingICD;
65     }
66     if (androidIcd == "lavapipe") {
67         INFO("%s: ICD set to 'lavapipe', using Lavapipe ICD", __func__);
68         setIcdPaths("lvp_icd.x86_64.json");
69     } else if (androidIcd == "swiftshader") {
70         INFO("%s: ICD set to 'swiftshader', using Swiftshader ICD", __func__);
71         setIcdPaths("vk_swiftshader_icd.json");
72     } else {
73 #ifdef __APPLE__
74         // Mac: Use MoltenVK by default unless GPU mode is set to swiftshader
75         if (androidIcd != "moltenvk") {
76             WARN("%s: Unknown ICD, resetting to MoltenVK", __func__);
77             android::base::setEnvironmentVariable("ANDROID_EMU_VK_ICD", "moltenvk");
78         }
79         setIcdPaths("MoltenVK_icd.json");
80 
81         // Configure MoltenVK library with environment variables
82         // 0: No logging.
83         // 1: Log errors only.
84         // 2: Log errors and warning messages.
85         // 3: Log errors, warnings and informational messages.
86         // 4: Log errors, warnings, infos and debug messages.
87         const bool verboseLogs =
88             (android::base::getEnvironmentVariable("ANDROID_EMUGL_VERBOSE") == "1");
89         const char* logLevelValue = verboseLogs ? "4" : "1";
90         android::base::setEnvironmentVariable("MVK_CONFIG_LOG_LEVEL", logLevelValue);
91 
92         //  Limit MoltenVK to use single queue, as some older ANGLE versions
93         //  expect this for -guest-angle to work.
94         //  0: Limit Vulkan to a single queue, with no explicit semaphore
95         //  synchronization, and use Metal's implicit guarantees that all operations
96         //  submitted to a queue will give the same result as if they had been run in
97         //  submission order.
98         android::base::setEnvironmentVariable("MVK_CONFIG_VK_SEMAPHORE_SUPPORT_STYLE", "0");
99 
100         // TODO(b/364055067)
101         // MVK_CONFIG_USE_METAL_ARGUMENT_BUFFERS is not working correctly
102         android::base::setEnvironmentVariable("MVK_CONFIG_USE_METAL_ARGUMENT_BUFFERS", "0");
103 
104         // MVK_CONFIG_USE_MTLHEAP is required for VK_EXT_external_memory_metal
105         android::base::setEnvironmentVariable("MVK_CONFIG_USE_MTLHEAP", "1");
106 
107         // TODO(b/351765838): VVL won't work with MoltenVK due to the current
108         //  way of external memory handling, add it into disable list to
109         //  avoid users enabling it implicitly (i.e. via vkconfig).
110         //  It can be enabled with VK_LOADER_LAYERS_ALLOW=VK_LAYER_KHRONOS_validation
111         INFO("Vulkan Validation Layers won't be enabled with MoltenVK");
112         android::base::setEnvironmentVariable("VK_LOADER_LAYERS_DISABLE",
113                                               "VK_LAYER_KHRONOS_validation");
114 #else
115         // By default, on other platforms, just use whatever the system
116         // is packing.
117 #endif
118     }
119 }
120 
121 class SharedLibraries {
122    public:
SharedLibraries(size_t sizeLimit=1)123     explicit SharedLibraries(size_t sizeLimit = 1) : mSizeLimit(sizeLimit) {}
124 
size() const125     size_t size() const { return mLibs.size(); }
126 
addLibrary(const std::string & path)127     bool addLibrary(const std::string& path) {
128         if (size() >= mSizeLimit) {
129             WARN("Cannot add library %s due to size limit(%d)", path.c_str(), mSizeLimit);
130             return false;
131         }
132 
133         auto library = android::base::SharedLibrary::open(path.c_str());
134         if (library) {
135             mLibs.push_back(library);
136             INFO("Added library: %s", path.c_str());
137             return true;
138         } else {
139             // This is expected when searching for a valid library path
140             VERBOSE("Library cannot be added: %s", path.c_str());
141             return false;
142         }
143     }
144 
addFirstAvailableLibrary(const std::vector<std::string> & possiblePaths)145     bool addFirstAvailableLibrary(const std::vector<std::string>& possiblePaths) {
146         for (const std::string& possiblePath : possiblePaths) {
147             if (addLibrary(possiblePath)) {
148                 return true;
149             }
150         }
151         return false;
152     }
153 
154     ~SharedLibraries() = default;
155 
dlsym(const char * name)156     void* dlsym(const char* name) {
157         for (const auto& lib : mLibs) {
158             void* funcPtr = reinterpret_cast<void*>(lib->findSymbol(name));
159             if (funcPtr) {
160                 return funcPtr;
161             }
162         }
163         return nullptr;
164     }
165 
166    private:
167     size_t mSizeLimit;
168     std::vector<android::base::SharedLibrary*> mLibs;
169 };
170 
getVulkanLibraryNumLimits()171 static constexpr size_t getVulkanLibraryNumLimits() {
172     return 1;
173 }
174 
175 class VulkanDispatchImpl {
176    public:
VulkanDispatchImpl()177     VulkanDispatchImpl() : mVulkanLibs(getVulkanLibraryNumLimits()) {}
178 
179     void initialize(bool forTesting);
180 
getPossibleLoaderPathBasenames()181     static std::vector<std::string> getPossibleLoaderPathBasenames() {
182 #if defined(__APPLE__)
183         return std::vector<std::string>{"libvulkan.dylib"};
184 #elif defined(__linux__)
185         return std::vector<std::string>{
186             "libvulkan.so",
187             "libvulkan.so.1",
188         };
189 #elif defined(_WIN32)
190         return std::vector<std::string>{"vulkan-1.dll"};
191 #elif defined(__QNX__)
192         return std::vector<std::string>{
193             "libvulkan.so",
194             "libvulkan.so.1",
195         };
196 #else
197 #error "Unhandled platform in VulkanDispatchImpl."
198 #endif
199     }
200 
getPossibleLoaderPaths()201     std::vector<std::string> getPossibleLoaderPaths() {
202         const std::string explicitPath =
203             android::base::getEnvironmentVariable("ANDROID_EMU_VK_LOADER_PATH");
204         if (!explicitPath.empty()) {
205             return {
206                 explicitPath,
207             };
208         }
209 
210         const std::vector<std::string> possibleBasenames = getPossibleLoaderPathBasenames();
211 
212         const std::string explicitIcd = android::base::getEnvironmentVariable("ANDROID_EMU_VK_ICD");
213 
214 #ifdef _WIN32
215         constexpr const bool isWindows = true;
216 #else
217         constexpr const bool isWindows = false;
218 #endif
219         if (explicitIcd.empty() || isWindows) {
220             return possibleBasenames;
221         }
222 
223         std::vector<std::string> possibleDirectories;
224 
225         if (mForTesting || explicitIcd == "mock") {
226             possibleDirectories = {
227                 pj({android::base::getProgramDirectory(), "testlib64"}),
228                 pj({android::base::getLauncherDirectory(), "testlib64"}),
229             };
230         }
231 
232         possibleDirectories.push_back(
233             pj({android::base::getProgramDirectory(), "lib64", "vulkan"}));
234         possibleDirectories.push_back(
235             pj({android::base::getLauncherDirectory(), "lib64", "vulkan"}));
236 
237         std::vector<std::string> possiblePaths;
238         for (const std::string& possibleDirectory : possibleDirectories) {
239             for (const std::string& possibleBasename : possibleBasenames) {
240                 possiblePaths.push_back(pj({possibleDirectory, possibleBasename}));
241             }
242         }
243         return possiblePaths;
244     }
245 
dlopen()246     void* dlopen() {
247         if (mVulkanLibs.size() == 0) {
248             const std::vector<std::string> possiblePaths = getPossibleLoaderPaths();
249             if (!mVulkanLibs.addFirstAvailableLibrary(possiblePaths)) {
250                 ERR("Cannot add any library for Vulkan loader from the list of %d items",
251                     possiblePaths.size());
252             }
253         }
254         return static_cast<void*>(&mVulkanLibs);
255     }
256 
dlsym(void * lib,const char * name)257     void* dlsym(void* lib, const char* name) {
258         return (void*)((SharedLibraries*)(lib))->dlsym(name);
259     }
260 
dispatch()261     VulkanDispatch* dispatch() { return &mDispatch; }
262 
263    private:
264     Lock mLock;
265     bool mForTesting = false;
266     bool mInitialized = false;
267     VulkanDispatch mDispatch;
268     SharedLibraries mVulkanLibs;
269 };
270 
sVulkanDispatchImpl()271 VulkanDispatchImpl* sVulkanDispatchImpl() {
272     static VulkanDispatchImpl* impl = new VulkanDispatchImpl;
273     return impl;
274 }
275 
sVulkanDispatchDlOpen()276 static void* sVulkanDispatchDlOpen() { return sVulkanDispatchImpl()->dlopen(); }
277 
sVulkanDispatchDlSym(void * lib,const char * sym)278 static void* sVulkanDispatchDlSym(void* lib, const char* sym) {
279     return sVulkanDispatchImpl()->dlsym(lib, sym);
280 }
281 
initialize(bool forTesting)282 void VulkanDispatchImpl::initialize(bool forTesting) {
283     AutoLock lock(mLock);
284 
285     if (mInitialized) {
286         return;
287     }
288 
289     mForTesting = forTesting;
290     initIcdPaths(mForTesting);
291 
292     init_vulkan_dispatch_from_system_loader(sVulkanDispatchDlOpen, sVulkanDispatchDlSym,
293                                             &mDispatch);
294 
295     mInitialized = true;
296 }
297 
vkDispatch(bool forTesting)298 VulkanDispatch* vkDispatch(bool forTesting) {
299     sVulkanDispatchImpl()->initialize(forTesting);
300     return sVulkanDispatchImpl()->dispatch();
301 }
302 
vkDispatchValid(const VulkanDispatch * vk)303 bool vkDispatchValid(const VulkanDispatch* vk) {
304     return vk->vkEnumerateInstanceExtensionProperties != nullptr ||
305            vk->vkGetInstanceProcAddr != nullptr || vk->vkGetDeviceProcAddr != nullptr;
306 }
307 
308 }  // namespace vk
309 }  // namespace gfxstream
310