Load Swiftshader Vulkan separately from the system Vulkan

This enables both Swiftshader Vulkan and the system Vulkan drivers
to be discovered and used simultaneously.

Bug: dawn:396, dawn:283
Change-Id: I580ca26c12296fe13c0331c4faf6e7a4520664e4
Reviewed-on: https://dawn-review.googlesource.com/c/dawn/+/21041
Commit-Queue: Austin Eng <enga@chromium.org>
Reviewed-by: Corentin Wallez <cwallez@chromium.org>
diff --git a/src/dawn_native/Instance.cpp b/src/dawn_native/Instance.cpp
index 982c979..c7a1e31 100644
--- a/src/dawn_native/Instance.cpp
+++ b/src/dawn_native/Instance.cpp
@@ -45,7 +45,7 @@
 #endif  // defined(DAWN_ENABLE_BACKEND_OPENGL)
 #if defined(DAWN_ENABLE_BACKEND_VULKAN)
     namespace vulkan {
-        BackendConnection* Connect(InstanceBase* instance);
+        BackendConnection* Connect(InstanceBase* instance, bool useSwiftshader);
     }
 #endif  // defined(DAWN_ENABLE_BACKEND_VULKAN)
 
@@ -136,7 +136,15 @@
         Register(metal::Connect(this), wgpu::BackendType::Metal);
 #endif  // defined(DAWN_ENABLE_BACKEND_METAL)
 #if defined(DAWN_ENABLE_BACKEND_VULKAN)
-        Register(vulkan::Connect(this), wgpu::BackendType::Vulkan);
+        // TODO(https://github.com/KhronosGroup/Vulkan-Loader/issues/287):
+        // When we can load SwiftShader in parallel with the system driver, we should create the
+        // backend only once and expose SwiftShader as an additional adapter. For now, we create two
+        // VkInstances, one from SwiftShader, and one from the system. Note: If the Vulkan driver
+        // *is* SwiftShader, then this would load SwiftShader twice.
+        Register(vulkan::Connect(this, false), wgpu::BackendType::Vulkan);
+#    if defined(DAWN_ENABLE_SWIFTSHADER)
+        Register(vulkan::Connect(this, true), wgpu::BackendType::Vulkan);
+#    endif  // defined(DAWN_ENABLE_SWIFTSHADER)
 #endif  // defined(DAWN_ENABLE_BACKEND_VULKAN)
 #if defined(DAWN_ENABLE_BACKEND_OPENGL)
         Register(opengl::Connect(this), wgpu::BackendType::OpenGL);
@@ -148,31 +156,29 @@
         mBackendsConnected = true;
     }
 
-    ResultOrError<BackendConnection*> InstanceBase::FindBackend(wgpu::BackendType type) {
-        for (std::unique_ptr<BackendConnection>& backend : mBackends) {
-            if (backend->GetType() == type) {
-                return backend.get();
-            }
-        }
-
-        return DAWN_VALIDATION_ERROR("Backend isn't present.");
-    }
-
     MaybeError InstanceBase::DiscoverAdaptersInternal(const AdapterDiscoveryOptionsBase* options) {
         EnsureBackendConnections();
 
-        BackendConnection* backend;
-        DAWN_TRY_ASSIGN(backend, FindBackend(static_cast<wgpu::BackendType>(options->backendType)));
+        bool foundBackend = false;
+        for (std::unique_ptr<BackendConnection>& backend : mBackends) {
+            if (backend->GetType() != static_cast<wgpu::BackendType>(options->backendType)) {
+                continue;
+            }
+            foundBackend = true;
 
-        std::vector<std::unique_ptr<AdapterBase>> newAdapters;
-        DAWN_TRY_ASSIGN(newAdapters, backend->DiscoverAdapters(options));
+            std::vector<std::unique_ptr<AdapterBase>> newAdapters;
+            DAWN_TRY_ASSIGN(newAdapters, backend->DiscoverAdapters(options));
 
-        for (std::unique_ptr<AdapterBase>& adapter : newAdapters) {
-            ASSERT(adapter->GetBackendType() == backend->GetType());
-            ASSERT(adapter->GetInstance() == this);
-            mAdapters.push_back(std::move(adapter));
+            for (std::unique_ptr<AdapterBase>& adapter : newAdapters) {
+                ASSERT(adapter->GetBackendType() == backend->GetType());
+                ASSERT(adapter->GetInstance() == this);
+                mAdapters.push_back(std::move(adapter));
+            }
         }
 
+        if (!foundBackend) {
+            return DAWN_VALIDATION_ERROR("Backend isn't present.");
+        }
         return {};
     }
 
diff --git a/src/dawn_native/Instance.h b/src/dawn_native/Instance.h
index f297c03..0ade98b 100644
--- a/src/dawn_native/Instance.h
+++ b/src/dawn_native/Instance.h
@@ -81,9 +81,6 @@
         // Lazily creates connections to all backends that have been compiled.
         void EnsureBackendConnections();
 
-        // Finds the BackendConnection for `type` or returns an error.
-        ResultOrError<BackendConnection*> FindBackend(wgpu::BackendType type);
-
         MaybeError DiscoverAdaptersInternal(const AdapterDiscoveryOptionsBase* options);
 
         bool mBackendsConnected = false;
diff --git a/src/dawn_native/vulkan/BackendVk.cpp b/src/dawn_native/vulkan/BackendVk.cpp
index d03bffd..eef62e8 100644
--- a/src/dawn_native/vulkan/BackendVk.cpp
+++ b/src/dawn_native/vulkan/BackendVk.cpp
@@ -81,42 +81,61 @@
         return mGlobalInfo;
     }
 
