vulkan_icd.cpp (11820B)
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 { 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 50 [[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 [[maybe_unused]] constexpr char kLoaderLayersPathEnv[] = "VK_LAYER_PATH"; 58 [[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 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. 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 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 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 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 263 void ChoosePhysicalDevice(PFN_vkGetPhysicalDeviceProperties pGetPhysicalDeviceProperties, 264 const std::vector<VkPhysicalDevice> &physicalDevices, 265 vk::ICD preferredICD, 266 uint32_t preferredVendorID, 267 uint32_t preferredDeviceID, 268 VkPhysicalDevice *physicalDeviceOut, 269 VkPhysicalDeviceProperties *physicalDevicePropertiesOut) 270 { 271 ASSERT(!physicalDevices.empty()); 272 273 ICDFilterFunc filter = GetFilterForICD(preferredICD); 274 275 const bool shouldChooseByID = (preferredVendorID != 0 || preferredDeviceID != 0); 276 277 for (const VkPhysicalDevice &physicalDevice : physicalDevices) 278 { 279 pGetPhysicalDeviceProperties(physicalDevice, physicalDevicePropertiesOut); 280 if (filter(*physicalDevicePropertiesOut)) 281 { 282 *physicalDeviceOut = physicalDevice; 283 return; 284 } 285 286 if (shouldChooseByID) 287 { 288 // NOTE: If the system has multiple GPUs with the same vendor and 289 // device IDs, this will arbitrarily select one of them. 290 bool matchVendorID = true; 291 bool matchDeviceID = true; 292 293 if (preferredVendorID != 0 && 294 preferredVendorID != physicalDevicePropertiesOut->vendorID) 295 { 296 matchVendorID = false; 297 } 298 299 if (preferredDeviceID != 0 && 300 preferredDeviceID != physicalDevicePropertiesOut->deviceID) 301 { 302 matchDeviceID = false; 303 } 304 305 if (matchVendorID && matchDeviceID) 306 { 307 *physicalDeviceOut = physicalDevice; 308 return; 309 } 310 } 311 } 312 313 Optional<VkPhysicalDevice> integratedDevice; 314 VkPhysicalDeviceProperties integratedDeviceProperties; 315 for (const VkPhysicalDevice &physicalDevice : physicalDevices) 316 { 317 pGetPhysicalDeviceProperties(physicalDevice, physicalDevicePropertiesOut); 318 // If discrete GPU exists, uses it by default. 319 if (physicalDevicePropertiesOut->deviceType == VK_PHYSICAL_DEVICE_TYPE_DISCRETE_GPU) 320 { 321 *physicalDeviceOut = physicalDevice; 322 return; 323 } 324 if (physicalDevicePropertiesOut->deviceType == VK_PHYSICAL_DEVICE_TYPE_INTEGRATED_GPU && 325 !integratedDevice.valid()) 326 { 327 integratedDevice = physicalDevice; 328 integratedDeviceProperties = *physicalDevicePropertiesOut; 329 continue; 330 } 331 } 332 333 // If only integrated GPU exists, use it by default. 334 if (integratedDevice.valid()) 335 { 336 *physicalDeviceOut = integratedDevice.value(); 337 *physicalDevicePropertiesOut = integratedDeviceProperties; 338 return; 339 } 340 341 WARN() << "Preferred device ICD not found. Using default physicalDevice instead."; 342 // Fallback to the first device. 343 *physicalDeviceOut = physicalDevices[0]; 344 pGetPhysicalDeviceProperties(*physicalDeviceOut, physicalDevicePropertiesOut); 345 } 346 347 } // namespace vk 348 349 } // namespace angle