• 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 validation 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 enableValidationLayers,vk::ICD icd)109 ScopedVkLoaderEnvironment::ScopedVkLoaderEnvironment(bool enableValidationLayers, vk::ICD icd)
110     : mEnableValidationLayers(enableValidationLayers),
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) && !defined(ANGLE_PLATFORM_GGP)
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 (mEnableValidationLayers || 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             mEnableValidationLayers = 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                 mEnableValidationLayers = 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 (mEnableValidationLayers)
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             mEnableValidationLayers = 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_vkGetPhysicalDeviceProperties pGetPhysicalDeviceProperties,const std::vector<VkPhysicalDevice> & physicalDevices,vk::ICD preferredICD,uint32_t preferredVendorID,uint32_t preferredDeviceID,VkPhysicalDevice * physicalDeviceOut,VkPhysicalDeviceProperties * physicalDevicePropertiesOut)221 void ChoosePhysicalDevice(PFN_vkGetPhysicalDeviceProperties pGetPhysicalDeviceProperties,
222                           const std::vector<VkPhysicalDevice> &physicalDevices,
223                           vk::ICD preferredICD,
224                           uint32_t preferredVendorID,
225                           uint32_t preferredDeviceID,
226                           VkPhysicalDevice *physicalDeviceOut,
227                           VkPhysicalDeviceProperties *physicalDevicePropertiesOut)
228 {
229     ASSERT(!physicalDevices.empty());
230 
231     ICDFilterFunc filter = GetFilterForICD(preferredICD);
232 
233     const bool shouldChooseByID = (preferredVendorID != 0 || preferredDeviceID != 0);
234 
235     for (const VkPhysicalDevice &physicalDevice : physicalDevices)
236     {
237         pGetPhysicalDeviceProperties(physicalDevice, physicalDevicePropertiesOut);
238         if (filter(*physicalDevicePropertiesOut))
239         {
240             *physicalDeviceOut = physicalDevice;
241             return;
242         }
243 
244         if (shouldChooseByID)
245         {
246             // NOTE: If the system has multiple GPUs with the same vendor and
247             // device IDs, this will arbitrarily select one of them.
248             bool matchVendorID = true;
249             bool matchDeviceID = true;
250 
251             if (preferredVendorID != 0 &&
252                 preferredVendorID != physicalDevicePropertiesOut->vendorID)
253             {
254                 matchVendorID = false;
255             }
256 
257             if (preferredDeviceID != 0 &&
258                 preferredDeviceID != physicalDevicePropertiesOut->deviceID)
259             {
260                 matchDeviceID = false;
261             }
262 
263             if (matchVendorID && matchDeviceID)
264             {
265                 *physicalDeviceOut = physicalDevice;
266                 return;
267             }
268         }
269     }
270 
271     Optional<VkPhysicalDevice> integratedDevice;
272     VkPhysicalDeviceProperties integratedDeviceProperties;
273     for (const VkPhysicalDevice &physicalDevice : physicalDevices)
274     {
275         pGetPhysicalDeviceProperties(physicalDevice, physicalDevicePropertiesOut);
276         // If discrete GPU exists, uses it by default.
277         if (physicalDevicePropertiesOut->deviceType == VK_PHYSICAL_DEVICE_TYPE_DISCRETE_GPU)
278         {
279             *physicalDeviceOut = physicalDevice;
280             return;
281         }
282         if (physicalDevicePropertiesOut->deviceType == VK_PHYSICAL_DEVICE_TYPE_INTEGRATED_GPU &&
283             !integratedDevice.valid())
284         {
285             integratedDevice           = physicalDevice;
286             integratedDeviceProperties = *physicalDevicePropertiesOut;
287             continue;
288         }
289     }
290 
291     // If only integrated GPU exists, use it by default.
292     if (integratedDevice.valid())
293     {
294         *physicalDeviceOut           = integratedDevice.value();
295         *physicalDevicePropertiesOut = integratedDeviceProperties;
296         return;
297     }
298 
299     WARN() << "Preferred device ICD not found. Using default physicalDevice instead.";
300     // Fallback to the first device.
301     *physicalDeviceOut = physicalDevices[0];
302     pGetPhysicalDeviceProperties(*physicalDeviceOut, physicalDevicePropertiesOut);
303 }
304 
305 }  // namespace vk
306 
307 }  // namespace angle
308