Vulkan: Adding null driver as device option

Bug: angleproject:2159

Plumbing to allow VK Mock ICD to be selected as the Vulkan driver.

Adding new ANGLE env var "ANGLE_VK_ICD_JSON" to point to json file
of mock ICD in angle and use this to override Vk loader ICD search
path.

At Vulkan renderer initialization time, enable the Mock ICD if device
is EGL_PLATFORM_ANGLE_DEVICE_TYPE_NULL_ANGLE.

Default physicalDevice is still the first detected device.
If Mock ICD is requested but not available, fall back to the first
device that was detected.

Added a VULKAN_NULL() testing config that uses Vulkan as renderer but
NULL as device. Turned on Vulkan NULL testing for DrawCallPerf.

Change-Id: I04e15c14e998448f8c98f9fd72e796389f297993
Reviewed-on: https://chromium-review.googlesource.com/961494
Commit-Queue: Jamie Madill <jmadill@chromium.org>
Reviewed-by: Jamie Madill <jmadill@chromium.org>
diff --git a/src/libANGLE/renderer/vulkan/RendererVk.cpp b/src/libANGLE/renderer/vulkan/RendererVk.cpp
index 4e0d576..b83276d 100644
--- a/src/libANGLE/renderer/vulkan/RendererVk.cpp
+++ b/src/libANGLE/renderer/vulkan/RendererVk.cpp
@@ -28,6 +28,14 @@
 #include "libANGLE/renderer/vulkan/vk_format_utils.h"
 #include "platform/Platform.h"
 
