vulkan: implement EGLDevice for vulkan backend

It allows ANGLE clients to get and use VkDevice and VkQueue
used by vulkan backend.

Bug: chromium:1264439
Change-Id: I338ac08152cfec50bb34c5025730e5e6368efba9
Reviewed-on: https://chromium-review.googlesource.com/c/angle/angle/+/3250964
Reviewed-by: Peng Huang <penghuang@chromium.org>
Reviewed-by: Jamie Madill <jmadill@chromium.org>
Reviewed-by: Geoff Lang <geofflang@chromium.org>
Reviewed-by: Shahbaz Youssefi <syoussefi@chromium.org>
Commit-Queue: Peng Huang <penghuang@chromium.org>
diff --git a/extensions/EGL_ANGLE_device_vulkan.txt b/extensions/EGL_ANGLE_device_vulkan.txt
new file mode 100644
index 0000000..08d9335
--- /dev/null
+++ b/extensions/EGL_ANGLE_device_vulkan.txt
@@ -0,0 +1,125 @@
+Name
+
+    ANGLE_device_vulkan
+
+Name Strings
+
+    EGL_ANGLE_device_vulkan
+
+Contributors
+
+    Peng Huang  (penghuang 'at' google.com)
+
+Contact
+
+    Peng Huang  (penghuang 'at' google.com)
+
+Status
+
+    Draft
+
+Version
+
+    Version 1, Oct 28, 2021
+
+Number
+
+    EGL Extension #XXX
+
+Extension Type
+
+    EGL device extension
+
+Dependencies
+
+    This extension is written against the language of EGL 1.5 as
+    modified by EGL_EXT_device_query.
+
+    EGL_EXT_device_query is required.
+
+Overview
+
+    ANGLE has the ability to run GPU commands on a native Vulkan device.
+    This extension defines a mapping from an EGL device to a Vulkan
+    device, after it's queried from an EGL display.
+
+IP Status
+
+    No known claims.
+
+New Types
+
+    None.
+
+New Procedures and Functions
+
+    None.
+
+New Tokens
+
+    Accepted as a queried <attribute> in eglQueryDeviceAttribEXT:
+
+        EGL_VULKAN_DEVICE_ANGLE              0x34A8
+        EGL_VULKAN_EXTENSIONS_ANGLE          0x34A9
+        EGL_VULKAN_PHYSICAL_DEVICE_ANGLE     0x34AA
+        EGL_VULKAN_QUEUE_ANGLE               0x34AB
+        EGL_VULKAN_QUEUE_FAMILIY_INDEX_ANGLE 0x34AC
+
+Add a new section 2.1.3 (Vulkan Devices) after 2.1.2 (Devices)
+
+    Somewhat analogous to an EGL device, a Vulkan device establishes a
+    namespace for Vulkan operations. In the Vulkan APIs, such devices are
+    represented by pointers. For more details, see the Vulkan
+    documentation.
+
+Changes to section 3.2 (Devices)
+
+    Replace the paragraph immediately following the prototype for
+    eglQueryDeviceAttribEXT:
+
+    <attribute> may be EGL_VULKAN_DEVICE_ANGLE.
+    On success, EGL_TRUE is returned, and a valid Vulkan device handle VkDevice
+    corresponding to the EGL device is returned in <value>. This handle
+    is compatible with Vulkan API functions. If the EGL device is not currently
+    associated with a Vulkan device and <attribute> is EGL_VULKAN_DEVICE_ANGLE,
+    EGL_BAD_ATTRIBUTE is returned, and <value> is left unchanged.
+
+    <attribute> may be EGL_VULKAN_EXTENSIONS_ANGLE.
+    On success, EGL_TRUE is returned, and a pointer to a null terminated static
+    string array is returned in <value>. The array contains enabled Vulkan
+    device extensions for the Vulkan device used by the display.
+    If the EGL device is not currently associated with a Vulkan device and
+    <attribute> is EGL_VULKAN_EXTENSIONS_ANGLE, EGL_BAD_ATTRIBUTE is returned,
+    and <value> is left unchanged.
+
+    <attribute> may be EGL_VULKAN_PHYSICAL_DEVICE_ANGLE.
+    On success, EGL_TRUE is returned, and a valid Vulkan physical device handle
+    VkPhysicalDevice corresponding to the EGL device is returned in <value>.
+    This handle is compatible with Vulkan API functions.
+    If the EGL device is not currently associated with a Vulkan device and
+    <attribute> is EGL_VULKAN_PHYSICAL_DEVICE_ANGLE, EGL_BAD_ATTRIBUTE
+    is returned, and <value> is left unchanged.
+
+    <attribute> may be EGL_VULKAN_QUEUE_ANGLE.
+    On success, EGL_TRUE is returned, and a valid Vulkan device queue handle
+    VkQueue corresponding to the EGL device is returned in <value>.
+    This handle is compatible with Vulkan API functions.
+    If the EGL device is not currently associated with a Vulkan device and
+    <attribute> is EGL_VULKAN_QUEUE_ANGLE, EGL_BAD_ATTRIBUTE is returned,
+    and <value> is left unchanged.
+
+    <attribute> may be EGL_VULKAN_QUEUE_FAMILIY_INDEX_ANGLE.
+    On success, EGL_TRUE is returned, and the Vulkan queue familiy index
+    corresponding to the EGL device is returned in <value>.
+    If the EGL device is not currently associated with a Vulkan device and
+    <attribute> is EGL_VULKAN_QUEUE_FAMILIY_INDEX_ANGLE, EGL_BAD_ATTRIBUTE
+    is returned, and <value> is left unchanged.
+
+Issues
+
+    None
+
+Revision History
+
+    Version 1, Oct 28, 2021 (Peng Huang)
+        - Initial Draft
\ No newline at end of file
diff --git a/include/EGL/eglext_angle.h b/include/EGL/eglext_angle.h
index bf4ef41..cef03c2 100644
--- a/include/EGL/eglext_angle.h
+++ b/include/EGL/eglext_angle.h
@@ -355,6 +355,15 @@
 #define EGL_SWAP_INTERVAL_ANGLE 0x322F
 #endif /* EGL_ANGLE_create_surface_swap_interval */
 