-    MaybeError Backend::LoadVulkan() {
-#if defined(DAWN_ENABLE_VULKAN_VALIDATION_LAYERS)
-        if (GetInstance()->IsBackendValidationEnabled()) {
-            std::string vkDataDir = GetExecutableDirectory() + DAWN_VK_DATA_DIR;
-            if (!SetEnvironmentVar("VK_LAYER_PATH", vkDataDir.c_str())) {
-                return DAWN_INTERNAL_ERROR("Couldn't set VK_LAYER_PATH");
-            }
-        }
-#endif
-#if defined(DAWN_SWIFTSHADER_VK_ICD_JSON)
-        std::string fullSwiftshaderICDPath =
-            GetExecutableDirectory() + DAWN_SWIFTSHADER_VK_ICD_JSON;
-        if (!SetEnvironmentVar("VK_ICD_FILENAMES", fullSwiftshaderICDPath.c_str())) {
-            return DAWN_INTERNAL_ERROR("Couldn't set VK_ICD_FILENAMES");
-        }
-#endif
-
+    MaybeError Backend::LoadVulkan(bool useSwiftshader) {
+        // First try to load the system Vulkan driver, if that fails,
+        // try to load with Swiftshader. Note: The system driver could potentially be Swiftshader
+        // if it was installed.
         if (mVulkanLib.Open(kVulkanLibName)) {
             return {};
         }
         dawn::WarningLog() << std::string("Couldn't open ") + kVulkanLibName;
 
+        // If |useSwiftshader == true|, fallback and try to directly load the Swiftshader
+        // library.
+        if (useSwiftshader) {
 #if defined(DAWN_ENABLE_SWIFTSHADER)
-        if (strcmp(kVulkanLibName, kSwiftshaderLibName) != 0) {
             if (mVulkanLib.Open(kSwiftshaderLibName)) {
                 return {};
             }
             dawn::WarningLog() << std::string("Couldn't open ") + kSwiftshaderLibName;
+#else
+            UNREACHABLE();
+#endif  // defined(DAWN_ENABLE_SWIFTSHADER)
         }
-#endif
 
         return DAWN_INTERNAL_ERROR("Couldn't load Vulkan");
     }
 
-    MaybeError Backend::Initialize() {
-        DAWN_TRY(LoadVulkan());
+    MaybeError Backend::Initialize(bool useSwiftshader) {
+        DAWN_TRY(LoadVulkan(useSwiftshader));
+
+        // TODO(crbug.com/dawn/406): In order to not modify the environment variables of
+        // the rest of an application embedding Dawn, we should set these only
+        // in the scope of this function. See ANGLE's ScopedVkLoaderEnvironment
+        if (useSwiftshader) {
+#if defined(DAWN_SWIFTSHADER_VK_ICD_JSON)
+            std::string fullSwiftshaderICDPath =
+                GetExecutableDirectory() + DAWN_SWIFTSHADER_VK_ICD_JSON;
+            if (!SetEnvironmentVar("VK_ICD_FILENAMES", fullSwiftshaderICDPath.c_str())) {
+                return DAWN_INTERNAL_ERROR("Couldn't set VK_ICD_FILENAMES");
+            }
+#else
+            dawn::WarningLog() << "Swiftshader enabled but Dawn was not built with "
+                                  "DAWN_SWIFTSHADER_VK_ICD_JSON.";
+#endif
+        }
+
+        if (GetInstance()->IsBackendValidationEnabled()) {
+#if defined(DAWN_ENABLE_VULKAN_VALIDATION_LAYERS)
+            std::string vkDataDir = GetExecutableDirectory() + DAWN_VK_DATA_DIR;
+            if (!SetEnvironmentVar("VK_LAYER_PATH", vkDataDir.c_str())) {
+                return DAWN_INTERNAL_ERROR("Couldn't set VK_LAYER_PATH");
+            }
+#else
+            dawn::WarningLog() << "Backend validation enabled but Dawn was not built with "
+                                  "DAWN_ENABLE_VULKAN_VALIDATION_LAYERS.";
+#endif
+        }
 
         DAWN_TRY(mFunctions.LoadGlobalProcs(mVulkanLib));
 
@@ -300,10 +319,10 @@
         return VK_FALSE;
     }
 
-    BackendConnection* Connect(InstanceBase* instance) {
+    BackendConnection* Connect(InstanceBase* instance, bool useSwiftshader) {
         Backend* backend = new Backend(instance);
 
-        if (instance->ConsumedError(backend->Initialize())) {
+        if (instance->ConsumedError(backend->Initialize(useSwiftshader))) {
             delete backend;
             return nullptr;
         }
diff --git a/src/dawn_native/vulkan/BackendVk.h b/src/dawn_native/vulkan/BackendVk.h
index 57a25b2..7d22b67 100644
--- a/src/dawn_native/vulkan/BackendVk.h
+++ b/src/dawn_native/vulkan/BackendVk.h
@@ -32,12 +32,12 @@
         VkInstance GetVkInstance() const;
         const VulkanGlobalInfo& GetGlobalInfo() const;
 
-        MaybeError Initialize();
+        MaybeError Initialize(bool useSwiftshader);
 
         std::vector<std::unique_ptr<AdapterBase>> DiscoverDefaultAdapters() override;
 
       private:
-        MaybeError LoadVulkan();
+        MaybeError LoadVulkan(bool useSwiftshader);
         ResultOrError<VulkanGlobalKnobs> CreateInstance();
 
         MaybeError RegisterDebugReport();