+// Consts
+namespace
+{
+const uint32_t kMockVendorID     = 0xba5eba11;
+const uint32_t kMockDeviceID     = 0xf005ba11;
+constexpr char kMockDeviceName[] = "Vulkan Mock Device";
+}  // anonymous namespace
+
 namespace rx
 {
 
@@ -125,7 +133,7 @@
         // Override environment variable to use the ANGLE layers.
         if (mEnableValidationLayers)
         {
-            if (!angle::PrependPathToEnvironmentVar(g_VkLoaderLayersPathEnv, ANGLE_VK_LAYERS_DIR))
+            if (!angle::PrependPathToEnvironmentVar(g_VkLoaderLayersPathEnv, ANGLE_VK_DATA_DIR))
             {
                 ERR() << "Error setting environment for Vulkan layers init.";
                 mEnableValidationLayers = false;
@@ -251,11 +259,53 @@
     mPhysicalDevice = VK_NULL_HANDLE;
 }
 
+void ChoosePhysicalDevice(const std::vector<VkPhysicalDevice> &physicalDevices,
+                          bool preferMockICD,
+                          VkPhysicalDevice *physicalDeviceOut,
+                          VkPhysicalDeviceProperties *physicalDevicePropertiesOut)
+{
+    ASSERT(!physicalDevices.empty());
+    if (preferMockICD)
+    {
+        for (const VkPhysicalDevice &physicalDevice : physicalDevices)
+        {
+            vkGetPhysicalDeviceProperties(physicalDevice, physicalDevicePropertiesOut);
+            if ((kMockVendorID == physicalDevicePropertiesOut->vendorID) &&
+                (kMockDeviceID == physicalDevicePropertiesOut->deviceID) &&
+                (strcmp(kMockDeviceName, physicalDevicePropertiesOut->deviceName) == 0))
+            {
+                *physicalDeviceOut = physicalDevice;
+                return;
+            }
+        }
+        WARN() << "Vulkan Mock Driver was requested but Mock Device was not found. Using default "
+                  "physicalDevice instead.";
+    }
+
+    // Fall back to first device.
+    *physicalDeviceOut = physicalDevices[0];
+    vkGetPhysicalDeviceProperties(*physicalDeviceOut, physicalDevicePropertiesOut);
+}
+
 vk::Error RendererVk::initialize(const egl::AttributeMap &attribs, const char *wsiName)
 {
     ScopedVkLoaderEnvironment scopedEnvironment(ShouldUseDebugLayers(attribs));
     mEnableValidationLayers = scopedEnvironment.canEnableValidationLayers();
 
+    bool enableNullDriver = false;
+#if !defined(ANGLE_PLATFORM_ANDROID)
+    // Mock ICD does not currently run on Android
+    enableNullDriver = (attribs.get(EGL_PLATFORM_ANGLE_DEVICE_TYPE_ANGLE,
+                                    EGL_PLATFORM_ANGLE_DEVICE_TYPE_HARDWARE_ANGLE) ==
+                        EGL_PLATFORM_ANGLE_DEVICE_TYPE_NULL_ANGLE);
+    if (enableNullDriver)
+    {
+        // Override environment variable to use built Mock ICD
+        // ANGLE_VK_ICD_JSON gets set to the built mock ICD in BUILD.gn
+        ANGLE_VK_CHECK(angle::SetEnvironmentVar(g_VkICDPathEnv, ANGLE_VK_ICD_JSON),
+                       VK_ERROR_INITIALIZATION_FAILED);
+    }
+#endif  // !defined(ANGLE_PLATFORM_ANDROID)
     // Gather global layer properties.
     uint32_t instanceLayerCount = 0;
     ANGLE_VK_TRY(vkEnumerateInstanceLayerProperties(&instanceLayerCount, nullptr));
@@ -348,10 +398,11 @@
     ANGLE_VK_CHECK(physicalDeviceCount > 0, VK_ERROR_INITIALIZATION_FAILED);
 
     // TODO(jmadill): Handle multiple physical devices. For now, use the first device.
-    physicalDeviceCount = 1;
-    ANGLE_VK_TRY(vkEnumeratePhysicalDevices(mInstance, &physicalDeviceCount, &mPhysicalDevice));
-
-    vkGetPhysicalDeviceProperties(mPhysicalDevice, &mPhysicalDeviceProperties);
+    std::vector<VkPhysicalDevice> physicalDevices(physicalDeviceCount);
+    ANGLE_VK_TRY(
+        vkEnumeratePhysicalDevices(mInstance, &physicalDeviceCount, physicalDevices.data()));
+    ChoosePhysicalDevice(physicalDevices, enableNullDriver, &mPhysicalDevice,
+                         &mPhysicalDeviceProperties);
 
     // Ensure we can find a graphics queue family.
     uint32_t queueCount = 0;
diff --git a/src/libANGLE/renderer/vulkan/vk_utils.cpp b/src/libANGLE/renderer/vulkan/vk_utils.cpp
index 5fb76a4..851b416 100644
--- a/src/libANGLE/renderer/vulkan/vk_utils.cpp
+++ b/src/libANGLE/renderer/vulkan/vk_utils.cpp
@@ -198,6 +198,7 @@
 }  // anonymous namespace
 
 const char *g_VkLoaderLayersPathEnv    = "VK_LAYER_PATH";
+const char *g_VkICDPathEnv             = "VK_ICD_FILENAMES";
 
 const char *VulkanResultString(VkResult result)
 {
diff --git a/src/libANGLE/renderer/vulkan/vk_utils.h b/src/libANGLE/renderer/vulkan/vk_utils.h
index a44cb2a..fe3afc7 100644
--- a/src/libANGLE/renderer/vulkan/vk_utils.h
+++ b/src/libANGLE/renderer/vulkan/vk_utils.h
@@ -74,6 +74,7 @@
                                   uint32_t *enabledLayerCount);
 
 extern const char *g_VkLoaderLayersPathEnv;
+extern const char *g_VkICDPathEnv;
 
 enum class TextureDimension
 {
diff --git a/src/tests/perf_tests/DrawCallPerf.cpp b/src/tests/perf_tests/DrawCallPerf.cpp
index 2b7280b..31db0cd 100644
--- a/src/tests/perf_tests/DrawCallPerf.cpp
+++ b/src/tests/perf_tests/DrawCallPerf.cpp
@@ -190,7 +190,9 @@
                        DrawArrays(DrawCallPerfOpenGLOrGLESParams(true, true), false),
                        DrawArrays(DrawCallPerfOpenGLOrGLESParams(true, false), true),
                        DrawArrays(DrawCallPerfValidationOnly(), false),
-                       DrawArrays(DrawCallPerfVulkanParams(false), false),
-                       DrawArrays(DrawCallPerfVulkanParams(false), true));
+                       DrawArrays(DrawCallPerfVulkanParams(true, false), true),
+                       DrawArrays(DrawCallPerfVulkanParams(true, false), false),
+                       DrawArrays(DrawCallPerfVulkanParams(false, false), false),
+                       DrawArrays(DrawCallPerfVulkanParams(false, false), true));
 
 } // namespace
diff --git a/src/tests/perf_tests/DrawCallPerfParams.cpp b/src/tests/perf_tests/DrawCallPerfParams.cpp
index f46aeb6..609a517 100644
--- a/src/tests/perf_tests/DrawCallPerfParams.cpp
+++ b/src/tests/perf_tests/DrawCallPerfParams.cpp
@@ -71,10 +71,10 @@
     return params;
 }
 
-DrawCallPerfParams DrawCallPerfVulkanParams(bool renderToTexture)
+DrawCallPerfParams DrawCallPerfVulkanParams(bool useNullDevice, bool renderToTexture)
 {
     DrawCallPerfParams params;
-    params.eglParameters = VULKAN();
+    params.eglParameters = useNullDevice ? VULKAN_NULL() : VULKAN();
     params.useFBO        = renderToTexture;
     return params;
 }
