Vulkan: Add support for layer extensions.

This commit generalizes the way layers are handled to be more like
extensions, and adds support for gathering and enabling layer
extensions.

This is in preparation for using the VK_EXT_validation_features
extension to enable barrier validation.

Also adds logic to use the Fuchsia swapchain layer when it is available.
It seems to have been removed by mistake some time ago.

Bug: dawn:635

Change-Id: I8e5776d546ddd7940238465c7b0f187d8dd3c5bc
Reviewed-on: https://dawn-review.googlesource.com/c/dawn/+/38104
Commit-Queue: Corentin Wallez <cwallez@chromium.org>
Auto-Submit: Corentin Wallez <cwallez@chromium.org>
Reviewed-by: Austin Eng <enga@chromium.org>
diff --git a/src/dawn_native/vulkan/BackendVk.cpp b/src/dawn_native/vulkan/BackendVk.cpp
index 2543828..6e431f1 100644
--- a/src/dawn_native/vulkan/BackendVk.cpp
+++ b/src/dawn_native/vulkan/BackendVk.cpp
@@ -206,37 +206,39 @@
     ResultOrError<VulkanGlobalKnobs> Backend::CreateInstance() {
         VulkanGlobalKnobs usedKnobs = {};
         std::vector<const char*> layerNames;
+        InstanceExtSet extensionsToRequest = mGlobalInfo.extensions;
 
-        // vktrace works by instering a layer, but we hide it behind a macro due to the vktrace
+        auto UseLayerIfAvailable = [&](VulkanLayer layer) {
+            if (mGlobalInfo.layers[layer]) {
+                layerNames.push_back(GetVulkanLayerInfo(layer).name);
+                usedKnobs.layers.set(layer, true);
+                extensionsToRequest |= mGlobalInfo.layerExtensions[layer];
+            }
+        };
+
+        // vktrace works by instering a layer, but we hide it behind a macro because the vktrace
         // layer crashes when used without vktrace server started. See this vktrace issue:
         // https://github.com/LunarG/VulkanTools/issues/254
         // Also it is good to put it in first position so that it doesn't see Vulkan calls inserted
         // by other layers.
 #if defined(DAWN_USE_VKTRACE)
-        if (mGlobalInfo.vktrace) {
-            layerNames.push_back(kLayerNameLunargVKTrace);
-            usedKnobs.vktrace = true;
-        }
+        UseLayerIfAvailable(VulkanLayer::LunargVkTrace);
 #endif
         // RenderDoc installs a layer at the system level for its capture but we don't want to use
         // it unless we are debugging in RenderDoc so we hide it behind a macro.
 #if defined(DAWN_USE_RENDERDOC)
-        if (mGlobalInfo.renderDocCapture) {
-            layerNames.push_back(kLayerNameRenderDocCapture);
-            usedKnobs.renderDocCapture = true;
-        }
+        UseLayerIfAvailable(VulkanLayer::RenderDocCapture);
 #endif
 
         if (GetInstance()->IsBackendValidationEnabled()) {
-            if (mGlobalInfo.validation) {
-                layerNames.push_back(kLayerNameKhronosValidation);
-                usedKnobs.validation = true;
-            }
+            UseLayerIfAvailable(VulkanLayer::Validation);
         }
 
+        // Always use the Fuchsia swapchain layer if available.
+        UseLayerIfAvailable(VulkanLayer::FuchsiaImagePipeSwapchain);
+
         // Available and known instance extensions default to being requested, but some special
         // cases are removed.
-        InstanceExtSet extensionsToRequest = mGlobalInfo.extensions;
         usedKnobs.extensions = extensionsToRequest;
 
         std::vector<const char*> extensionNames;
@@ -282,7 +284,7 @@
         // Register the debug callback for instance creation so we receive message for any errors
         // (validation or other).
         VkDebugUtilsMessengerCreateInfoEXT utilsMessengerCreateInfo;
-        if (mGlobalInfo.HasExt(InstanceExt::DebugUtils)) {
+        if (usedKnobs.HasExt(InstanceExt::DebugUtils)) {
             utilsMessengerCreateInfo.flags = 0;
             utilsMessengerCreateInfo.messageSeverity =
                 VK_DEBUG_UTILS_MESSAGE_SEVERITY_ERROR_BIT_EXT |
diff --git a/src/dawn_native/vulkan/VulkanExtensions.cpp b/src/dawn_native/vulkan/VulkanExtensions.cpp
index 05a8d60..4c07d69 100644
--- a/src/dawn_native/vulkan/VulkanExtensions.cpp
+++ b/src/dawn_native/vulkan/VulkanExtensions.cpp
@@ -293,4 +293,32 @@
         }
     }
 
+    // A static array for VulkanLayerInfo that can be indexed with VulkanLayers.
+    // GetVulkanLayerInfo checks that "index" matches the index used to access this array so an
+    // assert will fire if it isn't in the correct order.
+    static constexpr size_t kVulkanLayerCount = static_cast<size_t>(VulkanLayer::EnumCount);
+    static constexpr std::array<VulkanLayerInfo, kVulkanLayerCount> sVulkanLayerInfos{{
+        //
+        {VulkanLayer::Validation, "VK_LAYER_KHRONOS_validation"},
+        {VulkanLayer::LunargVkTrace, "VK_LAYER_LUNARG_vktrace"},
+        {VulkanLayer::RenderDocCapture, "VK_LAYER_RENDERDOC_Capture"},
+        {VulkanLayer::FuchsiaImagePipeSwapchain, "VK_LAYER_FUCHSIA_imagepipe_swapchain"},
+        //
+    }};
+
+    const VulkanLayerInfo& GetVulkanLayerInfo(VulkanLayer layer) {
+        uint32_t index = static_cast<uint32_t>(layer);
+        ASSERT(index < sVulkanLayerInfos.size());
+        ASSERT(sVulkanLayerInfos[index].layer == layer);
+        return sVulkanLayerInfos[index];
+    }
+
+    std::unordered_map<std::string, VulkanLayer> CreateVulkanLayerNameMap() {
+        std::unordered_map<std::string, VulkanLayer> result;
+        for (const VulkanLayerInfo& info : sVulkanLayerInfos) {
+            result[info.name] = info.layer;
+        }
+        return result;
+    }
+
 }}  // namespace dawn_native::vulkan
