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