xref: /aosp_15_r20/external/angle/src/common/vulkan/vulkan_icd.cpp (revision 8975f5c5ed3d1c378011245431ada316dfb6f244)
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) && !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 (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_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 
239         if (physicalDevicePropertiesOut->apiVersion < kMinimumVulkanAPIVersion)
240         {
241             // Skip any devices that don't support our minimum API version. This
242             // takes precedence over all other considerations.
243             continue;
244         }
245 
246         if (filter(*physicalDevicePropertiesOut))
247         {
248             *physicalDeviceOut = physicalDevice;
249             return;
250         }
251 
252         if (shouldChooseByID)
253         {
254             // NOTE: If the system has multiple GPUs with the same vendor and
255             // device IDs, this will arbitrarily select one of them.
256             bool matchVendorID = true;
257             bool matchDeviceID = true;
258 
259             if (preferredVendorID != 0 &&
260                 preferredVendorID != physicalDevicePropertiesOut->vendorID)
261             {
262                 matchVendorID = false;
263             }
264 
265             if (preferredDeviceID != 0 &&
266                 preferredDeviceID != physicalDevicePropertiesOut->deviceID)
267             {
268                 matchDeviceID = false;
269             }
270 
271             if (matchVendorID && matchDeviceID)
272             {
273                 *physicalDeviceOut = physicalDevice;
274                 return;
275             }
276         }
277     }
278 
279     Optional<VkPhysicalDevice> integratedDevice;
280     VkPhysicalDeviceProperties integratedDeviceProperties;
281     for (const VkPhysicalDevice &physicalDevice : physicalDevices)
282     {
283         pGetPhysicalDeviceProperties(physicalDevice, physicalDevicePropertiesOut);
284 
285         if (physicalDevicePropertiesOut->apiVersion < kMinimumVulkanAPIVersion)
286         {
287             // Skip any devices that don't support our minimum API version. This
288             // takes precedence over all other considerations.
289             continue;
290         }
291 
292         // If discrete GPU exists, uses it by default.
293         if (physicalDevicePropertiesOut->deviceType == VK_PHYSICAL_DEVICE_TYPE_DISCRETE_GPU)
294         {
295             *physicalDeviceOut = physicalDevice;
296             return;
297         }
298         if (physicalDevicePropertiesOut->deviceType == VK_PHYSICAL_DEVICE_TYPE_INTEGRATED_GPU &&
299             !integratedDevice.valid())
300         {
301             integratedDevice           = physicalDevice;
302             integratedDeviceProperties = *physicalDevicePropertiesOut;
303             continue;
304         }
305     }
306 
307     // If only integrated GPU exists, use it by default.
308     if (integratedDevice.valid())
309     {
310         *physicalDeviceOut           = integratedDevice.value();
311         *physicalDevicePropertiesOut = integratedDeviceProperties;
312         return;
313     }
314 
315     WARN() << "Preferred device ICD not found. Using default physicalDevice instead.";
316     // Fallback to the first device.
317     *physicalDeviceOut = physicalDevices[0];
318     pGetPhysicalDeviceProperties(*physicalDeviceOut, physicalDevicePropertiesOut);
319 }
320 
321 }  // namespace vk
322 
323 }  // namespace angle
324