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