Use a nested Vulkan Loader when GFXSTREAM_VK_ env var found

... and add script to build a version of the Vulkan Loader which
uses "GFXSTREAM" prefixed env vars and logging.

Bug: b/315317383
Test: deqp-vk w/ Gfxstream ICD
Change-Id: I7202379dbc27a9519b55434e889139810078020d
diff --git a/host/vulkan/VulkanDispatch.cpp b/host/vulkan/VulkanDispatch.cpp
index 7d0fedc..7d2b6a5 100644
--- a/host/vulkan/VulkanDispatch.cpp
+++ b/host/vulkan/VulkanDispatch.cpp
@@ -183,18 +183,56 @@
     void initialize(bool forTesting);
 
     static std::vector<std::string> getPossibleLoaderPathBasenames() {
-        return std::vector<std::string>{
 #if defined(__APPLE__)
-            "libvulkan.dylib"
-#elif defined(__linux)
-            "libvulkan.so",
-            "libvulkan.so.1",
+        return std::vector<std::string>{"libvulkan.dylib"};
+#elif defined(__linux__)
+        // When running applications with Gfxstream as the Vulkan ICD, i.e. with
+        //
+        //   App -> Vulkan Loader -> Gfxstream ICD -> Vulkan Loader -> Driver ICD
+        //
+        // Gfxstream needs to use a different nested loader library to avoid issues with
+        // conflating/deadlock with the first level loader. Detect that here and look for
+        // a "libvulkan_gfxstream" which can be generated with the provided
+        // scripts/build-nested-vulkan-loader.sh script.
+        const std::vector<std::string> nestedVulkanLoaderVars = {
+            "GFXSTREAM_VK_ADD_DRIVER_FILES",
+            "GFXSTREAM_VK_ADD_LAYER_PATH",
+            "GFXSTREAM_VK_DRIVER_FILES",
+            "GFXSTREAM_VK_ICD_FILENAMES",
+            "GFXSTREAM_VK_INSTANCE_LAYERS",
+            "GFXSTREAM_VK_LAYER_PATH",
+            "GFXSTREAM_VK_LAYER_PATH",
+            "GFXSTREAM_VK_LOADER_DEBUG",
+            "GFXSTREAM_VK_LOADER_DRIVERS_DISABLE",
+            "GFXSTREAM_VK_LOADER_DRIVERS_SELECT",
+            "GFXSTREAM_VK_LOADER_LAYERS_ALLOW",
+            "GFXSTREAM_VK_LOADER_LAYERS_DISABLE",
+            "GFXSTREAM_VK_LOADER_LAYERS_ENABLE",
+        };
+        bool usesNestedVulkanLoader = false;
+        for (const std::string& var : nestedVulkanLoaderVars) {
+            if (android::base::getEnvironmentVariable(var) != "") {
+                usesNestedVulkanLoader = true;
+                break;
+            }
+        }
+        if (usesNestedVulkanLoader) {
+            return std::vector<std::string>{
+                "libvulkan_gfxstream.so",
+                "libvulkan_gfxstream.so.1",
+            };
+        } else {
+            return std::vector<std::string>{
+                "libvulkan.so",
+                "libvulkan.so.1",
+            };
+        }
+
 #elif defined(_WIN32)
-            "vulkan-1.dll"
+        return std::vector<std::string>{"vulkan-1.dll"};
 #else
 #error "Unhandled platform in VulkanDispatchImpl."
 #endif
-        };
     }
 
     std::vector<std::string> getPossibleLoaderPaths() {
diff --git a/scripts/build-nested-vulkan-loader.sh b/scripts/build-nested-vulkan-loader.sh
new file mode 100755
index 0000000..b67362e
--- /dev/null
+++ b/scripts/build-nested-vulkan-loader.sh
@@ -0,0 +1,75 @@
+#!/bin/bash
+
+# Copyright 2023 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+set -o errexit
+
+WORKING_DIRECTORY="/tmp/gfxstream-nested-vulkan-loader"
+if [ ! -z "$1" ]; then
+    WORKING_DIRECTORY=$1
+fi
+
+if [ ! -d $WORKING_DIRECTORY ]; then
+    echo "Creating working directory $WORKING_DIRECTORY."
+    mkdir -p $WORKING_DIRECTORY
+    cd $WORKING_DIRECTORY
+    git clone https://github.com/KhronosGroup/Vulkan-Loader.git .
+    git checkout -b v1.3.273 tags/v1.3.273
+else
+    echo "Reusing existing working directory $WORKING_DIRECTORY."
+    cd $WORKING_DIRECTORY
+fi
+
+echo "Applying edits for Gfxstream."
+VK_LOADER_ENV_VARS=(
+    "VK_ADD_DRIVER_FILES"
+    "VK_ADD_LAYER_PATH"
+    "VK_DRIVER_FILES"
+    "VK_ICD_FILENAMES"
+    "VK_INSTANCE_LAYERS"
+    "VK_LAYER_PATH"
+    "VK_LAYER_PATH"
+    "VK_LOADER_DEBUG"
+    "VK_LOADER_DRIVERS_DISABLE"
+    "VK_LOADER_DRIVERS_SELECT"
+    "VK_LOADER_LAYERS_ALLOW"
+    "VK_LOADER_LAYERS_DISABLE"
+    "VK_LOADER_LAYERS_ENABLE"
+)
+for var in ${VK_LOADER_ENV_VARS[@]}; do
+    echo "- Replacing occurrences of \"$var\" with \"GFXSTREAM_$var\"."
+    sed -i -e "s/\\\"$var\\\"/\\\"GFXSTREAM_$var\\\"/g" loader/*.c
+    sed -i -e "s/\\\"$var\\\"/\\\"GFXSTREAM_$var\\\"/g" loader/*.h
+done
+echo "- Adding in \"GFXSTREAM\" prefix to logging."
+sed -i -e "s/    fputs(cmd_line_msg, stderr);/    fputs(\\\"GFXSTREAM-LOADER: \\\", stderr); fputs(cmd_line_msg, stderr);/g" loader/log.c
+
+if [ ! -d build ]; then
+    echo "Creating build directory."
+    mkdir build
+    cd build
+    python ../scripts/update_deps.py
+    cmake -C helper.cmake -D CMAKE_BUILD_TYPE=Debug ..
+else
+    echo "Reusing existing build directory."
+    cd build
+fi
+
+echo "Building."
+cmake --build .
+
+cp loader/libvulkan.so loader/libvulkan_gfxstream.so
+
+echo "Use export LD_LIBRARY_PATH=\"\$LD_LIBRARY_PATH:$(pwd)/loader\" to add to your path."