+#ifndef EGL_ANGLE_device_vulkan
+#define EGL_ANGLE_device_vulkan 1
+#define EGL_VULKAN_DEVICE_ANGLE 0x34A8
+#define EGL_VULKAN_EXTENSIONS_ANGLE 0x34A9
+#define EGL_VULKAN_PHYSICAL_DEVICE_ANGLE 0x34AA
+#define EGL_VULKAN_QUEUE_ANGLE 0x34AB
+#define EGL_VULKAN_QUEUE_FAMILIY_INDEX_ANGLE 0x34AC
+#endif /* EGL_ANGLE_device_vulkan */
+
 // clang-format on
 
 #endif  // INCLUDE_EGL_EGLEXT_ANGLE_
diff --git a/src/libANGLE/Caps.cpp b/src/libANGLE/Caps.cpp
index 7bf2ab5..b68153d 100644
--- a/src/libANGLE/Caps.cpp
+++ b/src/libANGLE/Caps.cpp
@@ -1308,6 +1308,7 @@
     InsertExtensionString("EGL_ANGLE_device_cgl",                          deviceCGL,                      &extensionStrings);
     InsertExtensionString("EGL_ANGLE_device_eagl",                         deviceEAGL,                     &extensionStrings);
     InsertExtensionString("EGL_ANGLE_device_metal",                        deviceMetal,                    &extensionStrings);
+    InsertExtensionString("EGL_ANGLE_device_vulkan",                       deviceVulkan,                   &extensionStrings);
 
     // clang-format on
 
diff --git a/src/libANGLE/Caps.h b/src/libANGLE/Caps.h
index 8ebad57..5060499 100644
--- a/src/libANGLE/Caps.h
+++ b/src/libANGLE/Caps.h
@@ -656,6 +656,9 @@
 
     // EGL_ANGLE_device_metal
     bool deviceMetal = false;
+
+    // EGL_ANGLE_device_vulkan
+    bool deviceVulkan = false;
 };
 
 struct ClientExtensions
diff --git a/src/libANGLE/renderer/vulkan/CommandProcessor.h b/src/libANGLE/renderer/vulkan/CommandProcessor.h
index 1bdbc22..60fd45e 100644
--- a/src/libANGLE/renderer/vulkan/CommandProcessor.h
+++ b/src/libANGLE/renderer/vulkan/CommandProcessor.h
@@ -394,6 +394,8 @@
     }
     uint32_t getDeviceQueueIndex() const { return mQueueMap.getIndex(); }
 