diff --git a/src/dawn_native/vulkan/VulkanExtensions.h b/src/dawn_native/vulkan/VulkanExtensions.h
index d3950f1..91af192 100644
--- a/src/dawn_native/vulkan/VulkanExtensions.h
+++ b/src/dawn_native/vulkan/VulkanExtensions.h
@@ -129,6 +129,33 @@
                                     const InstanceExtSet& instanceExts,
                                     uint32_t icdVersion);
 
+    // The list of all known Vulkan layers.
+    enum class VulkanLayer {
+        Validation,
+        LunargVkTrace,
+        RenderDocCapture,
+
+        // Fuchsia implements the swapchain through a layer (VK_LAYER_FUCHSIA_image_pipe_swapchain),
+        // which adds an instance extensions (VK_FUCHSIA_image_surface) to all ICDs.
+        FuchsiaImagePipeSwapchain,
+
+        EnumCount,
+    };
+
+    // A bitset that is indexed with VulkanLayer.
+    using VulkanLayerSet = ityp::bitset<VulkanLayer, static_cast<uint32_t>(VulkanLayer::EnumCount)>;
+
+    // Information about a known layer
+    struct VulkanLayerInfo {
+        VulkanLayer layer;
+        const char* name;
+    };
+
+    // Returns the information about a known VulkanLayer
+    const VulkanLayerInfo& GetVulkanLayerInfo(VulkanLayer layer);
+    // Returns a map that maps a Vulkan layer name to its VulkanLayer.
+    std::unordered_map<std::string, VulkanLayer> CreateVulkanLayerNameMap();
+
 }}  // namespace dawn_native::vulkan
 
 #endif  // DAWNNATIVE_VULKAN_VULKANEXTENSIONS_H_
diff --git a/src/dawn_native/vulkan/VulkanInfo.cpp b/src/dawn_native/vulkan/VulkanInfo.cpp
index 365ffa7..9ca7634 100644
--- a/src/dawn_native/vulkan/VulkanInfo.cpp
+++ b/src/dawn_native/vulkan/VulkanInfo.cpp
@@ -14,7 +14,6 @@
 
 #include "dawn_native/vulkan/VulkanInfo.h"
 
-#include "common/Log.h"
 #include "dawn_native/vulkan/AdapterVk.h"
 #include "dawn_native/vulkan/BackendVk.h"
 #include "dawn_native/vulkan/UtilsVulkan.h"
