Fixes Dawn to use custom Vulkan loader on Linux systems.

- Adds system utility to get the module directory for dawn native.
- Updates Vulkan backend to use the module directory to find loader.
- Test ran on NVIDIA GTX 1660 here: https://chromium-swarm.appspot.com/task?id=576d77991add7c10.

Bug: dawn:1191
Change-Id: I7c577008b5252ac94f38c8cdb56f7e8d8a0aa956
Reviewed-on: https://dawn-review.googlesource.com/c/dawn/+/70860
Commit-Queue: Loko Kung <lokokung@google.com>
Reviewed-by: Corentin Wallez <cwallez@chromium.org>
diff --git a/scripts/dawn_features.gni b/scripts/dawn_features.gni
index 5a80f4d..03d3449 100644
--- a/scripts/dawn_features.gni
+++ b/scripts/dawn_features.gni
@@ -15,8 +15,8 @@
 import("//build_overrides/build.gni")
 
 if (build_with_chromium) {
-  import("//build/config/sanitizers/sanitizers.gni")
   import("//build/config/ozone.gni")
+  import("//build/config/sanitizers/sanitizers.gni")
 
   dawn_use_x11 = ozone_platform_x11
 } else {
@@ -97,7 +97,8 @@
 
   # Uses our built version of the Vulkan loader on platforms where we can't
   # assume to have one present at the system level.
-  dawn_enable_vulkan_loader = dawn_enable_vulkan && is_mac
+  dawn_enable_vulkan_loader =
+      dawn_enable_vulkan && (is_mac || (is_linux && !is_android))
 }
 
 # UWP only supports CoreWindow for windowing
diff --git a/scripts/dawn_overrides_with_defaults.gni b/scripts/dawn_overrides_with_defaults.gni
index 4b30ec2..eb3de8e 100644
--- a/scripts/dawn_overrides_with_defaults.gni
+++ b/scripts/dawn_overrides_with_defaults.gni
@@ -63,8 +63,7 @@
 }
 
 if (!defined(dawn_vulkan_loader_dir)) {
-  # Default to the Vulkan loader not being available.
-  dawn_vulkan_loader_dir = ""
+  dawn_vulkan_loader_dir = "//third_party/vulkan-deps/vulkan-loader/src"
 }
 
 if (!defined(dawn_vulkan_validation_layers_dir)) {
diff --git a/src/common/SystemUtils.cpp b/src/common/SystemUtils.cpp
index b56a08c..9dc066a 100644
--- a/src/common/SystemUtils.cpp
+++ b/src/common/SystemUtils.cpp
@@ -21,10 +21,12 @@
 #    include <Windows.h>
 #    include <vector>
 #elif defined(DAWN_PLATFORM_LINUX)
+#    include <dlfcn.h>
 #    include <limits.h>
 #    include <unistd.h>
 #    include <cstdlib>
 #elif defined(DAWN_PLATFORM_MACOS) || defined(DAWN_PLATFORM_IOS)
+#    include <dlfcn.h>
 #    include <mach-o/dyld.h>
 #    include <vector>
 #endif
@@ -136,6 +138,45 @@
     return lastPathSepLoc != std::string::npos ? exePath.substr(0, lastPathSepLoc + 1) : "";
 }
 