+    VkQueue getQueue(egl::ContextPriority priority) { return mQueueMap[priority]; }
+
   private:
     void releaseToCommandBatch(bool hasProtectedContent,
                                PrimaryCommandBuffer &&commandBuffer,
@@ -404,8 +406,6 @@
 
     bool allInFlightCommandsAreAfterSerial(Serial serial);
 
-    VkQueue getQueue(egl::ContextPriority priority) { return mQueueMap[priority]; }
-
     PrimaryCommandBuffer &getCommandBuffer(bool hasProtectedContent)
     {
         if (hasProtectedContent)
@@ -533,6 +533,7 @@
         return mCommandQueue.getDriverPriority(priority);
     }
     uint32_t getDeviceQueueIndex() const { return mCommandQueue.getDeviceQueueIndex(); }
+    VkQueue getQueue(egl::ContextPriority priority) { return mCommandQueue.getQueue(priority); }
 
   private:
     bool hasPendingError() const
diff --git a/src/libANGLE/renderer/vulkan/DeviceVk.cpp b/src/libANGLE/renderer/vulkan/DeviceVk.cpp
index 5d5883f..f5136ae 100644
--- a/src/libANGLE/renderer/vulkan/DeviceVk.cpp
+++ b/src/libANGLE/renderer/vulkan/DeviceVk.cpp
@@ -9,36 +9,72 @@
 
 #include "libANGLE/renderer/vulkan/DeviceVk.h"
 
+#include <stdint.h>
+
 #include "common/debug.h"
+#include "libANGLE/Display.h"
+#include "libANGLE/renderer/vulkan/DisplayVk.h"
+#include "libANGLE/renderer/vulkan/RendererVk.h"
 
 namespace rx
 {
 
-DeviceVk::DeviceVk() : DeviceImpl() {}
+DeviceVk::DeviceVk() = default;
 
-DeviceVk::~DeviceVk() {}
+DeviceVk::~DeviceVk() = default;
 
 egl::Error DeviceVk::initialize()
 {
-    UNIMPLEMENTED();
     return egl::NoError();
 }
 
 egl::Error DeviceVk::getAttribute(const egl::Display *display, EGLint attribute, void **outValue)
 {
-    UNIMPLEMENTED();
-    return egl::EglBadAccess();
+    RendererVk *renderer =
+        static_cast<rx::DisplayVk *>(display->getImplementation())->getRenderer();
+    switch (attribute)
+    {
+        case EGL_VULKAN_DEVICE_ANGLE:
+        {
+            *outValue = renderer->getDevice();
+            return egl::NoError();
+        }
+        case EGL_VULKAN_PHYSICAL_DEVICE_ANGLE:
+        {
+            *outValue = renderer->getPhysicalDevice();
+            return egl::NoError();
+        }
+        case EGL_VULKAN_QUEUE_ANGLE:
+        {
+            // egl::ContextPriority::Medium is the default context priority.
+            *outValue = renderer->getQueue(egl::ContextPriority::Medium);
+            return egl::NoError();
+        }
+        case EGL_VULKAN_QUEUE_FAMILIY_INDEX_ANGLE:
+        {
+            intptr_t index = static_cast<intptr_t>(renderer->getQueueFamilyIndex());
+            *outValue      = reinterpret_cast<void *>(index);
+            return egl::NoError();
+        }
+        case EGL_VULKAN_EXTENSIONS_ANGLE:
+        {
+            char **extensions = const_cast<char **>(renderer->getEnabledDeviceExtensions().data());
+            *outValue         = reinterpret_cast<void *>(extensions);
+            return egl::NoError();
+        }
+        default:
+            return egl::EglBadAccess();
+    }
 }
 
 EGLint DeviceVk::getType()
 {
-    UNIMPLEMENTED();
-    return EGLint();
+    return EGL_VULKAN_DEVICE_ANGLE;
 }
 
 void DeviceVk::generateExtensions(egl::DeviceExtensions *outExtensions) const
 {
-    UNIMPLEMENTED();
+    outExtensions->deviceVulkan = true;
 }
 
 }  // namespace rx
diff --git a/src/libANGLE/renderer/vulkan/DisplayVk.cpp b/src/libANGLE/renderer/vulkan/DisplayVk.cpp
index 6e13c85..c18b25a 100644
--- a/src/libANGLE/renderer/vulkan/DisplayVk.cpp
+++ b/src/libANGLE/renderer/vulkan/DisplayVk.cpp
@@ -13,6 +13,7 @@
 #include "libANGLE/Context.h"
 #include "libANGLE/Display.h"
 #include "libANGLE/renderer/vulkan/ContextVk.h"
+#include "libANGLE/renderer/vulkan/DeviceVk.h"
 #include "libANGLE/renderer/vulkan/ImageVk.h"
 #include "libANGLE/renderer/vulkan/RendererVk.h"
 #include "libANGLE/renderer/vulkan/SurfaceVk.h"
@@ -101,6 +102,11 @@
     return std::string();
 }
 
