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