1 //
2 // Copyright 2020 The ANGLE Project Authors. All rights reserved.
3 // Use of this source code is governed by a BSD-style license that can be
4 // found in the LICENSE file.
5 //
6
7 // vulkan_icd.cpp : Helper for creating vulkan instances & selecting physical device.
8
9 #include "common/vulkan/vulkan_icd.h"
10
11 #include <functional>
12 #include <vector>
13
14 #include "common/bitset_utils.h"
15 #include "common/debug.h"
16 #include "common/system_utils.h"
17
18 namespace angle
19 {
20
21 namespace vk
22 {
23
24 namespace
25 {
26
27 // This function is unused on Android/Fuschia/GGP
28 #if !defined(ANGLE_PLATFORM_ANDROID) && !defined(ANGLE_PLATFORM_FUCHSIA) && \
29 !defined(ANGLE_PLATFORM_GGP)
WrapICDEnvironment(const char * icdEnvironment)30 const std::string WrapICDEnvironment(const char *icdEnvironment)
31 {
32 # if defined(ANGLE_PLATFORM_APPLE)
33 // On MacOS the libraries are bundled into the application directory
34 std::string ret = angle::GetHelperExecutableDir() + icdEnvironment;
35 return ret;
36 # endif // defined(ANGLE_PLATFORM_APPLE)
37 return icdEnvironment;
38 }
39
40 constexpr char kLoaderLayersPathEnv[] = "VK_LAYER_PATH";
41 #endif
42
43 constexpr char kLoaderICDFilenamesEnv[] = "VK_ICD_FILENAMES";
44 constexpr char kANGLEPreferredDeviceEnv[] = "ANGLE_PREFERRED_DEVICE";
45
46 constexpr uint32_t kMockVendorID = 0xba5eba11;
47 constexpr uint32_t kMockDeviceID = 0xf005ba11;
48 constexpr char kMockDeviceName[] = "Vulkan Mock Device";
49
50 constexpr uint32_t kGoogleVendorID = 0x1AE0;
51 constexpr uint32_t kSwiftShaderDeviceID = 0xC0DE;
52 constexpr char kSwiftShaderDeviceName[] = "SwiftShader Device";
53
54 using ICDFilterFunc = std::function<bool(const VkPhysicalDeviceProperties &)>;
55
GetFilterForICD(vk::ICD preferredICD)56 ICDFilterFunc GetFilterForICD(vk::ICD preferredICD)
57 {
58 switch (preferredICD)
59 {
60 case vk::ICD::Mock:
61 return [](const VkPhysicalDeviceProperties &deviceProperties) {
62 return ((deviceProperties.vendorID == kMockVendorID) &&
63 (deviceProperties.deviceID == kMockDeviceID) &&
64 (strcmp(deviceProperties.deviceName, kMockDeviceName) == 0));
65 };
66 case vk::ICD::SwiftShader:
67 return [](const VkPhysicalDeviceProperties &deviceProperties) {
68 return ((deviceProperties.vendorID == kGoogleVendorID) &&
69 (deviceProperties.deviceID == kSwiftShaderDeviceID) &&
70 (strncmp(deviceProperties.deviceName, kSwiftShaderDeviceName,
71 strlen(kSwiftShaderDeviceName)) == 0));
72 };
73 default:
74 const std::string anglePreferredDevice =
75 angle::GetEnvironmentVar(kANGLEPreferredDeviceEnv);
76 return [anglePreferredDevice](const VkPhysicalDeviceProperties &deviceProperties) {
77 return (anglePreferredDevice.empty() ||
78 anglePreferredDevice == deviceProperties.deviceName);
79 };
80 }
81 }
82
83 } // namespace
84
85 // If we're loading the validation layers, we could be running from any random directory.
86 // Change to the executable directory so we can find the layers, then change back to the
87 // previous directory to be safe we don't disrupt the application.
ScopedVkLoaderEnvironment(bool enableValidationLayers,vk::ICD icd)88 ScopedVkLoaderEnvironment::ScopedVkLoaderEnvironment(bool enableValidationLayers, vk::ICD icd)
89 : mEnableValidationLayers(enableValidationLayers),
90 mICD(icd),
91 mChangedCWD(false),
92 mChangedICDEnv(false)
93 {
94 // Changing CWD and setting environment variables makes no sense on Android,
95 // since this code is a part of Java application there.
96 // Android Vulkan loader doesn't need this either.
97 #if !defined(ANGLE_PLATFORM_ANDROID) && !defined(ANGLE_PLATFORM_FUCHSIA) && \
98 !defined(ANGLE_PLATFORM_GGP)
99 if (icd == vk::ICD::Mock)
100 {
101 if (!setICDEnvironment(WrapICDEnvironment(ANGLE_VK_MOCK_ICD_JSON).c_str()))
102 {
103 ERR() << "Error setting environment for Mock/Null Driver.";
104 }
105 }
106 # if defined(ANGLE_VK_SWIFTSHADER_ICD_JSON)
107 else if (icd == vk::ICD::SwiftShader)
108 {
109 if (!setICDEnvironment(WrapICDEnvironment(ANGLE_VK_SWIFTSHADER_ICD_JSON).c_str()))
110 {
111 ERR() << "Error setting environment for SwiftShader.";
112 }
113 }
114 # endif // defined(ANGLE_VK_SWIFTSHADER_ICD_JSON)
115 if (mEnableValidationLayers || icd != vk::ICD::Default)
116 {
117 const auto &cwd = angle::GetCWD();
118 if (!cwd.valid())
119 {
120 ERR() << "Error getting CWD for Vulkan layers init.";
121 mEnableValidationLayers = false;
122 mICD = vk::ICD::Default;
123 }
124 else
125 {
126 mPreviousCWD = cwd.value();
127 std::string exeDir = angle::GetExecutableDirectory();
128 mChangedCWD = angle::SetCWD(exeDir.c_str());
129 if (!mChangedCWD)
130 {
131 ERR() << "Error setting CWD for Vulkan layers init.";
132 mEnableValidationLayers = false;
133 mICD = vk::ICD::Default;
134 }
135 }
136 }
137
138 // Override environment variable to use the ANGLE layers.
139 if (mEnableValidationLayers)
140 {
141 if (!angle::PrependPathToEnvironmentVar(kLoaderLayersPathEnv, ANGLE_VK_LAYERS_DIR))
142 {
143 ERR() << "Error setting environment for Vulkan layers init.";
144 mEnableValidationLayers = false;
145 }
146 }
147 #endif // !defined(ANGLE_PLATFORM_ANDROID)
148 }
149
~ScopedVkLoaderEnvironment()150 ScopedVkLoaderEnvironment::~ScopedVkLoaderEnvironment()
151 {
152 if (mChangedCWD)
153 {
154 #if !defined(ANGLE_PLATFORM_ANDROID)
155 ASSERT(mPreviousCWD.valid());
156 angle::SetCWD(mPreviousCWD.value().c_str());
157 #endif // !defined(ANGLE_PLATFORM_ANDROID)
158 }
159 if (mChangedICDEnv)
160 {
161 if (mPreviousICDEnv.value().empty())
162 {
163 angle::UnsetEnvironmentVar(kLoaderICDFilenamesEnv);
164 }
165 else
166 {
167 angle::SetEnvironmentVar(kLoaderICDFilenamesEnv, mPreviousICDEnv.value().c_str());
168 }
169 }
170 }
171
setICDEnvironment(const char * icd)172 bool ScopedVkLoaderEnvironment::setICDEnvironment(const char *icd)
173 {
174 // Override environment variable to use built Mock ICD
175 // ANGLE_VK_ICD_JSON gets set to the built mock ICD in BUILD.gn
176 mPreviousICDEnv = angle::GetEnvironmentVar(kLoaderICDFilenamesEnv);
177 mChangedICDEnv = angle::SetEnvironmentVar(kLoaderICDFilenamesEnv, icd);
178
179 if (!mChangedICDEnv)
180 {
181 mICD = vk::ICD::Default;
182 }
183 return mChangedICDEnv;
184 }
185
ChoosePhysicalDevice(const std::vector<VkPhysicalDevice> & physicalDevices,vk::ICD preferredICD,VkPhysicalDevice * physicalDeviceOut,VkPhysicalDeviceProperties * physicalDevicePropertiesOut)186 void ChoosePhysicalDevice(const std::vector<VkPhysicalDevice> &physicalDevices,
187 vk::ICD preferredICD,
188 VkPhysicalDevice *physicalDeviceOut,
189 VkPhysicalDeviceProperties *physicalDevicePropertiesOut)
190 {
191 ASSERT(!physicalDevices.empty());
192
193 ICDFilterFunc filter = GetFilterForICD(preferredICD);
194
195 for (const VkPhysicalDevice &physicalDevice : physicalDevices)
196 {
197 vkGetPhysicalDeviceProperties(physicalDevice, physicalDevicePropertiesOut);
198 if (filter(*physicalDevicePropertiesOut))
199 {
200 *physicalDeviceOut = physicalDevice;
201 return;
202 }
203 }
204 WARN() << "Preferred device ICD not found. Using default physicalDevice instead.";
205
206 // Fall back to first device.
207 *physicalDeviceOut = physicalDevices[0];
208 vkGetPhysicalDeviceProperties(*physicalDeviceOut, physicalDevicePropertiesOut);
209 }
210
211 } // namespace vk
212
213 } // namespace angle
214