+DeviceImpl *DisplayVk::createDevice()
+{
+    return new DeviceVk();
+}
+
 egl::Error DisplayVk::waitClient(const gl::Context *context)
 {
     ANGLE_TRACE_EVENT0("gpu.angle", "DisplayVk::waitClient");
diff --git a/src/libANGLE/renderer/vulkan/DisplayVk.h b/src/libANGLE/renderer/vulkan/DisplayVk.h
index baa15a6..7fca3f5 100644
--- a/src/libANGLE/renderer/vulkan/DisplayVk.h
+++ b/src/libANGLE/renderer/vulkan/DisplayVk.h
@@ -80,6 +80,8 @@
     std::string getVendorString() override;
     std::string getVersionString() override;
 
+    DeviceImpl *createDevice() override;
+
     egl::Error waitClient(const gl::Context *context) override;
     egl::Error waitNative(const gl::Context *context, EGLint engine) override;
 
diff --git a/src/libANGLE/renderer/vulkan/RendererVk.cpp b/src/libANGLE/renderer/vulkan/RendererVk.cpp
index 68ab75b..59a47b7 100644
--- a/src/libANGLE/renderer/vulkan/RendererVk.cpp
+++ b/src/libANGLE/renderer/vulkan/RendererVk.cpp
@@ -1726,10 +1726,9 @@
         std::sort(deviceExtensionNames.begin(), deviceExtensionNames.end(), StrLess);
     }
 
-    vk::ExtensionNameList enabledDeviceExtensions;
     if (displayVk->isUsingSwapchain())
     {
-        enabledDeviceExtensions.push_back(VK_KHR_SWAPCHAIN_EXTENSION_NAME);
+        mEnabledDeviceExtensions.push_back(VK_KHR_SWAPCHAIN_EXTENSION_NAME);
     }
 
     // Query extensions and their features.
@@ -1741,14 +1740,14 @@
     // Enable VK_EXT_depth_clip_enable, if supported
     if (ExtensionFound(VK_EXT_DEPTH_CLIP_ENABLE_EXTENSION_NAME, deviceExtensionNames))
     {
-        enabledDeviceExtensions.push_back(VK_EXT_DEPTH_CLIP_ENABLE_EXTENSION_NAME);
+        mEnabledDeviceExtensions.push_back(VK_EXT_DEPTH_CLIP_ENABLE_EXTENSION_NAME);
     }
 
     // Enable VK_KHR_get_memory_requirements2, if supported
     bool hasGetMemoryRequirements2KHR = false;
     if (ExtensionFound(VK_KHR_GET_MEMORY_REQUIREMENTS_2_EXTENSION_NAME, deviceExtensionNames))
     {
-        enabledDeviceExtensions.push_back(VK_KHR_GET_MEMORY_REQUIREMENTS_2_EXTENSION_NAME);
+        mEnabledDeviceExtensions.push_back(VK_KHR_GET_MEMORY_REQUIREMENTS_2_EXTENSION_NAME);
         hasGetMemoryRequirements2KHR = true;
     }
 