+#if defined(DAWN_PLATFORM_LINUX) || defined(DAWN_PLATFORM_MACOS) || defined(DAWN_PLATFORM_IOS)
+std::string GetModulePath() {
+    static int placeholderSymbol = 0;
+    Dl_info dlInfo;
+    if (dladdr(&placeholderSymbol, &dlInfo) == 0) {
+        return "";
+    }
+
+    std::array<char, PATH_MAX> absolutePath;
+    if (realpath(dlInfo.dli_fname, absolutePath.data()) == NULL) {
+        return "";
+    }
+    return absolutePath.data();
+}
+#elif defined(DAWN_PLATFORM_WINDOWS)
+std::string GetModulePath() {
+    UNREACHABLE();
+    return "";
+}
+#elif defined(DAWN_PLATFORM_FUCHSIA)
+std::string GetModulePath() {
+    UNREACHABLE();
+    return "";
+}
+#elif defined(DAWN_PLATFORM_EMSCRIPTEN)
+std::string GetModulePath() {
+    UNREACHABLE();
+    return "";
+}
+#else
+#    error "Implement GetModulePath for your platform."
+#endif
+
+std::string GetModuleDirectory() {
+    std::string modPath = GetModulePath();
+    size_t lastPathSepLoc = modPath.find_last_of(GetPathSeparator());
+    return lastPathSepLoc != std::string::npos ? modPath.substr(0, lastPathSepLoc + 1) : "";
+}
+
 // ScopedEnvironmentVar
 
 ScopedEnvironmentVar::ScopedEnvironmentVar(const char* variableName, const char* value)
diff --git a/src/common/SystemUtils.h b/src/common/SystemUtils.h
index 63cf541..875c5aa 100644
--- a/src/common/SystemUtils.h
+++ b/src/common/SystemUtils.h
@@ -24,7 +24,9 @@
 // was present.
 std::pair<std::string, bool> GetEnvironmentVar(const char* variableName);
 bool SetEnvironmentVar(const char* variableName, const char* value);
+// Directories are always returned with a trailing path separator.
 std::string GetExecutableDirectory();
+std::string GetModuleDirectory();
 
 #ifdef DAWN_PLATFORM_MACOS
 void GetMacOSVersion(int32_t* majorVersion, int32_t* minorVersion = nullptr);
diff --git a/src/dawn_native/BUILD.gn b/src/dawn_native/BUILD.gn
index 465aa0a..cd977fa 100644
--- a/src/dawn_native/BUILD.gn
+++ b/src/dawn_native/BUILD.gn
@@ -679,6 +679,7 @@
     }
     if (enable_vulkan_loader) {
       data_deps += [ "${dawn_vulkan_loader_dir}:libvulkan" ]
+      defines += [ "DAWN_ENABLE_VULKAN_LOADER" ]
     }
     if (use_swiftshader) {
       data_deps += [
diff --git a/src/dawn_native/vulkan/BackendVk.cpp b/src/dawn_native/vulkan/BackendVk.cpp
index 484c246..c055249 100644
--- a/src/dawn_native/vulkan/BackendVk.cpp
+++ b/src/dawn_native/vulkan/BackendVk.cpp
@@ -111,13 +111,25 @@
     }
 
     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)) {
+        // 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 defined(DAWN_ENABLE_VULKAN_LOADER)
+        // If enabled, we use our own built Vulkan loader by specifying an absolute path to the
+        // shared library. Note that when we are currently getting the absolute path for the custom
+        // loader by getting the path to the dawn native library and traversing relative from there.
+        // This has implications for dawn tests because some of them are linking statically to
+        // dawn_native which means the "module" is actually the test as well. If the directory
+        // location of the tests change w.r.t the shared lib then this may break. Essentially we are
+        // assuming that our custom built Vulkan loader will always be in the same directory as the
+        // shared dawn native library and all test binaries that link statically.
+        const std::string resolvedVulkanLibPath = GetModuleDirectory() + kVulkanLibName;
+#else
+        const std::string resolvedVulkanLibPath = kVulkanLibName;
+#endif  // defined(DAWN_ENABLE_VULKAN_LOADER)
+        if (mVulkanLib.Open(resolvedVulkanLibPath)) {
             return {};
         }
-        dawn::WarningLog() << std::string("Couldn't open ") + kVulkanLibName;
+        dawn::WarningLog() << std::string("Couldn't open ") + resolvedVulkanLibPath;
 
         // If |useSwiftshader == true|, fallback and try to directly load the Swiftshader
         // library.