@@ -25,32 +24,35 @@
 namespace dawn_native { namespace vulkan {
 
     namespace {
-        bool IsLayerName(const VkLayerProperties& layer, const char* name) {
-            return strncmp(layer.layerName, name, VK_MAX_EXTENSION_NAME_SIZE) == 0;
-        }
-
-        bool EnumerateInstanceExtensions(const char* layerName,
-                                         const dawn_native::vulkan::VulkanFunctions& vkFunctions,
-                                         std::vector<VkExtensionProperties>* extensions) {
+        ResultOrError<InstanceExtSet> GatherInstanceExtensions(
+            const char* layerName,
+            const dawn_native::vulkan::VulkanFunctions& vkFunctions,
+            const std::unordered_map<std::string, InstanceExt>& knownExts) {
             uint32_t count = 0;
-            VkResult result = VkResult::WrapUnsafe(
+            VkResult vkResult = VkResult::WrapUnsafe(
                 vkFunctions.EnumerateInstanceExtensionProperties(layerName, &count, nullptr));
-            if (result != VK_SUCCESS && result != VK_INCOMPLETE) {
-                return false;
+            if (vkResult != VK_SUCCESS && vkResult != VK_INCOMPLETE) {
+                return DAWN_INTERNAL_ERROR("vkEnumerateInstanceExtensionProperties");
             }
-            extensions->resize(count);
-            result = VkResult::WrapUnsafe(vkFunctions.EnumerateInstanceExtensionProperties(
-                layerName, &count, extensions->data()));
-            return (result == VK_SUCCESS);
+
+            std::vector<VkExtensionProperties> extensions(count);
+            DAWN_TRY(CheckVkSuccess(vkFunctions.EnumerateInstanceExtensionProperties(
+                                        layerName, &count, extensions.data()),
+                                    "vkEnumerateInstanceExtensionProperties"));
+
+            InstanceExtSet result;
+            for (const VkExtensionProperties& extension : extensions) {
+                auto it = knownExts.find(extension.extensionName);
+                if (it != knownExts.end()) {
+                    result.set(it->second, true);
+                }
+            }
+
+            return result;
         }
 
     }  // namespace
 
-    const char kLayerNameKhronosValidation[] = "VK_LAYER_KHRONOS_validation";
-    const char kLayerNameLunargVKTrace[] = "VK_LAYER_LUNARG_vktrace";
-    const char kLayerNameRenderDocCapture[] = "VK_LAYER_RENDERDOC_Capture";
-    const char kLayerNameFuchsiaImagePipeSwapchain[] = "VK_LAYER_FUCHSIA_imagepipe_swapchain";
-
     bool VulkanGlobalKnobs::HasExt(InstanceExt ext) const {
         return extensions[ext];
     }
@@ -88,26 +90,16 @@
                 return DAWN_INTERNAL_ERROR("vkEnumerateInstanceLayerProperties");
             }
 
-            info.layers.resize(count);
+            std::vector<VkLayerProperties> layersProperties(count);
             DAWN_TRY(CheckVkSuccess(
-                vkFunctions.EnumerateInstanceLayerProperties(&count, info.layers.data()),
+                vkFunctions.EnumerateInstanceLayerProperties(&count, layersProperties.data()),
                 "vkEnumerateInstanceLayerProperties"));
 
-            for (const auto& layer : info.layers) {
-                if (IsLayerName(layer, kLayerNameKhronosValidation)) {
-                    info.validation = true;
-                }
-                if (IsLayerName(layer, kLayerNameLunargVKTrace)) {
-                    info.vktrace = true;
-                }
-                if (IsLayerName(layer, kLayerNameRenderDocCapture)) {
-                    info.renderDocCapture = true;
-                }
-                // Technical note: Fuchsia implements the swapchain through
-                // a layer (VK_LAYER_FUCHSIA_image_pipe_swapchain), which adds
-                // an instance extensions (VK_FUCHSIA_image_surface) to all ICDs.
-                if (IsLayerName(layer, kLayerNameFuchsiaImagePipeSwapchain)) {
-                    info.fuchsiaImagePipeSwapchain = true;
+            std::unordered_map<std::string, VulkanLayer> knownLayers = CreateVulkanLayerNameMap();
+            for (const VkLayerProperties& layer : layersProperties) {
+                auto it = knownLayers.find(layer.layerName);
+                if (it != knownLayers.end()) {
+                    info.layers.set(it->second, true);
                 }
             }
         }
@@ -116,41 +108,19 @@
         {
             std::unordered_map<std::string, InstanceExt> knownExts = CreateInstanceExtNameMap();
 
-            std::vector<VkExtensionProperties> extensionsProperties;
-            if (!EnumerateInstanceExtensions(nullptr, vkFunctions, &extensionsProperties)) {
-                return DAWN_INTERNAL_ERROR("vkEnumerateInstanceExtensionProperties");
-            }
-
-            for (const VkExtensionProperties& extension : extensionsProperties) {
-                auto it = knownExts.find(extension.extensionName);
-                if (it != knownExts.end()) {
-                    info.extensions.set(it->second, true);
-                }
-            }
-
-            // Specific handling for the Fuchsia swapchain surface creation extension
-            // which is normally part of the Fuchsia-specific swapchain layer.
-            if (info.fuchsiaImagePipeSwapchain &&
-                !info.HasExt(InstanceExt::FuchsiaImagePipeSurface)) {
-                if (!EnumerateInstanceExtensions(kLayerNameFuchsiaImagePipeSwapchain, vkFunctions,
-                                                 &extensionsProperties)) {
-                    return DAWN_INTERNAL_ERROR("vkEnumerateInstanceExtensionProperties");
-                }
-
-                for (const VkExtensionProperties& extension : extensionsProperties) {
-                    auto it = knownExts.find(extension.extensionName);
-                    if (it != knownExts.end() &&
-                        it->second == InstanceExt::FuchsiaImagePipeSurface) {
-                        info.extensions.set(InstanceExt::FuchsiaImagePipeSurface, true);
-                    }
-                }
-            }
-
+            DAWN_TRY_ASSIGN(info.extensions,
+                            GatherInstanceExtensions(nullptr, vkFunctions, knownExts));
             MarkPromotedExtensions(&info.extensions, info.apiVersion);
             info.extensions = EnsureDependencies(info.extensions);
-        }
 
-        // TODO(cwallez@chromium:org): Each layer can expose additional extensions, query them?
+            for (VulkanLayer layer : IterateBitSet(info.layers)) {
+                DAWN_TRY_ASSIGN(info.layerExtensions[layer],
+                                GatherInstanceExtensions(GetVulkanLayerInfo(layer).name,
+                                                         vkFunctions, knownExts));
+                MarkPromotedExtensions(&info.layerExtensions[layer], info.apiVersion);
+                info.layerExtensions[layer] = EnsureDependencies(info.layerExtensions[layer]);
+            }
+        }
 
         return std::move(info);
     }
diff --git a/src/dawn_native/vulkan/VulkanInfo.h b/src/dawn_native/vulkan/VulkanInfo.h
index 0e0b717..4e7990e 100644
--- a/src/dawn_native/vulkan/VulkanInfo.h
+++ b/src/dawn_native/vulkan/VulkanInfo.h
@@ -15,6 +15,7 @@
 #ifndef DAWNNATIVE_VULKAN_VULKANINFO_H_
 #define DAWNNATIVE_VULKAN_VULKANINFO_H_
 
+#include "common/ityp_array.h"
 #include "common/vulkan_platform.h"
 #include "dawn_native/Error.h"
 #include "dawn_native/vulkan/VulkanExtensions.h"
@@ -26,27 +27,22 @@
     class Adapter;
     class Backend;
 
-    extern const char kLayerNameKhronosValidation[];
-    extern const char kLayerNameLunargVKTrace[];
-    extern const char kLayerNameRenderDocCapture[];
-    extern const char kLayerNameFuchsiaImagePipeSwapchain[];
-
     // Global information - gathered before the instance is created
     struct VulkanGlobalKnobs {
-        // Layers
-        bool validation = false;
-        bool vktrace = false;
-        bool renderDocCapture = false;
-        bool fuchsiaImagePipeSwapchain = false;
+        bool HasLayer(VulkanLayer layer) const;
+        VulkanLayerSet layers;
+        ityp::array<VulkanLayer, InstanceExtSet, static_cast<uint32_t>(VulkanLayer::EnumCount)>
+            layerExtensions;
 
-        bool HasExt(InstanceExt ext) const;
+        // During information gathering `extensions` only contains the instance's extensions but
+        // during the instance creation logic it becomes the OR of the instance's extensions and
+        // the selected layers' extensions.
         InstanceExtSet extensions;
+        bool HasExt(InstanceExt ext) const;
     };
 
     struct VulkanGlobalInfo : VulkanGlobalKnobs {
-        std::vector<VkLayerProperties> layers;
         uint32_t apiVersion;
-        // TODO(cwallez@chromium.org): layer instance extensions
     };
 
     // Device information - gathered before the device is created.