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/Optional.h"
15 #include "common/bitset_utils.h"
16 #include "common/debug.h"
17 #include "common/system_utils.h"
18
19 #include "common/vulkan/vk_google_filtering_precision.h"
20
21 namespace
22 {
ResetEnvironmentVar(const char * variableName,const Optional<std::string> & value)23 void ResetEnvironmentVar(const char *variableName, const Optional<std::string> &value)
24 {
25 if (!value.valid())
26 {
27 return;
28 }
29
30 if (value.value().empty())
31 {
32 angle::UnsetEnvironmentVar(variableName);
33 }
34 else
35 {
36 angle::SetEnvironmentVar(variableName, value.value().c_str());
37 }
38 }
39 } // namespace
40
41 namespace angle
42 {
43
44 namespace vk
45 {
46
47 namespace
48 {
49
WrapICDEnvironment(const char * icdEnvironment)50 ANGLE_MAYBE_UNUSED const std::string WrapICDEnvironment(const char *icdEnvironment)
51 {
52 // The libraries are bundled into the module directory
53 std::string ret = ConcatenatePath(angle::GetModuleDirectory(), icdEnvironment);
54 return ret;
55 }
56
57 ANGLE_MAYBE_UNUSED constexpr char kLoaderLayersPathEnv[] = "VK_LAYER_PATH";
58 ANGLE_MAYBE_UNUSED constexpr char kLayerEnablesEnv[] = "VK_LAYER_ENABLES";
59
60 constexpr char kLoaderICDFilenamesEnv[] = "VK_ICD_FILENAMES";
61 constexpr char kANGLEPreferredDeviceEnv[] = "ANGLE_PREFERRED_DEVICE";
62 constexpr char kValidationLayersCustomSTypeListEnv[] = "VK_LAYER_CUSTOM_STYPE_LIST";
63 constexpr char kNoDeviceSelect[] = "NODEVICE_SELECT";
64
65 constexpr uint32_t kMockVendorID = 0xba5eba11;
66 constexpr uint32_t kMockDeviceID = 0xf005ba11;
67 constexpr char kMockDeviceName[] = "Vulkan Mock Device";
68
69 constexpr uint32_t kGoogleVendorID = 0x1AE0;
70 constexpr uint32_t kSwiftShaderDeviceID = 0xC0DE;
71 constexpr char kSwiftShaderDeviceName[] = "SwiftShader Device";
72
73 using ICDFilterFunc = std::function<bool(const VkPhysicalDeviceProperties &)>;
74
GetFilterForICD(vk::ICD preferredICD)75 ICDFilterFunc GetFilterForICD(vk::ICD preferredICD)
76 {
77 switch (preferredICD)
78 {
79 case vk::ICD::Mock:
80 return [](const VkPhysicalDeviceProperties &deviceProperties) {
81 return ((deviceProperties.vendorID == kMockVendorID) &&
82 (deviceProperties.deviceID == kMockDeviceID) &&
83 (strcmp(deviceProperties.deviceName, kMockDeviceName) == 0));
84 };
85 case vk::ICD::SwiftShader:
86 return [](const VkPhysicalDeviceProperties &deviceProperties) {
87 return ((deviceProperties.vendorID == kGoogleVendorID) &&
88 (deviceProperties.deviceID == kSwiftShaderDeviceID) &&
89 (strncmp(deviceProperties.deviceName, kSwiftShaderDeviceName,
90 strlen(kSwiftShaderDeviceName)) == 0));
91 };
92 default:
93 const std::string anglePreferredDevice =
94 angle::GetEnvironmentVar(kANGLEPreferredDeviceEnv);
95 return [anglePreferredDevice](const VkPhysicalDeviceProperties &deviceProperties) {
96 return (anglePreferredDevice == deviceProperties.deviceName);
97 };
98 }
99 }
100
101 } // namespace
102
103 // If we're loading the validation layers, we could be running from any random directory.
104 // Change to the executable directory so we can find the layers, then change back to the
105 // previous directory to be safe we don't disrupt the application.
ScopedVkLoaderEnvironment(bool enableValidationLayers,vk::ICD icd)106 ScopedVkLoaderEnvironment::ScopedVkLoaderEnvironment(bool enableValidationLayers, vk::ICD icd)
107 : mEnableValidationLayers(enableValidationLayers),
108 mICD(icd),
109 mChangedCWD(false),
110 mChangedICDEnv(false),
111 mChangedNoDeviceSelect(false)
112 {
113 // Changing CWD and setting environment variables makes no sense on Android,
114 // since this code is a part of Java application there.
115 // Android Vulkan loader doesn't need this either.
116 #if !defined(ANGLE_PLATFORM_ANDROID) && !defined(ANGLE_PLATFORM_GGP)
117 if (icd == vk::ICD::Mock)
118 {
119 if (!setICDEnvironment(WrapICDEnvironment(ANGLE_VK_MOCK_ICD_JSON).c_str()))
120 {
121 ERR() << "Error setting environment for Mock/Null Driver.";
122 }
123 }
124 # if defined(ANGLE_VK_SWIFTSHADER_ICD_JSON)
125 else if (icd == vk::ICD::SwiftShader)
126 {
127 if (!setICDEnvironment(WrapICDEnvironment(ANGLE_VK_SWIFTSHADER_ICD_JSON).c_str()))
128 {
129 ERR() << "Error setting environment for SwiftShader.";
130 }
131 }
132 # endif // defined(ANGLE_VK_SWIFTSHADER_ICD_JSON)
133
134 # if !defined(ANGLE_PLATFORM_MACOS)
135 if (mEnableValidationLayers || icd != vk::ICD::Default)
136 {
137 const auto &cwd = angle::GetCWD();
138 if (!cwd.valid())
139 {
140 ERR() << "Error getting CWD for Vulkan layers init.";
141 mEnableValidationLayers = false;
142 mICD = vk::ICD::Default;
143 }
144 else
145 {
146 mPreviousCWD = cwd.value();
147 std::string moduleDir = angle::GetModuleDirectory();
148 mChangedCWD = angle::SetCWD(moduleDir.c_str());
149 if (!mChangedCWD)
150 {
151 ERR() << "Error setting CWD for Vulkan layers init.";
152 mEnableValidationLayers = false;
153 mICD = vk::ICD::Default;
154 }
155 }
156 }
157 # endif // defined(ANGLE_PLATFORM_MACOS)
158
159 // Override environment variable to use the ANGLE layers.
160 if (mEnableValidationLayers)
161 {
162 # if defined(ANGLE_VK_LAYERS_DIR)
163 if (!angle::PrependPathToEnvironmentVar(kLoaderLayersPathEnv, ANGLE_VK_LAYERS_DIR))
164 {
165 ERR() << "Error setting environment for Vulkan layers init.";
166 mEnableValidationLayers = false;
167 }
168 # endif // defined(ANGLE_VK_LAYERS_DIR)
169
170 if (!angle::PrependPathToEnvironmentVar(
171 kLayerEnablesEnv, "VK_VALIDATION_FEATURE_ENABLE_SYNCHRONIZATION_VALIDATION"))
172 {
173 ERR() << "Error setting synchronization validation environment for Vulkan validation "
174 "layers init.";
175 }
176
177 if (!setCustomExtensionsEnvironment())
178 {
179 ERR() << "Error setting custom list for custom extensions for Vulkan layers init.";
180 mEnableValidationLayers = false;
181 }
182 }
183 #endif // !defined(ANGLE_PLATFORM_ANDROID)
184
185 if (IsMSan() || IsASan())
186 {
187 // device select layer cause memory sanitizer false positive, so disable
188 // it for msan build.
189 mPreviousNoDeviceSelectEnv = angle::GetEnvironmentVar(kNoDeviceSelect);
190 angle::SetEnvironmentVar(kNoDeviceSelect, "1");
191 mChangedNoDeviceSelect = true;
192 }
193 }
194
~ScopedVkLoaderEnvironment()195 ScopedVkLoaderEnvironment::~ScopedVkLoaderEnvironment()
196 {
197 if (mChangedCWD)
198 {
199 #if !defined(ANGLE_PLATFORM_ANDROID)
200 ASSERT(mPreviousCWD.valid());
201 angle::SetCWD(mPreviousCWD.value().c_str());
202 #endif // !defined(ANGLE_PLATFORM_ANDROID)
203 }
204 if (mChangedICDEnv)
205 {
206 ResetEnvironmentVar(kLoaderICDFilenamesEnv, mPreviousICDEnv);
207 }
208
209 ResetEnvironmentVar(kValidationLayersCustomSTypeListEnv, mPreviousCustomExtensionsEnv);
210
211 if (mChangedNoDeviceSelect)
212 {
213 ResetEnvironmentVar(kNoDeviceSelect, mPreviousNoDeviceSelectEnv);
214 }
215 }
216
setICDEnvironment(const char * icd)217 bool ScopedVkLoaderEnvironment::setICDEnvironment(const char *icd)
218 {
219 // Override environment variable to use built Mock ICD
220 // ANGLE_VK_ICD_JSON gets set to the built mock ICD in BUILD.gn
221 mPreviousICDEnv = angle::GetEnvironmentVar(kLoaderICDFilenamesEnv);
222 mChangedICDEnv = angle::SetEnvironmentVar(kLoaderICDFilenamesEnv, icd);
223
224 if (!mChangedICDEnv)
225 {
226 mICD = vk::ICD::Default;
227 }
228 return mChangedICDEnv;
229 }
230
setCustomExtensionsEnvironment()231 bool ScopedVkLoaderEnvironment::setCustomExtensionsEnvironment()
232 {
233 struct CustomExtension
234 {
235 VkStructureType type;
236 size_t size;
237 };
238
239 CustomExtension customExtensions[] = {
240
241 {VK_STRUCTURE_TYPE_SAMPLER_FILTERING_PRECISION_GOOGLE,
242 sizeof(VkSamplerFilteringPrecisionGOOGLE)},
243
244 };
245
246 mPreviousCustomExtensionsEnv = angle::GetEnvironmentVar(kValidationLayersCustomSTypeListEnv);
247
248 std::stringstream strstr;
249 for (CustomExtension &extension : customExtensions)
250 {
251 if (strstr.tellp() != std::streampos(0))
252 {
253 strstr << angle::GetPathSeparatorForEnvironmentVar();
254 }
255
256 strstr << extension.type << angle::GetPathSeparatorForEnvironmentVar() << extension.size;
257 }
258
259 return angle::PrependPathToEnvironmentVar(kValidationLayersCustomSTypeListEnv,
260 strstr.str().c_str());
261 }
262
ChoosePhysicalDevice(PFN_vkGetPhysicalDeviceProperties pGetPhysicalDeviceProperties,const std::vector<VkPhysicalDevice> & physicalDevices,vk::ICD preferredICD,VkPhysicalDevice * physicalDeviceOut,VkPhysicalDeviceProperties * physicalDevicePropertiesOut)263 void ChoosePhysicalDevice(PFN_vkGetPhysicalDeviceProperties pGetPhysicalDeviceProperties,
264 const std::vector<VkPhysicalDevice> &physicalDevices,
265 vk::ICD preferredICD,
266 VkPhysicalDevice *physicalDeviceOut,
267 VkPhysicalDeviceProperties *physicalDevicePropertiesOut)
268 {
269 ASSERT(!physicalDevices.empty());
270
271 ICDFilterFunc filter = GetFilterForICD(preferredICD);
272
273 for (const VkPhysicalDevice &physicalDevice : physicalDevices)
274 {
275 pGetPhysicalDeviceProperties(physicalDevice, physicalDevicePropertiesOut);
276 if (filter(*physicalDevicePropertiesOut))
277 {
278 *physicalDeviceOut = physicalDevice;
279 return;
280 }
281 }
282
283 Optional<VkPhysicalDevice> integratedDevice;
284 VkPhysicalDeviceProperties integratedDeviceProperties;
285 for (const VkPhysicalDevice &physicalDevice : physicalDevices)
286 {
287 pGetPhysicalDeviceProperties(physicalDevice, physicalDevicePropertiesOut);
288 // If discrete GPU exists, uses it by default.
289 if (physicalDevicePropertiesOut->deviceType == VK_PHYSICAL_DEVICE_TYPE_DISCRETE_GPU)
290 {
291 *physicalDeviceOut = physicalDevice;
292 return;
293 }
294 if (physicalDevicePropertiesOut->deviceType == VK_PHYSICAL_DEVICE_TYPE_INTEGRATED_GPU &&
295 !integratedDevice.valid())
296 {
297 integratedDevice = physicalDevice;
298 integratedDeviceProperties = *physicalDevicePropertiesOut;
299 continue;
300 }
301 }
302
303 // If only integrated GPU exists, use it by default.
304 if (integratedDevice.valid())
305 {
306 *physicalDeviceOut = integratedDevice.value();
307 *physicalDevicePropertiesOut = integratedDeviceProperties;
308 return;
309 }
310
311 WARN() << "Preferred device ICD not found. Using default physicalDevice instead.";
312 // Fallback to the first device.
313 *physicalDeviceOut = physicalDevices[0];
314 pGetPhysicalDeviceProperties(*physicalDeviceOut, physicalDevicePropertiesOut);
315 }
316
317 } // namespace vk
318
319 } // namespace angle
320