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/bitset_utils.h"
15 #include "common/debug.h"
16 #include "common/system_utils.h"
17
18 #include "common/vulkan/vk_google_filtering_precision.h"
19
20 namespace
21 {
ResetEnvironmentVar(const char * variableName,const Optional<std::string> & value)22 void ResetEnvironmentVar(const char *variableName, const Optional<std::string> &value)
23 {
24 if (!value.valid())
25 {
26 return;
27 }
28
29 if (value.value().empty())
30 {
31 angle::UnsetEnvironmentVar(variableName);
32 }
33 else
34 {
35 angle::SetEnvironmentVar(variableName, value.value().c_str());
36 }
37 }
38 } // namespace
39
40 namespace angle
41 {
42
43 namespace vk
44 {
45
46 namespace
47 {
48
49 // This function is unused on Android/Fuschia/GGP
50 #if !defined(ANGLE_PLATFORM_ANDROID) && !defined(ANGLE_PLATFORM_FUCHSIA) && \
51 !defined(ANGLE_PLATFORM_GGP)
WrapICDEnvironment(const char * icdEnvironment)52 const std::string WrapICDEnvironment(const char *icdEnvironment)
53 {
54 // The libraries are bundled into the module directory
55 std::string ret = ConcatenatePath(angle::GetModuleDirectory(), icdEnvironment);
56 return ret;
57 }
58
59 constexpr char kLoaderLayersPathEnv[] = "VK_LAYER_PATH";
60 #endif
61
62 constexpr char kLoaderICDFilenamesEnv[] = "VK_ICD_FILENAMES";
63 constexpr char kANGLEPreferredDeviceEnv[] = "ANGLE_PREFERRED_DEVICE";
64 constexpr char kValidationLayersCustomSTypeListEnv[] = "VK_LAYER_CUSTOM_STYPE_LIST";
65
66 constexpr uint32_t kMockVendorID = 0xba5eba11;
67 constexpr uint32_t kMockDeviceID = 0xf005ba11;
68 constexpr char kMockDeviceName[] = "Vulkan Mock Device";
69
70 constexpr uint32_t kGoogleVendorID = 0x1AE0;
71 constexpr uint32_t kSwiftShaderDeviceID = 0xC0DE;
72 constexpr char kSwiftShaderDeviceName[] = "SwiftShader Device";
73
74 using ICDFilterFunc = std::function<bool(const VkPhysicalDeviceProperties &)>;
75
GetFilterForICD(vk::ICD preferredICD)76 ICDFilterFunc GetFilterForICD(vk::ICD preferredICD)
77 {
78 switch (preferredICD)
79 {
80 case vk::ICD::Mock:
81 return [](const VkPhysicalDeviceProperties &deviceProperties) {
82 return ((deviceProperties.vendorID == kMockVendorID) &&
83 (deviceProperties.deviceID == kMockDeviceID) &&
84 (strcmp(deviceProperties.deviceName, kMockDeviceName) == 0));
85 };
86 case vk::ICD::SwiftShader:
87 return [](const VkPhysicalDeviceProperties &deviceProperties) {
88 return ((deviceProperties.vendorID == kGoogleVendorID) &&
89 (deviceProperties.deviceID == kSwiftShaderDeviceID) &&
90 (strncmp(deviceProperties.deviceName, kSwiftShaderDeviceName,
91 strlen(kSwiftShaderDeviceName)) == 0));
92 };
93 default:
94 const std::string anglePreferredDevice =
95 angle::GetEnvironmentVar(kANGLEPreferredDeviceEnv);
96 return [anglePreferredDevice](const VkPhysicalDeviceProperties &deviceProperties) {
97 return (anglePreferredDevice.empty() ||
98 anglePreferredDevice == deviceProperties.deviceName);
99 };
100 }
101 }
102
103 } // namespace
104
105 // If we're loading the validation layers, we could be running from any random directory.
106 // Change to the executable directory so we can find the layers, then change back to the
107 // previous directory to be safe we don't disrupt the application.
ScopedVkLoaderEnvironment(bool enableValidationLayers,vk::ICD icd)108 ScopedVkLoaderEnvironment::ScopedVkLoaderEnvironment(bool enableValidationLayers, vk::ICD icd)
109 : mEnableValidationLayers(enableValidationLayers),
110 mICD(icd),
111 mChangedCWD(false),
112 mChangedICDEnv(false)
113 {
114 // Changing CWD and setting environment variables makes no sense on Android,
115 // since this code is a part of Java application there.
116 // Android Vulkan loader doesn't need this either.
117 #if !defined(ANGLE_PLATFORM_ANDROID) && !defined(ANGLE_PLATFORM_FUCHSIA) && \
118 !defined(ANGLE_PLATFORM_GGP)
119 if (icd == vk::ICD::Mock)
120 {
121 if (!setICDEnvironment(WrapICDEnvironment(ANGLE_VK_MOCK_ICD_JSON).c_str()))
122 {
123 ERR() << "Error setting environment for Mock/Null Driver.";
124 }
125 }
126 # if defined(ANGLE_VK_SWIFTSHADER_ICD_JSON)
127 else if (icd == vk::ICD::SwiftShader)
128 {
129 if (!setICDEnvironment(WrapICDEnvironment(ANGLE_VK_SWIFTSHADER_ICD_JSON).c_str()))
130 {
131 ERR() << "Error setting environment for SwiftShader.";
132 }
133 }
134 # endif // defined(ANGLE_VK_SWIFTSHADER_ICD_JSON)
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
158 // Override environment variable to use the ANGLE layers.
159 if (mEnableValidationLayers)
160 {
161 if (!angle::PrependPathToEnvironmentVar(kLoaderLayersPathEnv, ANGLE_VK_LAYERS_DIR))
162 {
163 ERR() << "Error setting environment for Vulkan layers init.";
164 mEnableValidationLayers = false;
165 }
166
167 if (!setCustomExtensionsEnvironment())
168 {
169 ERR() << "Error setting custom list for custom extensions for Vulkan layers init.";
170 mEnableValidationLayers = false;
171 }
172 }
173 #endif // !defined(ANGLE_PLATFORM_ANDROID)
174 }
175
~ScopedVkLoaderEnvironment()176 ScopedVkLoaderEnvironment::~ScopedVkLoaderEnvironment()
177 {
178 if (mChangedCWD)
179 {
180 #if !defined(ANGLE_PLATFORM_ANDROID)
181 ASSERT(mPreviousCWD.valid());
182 angle::SetCWD(mPreviousCWD.value().c_str());
183 #endif // !defined(ANGLE_PLATFORM_ANDROID)
184 }
185 if (mChangedICDEnv)
186 {
187 ResetEnvironmentVar(kLoaderICDFilenamesEnv, mPreviousICDEnv);
188 }
189
190 ResetEnvironmentVar(kValidationLayersCustomSTypeListEnv, mPreviousCustomExtensionsEnv);
191 }
192
setICDEnvironment(const char * icd)193 bool ScopedVkLoaderEnvironment::setICDEnvironment(const char *icd)
194 {
195 // Override environment variable to use built Mock ICD
196 // ANGLE_VK_ICD_JSON gets set to the built mock ICD in BUILD.gn
197 mPreviousICDEnv = angle::GetEnvironmentVar(kLoaderICDFilenamesEnv);
198 mChangedICDEnv = angle::SetEnvironmentVar(kLoaderICDFilenamesEnv, icd);
199
200 if (!mChangedICDEnv)
201 {
202 mICD = vk::ICD::Default;
203 }
204 return mChangedICDEnv;
205 }
206
setCustomExtensionsEnvironment()207 bool ScopedVkLoaderEnvironment::setCustomExtensionsEnvironment()
208 {
209 struct CustomExtension
210 {
211 VkStructureType type;
212 size_t size;
213 };
214
215 CustomExtension customExtensions[] = {
216
217 {VK_STRUCTURE_TYPE_SAMPLER_FILTERING_PRECISION_GOOGLE,
218 sizeof(VkSamplerFilteringPrecisionGOOGLE)},
219
220 {VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PROVOKING_VERTEX_FEATURES_EXT,
221 sizeof(VkPhysicalDeviceProvokingVertexFeaturesEXT)},
222
223 {VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PROVOKING_VERTEX_PROPERTIES_EXT,
224 sizeof(VkPhysicalDeviceProvokingVertexPropertiesEXT)},
225
226 {VK_STRUCTURE_TYPE_PIPELINE_RASTERIZATION_PROVOKING_VERTEX_STATE_CREATE_INFO_EXT,
227 sizeof(VkPipelineRasterizationProvokingVertexStateCreateInfoEXT)},
228 };
229
230 mPreviousCustomExtensionsEnv = angle::GetEnvironmentVar(kValidationLayersCustomSTypeListEnv);
231
232 std::stringstream strstr;
233 for (CustomExtension &extension : customExtensions)
234 {
235 if (strstr.tellp() != std::streampos(0))
236 {
237 strstr << angle::GetPathSeparatorForEnvironmentVar();
238 }
239
240 strstr << extension.type << angle::GetPathSeparatorForEnvironmentVar() << extension.size;
241 }
242
243 return angle::PrependPathToEnvironmentVar(kValidationLayersCustomSTypeListEnv,
244 strstr.str().c_str());
245 }
246
ChoosePhysicalDevice(const std::vector<VkPhysicalDevice> & physicalDevices,vk::ICD preferredICD,VkPhysicalDevice * physicalDeviceOut,VkPhysicalDeviceProperties * physicalDevicePropertiesOut)247 void ChoosePhysicalDevice(const std::vector<VkPhysicalDevice> &physicalDevices,
248 vk::ICD preferredICD,
249 VkPhysicalDevice *physicalDeviceOut,
250 VkPhysicalDeviceProperties *physicalDevicePropertiesOut)
251 {
252 ASSERT(!physicalDevices.empty());
253
254 ICDFilterFunc filter = GetFilterForICD(preferredICD);
255
256 for (const VkPhysicalDevice &physicalDevice : physicalDevices)
257 {
258 vkGetPhysicalDeviceProperties(physicalDevice, physicalDevicePropertiesOut);
259 if (filter(*physicalDevicePropertiesOut))
260 {
261 *physicalDeviceOut = physicalDevice;
262 return;
263 }
264 }
265 WARN() << "Preferred device ICD not found. Using default physicalDevice instead.";
266
267 // Fall back to first device.
268 *physicalDeviceOut = physicalDevices[0];
269 vkGetPhysicalDeviceProperties(*physicalDeviceOut, physicalDevicePropertiesOut);
270 }
271
272 } // namespace vk
273
274 } // namespace angle
275