diff --git a/src/tests/perf_tests/DrawCallPerfParams.h b/src/tests/perf_tests/DrawCallPerfParams.h
index a9c2da8..d27ca92 100644
--- a/src/tests/perf_tests/DrawCallPerfParams.h
+++ b/src/tests/perf_tests/DrawCallPerfParams.h
@@ -38,6 +38,6 @@
 DrawCallPerfParams DrawCallPerfD3D9Params(bool useNullDevice, bool renderToTexture);
 DrawCallPerfParams DrawCallPerfOpenGLOrGLESParams(bool useNullDevice, bool renderToTexture);
 DrawCallPerfParams DrawCallPerfValidationOnly();
-DrawCallPerfParams DrawCallPerfVulkanParams(bool renderToTexture);
+DrawCallPerfParams DrawCallPerfVulkanParams(bool useNullDevice, bool renderToTexture);
 
 #endif  // TESTS_PERF_TESTS_DRAW_CALL_PERF_PARAMS_H_
diff --git a/src/tests/test_utils/angle_test_configs.cpp b/src/tests/test_utils/angle_test_configs.cpp
index 9aa6c72..e877752 100644
--- a/src/tests/test_utils/angle_test_configs.cpp
+++ b/src/tests/test_utils/angle_test_configs.cpp
@@ -398,6 +398,12 @@
     return EGLPlatformParameters(EGL_PLATFORM_ANGLE_TYPE_VULKAN_ANGLE);
 }
 
+EGLPlatformParameters VULKAN_NULL()
+{
+    return EGLPlatformParameters(EGL_PLATFORM_ANGLE_TYPE_VULKAN_ANGLE, EGL_DONT_CARE, EGL_DONT_CARE,
+                                 EGL_PLATFORM_ANGLE_DEVICE_TYPE_NULL_ANGLE);
+}
+
 }  // namespace egl_platform
 
 // ANGLE tests platforms
@@ -671,9 +677,19 @@
     return PlatformParameters(1, 0, egl_platform::VULKAN());
 }
 
+PlatformParameters ES1_VULKAN_NULL()
+{
+    return PlatformParameters(1, 0, egl_platform::VULKAN_NULL());
+}
+
 PlatformParameters ES2_VULKAN()
 {
     return PlatformParameters(2, 0, egl_platform::VULKAN());
 }
 
+PlatformParameters ES2_VULKAN_NULL()
+{
+    return PlatformParameters(2, 0, egl_platform::VULKAN_NULL());
+}
+
 }  // namespace angle
diff --git a/src/tests/test_utils/angle_test_configs.h b/src/tests/test_utils/angle_test_configs.h
index c1fda37..e151196 100644
--- a/src/tests/test_utils/angle_test_configs.h
+++ b/src/tests/test_utils/angle_test_configs.h
@@ -99,6 +99,7 @@
 EGLPlatformParameters OPENGL_OR_GLES(bool useNullDevice);
 
 EGLPlatformParameters VULKAN();
+EGLPlatformParameters VULKAN_NULL();
 
 }  // namespace egl_platform
 
@@ -167,7 +168,9 @@
 PlatformParameters ES31_NULL();
 
 PlatformParameters ES1_VULKAN();
+PlatformParameters ES1_VULKAN_NULL();
 PlatformParameters ES2_VULKAN();
+PlatformParameters ES2_VULKAN_NULL();
 
 }  // namespace angle
 
diff --git a/third_party/vulkan-validation-layers/BUILD.gn b/third_party/vulkan-validation-layers/BUILD.gn
index d84a266..5a52d6e 100644
--- a/third_party/vulkan-validation-layers/BUILD.gn
+++ b/third_party/vulkan-validation-layers/BUILD.gn
@@ -259,7 +259,8 @@
     "src/loader",
   ]
   defines = [
-    "ANGLE_VK_LAYERS_DIR=\"$data_dir\"",
+    "ANGLE_VK_DATA_DIR=\"$data_dir\"",
+    "ANGLE_VK_ICD_JSON=\"$data_dir/VkICD_mock_icd.json\"",
     "API_NAME=\"Vulkan\"",
   ]
 
@@ -524,12 +525,12 @@
 
     # The layer JSON files are part of the necessary data deps.
     outputs = [
-      "$root_out_dir/$data_dir/icd/VkICD_mock_icd.json",
+      "$root_out_dir/$data_dir/VkICD_mock_icd.json",
     ]
     data = [
-      "$root_out_dir/$data_dir/icd/VkICD_mock_icd.json",
+      "$root_out_dir/$data_dir/VkICD_mock_icd.json",
     ]
-    args += [ rebase_path("$root_out_dir/$data_dir/icd", root_build_dir) ]
+    args += [ rebase_path("$root_out_dir/$data_dir", root_build_dir) ]
   }
 }