vulkan: Implement VkSurfaceKHR and use vulkanext.h

Between header version 0.183.0 and 0.184.0, a copy of vulkan.h which
includes extensions was added to the registry, called vulkanext.h. The
vulkan.h included here is actually the registry's vulkanext.h.
(In a later upstream change, the no-extensions version was removed
from the registry, and vulkanext.h was renamed vulkan.h, matching what
we're doing here.)

The version of the extensions picked up in the header file is later
than the ones used in the previous SDK, so this change also updates
our implementation to the extension versions included in the header.
The main change is replacing the VkSurfaceDescriptionKHR structure
with a VkSurfaceKHR object.

Change-Id: I18fa5a269db0fcdbdbde3e9304167bc15e456f85
(cherry picked from commit 957a59a48a8d2e81ca3bb52aacd8d08b1b43dc74)
diff --git a/vulkan/libvulkan/entry.cpp b/vulkan/libvulkan/entry.cpp
index 0af3bc8..339101f 100644
--- a/vulkan/libvulkan/entry.cpp
+++ b/vulkan/libvulkan/entry.cpp
@@ -762,23 +762,28 @@
 }
 
 __attribute__((visibility("default")))
-VkResult vkGetPhysicalDeviceSurfaceSupportKHR(VkPhysicalDevice physicalDevice, uint32_t queueFamilyIndex, const VkSurfaceDescriptionKHR* pSurfaceDescription, VkBool32* pSupported) {
-    return GetVtbl(physicalDevice).GetPhysicalDeviceSurfaceSupportKHR(physicalDevice, queueFamilyIndex, pSurfaceDescription, pSupported);
+void vkDestroySurfaceKHR(VkInstance instance, VkSurfaceKHR surface) {
+    GetVtbl(instance).DestroySurfaceKHR(instance, surface);
 }
 
 __attribute__((visibility("default")))
-VkResult vkGetSurfacePropertiesKHR(VkDevice device, const VkSurfaceDescriptionKHR* pSurfaceDescription, VkSurfacePropertiesKHR* pSurfaceProperties) {
-    return GetVtbl(device).GetSurfacePropertiesKHR(device, pSurfaceDescription, pSurfaceProperties);
+VkBool32 vkGetPhysicalDeviceSurfaceSupportKHR(VkPhysicalDevice physicalDevice, uint32_t queueFamilyIndex, VkSurfaceKHR surface) {
+    return GetVtbl(physicalDevice).GetPhysicalDeviceSurfaceSupportKHR(physicalDevice, queueFamilyIndex, surface);
 }
 
 __attribute__((visibility("default")))
-VkResult vkGetSurfaceFormatsKHR(VkDevice device, const VkSurfaceDescriptionKHR* pSurfaceDescription, uint32_t* pCount, VkSurfaceFormatKHR* pSurfaceFormats) {
-    return GetVtbl(device).GetSurfaceFormatsKHR(device, pSurfaceDescription, pCount, pSurfaceFormats);
+VkResult vkGetSurfacePropertiesKHR(VkDevice device, VkSurfaceKHR surface, VkSurfacePropertiesKHR* pSurfaceProperties) {
+    return GetVtbl(device).GetSurfacePropertiesKHR(device, surface, pSurfaceProperties);
 }
 
 __attribute__((visibility("default")))
-VkResult vkGetSurfacePresentModesKHR(VkDevice device, const VkSurfaceDescriptionKHR* pSurfaceDescription, uint32_t* pCount, VkPresentModeKHR* pPresentModes) {
-    return GetVtbl(device).GetSurfacePresentModesKHR(device, pSurfaceDescription, pCount, pPresentModes);
+VkResult vkGetSurfaceFormatsKHR(VkDevice device, VkSurfaceKHR surface, uint32_t* pCount, VkSurfaceFormatKHR* pSurfaceFormats) {
+    return GetVtbl(device).GetSurfaceFormatsKHR(device, surface, pCount, pSurfaceFormats);
+}
+
+__attribute__((visibility("default")))
+VkResult vkGetSurfacePresentModesKHR(VkDevice device, VkSurfaceKHR surface, uint32_t* pCount, VkPresentModeKHR* pPresentModes) {
+    return GetVtbl(device).GetSurfacePresentModesKHR(device, surface, pCount, pPresentModes);
 }
 
 __attribute__((visibility("default")))
@@ -787,8 +792,8 @@
 }
 
 __attribute__((visibility("default")))
-VkResult vkDestroySwapchainKHR(VkDevice device, VkSwapchainKHR swapchain) {
-    return GetVtbl(device).DestroySwapchainKHR(device, swapchain);
+void vkDestroySwapchainKHR(VkDevice device, VkSwapchainKHR swapchain) {
+    GetVtbl(device).DestroySwapchainKHR(device, swapchain);
 }
 
 __attribute__((visibility("default")))
@@ -797,11 +802,16 @@
 }
 
 __attribute__((visibility("default")))
-VkResult vkAcquireNextImageKHR(VkDevice device, VkSwapchainKHR swapchain, uint64_t timeout, VkSemaphore semaphore, uint32_t* pImageIndex) {
-    return GetVtbl(device).AcquireNextImageKHR(device, swapchain, timeout, semaphore, pImageIndex);
+VkResult vkAcquireNextImageKHR(VkDevice device, VkSwapchainKHR swapchain, uint64_t timeout, VkSemaphore semaphore, VkFence fence, uint32_t* pImageIndex) {
+    return GetVtbl(device).AcquireNextImageKHR(device, swapchain, timeout, semaphore, fence, pImageIndex);
 }
 
 __attribute__((visibility("default")))
 VkResult vkQueuePresentKHR(VkQueue queue, VkPresentInfoKHR* pPresentInfo) {
     return GetVtbl(queue).QueuePresentKHR(queue, pPresentInfo);
 }
+
+__attribute__((visibility("default")))
+VkResult vkCreateAndroidSurfaceKHR(VkInstance instance, struct ANativeWindow* window, VkSurfaceKHR* pSurface) {
+    return GetVtbl(instance).CreateAndroidSurfaceKHR(instance, window, pSurface);
+}
diff --git a/vulkan/libvulkan/entry.cpp.tmpl b/vulkan/libvulkan/entry.cpp.tmpl
index 72185e7..d0e665f 100644
--- a/vulkan/libvulkan/entry.cpp.tmpl
+++ b/vulkan/libvulkan/entry.cpp.tmpl
@@ -69,7 +69,7 @@
 } // namespace

   {{range $f := AllCommands $}}
-    {{if not (GetAnnotation $f "pfn")}}
+    {{if and (not (GetAnnotation $f "pfn")) (Macro "IsExportedEntry" $f)}}
       __attribute__((visibility("default")))
       {{if eq (Macro "IsSpecialEntry" $f.Name) "true"}}
         {{Macro "EmitSpecialEntry" $f}}
@@ -84,6 +84,25 @@
 
 {{/*
 -------------------------------------------------------------------------------
+  Decides whether an entrypoint should be exported from the Android Vulkan
+  library. Entrypoints in the core API and in mandatory extensions are
+  exported.
+-------------------------------------------------------------------------------
+*/}}
+{{define "IsExportedEntry"}}
+  {{AssertType $ "Function"}}
+  {{$ext := GetAnnotation $ "extension"}}
+  {{if $ext}}
+    {{$extval := index $ext.Arguments 0}}
+    {{if      eq $extval "VK_EXT_KHR_surface"}}true
+    {{else if eq $extval "VK_EXT_KHR_swapchain"}}true
+    {{else if eq $extval "VK_EXT_KHR_android_surface"}}true{{end}}
+  {{else}}true{{end}}
+{{end}}
+
+
+{{/*
+-------------------------------------------------------------------------------
   Decides whether an entrypoint needs special-case handling. Emits the string
   "true" if so, nothing otherwise.
 -------------------------------------------------------------------------------
@@ -95,6 +114,7 @@
   {{end}}
 {{end}}
 
+
 {{/*
 -------------------------------------------------------------------------------
   Emits the entrypoint definition for the specified command, which always
diff --git a/vulkan/libvulkan/get_proc_addr.cpp b/vulkan/libvulkan/get_proc_addr.cpp
index b9bd689..f1ad5f3 100644
--- a/vulkan/libvulkan/get_proc_addr.cpp
+++ b/vulkan/libvulkan/get_proc_addr.cpp
@@ -49,8 +49,10 @@
 
 const NameProcEntry kInstanceProcTbl[] = {
     // clang-format off
+    {"vkCreateAndroidSurfaceKHR", reinterpret_cast<PFN_vkVoidFunction>(vkCreateAndroidSurfaceKHR)},
     {"vkCreateDevice", reinterpret_cast<PFN_vkVoidFunction>(vkCreateDevice)},
     {"vkDestroyInstance", reinterpret_cast<PFN_vkVoidFunction>(vkDestroyInstance)},
+    {"vkDestroySurfaceKHR", reinterpret_cast<PFN_vkVoidFunction>(vkDestroySurfaceKHR)},
     {"vkEnumerateDeviceExtensionProperties", reinterpret_cast<PFN_vkVoidFunction>(vkEnumerateDeviceExtensionProperties)},
     {"vkEnumerateDeviceLayerProperties", reinterpret_cast<PFN_vkVoidFunction>(vkEnumerateDeviceLayerProperties)},
     {"vkEnumeratePhysicalDevices", reinterpret_cast<PFN_vkVoidFunction>(vkEnumeratePhysicalDevices)},
@@ -209,8 +211,10 @@
 
 const NameOffsetEntry kInstanceOffsetTbl[] = {
     // clang-format off
+    {"vkCreateAndroidSurfaceKHR", offsetof(InstanceVtbl, CreateAndroidSurfaceKHR)},
     {"vkCreateDevice", offsetof(InstanceVtbl, CreateDevice)},
     {"vkDestroyInstance", offsetof(InstanceVtbl, DestroyInstance)},
+    {"vkDestroySurfaceKHR", offsetof(InstanceVtbl, DestroySurfaceKHR)},
     {"vkEnumerateDeviceExtensionProperties", offsetof(InstanceVtbl, EnumerateDeviceExtensionProperties)},
     {"vkEnumerateDeviceLayerProperties", offsetof(InstanceVtbl, EnumerateDeviceLayerProperties)},
     {"vkEnumeratePhysicalDevices", offsetof(InstanceVtbl, EnumeratePhysicalDevices)},
@@ -490,7 +494,9 @@
         ALOGE("missing instance proc: %s", "vkGetPhysicalDeviceSparseImageFormatProperties");
         success = false;
     }
+    vtbl.DestroySurfaceKHR = reinterpret_cast<PFN_vkDestroySurfaceKHR>(get_proc_addr(instance, "vkDestroySurfaceKHR"));
     vtbl.GetPhysicalDeviceSurfaceSupportKHR = reinterpret_cast<PFN_vkGetPhysicalDeviceSurfaceSupportKHR>(get_proc_addr(instance, "vkGetPhysicalDeviceSurfaceSupportKHR"));
+    vtbl.CreateAndroidSurfaceKHR = reinterpret_cast<PFN_vkCreateAndroidSurfaceKHR>(get_proc_addr(instance, "vkCreateAndroidSurfaceKHR"));
     // clang-format on
     return success;
 }
diff --git a/vulkan/libvulkan/get_proc_addr.cpp.tmpl b/vulkan/libvulkan/get_proc_addr.cpp.tmpl
index 6d5f618..c6ec0d1 100644
--- a/vulkan/libvulkan/get_proc_addr.cpp.tmpl
+++ b/vulkan/libvulkan/get_proc_addr.cpp.tmpl
@@ -78,7 +78,7 @@
 const NameProcEntry kInstanceProcTbl[] = {«
   // clang-format off
   {{range $f := SortBy (AllCommands $) "FunctionName"}}
-    {{if eq (Macro "Vtbl" $f) "Instance"}}
+    {{if and (Macro "IsDispatched" $f) (eq (Macro "Vtbl" $f) "Instance")}}
       {"{{Macro "FunctionName" $f}}", reinterpret_cast<PFN_vkVoidFunction>({{Macro "FunctionName" $f}})},
     {{end}}
   {{end}}
@@ -88,7 +88,7 @@
 const NameProcEntry kDeviceProcTbl[] = {«
   // clang-format off
   {{range $f := SortBy (AllCommands $) "FunctionName"}}
-    {{if eq (Macro "Vtbl" $f) "Device"}}
+    {{if and (Macro "IsDispatched" $f) (eq (Macro "Vtbl" $f) "Device")}}
       {"{{Macro "FunctionName" $f}}", reinterpret_cast<PFN_vkVoidFunction>({{Macro "FunctionName" $f}})},
     {{end}}
   {{end}}
@@ -98,7 +98,7 @@
 const NameOffsetEntry kInstanceOffsetTbl[] = {«
   // clang-format off
   {{range $f := SortBy (AllCommands $) "FunctionName"}}
-    {{if eq (Macro "Vtbl" $f) "Instance"}}
+    {{if and (Macro "IsDispatched" $f) (eq (Macro "Vtbl" $f) "Instance")}}
       {"{{Macro "FunctionName" $f}}", offsetof(InstanceVtbl, {{TrimPrefix "vk" (Macro "FunctionName" $f)}})},
     {{end}}
   {{end}}
@@ -108,7 +108,7 @@
 const NameOffsetEntry kDeviceOffsetTbl[] = {«
   // clang-format off
   {{range $f := SortBy (AllCommands $) "FunctionName"}}
-    {{if eq (Macro "Vtbl" $f) "Device"}}
+    {{if and (Macro "IsDispatched" $f) (eq (Macro "Vtbl" $f) "Device")}}
       {"{{Macro "FunctionName" $f}}", offsetof(DeviceVtbl, {{TrimPrefix "vk" (Macro "FunctionName" $f)}})},
     {{end}}
   {{end}}
@@ -194,7 +194,7 @@
     {{end}}
     {{range $f := AllCommands $}}
       {{if eq (Macro "Vtbl" $f) "Instance"}}
-        {{if (GetAnnotation $f "extension")}}
+        {{if and (GetAnnotation $f "extension") (Macro "IsDispatched" $f)}}
     vtbl.{{TrimPrefix "vk" (Macro "FunctionName" $f)}} = §
         reinterpret_cast<{{Macro "FunctionPtrName" $f}}>(§
             get_proc_addr(instance, "{{Macro "FunctionName" $f}}"));
@@ -265,3 +265,32 @@
 } // namespace vulkan

 {{end}}
+
+
+{{/*
+-------------------------------------------------------------------------------
+  This function emits nil for extension entrypoints that should not be
+  included in dispatch tables, "true" otherwise. Extensions that are ony used
+  directly between the loader and driver, or that aren't supported on Android
+  at all, should be excluded from dispatch.
+-------------------------------------------------------------------------------
+*/}}
+{{define "IsUnsupportedExtension"}}
+  {{$ext := index $.Arguments 0}}
+  {{     if eq $ext "VK_EXT_KHR_display"}}true
+  {{else if eq $ext "VK_EXT_KHR_display_swapchain"}}true
+  {{else if eq $ext "VK_EXT_KHR_x11_surface"}}true
+  {{else if eq $ext "VK_EXT_KHR_xcb_surface"}}true
+  {{else if eq $ext "VK_EXT_KHR_wayland_surface"}}true
+  {{else if eq $ext "VK_EXT_KHR_mir_surface"}}true
+  {{else if eq $ext "VK_EXT_KHR_win32_surface"}}true
+  {{end}}
+{{end}}
+
+{{define "IsDispatched"}}
+  {{AssertType $ "Function"}}
+  {{$ext := GetAnnotation $ "extension"}}
+  {{if not $ext}}true
+  {{else if not (Macro "IsUnsupportedExtension" $ext)}}true
+  {{end}}
+{{end}}
diff --git a/vulkan/libvulkan/loader.cpp b/vulkan/libvulkan/loader.cpp
index c427918..a44026f 100644
--- a/vulkan/libvulkan/loader.cpp
+++ b/vulkan/libvulkan/loader.cpp
@@ -1102,16 +1102,29 @@
     return VK_SUCCESS;
 }
 
-void* AllocDeviceMem(VkDevice device,
-                     size_t size,
-                     size_t align,
-                     VkSystemAllocType type) {
+void* AllocMem(VkInstance instance,
+               size_t size,
+               size_t align,
+               VkSystemAllocType type) {
+    const VkAllocCallbacks* alloc_cb = instance->alloc;
+    return alloc_cb->pfnAlloc(alloc_cb->pUserData, size, align, type);
+}
+
+void FreeMem(VkInstance instance, void* ptr) {
+    const VkAllocCallbacks* alloc_cb = instance->alloc;
+    alloc_cb->pfnFree(alloc_cb->pUserData, ptr);
+}
+
+void* AllocMem(VkDevice device,
+               size_t size,
+               size_t align,
+               VkSystemAllocType type) {
     const VkAllocCallbacks* alloc_cb =
         static_cast<Device*>(GetVtbl(device)->device)->instance->alloc;
     return alloc_cb->pfnAlloc(alloc_cb->pUserData, size, align, type);
 }
 
-void FreeDeviceMem(VkDevice device, void* ptr) {
+void FreeMem(VkDevice device, void* ptr) {
     const VkAllocCallbacks* alloc_cb =
         static_cast<Device*>(GetVtbl(device)->device)->instance->alloc;
     alloc_cb->pfnFree(alloc_cb->pUserData, ptr);
diff --git a/vulkan/libvulkan/loader.h b/vulkan/libvulkan/loader.h
index 5e0a6c9..3e34e75 100644
--- a/vulkan/libvulkan/loader.h
+++ b/vulkan/libvulkan/loader.h
@@ -18,9 +18,8 @@
 #define LIBVULKAN_LOADER_H 1
 
 #define VK_PROTOTYPES
+#define VK_USE_PLATFORM_ANDROID_KHR
 #include <vulkan/vulkan.h>
-#include <vulkan/vk_ext_khr_swapchain.h>
-#include <vulkan/vk_ext_khr_device_swapchain.h>
 #include <vulkan/vk_ext_android_native_buffer.h>
 
 namespace vulkan {
@@ -52,6 +51,8 @@
     PFN_vkGetPhysicalDeviceSparseImageFormatProperties GetPhysicalDeviceSparseImageFormatProperties;
 
     // Layers and loader only, not implemented by drivers
+    PFN_vkCreateAndroidSurfaceKHR CreateAndroidSurfaceKHR;
+    PFN_vkDestroySurfaceKHR DestroySurfaceKHR;
     PFN_vkGetPhysicalDeviceSurfaceSupportKHR GetPhysicalDeviceSurfaceSupportKHR;
     // clang-format on
 };
@@ -230,11 +231,16 @@
                              VkCmdBuffer* cmdbuffers);
 VkResult DestroyDevice(VkDevice drv_device);
 
-void* AllocDeviceMem(VkDevice device,
-                     size_t size,
-                     size_t align,
-                     VkSystemAllocType type);
-void FreeDeviceMem(VkDevice device, void* ptr);
+void* AllocMem(VkInstance instance,
+               size_t size,
+               size_t align,
+               VkSystemAllocType type);
+void FreeMem(VkInstance instance, void* ptr);
+void* AllocMem(VkDevice device,
+               size_t size,
+               size_t align,
+               VkSystemAllocType type);
+void FreeMem(VkDevice device, void* ptr);
 const DeviceVtbl& GetDriverVtbl(VkDevice device);
 const DeviceVtbl& GetDriverVtbl(VkQueue queue);
 
@@ -260,20 +266,22 @@
 // -----------------------------------------------------------------------------
 // swapchain.cpp
 
-VkResult GetPhysicalDeviceSurfaceSupportKHR(
-    VkPhysicalDevice pdev,
-    uint32_t queue_family,
-    const VkSurfaceDescriptionKHR* surface_desc,
-    VkBool32* supported);
+VkResult CreateAndroidSurfaceKHR(VkInstance instance,
+                                 ANativeWindow* window,
+                                 VkSurfaceKHR* surface);
+void DestroySurfaceKHR(VkInstance instance, VkSurfaceKHR surface);
+VkBool32 GetPhysicalDeviceSurfaceSupportKHR(VkPhysicalDevice pdev,
+                                            uint32_t queue_family,
+                                            VkSurfaceKHR surface);
 VkResult GetSurfacePropertiesKHR(VkDevice device,
-                                 const VkSurfaceDescriptionKHR* surface_desc,
+                                 VkSurfaceKHR surface,
                                  VkSurfacePropertiesKHR* properties);
 VkResult GetSurfaceFormatsKHR(VkDevice device,
-                              const VkSurfaceDescriptionKHR* surface_desc,
+                              VkSurfaceKHR surface,
                               uint32_t* count,
                               VkSurfaceFormatKHR* formats);
 VkResult GetSurfacePresentModesKHR(VkDevice device,
-                                   const VkSurfaceDescriptionKHR* surface_desc,
+                                   VkSurfaceKHR surface,
                                    uint32_t* count,
                                    VkPresentModeKHR* modes);
 VkResult CreateSwapchainKHR(VkDevice device,
diff --git a/vulkan/libvulkan/swapchain.cpp b/vulkan/libvulkan/swapchain.cpp
index 70c0e33..6813680 100644
--- a/vulkan/libvulkan/swapchain.cpp
+++ b/vulkan/libvulkan/swapchain.cpp
@@ -49,43 +49,55 @@
     void operator()(T* obj) { obj->common.decRef(&obj->common); }
 };
 
-template <typename T>
+template <typename T, typename Host>
 class VulkanAllocator {
    public:
     typedef T value_type;
 
-    explicit VulkanAllocator(VkDevice device) : device_(device) {}
+    explicit VulkanAllocator(Host host) : host_(host) {}
 
     template <typename U>
-    explicit VulkanAllocator(const VulkanAllocator<U>& other)
-        : device_(other.device_) {}
+    explicit VulkanAllocator(const VulkanAllocator<U, Host>& other)
+        : host_(other.host_) {}
 
     T* allocate(size_t n) const {
-        return static_cast<T*>(AllocDeviceMem(
-            device_, n * sizeof(T), alignof(T), VK_SYSTEM_ALLOC_TYPE_INTERNAL));
+        return static_cast<T*>(AllocMem(host_, n * sizeof(T), alignof(T),
+                                        VK_SYSTEM_ALLOC_TYPE_INTERNAL));
     }
-    void deallocate(T* p, size_t) const { return FreeDeviceMem(device_, p); }
+    void deallocate(T* p, size_t) const { return FreeMem(host_, p); }
 
    private:
-    template <typename U>
+    template <typename U, typename H>
     friend class VulkanAllocator;
-    VkDevice device_;
+    Host host_;
 };
 
-template <typename T>
-std::shared_ptr<T> InitSharedPtr(VkDevice device, T* obj) {
+template <typename T, typename Host>
+std::shared_ptr<T> InitSharedPtr(Host host, T* obj) {
     obj->common.incRef(&obj->common);
     return std::shared_ptr<T>(obj, NativeBaseDeleter<T>(),
-                              VulkanAllocator<T>(device));
+                              VulkanAllocator<T, Host>(host));
 }
 
 // ----------------------------------------------------------------------------
 
-struct Swapchain {
-    Swapchain(std::shared_ptr<ANativeWindow> window_, uint32_t num_images_)
-        : window(window_), num_images(num_images_) {}
-
+struct Surface {
     std::shared_ptr<ANativeWindow> window;
+};
+
+VkSurfaceKHR HandleFromSurface(Surface* surface) {
+    return VkSurfaceKHR(reinterpret_cast<uint64_t>(surface));
+}
+
+Surface* SurfaceFromHandle(VkSurfaceKHR handle) {
+    return reinterpret_cast<Surface*>(handle.handle);
+}
+
+struct Swapchain {
+    Swapchain(Surface& surface_, uint32_t num_images_)
+        : surface(surface_), num_images(num_images_) {}
+
+    Surface& surface;
     uint32_t num_images;
 
     struct Image {
@@ -113,95 +125,69 @@
 
 namespace vulkan {
 
-VkResult GetPhysicalDeviceSurfaceSupportKHR(
-    VkPhysicalDevice /*pdev*/,
-    uint32_t /*queue_family*/,
-    const VkSurfaceDescriptionKHR* surface_desc,
-    VkBool32* supported) {
-// TODO(jessehall): Fix the header, preferrably upstream, so values added to
-// existing enums don't trigger warnings like this.
-#pragma clang diagnostic push
-#pragma clang diagnostic ignored "-Wold-style-cast"
-#pragma clang diagnostic ignored "-Wsign-conversion"
-    ALOGE_IF(
-        surface_desc->sType != VK_STRUCTURE_TYPE_SURFACE_DESCRIPTION_WINDOW_KHR,
-        "vkGetPhysicalDeviceSurfaceSupportKHR: pSurfaceDescription->sType=%#x "
-        "not supported",
-        surface_desc->sType);
-#pragma clang diagnostic pop
+VkResult CreateAndroidSurfaceKHR(VkInstance instance,
+                                 ANativeWindow* window,
+                                 VkSurfaceKHR* out_surface) {
+    void* mem = AllocMem(instance, sizeof(Surface), alignof(Surface),
+                         VK_SYSTEM_ALLOC_TYPE_API_OBJECT);
+    if (!mem)
+        return VK_ERROR_OUT_OF_HOST_MEMORY;
+    Surface* surface = new (mem) Surface;
 
-    const VkSurfaceDescriptionWindowKHR* window_desc =
-        reinterpret_cast<const VkSurfaceDescriptionWindowKHR*>(surface_desc);
+    surface->window = InitSharedPtr(instance, window);
 
-    // TODO(jessehall): Also check whether the physical device exports the
-    // VK_EXT_ANDROID_native_buffer extension. For now, assume it does.
-    *supported = (window_desc->platform == VK_PLATFORM_ANDROID_KHR &&
-                  !window_desc->pPlatformHandle &&
-                  static_cast<ANativeWindow*>(window_desc->pPlatformWindow)
-                          ->common.magic == ANDROID_NATIVE_WINDOW_MAGIC);
+    // TODO(jessehall): Create and use NATIVE_WINDOW_API_VULKAN.
+    int err =
+        native_window_api_connect(surface->window.get(), NATIVE_WINDOW_API_EGL);
+    if (err != 0) {
+        // TODO(jessehall): Improve error reporting. Can we enumerate possible
+        // errors and translate them to valid Vulkan result codes?
+        ALOGE("native_window_api_connect() failed: %s (%d)", strerror(-err),
+              err);
+        surface->~Surface();
+        FreeMem(instance, surface);
+        return VK_ERROR_INITIALIZATION_FAILED;
+    }
 
+    *out_surface = HandleFromSurface(surface);
     return VK_SUCCESS;
 }
 
+void DestroySurfaceKHR(VkInstance instance, VkSurfaceKHR surface_handle) {
+    Surface* surface = SurfaceFromHandle(surface_handle);
+    if (!surface)
+        return;
+    native_window_api_disconnect(surface->window.get(), NATIVE_WINDOW_API_EGL);
+    surface->~Surface();
+    FreeMem(instance, surface);
+}
+
+VkBool32 GetPhysicalDeviceSurfaceSupportKHR(VkPhysicalDevice /*pdev*/,
+                                            uint32_t /*queue_family*/,
+                                            VkSurfaceKHR /*surface*/) {
+    return VK_TRUE;
+}
+
 VkResult GetSurfacePropertiesKHR(VkDevice /*device*/,
-                                 const VkSurfaceDescriptionKHR* surface_desc,
+                                 VkSurfaceKHR surface,
                                  VkSurfacePropertiesKHR* properties) {
-    const VkSurfaceDescriptionWindowKHR* window_desc =
-        reinterpret_cast<const VkSurfaceDescriptionWindowKHR*>(surface_desc);
-    ANativeWindow* window =
-        static_cast<ANativeWindow*>(window_desc->pPlatformWindow);
-
     int err;
-
-    // TODO(jessehall): Currently the window must be connected for several
-    // queries -- including default dimensions -- to work, since Surface caches
-    // the queried values at connect() and queueBuffer(), and query() returns
-    // those cached values.
-    //
-    // The proposed refactoring to create a VkSurface object (bug 14596) will
-    // give us a place to connect once per window. If that doesn't end up
-    // happening, we'll probably need to maintain an internal list of windows
-    // that have swapchains created for them, search that list here, and
-    // only temporarily connect if the window doesn't have a swapchain.
-
-    bool disconnect = true;
-    err = native_window_api_connect(window, NATIVE_WINDOW_API_EGL);
-    if (err == -EINVAL) {
-        // This is returned if the window is already connected, among other
-        // things. We'll just assume we're already connected and charge ahead.
-        // See TODO above, this is not cool.
-        ALOGW(
-            "vkGetSurfacePropertiesKHR: native_window_api_connect returned "
-            "-EINVAL, assuming already connected");
-        err = 0;
-        disconnect = false;
-    } else if (err != 0) {
-        // TODO(jessehall): Improve error reporting. Can we enumerate possible
-        // errors and translate them to valid Vulkan result codes?
-        return VK_ERROR_INITIALIZATION_FAILED;
-    }
+    ANativeWindow* window = SurfaceFromHandle(surface)->window.get();
 
     int width, height;
     err = window->query(window, NATIVE_WINDOW_DEFAULT_WIDTH, &width);
     if (err != 0) {
         ALOGE("NATIVE_WINDOW_DEFAULT_WIDTH query failed: %s (%d)",
               strerror(-err), err);
-        if (disconnect)
-            native_window_api_disconnect(window, NATIVE_WINDOW_API_EGL);
         return VK_ERROR_INITIALIZATION_FAILED;
     }
     err = window->query(window, NATIVE_WINDOW_DEFAULT_HEIGHT, &height);
     if (err != 0) {
         ALOGE("NATIVE_WINDOW_DEFAULT_WIDTH query failed: %s (%d)",
               strerror(-err), err);
-        if (disconnect)
-            native_window_api_disconnect(window, NATIVE_WINDOW_API_EGL);
         return VK_ERROR_INITIALIZATION_FAILED;
     }
 
-    if (disconnect)
-        native_window_api_disconnect(window, NATIVE_WINDOW_API_EGL);
-
     properties->currentExtent = VkExtent2D{width, height};
 
     // TODO(jessehall): Figure out what the min/max values should be.
@@ -238,13 +224,14 @@
 }
 
 VkResult GetSurfaceFormatsKHR(VkDevice /*device*/,
-                              const VkSurfaceDescriptionKHR* /*surface_desc*/,
+                              VkSurfaceKHR /*surface*/,
                               uint32_t* count,
                               VkSurfaceFormatKHR* formats) {
-    // TODO(jessehall): Fill out the set of supported formats. Open question
-    // whether we should query the driver for support -- how does it know what
-    // the consumer can support? Should we support formats that don't
-    // correspond to gralloc formats?
+    // TODO(jessehall): Fill out the set of supported formats. Longer term, add
+    // a new gralloc method to query whether a (format, usage) pair is
+    // supported, and check that for each gralloc format that corresponds to a
+    // Vulkan format. Shorter term, just add a few more formats to the ones
+    // hardcoded below.
 
     const VkSurfaceFormatKHR kFormats[] = {
         {VK_FORMAT_R8G8B8A8_UNORM, VK_COLORSPACE_SRGB_NONLINEAR_KHR},
@@ -262,11 +249,10 @@
     return result;
 }
 
-VkResult GetSurfacePresentModesKHR(
-    VkDevice /*device*/,
-    const VkSurfaceDescriptionKHR* /*surface_desc*/,
-    uint32_t* count,
-    VkPresentModeKHR* modes) {
+VkResult GetSurfacePresentModesKHR(VkDevice /*device*/,
+                                   VkSurfaceKHR /*surface*/,
+                                   uint32_t* count,
+                                   VkPresentModeKHR* modes) {
     const VkPresentModeKHR kModes[] = {
         VK_PRESENT_MODE_MAILBOX_KHR, VK_PRESENT_MODE_FIFO_KHR,
     };
@@ -304,26 +290,11 @@
              "present modes other than FIFO are not yet implemented");
 
     // -- Configure the native window --
-    // Failure paths from here on need to disconnect the window.
 
+    Surface& surface = *SurfaceFromHandle(create_info->surface);
     const DeviceVtbl& driver_vtbl = GetDriverVtbl(device);
 
-    std::shared_ptr<ANativeWindow> window = InitSharedPtr(
-        device, static_cast<ANativeWindow*>(
-                    reinterpret_cast<const VkSurfaceDescriptionWindowKHR*>(
-                        create_info->pSurfaceDescription)->pPlatformWindow));
-
-    // TODO(jessehall): Create and use NATIVE_WINDOW_API_VULKAN.
-    err = native_window_api_connect(window.get(), NATIVE_WINDOW_API_EGL);
-    if (err != 0) {
-        // TODO(jessehall): Improve error reporting. Can we enumerate possible
-        // errors and translate them to valid Vulkan result codes?
-        ALOGE("native_window_api_connect() failed: %s (%d)", strerror(-err),
-              err);
-        return VK_ERROR_INITIALIZATION_FAILED;
-    }
-
-    err = native_window_set_buffers_dimensions(window.get(),
+    err = native_window_set_buffers_dimensions(surface.window.get(),
                                                create_info->imageExtent.width,
                                                create_info->imageExtent.height);
     if (err != 0) {
@@ -332,40 +303,37 @@
         ALOGE("native_window_set_buffers_dimensions(%d,%d) failed: %s (%d)",
               create_info->imageExtent.width, create_info->imageExtent.height,
               strerror(-err), err);
-        native_window_api_disconnect(window.get(), NATIVE_WINDOW_API_EGL);
         return VK_ERROR_INITIALIZATION_FAILED;
     }
 
     err = native_window_set_scaling_mode(
-        window.get(), NATIVE_WINDOW_SCALING_MODE_SCALE_TO_WINDOW);
+        surface.window.get(), NATIVE_WINDOW_SCALING_MODE_SCALE_TO_WINDOW);
     if (err != 0) {
         // TODO(jessehall): Improve error reporting. Can we enumerate possible
         // errors and translate them to valid Vulkan result codes?
         ALOGE("native_window_set_scaling_mode(SCALE_TO_WINDOW) failed: %s (%d)",
               strerror(-err), err);
-        native_window_api_disconnect(window.get(), NATIVE_WINDOW_API_EGL);
         return VK_ERROR_INITIALIZATION_FAILED;
     }
 
     uint32_t min_undequeued_buffers;
-    err = window->query(window.get(), NATIVE_WINDOW_MIN_UNDEQUEUED_BUFFERS,
-                        reinterpret_cast<int*>(&min_undequeued_buffers));
+    err = surface.window->query(
+        surface.window.get(), NATIVE_WINDOW_MIN_UNDEQUEUED_BUFFERS,
+        reinterpret_cast<int*>(&min_undequeued_buffers));
     if (err != 0) {
         // TODO(jessehall): Improve error reporting. Can we enumerate possible
         // errors and translate them to valid Vulkan result codes?
         ALOGE("window->query failed: %s (%d)", strerror(-err), err);
-        native_window_api_disconnect(window.get(), NATIVE_WINDOW_API_EGL);
         return VK_ERROR_INITIALIZATION_FAILED;
     }
     uint32_t num_images =
         (create_info->minImageCount - 1) + min_undequeued_buffers;
-    err = native_window_set_buffer_count(window.get(), num_images);
+    err = native_window_set_buffer_count(surface.window.get(), num_images);
     if (err != 0) {
         // TODO(jessehall): Improve error reporting. Can we enumerate possible
         // errors and translate them to valid Vulkan result codes?
         ALOGE("native_window_set_buffer_count failed: %s (%d)", strerror(-err),
               err);
-        native_window_api_disconnect(window.get(), NATIVE_WINDOW_API_EGL);
         return VK_ERROR_INITIALIZATION_FAILED;
     }
 
@@ -377,31 +345,27 @@
             &gralloc_usage);
         if (result != VK_SUCCESS) {
             ALOGE("vkGetSwapchainGrallocUsageANDROID failed: %d", result);
-            native_window_api_disconnect(window.get(), NATIVE_WINDOW_API_EGL);
             return VK_ERROR_INITIALIZATION_FAILED;
         }
     } else {
         gralloc_usage = GRALLOC_USAGE_HW_RENDER | GRALLOC_USAGE_HW_TEXTURE;
     }
-    err = native_window_set_usage(window.get(), gralloc_usage);
+    err = native_window_set_usage(surface.window.get(), gralloc_usage);
     if (err != 0) {
         // TODO(jessehall): Improve error reporting. Can we enumerate possible
         // errors and translate them to valid Vulkan result codes?
         ALOGE("native_window_set_usage failed: %s (%d)", strerror(-err), err);
-        native_window_api_disconnect(window.get(), NATIVE_WINDOW_API_EGL);
         return VK_ERROR_INITIALIZATION_FAILED;
     }
 
     // -- Allocate our Swapchain object --
     // After this point, we must deallocate the swapchain on error.
 
-    void* mem = AllocDeviceMem(device, sizeof(Swapchain), alignof(Swapchain),
-                               VK_SYSTEM_ALLOC_TYPE_API_OBJECT);
-    if (!mem) {
-        native_window_api_disconnect(window.get(), NATIVE_WINDOW_API_EGL);
+    void* mem = AllocMem(device, sizeof(Swapchain), alignof(Swapchain),
+                         VK_SYSTEM_ALLOC_TYPE_API_OBJECT);
+    if (!mem)
         return VK_ERROR_OUT_OF_HOST_MEMORY;
-    }
-    Swapchain* swapchain = new (mem) Swapchain(window, num_images);
+    Swapchain* swapchain = new (mem) Swapchain(surface, num_images);
 
     // -- Dequeue all buffers and create a VkImage for each --
     // Any failures during or after this must cancel the dequeued buffers.
@@ -435,7 +399,8 @@
         Swapchain::Image& img = swapchain->images[i];
 
         ANativeWindowBuffer* buffer;
-        err = window->dequeueBuffer(window.get(), &buffer, &img.dequeue_fence);
+        err = surface.window->dequeueBuffer(surface.window.get(), &buffer,
+                                            &img.dequeue_fence);
         if (err != 0) {
             // TODO(jessehall): Improve error reporting. Can we enumerate
             // possible errors and translate them to valid Vulkan result codes?
@@ -469,8 +434,8 @@
     for (uint32_t i = 0; i < num_images; i++) {
         Swapchain::Image& img = swapchain->images[i];
         if (img.dequeued) {
-            window->cancelBuffer(window.get(), img.buffer.get(),
-                                 img.dequeue_fence);
+            surface.window->cancelBuffer(surface.window.get(), img.buffer.get(),
+                                         img.dequeue_fence);
             img.dequeue_fence = -1;
             img.dequeued = false;
         }
@@ -481,9 +446,8 @@
     }
 
     if (result != VK_SUCCESS) {
-        native_window_api_disconnect(window.get(), NATIVE_WINDOW_API_EGL);
         swapchain->~Swapchain();
-        FreeDeviceMem(device, swapchain);
+        FreeMem(device, swapchain);
         return result;
     }
 
@@ -494,7 +458,7 @@
 VkResult DestroySwapchainKHR(VkDevice device, VkSwapchainKHR swapchain_handle) {
     const DeviceVtbl& driver_vtbl = GetDriverVtbl(device);
     Swapchain* swapchain = SwapchainFromHandle(swapchain_handle);
-    const std::shared_ptr<ANativeWindow>& window = swapchain->window;
+    const std::shared_ptr<ANativeWindow>& window = swapchain->surface.window;
 
     for (uint32_t i = 0; i < swapchain->num_images; i++) {
         Swapchain::Image& img = swapchain->images[i];
@@ -509,9 +473,8 @@
         }
     }
 
-    native_window_api_disconnect(window.get(), NATIVE_WINDOW_API_EGL);
     swapchain->~Swapchain();
-    FreeDeviceMem(device, swapchain);
+    FreeMem(device, swapchain);
 
     return VK_SUCCESS;
 }
@@ -541,6 +504,7 @@
                              VkSemaphore semaphore,
                              uint32_t* image_index) {
     Swapchain& swapchain = *SwapchainFromHandle(swapchain_handle);
+    ANativeWindow* window = swapchain.surface.window.get();
     VkResult result;
     int err;
 
@@ -550,8 +514,7 @@
 
     ANativeWindowBuffer* buffer;
     int fence;
-    err = swapchain.window->dequeueBuffer(swapchain.window.get(), &buffer,
-                                          &fence);
+    err = window->dequeueBuffer(window, &buffer, &fence);
     if (err != 0) {
         // TODO(jessehall): Improve error reporting. Can we enumerate possible
         // errors and translate them to valid Vulkan result codes?
@@ -569,7 +532,7 @@
     }
     if (idx == swapchain.num_images) {
         ALOGE("dequeueBuffer returned unrecognized buffer");
-        swapchain.window->cancelBuffer(swapchain.window.get(), buffer, fence);
+        window->cancelBuffer(window, buffer, fence);
 #pragma clang diagnostic push
 #pragma clang diagnostic ignored "-Wold-style-cast"
         return VK_ERROR_OUT_OF_DATE_KHR;
@@ -605,7 +568,7 @@
         // number between the time the driver closes it and the time we close
         // it. We must assume one of: the driver *always* closes it even on
         // failure, or *never* closes it on failure.
-        swapchain.window->cancelBuffer(swapchain.window.get(), buffer, fence);
+        window->cancelBuffer(window, buffer, fence);
         swapchain.images[idx].dequeued = false;
         swapchain.images[idx].dequeue_fence = -1;
         return result;
@@ -630,6 +593,7 @@
     for (uint32_t sc = 0; sc < present_info->swapchainCount; sc++) {
         Swapchain& swapchain =
             *SwapchainFromHandle(present_info->swapchains[sc]);
+        ANativeWindow* window = swapchain.surface.window.get();
         uint32_t image_idx = present_info->imageIndices[sc];
         Swapchain::Image& img = swapchain.images[image_idx];
         VkResult result;
@@ -656,8 +620,7 @@
             continue;
         }
 
-        err = swapchain.window->queueBuffer(swapchain.window.get(),
-                                            img.buffer.get(), fence);
+        err = window->queueBuffer(window, img.buffer.get(), fence);
         if (err != 0) {
             // TODO(jessehall): What now? We should probably cancel the buffer,
             // I guess?