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;