• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 namespace
20 {
ResetEnvironmentVar(const char * variableName,const Optional<std::string> & value)21 void ResetEnvironmentVar(const char *variableName, const Optional<std::string> &value)
22 {
23     if (!value.valid())
24     {
25         return;
26     }
27 
28     if (value.value().empty())
29     {
30         angle::UnsetEnvironmentVar(variableName);
31     }
32     else
33     {
34         angle::SetEnvironmentVar(variableName, value.value().c_str());
35     }
36 }
37 }  // namespace
38 
39 namespace angle
40 {
41 
42 namespace vk
43 {
44 
45 namespace
46 {
47 
WrapICDEnvironment(const char * icdEnvironment)48 [[maybe_unused]] const std::string WrapICDEnvironment(const char *icdEnvironment)
49 {
50     // The libraries are bundled into the module directory
51     std::string moduleDir = angle::GetModuleDirectory();
52     std::string ret       = ConcatenatePath(moduleDir, icdEnvironment);
53 #if defined(ANGLE_PLATFORM_MACOS)
54     std::string moduleDirWithLibraries = ConcatenatePath(moduleDir, "Libraries");
55     ret += ":" + ConcatenatePath(moduleDirWithLibraries, icdEnvironment);
56 #endif
57     return ret;
58 }
59 
60 [[maybe_unused]] constexpr char kLoaderLayersPathEnv[] = "VK_LAYER_PATH";
61 [[maybe_unused]] constexpr char kLayerEnablesEnv[]     = "VK_LAYER_ENABLES";
62 
63 constexpr char kLoaderICDFilenamesEnv[]              = "VK_ICD_FILENAMES";
64 constexpr char kANGLEPreferredDeviceEnv[]            = "ANGLE_PREFERRED_DEVICE";
65 constexpr char kValidationLayersCustomSTypeListEnv[] = "VK_LAYER_CUSTOM_STYPE_LIST";
66 constexpr char kNoDeviceSelect[]                     = "NODEVICE_SELECT";
67 
68 constexpr uint32_t kMockVendorID = 0xba5eba11;
69 constexpr uint32_t kMockDeviceID = 0xf005ba11;
70 constexpr char kMockDeviceName[] = "Vulkan Mock Device";
71 
72 constexpr uint32_t kGoogleVendorID      = 0x1AE0;
73 constexpr uint32_t kSwiftShaderDeviceID = 0xC0DE;
74 constexpr char kSwiftShaderDeviceName[] = "SwiftShader Device";
75 
76 using ICDFilterFunc = std::function<bool(const VkPhysicalDeviceProperties &)>;
77 
GetFilterForICD(vk::ICD preferredICD)78 ICDFilterFunc GetFilterForICD(vk::ICD preferredICD)
79 {
80     switch (preferredICD)
81     {
82         case vk::ICD::Mock:
83             return [](const VkPhysicalDeviceProperties &deviceProperties) {
84                 return ((deviceProperties.vendorID == kMockVendorID) &&
85                         (deviceProperties.deviceID == kMockDeviceID) &&
86                         (strcmp(deviceProperties.deviceName, kMockDeviceName) == 0));
87             };
88         case vk::ICD::SwiftShader:
89             return [](const VkPhysicalDeviceProperties &deviceProperties) {
90                 return ((deviceProperties.vendorID == kGoogleVendorID) &&
91                         (deviceProperties.deviceID == kSwiftShaderDeviceID) &&
92                         (strncmp(deviceProperties.deviceName, kSwiftShaderDeviceName,
93                                  strlen(kSwiftShaderDeviceName)) == 0));
94             };
95         default:
96             const std::string anglePreferredDevice =
97                 angle::GetEnvironmentVar(kANGLEPreferredDeviceEnv);
98             return [anglePreferredDevice](const VkPhysicalDeviceProperties &deviceProperties) {
99                 return (anglePreferredDevice == deviceProperties.deviceName);
100             };
101     }
102 }
103 
104 }  // namespace
105 
106 // If we're loading the vulkan layers, we could be running from any random directory.
107 // Change to the executable directory so we can find the layers, then change back to the
108 // previous directory to be safe we don't disrupt the application.
ScopedVkLoaderEnvironment(bool enableDebugLayers,vk::ICD icd)109 ScopedVkLoaderEnvironment::ScopedVkLoaderEnvironment(bool enableDebugLayers, vk::ICD icd)
110     : mEnableDebugLayers(enableDebugLayers),
111       mICD(icd),
112       mChangedCWD(false),
113       mChangedICDEnv(false),
114       mChangedNoDeviceSelect(false)
115 {
116 // Changing CWD and setting environment variables makes no sense on Android,
117 // since this code is a part of Java application there.
118 // Android Vulkan loader doesn't need this either.
119 #if !defined(ANGLE_PLATFORM_ANDROID)
120     if (icd == vk::ICD::Mock)
121     {
122         if (!setICDEnvironment(WrapICDEnvironment(ANGLE_VK_MOCK_ICD_JSON).c_str()))
123         {
124             ERR() << "Error setting environment for Mock/Null Driver.";
125         }
126     }
127 #    if defined(ANGLE_VK_SWIFTSHADER_ICD_JSON)
128     else if (icd == vk::ICD::SwiftShader)
129     {
130         if (!setICDEnvironment(WrapICDEnvironment(ANGLE_VK_SWIFTSHADER_ICD_JSON).c_str()))
131         {
132             ERR() << "Error setting environment for SwiftShader.";
133         }
134     }
135 #    endif  // defined(ANGLE_VK_SWIFTSHADER_ICD_JSON)
136 
137 #    if !defined(ANGLE_PLATFORM_MACOS)
138     if (mEnableDebugLayers || icd != vk::ICD::Default)
139     {
140         const auto &cwd = angle::GetCWD();
141         if (!cwd.valid())
142         {
143             ERR() << "Error getting CWD for Vulkan layers init.";
144             mEnableDebugLayers = false;
145             mICD               = vk::ICD::Default;
146         }
147         else
148         {
149             mPreviousCWD          = cwd.value();
150             std::string moduleDir = angle::GetModuleDirectory();
151             mChangedCWD           = angle::SetCWD(moduleDir.c_str());
152             if (!mChangedCWD)
153             {
154                 ERR() << "Error setting CWD for Vulkan layers init.";
155                 mEnableDebugLayers = false;
156                 mICD               = vk::ICD::Default;
157             }
158         }
159     }
160 #    endif  // defined(ANGLE_PLATFORM_MACOS)
161 
162     // Override environment variable to use the ANGLE layers.
163     if (mEnableDebugLayers)
164     {
165 #    if defined(ANGLE_VK_LAYERS_DIR)
166         if (!angle::PrependPathToEnvironmentVar(kLoaderLayersPathEnv, ANGLE_VK_LAYERS_DIR))
167         {
168             ERR() << "Error setting environment for Vulkan layers init.";
169             mEnableDebugLayers = false;
170         }
171 #    endif  // defined(ANGLE_VK_LAYERS_DIR)
172     }
173 #endif  // !defined(ANGLE_PLATFORM_ANDROID)
174 
175     if (IsMSan() || IsASan())
176     {
177         // device select layer cause memory sanitizer false positive, so disable
178         // it for msan build.
179         mPreviousNoDeviceSelectEnv = angle::GetEnvironmentVar(kNoDeviceSelect);
180         angle::SetEnvironmentVar(kNoDeviceSelect, "1");
181         mChangedNoDeviceSelect = true;
182     }
183 }
184 
~ScopedVkLoaderEnvironment()185 ScopedVkLoaderEnvironment::~ScopedVkLoaderEnvironment()
186 {
187     if (mChangedCWD)
188     {
189 #if !defined(ANGLE_PLATFORM_ANDROID)
190         ASSERT(mPreviousCWD.valid());
191         angle::SetCWD(mPreviousCWD.value().c_str());
192 #endif  // !defined(ANGLE_PLATFORM_ANDROID)
193     }
194     if (mChangedICDEnv)
195     {
196         ResetEnvironmentVar(kLoaderICDFilenamesEnv, mPreviousICDEnv);
197     }
198 
199     ResetEnvironmentVar(kValidationLayersCustomSTypeListEnv, mPreviousCustomExtensionsEnv);
200 
201     if (mChangedNoDeviceSelect)
202     {
203         ResetEnvironmentVar(kNoDeviceSelect, mPreviousNoDeviceSelectEnv);
204     }
205 }
206 
setICDEnvironment(const char * icd)207 bool ScopedVkLoaderEnvironment::setICDEnvironment(const char *icd)
208 {
209     // Override environment variable to use built Mock ICD
210     // ANGLE_VK_ICD_JSON gets set to the built mock ICD in BUILD.gn
211     mPreviousICDEnv = angle::GetEnvironmentVar(kLoaderICDFilenamesEnv);
212     mChangedICDEnv  = angle::SetEnvironmentVar(kLoaderICDFilenamesEnv, icd);
213 
214     if (!mChangedICDEnv)
215     {
216         mICD = vk::ICD::Default;
217     }
218     return mChangedICDEnv;
219 }
220 
ChoosePhysicalDevice(PFN_vkGetPhysicalDeviceProperties2 pGetPhysicalDeviceProperties2,const std::vector<VkPhysicalDevice> & physicalDevices,vk::ICD preferredICD,uint32_t preferredVendorID,uint32_t preferredDeviceID,const uint8_t * preferredDeviceUUID,const uint8_t * preferredDriverUUID,VkDriverId preferredDriverID,VkPhysicalDevice * physicalDeviceOut,VkPhysicalDeviceProperties2 * physicalDeviceProperties2Out,VkPhysicalDeviceIDProperties * physicalDeviceIDPropertiesOut,VkPhysicalDeviceDriverProperties * physicalDeviceDriverPropertiesOut)221 void ChoosePhysicalDevice(PFN_vkGetPhysicalDeviceProperties2 pGetPhysicalDeviceProperties2,
222                           const std::vector<VkPhysicalDevice> &physicalDevices,
223                           vk::ICD preferredICD,
224                           uint32_t preferredVendorID,
225                           uint32_t preferredDeviceID,
226                           const uint8_t *preferredDeviceUUID,
227                           const uint8_t *preferredDriverUUID,
228                           VkDriverId preferredDriverID,
229                           VkPhysicalDevice *physicalDeviceOut,
230                           VkPhysicalDeviceProperties2 *physicalDeviceProperties2Out,
231                           VkPhysicalDeviceIDProperties *physicalDeviceIDPropertiesOut,
232                           VkPhysicalDeviceDriverProperties *physicalDeviceDriverPropertiesOut)
233 {
234     ASSERT(!physicalDevices.empty());
235 
236     VkPhysicalDeviceProperties const *deviceProps = &physicalDeviceProperties2Out->properties;
237 
238     ICDFilterFunc filter = GetFilterForICD(preferredICD);
239 
240     const bool shouldChooseByPciId = (preferredVendorID != 0 || preferredDeviceID != 0);
241     const bool shouldChooseByUUIDs = (preferredDeviceUUID != nullptr ||
242                                       preferredDriverUUID != nullptr || preferredDriverID != 0);
243 
244     for (const VkPhysicalDevice &physicalDevice : physicalDevices)
245     {
246         *physicalDeviceProperties2Out       = {};
247         physicalDeviceProperties2Out->sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PROPERTIES_2;
248         physicalDeviceProperties2Out->pNext = physicalDeviceIDPropertiesOut;
249 
250         *physicalDeviceIDPropertiesOut       = {};
251         physicalDeviceIDPropertiesOut->sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_ID_PROPERTIES;
252         physicalDeviceIDPropertiesOut->pNext = physicalDeviceDriverPropertiesOut;
253 
254         *physicalDeviceDriverPropertiesOut = {};
255         physicalDeviceDriverPropertiesOut->sType =
256             VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_DRIVER_PROPERTIES;
257 
258         pGetPhysicalDeviceProperties2(physicalDevice, physicalDeviceProperties2Out);
259 
260         if (deviceProps->apiVersion < kMinimumVulkanAPIVersion)
261         {
262             // Skip any devices that don't support our minimum API version. This
263             // takes precedence over all other considerations.
264             continue;
265         }
266 
267         if (filter(*deviceProps))
268         {
269             *physicalDeviceOut = physicalDevice;
270             return;
271         }
272 
273         if (shouldChooseByUUIDs)
274         {
275             bool matched = true;
276 
277             if (preferredDriverID != 0 &&
278                 preferredDriverID != physicalDeviceDriverPropertiesOut->driverID)
279             {
280                 matched = false;
281             }
282             else if (preferredDeviceUUID != nullptr &&
283                      memcmp(preferredDeviceUUID, physicalDeviceIDPropertiesOut->deviceUUID,
284                             VK_UUID_SIZE) != 0)
285             {
286                 matched = false;
287             }
288             else if (preferredDriverUUID != nullptr &&
289                      memcmp(preferredDriverUUID, physicalDeviceIDPropertiesOut->driverUUID,
290                             VK_UUID_SIZE) != 0)
291             {
292                 matched = false;
293             }
294 
295             if (matched)
296             {
297                 *physicalDeviceOut = physicalDevice;
298                 return;
299             }
300         }
301 
302         if (shouldChooseByPciId)
303         {
304             // NOTE: If the system has multiple GPUs with the same vendor and
305             // device IDs, this will arbitrarily select one of them.
306             bool matchVendorID = true;
307             bool matchDeviceID = true;
308 
309             if (preferredVendorID != 0 && preferredVendorID != deviceProps->vendorID)
310             {
311                 matchVendorID = false;
312             }
313 
314             if (preferredDeviceID != 0 && preferredDeviceID != deviceProps->deviceID)
315             {
316                 matchDeviceID = false;
317             }
318 
319             if (matchVendorID && matchDeviceID)
320             {
321                 *physicalDeviceOut = physicalDevice;
322                 return;
323             }
324         }
325     }
326 
327     Optional<VkPhysicalDevice> integratedDevice;
328     VkPhysicalDeviceProperties2 integratedDeviceProperties2;
329     VkPhysicalDeviceIDProperties integratedDeviceIDProperties;
330     VkPhysicalDeviceDriverProperties integratedDeviceDriverProperties;
331 
332     for (const VkPhysicalDevice &physicalDevice : physicalDevices)
333     {
334         *physicalDeviceProperties2Out       = {};
335         physicalDeviceProperties2Out->sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PROPERTIES_2;
336         physicalDeviceProperties2Out->pNext = physicalDeviceIDPropertiesOut;
337 
338         *physicalDeviceIDPropertiesOut       = {};
339         physicalDeviceIDPropertiesOut->sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_ID_PROPERTIES;
340         physicalDeviceIDPropertiesOut->pNext = physicalDeviceDriverPropertiesOut;
341 
342         *physicalDeviceDriverPropertiesOut = {};
343         physicalDeviceDriverPropertiesOut->sType =
344             VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_DRIVER_PROPERTIES;
345 
346         pGetPhysicalDeviceProperties2(physicalDevice, physicalDeviceProperties2Out);
347 
348         if (deviceProps->apiVersion < kMinimumVulkanAPIVersion)
349         {
350             // Skip any devices that don't support our minimum API version. This
351             // takes precedence over all other considerations.
352             continue;
353         }
354 
355         // If discrete GPU exists, uses it by default.
356         if (deviceProps->deviceType == VK_PHYSICAL_DEVICE_TYPE_DISCRETE_GPU)
357         {
358             *physicalDeviceOut = physicalDevice;
359             return;
360         }
361         if (deviceProps->deviceType == VK_PHYSICAL_DEVICE_TYPE_INTEGRATED_GPU &&
362             !integratedDevice.valid())
363         {
364             integratedDevice                       = physicalDevice;
365             integratedDeviceProperties2            = *physicalDeviceProperties2Out;
366             integratedDeviceIDProperties           = *physicalDeviceIDPropertiesOut;
367             integratedDeviceDriverProperties       = *physicalDeviceDriverPropertiesOut;
368             integratedDeviceProperties2.pNext      = nullptr;
369             integratedDeviceIDProperties.pNext     = nullptr;
370             integratedDeviceDriverProperties.pNext = nullptr;
371             continue;
372         }
373     }
374 
375     // If only integrated GPU exists, use it by default.
376     if (integratedDevice.valid())
377     {
378         *physicalDeviceOut             = integratedDevice.value();
379         *physicalDeviceProperties2Out  = integratedDeviceProperties2;
380         *physicalDeviceIDPropertiesOut = integratedDeviceIDProperties;
381         return;
382     }
383 
384     WARN() << "Preferred device ICD not found. Using default physicalDevice instead.";
385     // Fallback to the first device.
386     *physicalDeviceOut = physicalDevices[0];
387     pGetPhysicalDeviceProperties2(*physicalDeviceOut, physicalDeviceProperties2Out);
388 }
389 
390 }  // namespace vk
391 
392 }  // namespace angle
393