1 // Copyright 2019 The Dawn Authors 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // http://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 15 #include "dawn_native/vulkan/BackendVk.h" 16 17 #include "common/BitSetIterator.h" 18 #include "common/Log.h" 19 #include "common/SystemUtils.h" 20 #include "dawn_native/Instance.h" 21 #include "dawn_native/VulkanBackend.h" 22 #include "dawn_native/vulkan/AdapterVk.h" 23 #include "dawn_native/vulkan/UtilsVulkan.h" 24 #include "dawn_native/vulkan/VulkanError.h" 25 26 // TODO(crbug.com/dawn/283): Link against the Vulkan Loader and remove this. 27 #if defined(DAWN_ENABLE_SWIFTSHADER) 28 # if defined(DAWN_PLATFORM_LINUX) || defined(DAWN_PLATFORM_FUSCHIA) 29 constexpr char kSwiftshaderLibName[] = "libvk_swiftshader.so"; 30 # elif defined(DAWN_PLATFORM_WINDOWS) 31 constexpr char kSwiftshaderLibName[] = "vk_swiftshader.dll"; 32 # elif defined(DAWN_PLATFORM_MACOS) 33 constexpr char kSwiftshaderLibName[] = "libvk_swiftshader.dylib"; 34 # else 35 # error "Unimplemented Swiftshader Vulkan backend platform" 36 # endif 37 #endif 38 39 #if defined(DAWN_PLATFORM_LINUX) 40 # if defined(DAWN_PLATFORM_ANDROID) 41 constexpr char kVulkanLibName[] = "libvulkan.so"; 42 # else 43 constexpr char kVulkanLibName[] = "libvulkan.so.1"; 44 # endif 45 #elif defined(DAWN_PLATFORM_WINDOWS) 46 constexpr char kVulkanLibName[] = "vulkan-1.dll"; 47 #elif defined(DAWN_PLATFORM_MACOS) 48 constexpr char kVulkanLibName[] = "libvulkan.dylib"; 49 #elif defined(DAWN_PLATFORM_FUCHSIA) 50 constexpr char kVulkanLibName[] = "libvulkan.so"; 51 #else 52 # error "Unimplemented Vulkan backend platform" 53 #endif 54 55 struct SkippedMessage { 56 const char* messageId; 57 const char* messageContents; 58 }; 59 60 // Array of Validation error/warning messages that will be ignored, should include bugID 61 constexpr SkippedMessage kSkippedMessages[] = { 62 // These errors are generated when simultaneously using a read-only depth/stencil attachment as 63 // a texture binding. This is valid Vulkan. 64 // 65 // When storeOp=NONE is not present, Dawn uses storeOp=STORE, but Vulkan validation layer 66 // considers the image read-only and produces a hazard. Dawn can't rely on storeOp=NONE and 67 // so this is not expected to be worked around. 68 // See http://crbug.com/dawn/1225 for more details. 69 {"SYNC-HAZARD-WRITE_AFTER_READ", 70 "depth aspect during store with storeOp VK_ATTACHMENT_STORE_OP_STORE. Access info (usage: " 71 "SYNC_LATE_FRAGMENT_TESTS_DEPTH_STENCIL_ATTACHMENT_WRITE, prior_usage: " 72 "SYNC_FRAGMENT_SHADER_SHADER_STORAGE_READ, read_barriers: VK_PIPELINE_STAGE_2_NONE_KHR, "}, 73 74 {"SYNC-HAZARD-WRITE_AFTER_READ", 75 "stencil aspect during store with stencilStoreOp VK_ATTACHMENT_STORE_OP_STORE. Access info " 76 "(usage: SYNC_LATE_FRAGMENT_TESTS_DEPTH_STENCIL_ATTACHMENT_WRITE, prior_usage: " 77 "SYNC_FRAGMENT_SHADER_SHADER_STORAGE_READ, read_barriers: VK_PIPELINE_STAGE_2_NONE_KHR, "}, 78 }; 79 80 namespace dawn_native { namespace vulkan { 81 82 namespace { 83 84 static constexpr ICD kICDs[] = { 85 ICD::None, 86 #if defined(DAWN_ENABLE_SWIFTSHADER) 87 ICD::SwiftShader, 88 #endif // defined(DAWN_ENABLE_SWIFTSHADER) 89 }; 90 91 // Suppress validation errors that are known. Returns false in that case. ShouldReportDebugMessage(const char * messageId,const char * message)92 bool ShouldReportDebugMessage(const char* messageId, const char* message) { 93 for (const SkippedMessage& msg : kSkippedMessages) { 94 if (strstr(messageId, msg.messageId) != nullptr && 95 strstr(message, msg.messageContents) != nullptr) { 96 return false; 97 } 98 } 99 return true; 100 } 101 102 VKAPI_ATTR VkBool32 VKAPI_CALL OnDebugUtilsCallback(VkDebugUtilsMessageSeverityFlagBitsEXT messageSeverity,VkDebugUtilsMessageTypeFlagsEXT,const VkDebugUtilsMessengerCallbackDataEXT * pCallbackData,void *)103 OnDebugUtilsCallback(VkDebugUtilsMessageSeverityFlagBitsEXT messageSeverity, 104 VkDebugUtilsMessageTypeFlagsEXT /* messageTypes */, 105 const VkDebugUtilsMessengerCallbackDataEXT* pCallbackData, 106 void* /* pUserData */) { 107 if (ShouldReportDebugMessage(pCallbackData->pMessageIdName, pCallbackData->pMessage)) { 108 dawn::WarningLog() << pCallbackData->pMessage; 109 ASSERT((messageSeverity & VK_DEBUG_UTILS_MESSAGE_SEVERITY_ERROR_BIT_EXT) == 0); 110 } 111 return VK_FALSE; 112 } 113 114 // A debug callback specifically for instance creation so that we don't fire an ASSERT when 115 // the instance fails creation in an expected manner (for example the system not having 116 // Vulkan drivers). OnInstanceCreationDebugUtilsCallback(VkDebugUtilsMessageSeverityFlagBitsEXT messageSeverity,VkDebugUtilsMessageTypeFlagsEXT,const VkDebugUtilsMessengerCallbackDataEXT * pCallbackData,void *)117 VKAPI_ATTR VkBool32 VKAPI_CALL OnInstanceCreationDebugUtilsCallback( 118 VkDebugUtilsMessageSeverityFlagBitsEXT messageSeverity, 119 VkDebugUtilsMessageTypeFlagsEXT /* messageTypes */, 120 const VkDebugUtilsMessengerCallbackDataEXT* pCallbackData, 121 void* /* pUserData */) { 122 dawn::WarningLog() << pCallbackData->pMessage; 123 return VK_FALSE; 124 } 125 126 } // anonymous namespace 127 128 VulkanInstance::VulkanInstance() = default; 129 ~VulkanInstance()130 VulkanInstance::~VulkanInstance() { 131 if (mDebugUtilsMessenger != VK_NULL_HANDLE) { 132 mFunctions.DestroyDebugUtilsMessengerEXT(mInstance, mDebugUtilsMessenger, nullptr); 133 mDebugUtilsMessenger = VK_NULL_HANDLE; 134 } 135 136 // VkPhysicalDevices are destroyed when the VkInstance is destroyed 137 if (mInstance != VK_NULL_HANDLE) { 138 mFunctions.DestroyInstance(mInstance, nullptr); 139 mInstance = VK_NULL_HANDLE; 140 } 141 } 142 GetFunctions() const143 const VulkanFunctions& VulkanInstance::GetFunctions() const { 144 return mFunctions; 145 } 146 GetVkInstance() const147 VkInstance VulkanInstance::GetVkInstance() const { 148 return mInstance; 149 } 150 GetGlobalInfo() const151 const VulkanGlobalInfo& VulkanInstance::GetGlobalInfo() const { 152 return mGlobalInfo; 153 } 154 GetPhysicalDevices() const155 const std::vector<VkPhysicalDevice>& VulkanInstance::GetPhysicalDevices() const { 156 return mPhysicalDevices; 157 } 158 159 // static Create(const InstanceBase * instance,ICD icd)160 ResultOrError<Ref<VulkanInstance>> VulkanInstance::Create(const InstanceBase* instance, 161 ICD icd) { 162 Ref<VulkanInstance> vulkanInstance = AcquireRef(new VulkanInstance()); 163 DAWN_TRY(vulkanInstance->Initialize(instance, icd)); 164 return std::move(vulkanInstance); 165 } 166 Initialize(const InstanceBase * instance,ICD icd)167 MaybeError VulkanInstance::Initialize(const InstanceBase* instance, ICD icd) { 168 // These environment variables need only be set while loading procs and gathering device 169 // info. 170 ScopedEnvironmentVar vkICDFilenames; 171 ScopedEnvironmentVar vkLayerPath; 172 173 #if defined(DAWN_ENABLE_VULKAN_LOADER) 174 // If enabled, we use our own built Vulkan loader by specifying an absolute path to the 175 // shared library. Note that when we are currently getting the absolute path for the custom 176 // loader by getting the path to the dawn native library and traversing relative from there. 177 // This has implications for dawn tests because some of them are linking statically to 178 // dawn_native which means the "module" is actually the test as well. If the directory 179 // location of the tests change w.r.t the shared lib then this may break. Essentially we are 180 // assuming that our custom built Vulkan loader will always be in the same directory as the 181 // shared dawn native library and all test binaries that link statically. 182 const std::string resolvedVulkanLibPath = GetModuleDirectory() + kVulkanLibName; 183 #else 184 const std::string resolvedVulkanLibPath = kVulkanLibName; 185 #endif // defined(DAWN_ENABLE_VULKAN_LOADER) 186 187 switch (icd) { 188 case ICD::None: { 189 if (!mVulkanLib.Open(resolvedVulkanLibPath)) { 190 return DAWN_FORMAT_INTERNAL_ERROR("Couldn't load %s.", resolvedVulkanLibPath); 191 } 192 break; 193 } 194 case ICD::SwiftShader: { 195 #if defined(DAWN_ENABLE_SWIFTSHADER) 196 // First try to load the system Vulkan driver, if that fails, try to load with 197 // Swiftshader. Note: The system driver could potentially be Swiftshader if it was 198 // installed. 199 # if defined(DAWN_SWIFTSHADER_VK_ICD_JSON) 200 if (mVulkanLib.Open(resolvedVulkanLibPath)) { 201 std::string fullSwiftshaderICDPath = 202 GetExecutableDirectory() + DAWN_SWIFTSHADER_VK_ICD_JSON; 203 if (!vkICDFilenames.Set("VK_ICD_FILENAMES", fullSwiftshaderICDPath.c_str())) { 204 return DAWN_FORMAT_INTERNAL_ERROR("Couldn't set VK_ICD_FILENAMES to %s.", 205 fullSwiftshaderICDPath); 206 } 207 // Succesfully loaded driver and set VK_ICD_FILENAMES. 208 break; 209 } else 210 # endif // defined(DAWN_SWIFTSHADER_VK_ICD_JSON) 211 // Fallback to loading SwiftShader directly. 212 if (mVulkanLib.Open(kSwiftshaderLibName)) { 213 // Succesfully loaded SwiftShader. 214 break; 215 } 216 return DAWN_FORMAT_INTERNAL_ERROR( 217 "Failed to load SwiftShader. DAWN_SWIFTSHADER_VK_ICD_JSON was not defined and " 218 "could not load %s.", 219 kSwiftshaderLibName); 220 #endif // defined(DAWN_ENABLE_SWIFTSHADER) 221 222 // ICD::SwiftShader should not be passed if SwiftShader is not enabled. 223 UNREACHABLE(); 224 } 225 } 226 227 if (instance->IsBackendValidationEnabled()) { 228 #if defined(DAWN_ENABLE_VULKAN_VALIDATION_LAYERS) 229 std::string vkDataDir = GetExecutableDirectory() + DAWN_VK_DATA_DIR; 230 if (!vkLayerPath.Set("VK_LAYER_PATH", vkDataDir.c_str())) { 231 return DAWN_INTERNAL_ERROR("Couldn't set VK_LAYER_PATH"); 232 } 233 #else 234 dawn::WarningLog() << "Backend validation enabled but Dawn was not built with " 235 "DAWN_ENABLE_VULKAN_VALIDATION_LAYERS."; 236 #endif 237 } 238 239 DAWN_TRY(mFunctions.LoadGlobalProcs(mVulkanLib)); 240 241 DAWN_TRY_ASSIGN(mGlobalInfo, GatherGlobalInfo(mFunctions)); 242 243 VulkanGlobalKnobs usedGlobalKnobs = {}; 244 DAWN_TRY_ASSIGN(usedGlobalKnobs, CreateVkInstance(instance)); 245 *static_cast<VulkanGlobalKnobs*>(&mGlobalInfo) = usedGlobalKnobs; 246 247 DAWN_TRY(mFunctions.LoadInstanceProcs(mInstance, mGlobalInfo)); 248 249 if (usedGlobalKnobs.HasExt(InstanceExt::DebugUtils)) { 250 DAWN_TRY(RegisterDebugUtils()); 251 } 252 253 DAWN_TRY_ASSIGN(mPhysicalDevices, GatherPhysicalDevices(mInstance, mFunctions)); 254 255 return {}; 256 } 257 CreateVkInstance(const InstanceBase * instance)258 ResultOrError<VulkanGlobalKnobs> VulkanInstance::CreateVkInstance( 259 const InstanceBase* instance) { 260 VulkanGlobalKnobs usedKnobs = {}; 261 std::vector<const char*> layerNames; 262 InstanceExtSet extensionsToRequest = mGlobalInfo.extensions; 263 264 auto UseLayerIfAvailable = [&](VulkanLayer layer) { 265 if (mGlobalInfo.layers[layer]) { 266 layerNames.push_back(GetVulkanLayerInfo(layer).name); 267 usedKnobs.layers.set(layer, true); 268 extensionsToRequest |= mGlobalInfo.layerExtensions[layer]; 269 } 270 }; 271 272 // vktrace works by instering a layer, but we hide it behind a macro because the vktrace 273 // layer crashes when used without vktrace server started. See this vktrace issue: 274 // https://github.com/LunarG/VulkanTools/issues/254 275 // Also it is good to put it in first position so that it doesn't see Vulkan calls inserted 276 // by other layers. 277 #if defined(DAWN_USE_VKTRACE) 278 UseLayerIfAvailable(VulkanLayer::LunargVkTrace); 279 #endif 280 // RenderDoc installs a layer at the system level for its capture but we don't want to use 281 // it unless we are debugging in RenderDoc so we hide it behind a macro. 282 #if defined(DAWN_USE_RENDERDOC) 283 UseLayerIfAvailable(VulkanLayer::RenderDocCapture); 284 #endif 285 286 if (instance->IsBackendValidationEnabled()) { 287 UseLayerIfAvailable(VulkanLayer::Validation); 288 } 289 290 // Always use the Fuchsia swapchain layer if available. 291 UseLayerIfAvailable(VulkanLayer::FuchsiaImagePipeSwapchain); 292 293 // Available and known instance extensions default to being requested, but some special 294 // cases are removed. 295 usedKnobs.extensions = extensionsToRequest; 296 297 std::vector<const char*> extensionNames; 298 for (InstanceExt ext : IterateBitSet(extensionsToRequest)) { 299 const InstanceExtInfo& info = GetInstanceExtInfo(ext); 300 301 if (info.versionPromoted > mGlobalInfo.apiVersion) { 302 extensionNames.push_back(info.name); 303 } 304 } 305 306 VkApplicationInfo appInfo; 307 appInfo.sType = VK_STRUCTURE_TYPE_APPLICATION_INFO; 308 appInfo.pNext = nullptr; 309 appInfo.pApplicationName = nullptr; 310 appInfo.applicationVersion = 0; 311 appInfo.pEngineName = nullptr; 312 appInfo.engineVersion = 0; 313 // Vulkan 1.0 implementations were required to return VK_ERROR_INCOMPATIBLE_DRIVER if 314 // apiVersion was larger than 1.0. Meanwhile, as long as the instance supports at least 315 // Vulkan 1.1, an application can use different versions of Vulkan with an instance than 316 // it does with a device or physical device. So we should set apiVersion to Vulkan 1.0 317 // if the instance only supports Vulkan 1.0. Otherwise we set apiVersion to Vulkan 1.2, 318 // treat 1.2 as the highest API version dawn targets. 319 if (mGlobalInfo.apiVersion == VK_MAKE_VERSION(1, 0, 0)) { 320 appInfo.apiVersion = VK_MAKE_VERSION(1, 0, 0); 321 } else { 322 appInfo.apiVersion = VK_MAKE_VERSION(1, 2, 0); 323 } 324 325 VkInstanceCreateInfo createInfo; 326 createInfo.sType = VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO; 327 createInfo.pNext = nullptr; 328 createInfo.flags = 0; 329 createInfo.pApplicationInfo = &appInfo; 330 createInfo.enabledLayerCount = static_cast<uint32_t>(layerNames.size()); 331 createInfo.ppEnabledLayerNames = layerNames.data(); 332 createInfo.enabledExtensionCount = static_cast<uint32_t>(extensionNames.size()); 333 createInfo.ppEnabledExtensionNames = extensionNames.data(); 334 335 PNextChainBuilder createInfoChain(&createInfo); 336 337 // Register the debug callback for instance creation so we receive message for any errors 338 // (validation or other). 339 VkDebugUtilsMessengerCreateInfoEXT utilsMessengerCreateInfo; 340 if (usedKnobs.HasExt(InstanceExt::DebugUtils)) { 341 utilsMessengerCreateInfo.flags = 0; 342 utilsMessengerCreateInfo.messageSeverity = 343 VK_DEBUG_UTILS_MESSAGE_SEVERITY_ERROR_BIT_EXT | 344 VK_DEBUG_UTILS_MESSAGE_SEVERITY_WARNING_BIT_EXT; 345 utilsMessengerCreateInfo.messageType = VK_DEBUG_UTILS_MESSAGE_TYPE_GENERAL_BIT_EXT | 346 VK_DEBUG_UTILS_MESSAGE_TYPE_VALIDATION_BIT_EXT; 347 utilsMessengerCreateInfo.pfnUserCallback = OnInstanceCreationDebugUtilsCallback; 348 utilsMessengerCreateInfo.pUserData = nullptr; 349 350 createInfoChain.Add(&utilsMessengerCreateInfo, 351 VK_STRUCTURE_TYPE_DEBUG_UTILS_MESSENGER_CREATE_INFO_EXT); 352 } 353 354 // Try to turn on synchronization validation if the instance was created with backend 355 // validation enabled. 356 VkValidationFeaturesEXT validationFeatures; 357 VkValidationFeatureEnableEXT kEnableSynchronizationValidation = 358 VK_VALIDATION_FEATURE_ENABLE_SYNCHRONIZATION_VALIDATION_EXT; 359 if (instance->IsBackendValidationEnabled() && 360 usedKnobs.HasExt(InstanceExt::ValidationFeatures)) { 361 validationFeatures.enabledValidationFeatureCount = 1; 362 validationFeatures.pEnabledValidationFeatures = &kEnableSynchronizationValidation; 363 validationFeatures.disabledValidationFeatureCount = 0; 364 validationFeatures.pDisabledValidationFeatures = nullptr; 365 366 createInfoChain.Add(&validationFeatures, VK_STRUCTURE_TYPE_VALIDATION_FEATURES_EXT); 367 } 368 369 DAWN_TRY(CheckVkSuccess(mFunctions.CreateInstance(&createInfo, nullptr, &mInstance), 370 "vkCreateInstance")); 371 372 return usedKnobs; 373 } 374 RegisterDebugUtils()375 MaybeError VulkanInstance::RegisterDebugUtils() { 376 VkDebugUtilsMessengerCreateInfoEXT createInfo; 377 createInfo.sType = VK_STRUCTURE_TYPE_DEBUG_UTILS_MESSENGER_CREATE_INFO_EXT; 378 createInfo.pNext = nullptr; 379 createInfo.flags = 0; 380 createInfo.messageSeverity = VK_DEBUG_UTILS_MESSAGE_SEVERITY_ERROR_BIT_EXT | 381 VK_DEBUG_UTILS_MESSAGE_SEVERITY_WARNING_BIT_EXT; 382 createInfo.messageType = VK_DEBUG_UTILS_MESSAGE_TYPE_GENERAL_BIT_EXT | 383 VK_DEBUG_UTILS_MESSAGE_TYPE_VALIDATION_BIT_EXT; 384 createInfo.pfnUserCallback = OnDebugUtilsCallback; 385 createInfo.pUserData = nullptr; 386 387 return CheckVkSuccess(mFunctions.CreateDebugUtilsMessengerEXT( 388 mInstance, &createInfo, nullptr, &*mDebugUtilsMessenger), 389 "vkCreateDebugUtilsMessengerEXT"); 390 } 391 Backend(InstanceBase * instance)392 Backend::Backend(InstanceBase* instance) 393 : BackendConnection(instance, wgpu::BackendType::Vulkan) { 394 } 395 396 Backend::~Backend() = default; 397 DiscoverDefaultAdapters()398 std::vector<std::unique_ptr<AdapterBase>> Backend::DiscoverDefaultAdapters() { 399 AdapterDiscoveryOptions options; 400 auto result = DiscoverAdapters(&options); 401 if (result.IsError()) { 402 GetInstance()->ConsumedError(result.AcquireError()); 403 return {}; 404 } 405 return result.AcquireSuccess(); 406 } 407 DiscoverAdapters(const AdapterDiscoveryOptionsBase * optionsBase)408 ResultOrError<std::vector<std::unique_ptr<AdapterBase>>> Backend::DiscoverAdapters( 409 const AdapterDiscoveryOptionsBase* optionsBase) { 410 ASSERT(optionsBase->backendType == WGPUBackendType_Vulkan); 411 412 const AdapterDiscoveryOptions* options = 413 static_cast<const AdapterDiscoveryOptions*>(optionsBase); 414 415 std::vector<std::unique_ptr<AdapterBase>> adapters; 416 417 InstanceBase* instance = GetInstance(); 418 for (ICD icd : kICDs) { 419 if (options->forceSwiftShader && icd != ICD::SwiftShader) { 420 continue; 421 } 422 if (mVulkanInstances[icd] == nullptr && instance->ConsumedError([&]() -> MaybeError { 423 DAWN_TRY_ASSIGN(mVulkanInstances[icd], VulkanInstance::Create(instance, icd)); 424 return {}; 425 }())) { 426 // Instance failed to initialize. 427 continue; 428 } 429 const std::vector<VkPhysicalDevice>& physicalDevices = 430 mVulkanInstances[icd]->GetPhysicalDevices(); 431 for (uint32_t i = 0; i < physicalDevices.size(); ++i) { 432 std::unique_ptr<Adapter> adapter = std::make_unique<Adapter>( 433 instance, mVulkanInstances[icd].Get(), physicalDevices[i]); 434 if (instance->ConsumedError(adapter->Initialize())) { 435 continue; 436 } 437 adapters.push_back(std::move(adapter)); 438 } 439 } 440 return adapters; 441 } 442 Connect(InstanceBase * instance)443 BackendConnection* Connect(InstanceBase* instance) { 444 return new Backend(instance); 445 } 446 447 }} // namespace dawn_native::vulkan 448