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