@@ -1757,30 +1756,30 @@
     if (ExtensionFound(VK_KHR_BIND_MEMORY_2_EXTENSION_NAME, deviceExtensionNames))
     {
         hasBindMemory2KHR = true;
-        enabledDeviceExtensions.push_back(VK_KHR_BIND_MEMORY_2_EXTENSION_NAME);
+        mEnabledDeviceExtensions.push_back(VK_KHR_BIND_MEMORY_2_EXTENSION_NAME);
     }
 
     // Enable KHR_MAINTENANCE1 to support viewport flipping.
     if (mPhysicalDeviceProperties.apiVersion < VK_MAKE_VERSION(1, 1, 0))
     {
-        enabledDeviceExtensions.push_back(VK_KHR_MAINTENANCE1_EXTENSION_NAME);
+        mEnabledDeviceExtensions.push_back(VK_KHR_MAINTENANCE1_EXTENSION_NAME);
     }
 
     if (getFeatures().supportsRenderpass2.enabled)
     {
-        enabledDeviceExtensions.push_back(VK_KHR_CREATE_RENDERPASS_2_EXTENSION_NAME);
+        mEnabledDeviceExtensions.push_back(VK_KHR_CREATE_RENDERPASS_2_EXTENSION_NAME);
     }
 
     if (getFeatures().supportsIncrementalPresent.enabled)
     {
-        enabledDeviceExtensions.push_back(VK_KHR_INCREMENTAL_PRESENT_EXTENSION_NAME);
+        mEnabledDeviceExtensions.push_back(VK_KHR_INCREMENTAL_PRESENT_EXTENSION_NAME);
     }
 
 #if defined(ANGLE_PLATFORM_ANDROID)
     if (getFeatures().supportsAndroidHardwareBuffer.enabled)
     {
-        enabledDeviceExtensions.push_back(VK_EXT_QUEUE_FAMILY_FOREIGN_EXTENSION_NAME);
-        enabledDeviceExtensions.push_back(
+        mEnabledDeviceExtensions.push_back(VK_EXT_QUEUE_FAMILY_FOREIGN_EXTENSION_NAME);
+        mEnabledDeviceExtensions.push_back(
             VK_ANDROID_EXTERNAL_MEMORY_ANDROID_HARDWARE_BUFFER_EXTENSION_NAME);
 #    if !defined(ANGLE_SHARED_LIBVULKAN)
         InitExternalMemoryHardwareBufferANDROIDFunctions(mInstance);
@@ -1793,7 +1792,7 @@
 #if defined(ANGLE_PLATFORM_GGP)
     if (getFeatures().supportsGGPFrameToken.enabled)
     {
-        enabledDeviceExtensions.push_back(VK_GGP_FRAME_TOKEN_EXTENSION_NAME);
+        mEnabledDeviceExtensions.push_back(VK_GGP_FRAME_TOKEN_EXTENSION_NAME);
     }
     ANGLE_VK_CHECK(displayVk, getFeatures().supportsGGPFrameToken.enabled,
                    VK_ERROR_EXTENSION_NOT_PRESENT);
@@ -1805,23 +1804,23 @@
         getFeatures().supportsExternalMemoryFd.enabled ||
         getFeatures().supportsExternalMemoryFuchsia.enabled)
     {
-        enabledDeviceExtensions.push_back(VK_KHR_EXTERNAL_MEMORY_EXTENSION_NAME);
+        mEnabledDeviceExtensions.push_back(VK_KHR_EXTERNAL_MEMORY_EXTENSION_NAME);
     }
 
     if (getFeatures().supportsExternalMemoryFd.enabled)
     {
-        enabledDeviceExtensions.push_back(VK_KHR_EXTERNAL_MEMORY_FD_EXTENSION_NAME);
+        mEnabledDeviceExtensions.push_back(VK_KHR_EXTERNAL_MEMORY_FD_EXTENSION_NAME);
     }
 
     if (getFeatures().supportsExternalMemoryFuchsia.enabled)
     {
-        enabledDeviceExtensions.push_back(VK_FUCHSIA_EXTERNAL_MEMORY_EXTENSION_NAME);
+        mEnabledDeviceExtensions.push_back(VK_FUCHSIA_EXTERNAL_MEMORY_EXTENSION_NAME);
     }
 
     if (getFeatures().supportsExternalSemaphoreFd.enabled ||
         getFeatures().supportsExternalSemaphoreFuchsia.enabled)
     {
-        enabledDeviceExtensions.push_back(VK_KHR_EXTERNAL_SEMAPHORE_EXTENSION_NAME);
+        mEnabledDeviceExtensions.push_back(VK_KHR_EXTERNAL_SEMAPHORE_EXTENSION_NAME);
 #if !defined(ANGLE_SHARED_LIBVULKAN)
         InitExternalSemaphoreFdFunctions(mInstance);
 #endif  // !defined(ANGLE_SHARED_LIBVULKAN)
@@ -1829,22 +1828,22 @@
 
     if (getFeatures().supportsExternalSemaphoreCapabilities.enabled)
     {
-        enabledDeviceExtensions.push_back(VK_KHR_EXTERNAL_SEMAPHORE_CAPABILITIES_EXTENSION_NAME);
+        mEnabledDeviceExtensions.push_back(VK_KHR_EXTERNAL_SEMAPHORE_CAPABILITIES_EXTENSION_NAME);
     }
 
     if (getFeatures().supportsExternalFenceCapabilities.enabled)
     {
-        enabledDeviceExtensions.push_back(VK_KHR_EXTERNAL_FENCE_CAPABILITIES_EXTENSION_NAME);
+        mEnabledDeviceExtensions.push_back(VK_KHR_EXTERNAL_FENCE_CAPABILITIES_EXTENSION_NAME);
     }
 
     if (getFeatures().supportsExternalSemaphoreFd.enabled)
     {
-        enabledDeviceExtensions.push_back(VK_KHR_EXTERNAL_SEMAPHORE_FD_EXTENSION_NAME);
+        mEnabledDeviceExtensions.push_back(VK_KHR_EXTERNAL_SEMAPHORE_FD_EXTENSION_NAME);
     }
 
     if (getFeatures().supportsExternalSemaphoreCapabilities.enabled)
     {
-        enabledDeviceExtensions.push_back(VK_KHR_EXTERNAL_SEMAPHORE_CAPABILITIES_EXTENSION_NAME);
+        mEnabledDeviceExtensions.push_back(VK_KHR_EXTERNAL_SEMAPHORE_CAPABILITIES_EXTENSION_NAME);
 #if !defined(ANGLE_SHARED_LIBVULKAN)
         InitExternalSemaphoreCapabilitiesFunctions(mInstance);
 #endif  // !defined(ANGLE_SHARED_LIBVULKAN)
@@ -1852,7 +1851,7 @@
 
     if (getFeatures().supportsExternalFenceFd.enabled)
     {
-        enabledDeviceExtensions.push_back(VK_KHR_EXTERNAL_FENCE_FD_EXTENSION_NAME);
+        mEnabledDeviceExtensions.push_back(VK_KHR_EXTERNAL_FENCE_FD_EXTENSION_NAME);
 #if !defined(ANGLE_SHARED_LIBVULKAN)
         InitExternalFenceFdFunctions(mInstance);
 #endif  // !defined(ANGLE_SHARED_LIBVULKAN)
@@ -1860,7 +1859,7 @@
 
     if (getFeatures().supportsExternalFenceCapabilities.enabled)
     {
-        enabledDeviceExtensions.push_back(VK_KHR_EXTERNAL_FENCE_CAPABILITIES_EXTENSION_NAME);
+        mEnabledDeviceExtensions.push_back(VK_KHR_EXTERNAL_FENCE_CAPABILITIES_EXTENSION_NAME);
 #if !defined(ANGLE_SHARED_LIBVULKAN)
         InitExternalFenceCapabilitiesFunctions(mInstance);
 #endif  // !defined(ANGLE_SHARED_LIBVULKAN)
@@ -1868,30 +1867,31 @@
 
     if (getFeatures().supportsExternalSemaphoreFuchsia.enabled)
     {
-        enabledDeviceExtensions.push_back(VK_FUCHSIA_EXTERNAL_SEMAPHORE_EXTENSION_NAME);
+        mEnabledDeviceExtensions.push_back(VK_FUCHSIA_EXTERNAL_SEMAPHORE_EXTENSION_NAME);
     }
 
     if (getFeatures().supportsShaderStencilExport.enabled)
     {
-        enabledDeviceExtensions.push_back(VK_EXT_SHADER_STENCIL_EXPORT_EXTENSION_NAME);
+        mEnabledDeviceExtensions.push_back(VK_EXT_SHADER_STENCIL_EXPORT_EXTENSION_NAME);
     }
 
     if (getFeatures().supportsRenderPassLoadStoreOpNone.enabled)
     {
-        enabledDeviceExtensions.push_back(VK_EXT_LOAD_STORE_OP_NONE_EXTENSION_NAME);
+        mEnabledDeviceExtensions.push_back(VK_EXT_LOAD_STORE_OP_NONE_EXTENSION_NAME);
     }
     else if (getFeatures().supportsRenderPassStoreOpNoneQCOM.enabled)
     {
-        enabledDeviceExtensions.push_back(VK_QCOM_RENDER_PASS_STORE_OPS_EXTENSION_NAME);
+        mEnabledDeviceExtensions.push_back(VK_QCOM_RENDER_PASS_STORE_OPS_EXTENSION_NAME);
     }
 
     if (getFeatures().supportsImageFormatList.enabled)
     {
-        enabledDeviceExtensions.push_back(VK_KHR_IMAGE_FORMAT_LIST_EXTENSION_NAME);
+        mEnabledDeviceExtensions.push_back(VK_KHR_IMAGE_FORMAT_LIST_EXTENSION_NAME);
     }
 
-    std::sort(enabledDeviceExtensions.begin(), enabledDeviceExtensions.end(), StrLess);
-    ANGLE_VK_TRY(displayVk, VerifyExtensionsPresent(deviceExtensionNames, enabledDeviceExtensions));
+    std::sort(mEnabledDeviceExtensions.begin(), mEnabledDeviceExtensions.end(), StrLess);
+    ANGLE_VK_TRY(displayVk,
+                 VerifyExtensionsPresent(deviceExtensionNames, mEnabledDeviceExtensions));
 
     // Select additional features to be enabled.
     VkPhysicalDeviceFeatures2KHR enabledFeatures = {};
@@ -1950,19 +1950,19 @@
 
     if (mLineRasterizationFeatures.bresenhamLines)
     {
-        enabledDeviceExtensions.push_back(VK_EXT_LINE_RASTERIZATION_EXTENSION_NAME);
+        mEnabledDeviceExtensions.push_back(VK_EXT_LINE_RASTERIZATION_EXTENSION_NAME);
         vk::AddToPNextChain(&createInfo, &mLineRasterizationFeatures);
     }
 
     if (mProvokingVertexFeatures.provokingVertexLast)
     {
-        enabledDeviceExtensions.push_back(VK_EXT_PROVOKING_VERTEX_EXTENSION_NAME);
+        mEnabledDeviceExtensions.push_back(VK_EXT_PROVOKING_VERTEX_EXTENSION_NAME);
         vk::AddToPNextChain(&createInfo, &mProvokingVertexFeatures);
     }
 
     if (mVertexAttributeDivisorFeatures.vertexAttributeInstanceRateDivisor)
     {
-        enabledDeviceExtensions.push_back(VK_EXT_VERTEX_ATTRIBUTE_DIVISOR_EXTENSION_NAME);
+        mEnabledDeviceExtensions.push_back(VK_EXT_VERTEX_ATTRIBUTE_DIVISOR_EXTENSION_NAME);
         vk::AddToPNextChain(&createInfo, &mVertexAttributeDivisorFeatures);
 
         // We only store 8 bit divisor in GraphicsPipelineDesc so capping value & we emulate if
@@ -1974,30 +1974,30 @@
 
     if (getFeatures().supportsTransformFeedbackExtension.enabled)
     {
-        enabledDeviceExtensions.push_back(VK_EXT_TRANSFORM_FEEDBACK_EXTENSION_NAME);
+        mEnabledDeviceExtensions.push_back(VK_EXT_TRANSFORM_FEEDBACK_EXTENSION_NAME);
         vk::AddToPNextChain(&createInfo, &mTransformFeedbackFeatures);
     }
 
     if (getFeatures().supportsCustomBorderColorEXT.enabled)
     {
-        enabledDeviceExtensions.push_back(VK_EXT_CUSTOM_BORDER_COLOR_EXTENSION_NAME);
+        mEnabledDeviceExtensions.push_back(VK_EXT_CUSTOM_BORDER_COLOR_EXTENSION_NAME);
         vk::AddToPNextChain(&createInfo, &mCustomBorderColorFeatures);
     }
 
     if (getFeatures().supportsIndexTypeUint8.enabled)
     {
-        enabledDeviceExtensions.push_back(VK_EXT_INDEX_TYPE_UINT8_EXTENSION_NAME);
+        mEnabledDeviceExtensions.push_back(VK_EXT_INDEX_TYPE_UINT8_EXTENSION_NAME);
         vk::AddToPNextChain(&createInfo, &mIndexTypeUint8Features);
     }
 
     if (getFeatures().supportsDepthStencilResolve.enabled)
     {
-        enabledDeviceExtensions.push_back(VK_KHR_DEPTH_STENCIL_RESOLVE_EXTENSION_NAME);
+        mEnabledDeviceExtensions.push_back(VK_KHR_DEPTH_STENCIL_RESOLVE_EXTENSION_NAME);
     }
 
     if (getFeatures().supportsMultisampledRenderToSingleSampled.enabled)
     {
-        enabledDeviceExtensions.push_back(
+        mEnabledDeviceExtensions.push_back(
             VK_EXT_MULTISAMPLED_RENDER_TO_SINGLE_SAMPLED_EXTENSION_NAME);
         vk::AddToPNextChain(&createInfo, &mMultisampledRenderToSingleSampledFeatures);
     }
@@ -2008,7 +2008,7 @@
         // features.
         mMultiviewFeatures.multiviewGeometryShader     = VK_FALSE;
         mMultiviewFeatures.multiviewTessellationShader = VK_FALSE;
-        enabledDeviceExtensions.push_back(VK_KHR_MULTIVIEW_EXTENSION_NAME);
+        mEnabledDeviceExtensions.push_back(VK_KHR_MULTIVIEW_EXTENSION_NAME);
         vk::AddToPNextChain(&createInfo, &mMultiviewFeatures);
     }
 
@@ -2017,7 +2017,7 @@
     {
         if (mMemoryReportFeatures.deviceMemoryReport)
         {
-            enabledDeviceExtensions.push_back(VK_EXT_DEVICE_MEMORY_REPORT_EXTENSION_NAME);
+            mEnabledDeviceExtensions.push_back(VK_EXT_DEVICE_MEMORY_REPORT_EXTENSION_NAME);
 
             mMemoryReportCallback = {};
             mMemoryReportCallback.sType =
@@ -2045,13 +2045,13 @@
 
     if (getFeatures().supportsExternalMemoryDmaBufAndModifiers.enabled)
     {
-        enabledDeviceExtensions.push_back(VK_EXT_EXTERNAL_MEMORY_DMA_BUF_EXTENSION_NAME);
-        enabledDeviceExtensions.push_back(VK_EXT_IMAGE_DRM_FORMAT_MODIFIER_EXTENSION_NAME);
+        mEnabledDeviceExtensions.push_back(VK_EXT_EXTERNAL_MEMORY_DMA_BUF_EXTENSION_NAME);
+        mEnabledDeviceExtensions.push_back(VK_EXT_IMAGE_DRM_FORMAT_MODIFIER_EXTENSION_NAME);
     }
 
     if (getFeatures().supportsExternalMemoryHost.enabled)
     {
-        enabledDeviceExtensions.push_back(VK_EXT_EXTERNAL_MEMORY_HOST_EXTENSION_NAME);
+        mEnabledDeviceExtensions.push_back(VK_EXT_EXTERNAL_MEMORY_HOST_EXTENSION_NAME);
         mMinImportedHostPointerAlignment =
             mExternalMemoryHostProperties.minImportedHostPointerAlignment;
 #if !defined(ANGLE_SHARED_LIBVULKAN)
@@ -2061,13 +2061,13 @@
 
     if (getFeatures().supportsYUVSamplerConversion.enabled)
     {
-        enabledDeviceExtensions.push_back(VK_KHR_SAMPLER_YCBCR_CONVERSION_EXTENSION_NAME);
+        mEnabledDeviceExtensions.push_back(VK_KHR_SAMPLER_YCBCR_CONVERSION_EXTENSION_NAME);
         vk::AddToPNextChain(&createInfo, &mSamplerYcbcrConversionFeatures);
     }
 
     if (getFeatures().supportsShaderFloat16.enabled)
     {
-        enabledDeviceExtensions.push_back(VK_KHR_SHADER_FLOAT16_INT8_EXTENSION_NAME);
+        mEnabledDeviceExtensions.push_back(VK_KHR_SHADER_FLOAT16_INT8_EXTENSION_NAME);
         vk::AddToPNextChain(&createInfo, &mShaderFloat16Int8Features);
     }
 
@@ -2102,9 +2102,9 @@
     createInfo.pQueueCreateInfos     = queueCreateInfo;
     createInfo.enabledLayerCount     = static_cast<uint32_t>(enabledDeviceLayerNames.size());
     createInfo.ppEnabledLayerNames   = enabledDeviceLayerNames.data();
-    createInfo.enabledExtensionCount = static_cast<uint32_t>(enabledDeviceExtensions.size());
+    createInfo.enabledExtensionCount = static_cast<uint32_t>(mEnabledDeviceExtensions.size());
     createInfo.ppEnabledExtensionNames =
-        enabledDeviceExtensions.empty() ? nullptr : enabledDeviceExtensions.data();
+        mEnabledDeviceExtensions.empty() ? nullptr : mEnabledDeviceExtensions.data();
 
     // Enable core features without assuming VkPhysicalDeviceFeatures2KHR is accepted in the
     // pNext chain of VkDeviceCreateInfo.
@@ -2184,7 +2184,7 @@
         unsupportedStages |= VK_PIPELINE_STAGE_GEOMETRY_SHADER_BIT;
     }
     mSupportedVulkanPipelineStageMask = ~unsupportedStages;
-
+    mEnabledDeviceExtensions.push_back(nullptr);
     return angle::Result::Continue;
 }
 
diff --git a/src/libANGLE/renderer/vulkan/RendererVk.h b/src/libANGLE/renderer/vulkan/RendererVk.h
index eabfb36..3abaa7e 100644
--- a/src/libANGLE/renderer/vulkan/RendererVk.h
+++ b/src/libANGLE/renderer/vulkan/RendererVk.h
@@ -276,6 +276,18 @@
         }
     }
 
+    VkQueue getQueue(egl::ContextPriority priority)
+    {
+        if (mFeatures.asyncCommandQueue.enabled)
+        {
+            return mCommandProcessor.getQueue(priority);
+        }
+        else
+        {
+            return mCommandQueue.getQueue(priority);
+        }
+    }
+
     // This command buffer should be submitted immediately via queueSubmitOneOff.
     angle::Result getCommandBufferOneOff(vk::Context *context,
                                          bool hasProtectedContent,
@@ -475,6 +487,11 @@
         return mMaxCopyBytesUsingCPUWhenPreservingBufferData;
     }
 
+    const vk::ExtensionNameList &getEnabledDeviceExtensions() const
+    {
+        return mEnabledDeviceExtensions;
+    }
+
   private:
     angle::Result initializeDevice(DisplayVk *displayVk, uint32_t queueFamilyIndex);
     void ensureCapsInitialized() const;
@@ -634,6 +651,8 @@
 
     // Use thread pool to compress cache data.
     std::shared_ptr<rx::WaitableCompressEvent> mCompressEvent;
+
+    vk::ExtensionNameList mEnabledDeviceExtensions;
 };
 
 }  // namespace rx
diff --git a/src/libANGLE/validationEGL.cpp b/src/libANGLE/validationEGL.cpp
index b2ab422..f6e43cd 100644
--- a/src/libANGLE/validationEGL.cpp
+++ b/src/libANGLE/validationEGL.cpp
@@ -5813,6 +5813,17 @@
                 return false;
             }
             break;
+        case EGL_VULKAN_DEVICE_ANGLE:
+        case EGL_VULKAN_EXTENSIONS_ANGLE:
+        case EGL_VULKAN_PHYSICAL_DEVICE_ANGLE:
+        case EGL_VULKAN_QUEUE_ANGLE:
+        case EGL_VULKAN_QUEUE_FAMILIY_INDEX_ANGLE:
+            if (!device->getExtensions().deviceVulkan)
+            {
+                val->setError(EGL_BAD_ATTRIBUTE);
+                return false;
+            }
+            break;
         case EGL_CGL_CONTEXT_ANGLE:
         case EGL_CGL_PIXEL_FORMAT_ANGLE:
             if (!device->getExtensions().deviceCGL)