vulkan: Add skeleton swapchain support

Loader and driver entry points exist and the call chains are hooked up
properly, but functions are just empty stubs for now.

Change-Id: I6ff95e47d1d09ebed41eda8accb71686c8249546
(cherry picked from commit 038c8a740bbda4650bec776023a26a7fcbf93864)
diff --git a/vulkan/libvulkan/Android.mk b/vulkan/libvulkan/Android.mk
index 3ba7ecd..5898678 100644
--- a/vulkan/libvulkan/Android.mk
+++ b/vulkan/libvulkan/Android.mk
@@ -30,7 +30,8 @@
 LOCAL_SRC_FILES := \
 	entry.cpp \
 	get_proc_addr.cpp \
-	loader.cpp
+	loader.cpp \
+	swapchain.cpp
 LOCAL_ADDITIONAL_DEPENDENCIES := $(LOCAL_PATH)/Android.mk
 
 LOCAL_SHARED_LIBRARIES := libhardware liblog
diff --git a/vulkan/libvulkan/entry.cpp b/vulkan/libvulkan/entry.cpp
index f2b4482..cd5dd99 100644
--- a/vulkan/libvulkan/entry.cpp
+++ b/vulkan/libvulkan/entry.cpp
@@ -800,3 +800,48 @@
 void vkCmdExecuteCommands(VkCmdBuffer cmdBuffer, uint32_t cmdBuffersCount, const VkCmdBuffer* pCmdBuffers) {
     GetVtbl(cmdBuffer).CmdExecuteCommands(cmdBuffer, cmdBuffersCount, pCmdBuffers);
 }
+
+__attribute__((visibility("default")))
+VkResult vkGetPhysicalDeviceSurfaceSupportKHR(VkPhysicalDevice physicalDevice, uint32_t queueFamilyIndex, const VkSurfaceDescriptionKHR* pSurfaceDescription, VkBool32* pSupported) {
+    return GetVtbl(physicalDevice).GetPhysicalDeviceSurfaceSupportKHR(physicalDevice, queueFamilyIndex, pSurfaceDescription, pSupported);
+}
+
+__attribute__((visibility("default")))
+VkResult vkGetSurfacePropertiesKHR(VkDevice device, const VkSurfaceDescriptionKHR* pSurfaceDescription, VkSurfacePropertiesKHR* pSurfaceProperties) {
+    return GetVtbl(device).GetSurfacePropertiesKHR(device, pSurfaceDescription, pSurfaceProperties);
+}
+
+__attribute__((visibility("default")))
+VkResult vkGetSurfaceFormatsKHR(VkDevice device, const VkSurfaceDescriptionKHR* pSurfaceDescription, uint32_t* pCount, VkSurfaceFormatKHR* pSurfaceFormats) {
+    return GetVtbl(device).GetSurfaceFormatsKHR(device, pSurfaceDescription, pCount, pSurfaceFormats);
+}
+
+__attribute__((visibility("default")))
+VkResult vkGetSurfacePresentModesKHR(VkDevice device, const VkSurfaceDescriptionKHR* pSurfaceDescription, uint32_t* pCount, VkPresentModeKHR* pPresentModes) {
+    return GetVtbl(device).GetSurfacePresentModesKHR(device, pSurfaceDescription, pCount, pPresentModes);
+}
+
+__attribute__((visibility("default")))
+VkResult vkCreateSwapchainKHR(VkDevice device, const VkSwapchainCreateInfoKHR* pCreateInfo, VkSwapchainKHR* pSwapchain) {
+    return GetVtbl(device).CreateSwapchainKHR(device, pCreateInfo, pSwapchain);
+}
+
+__attribute__((visibility("default")))
+VkResult vkDestroySwapchainKHR(VkDevice device, VkSwapchainKHR swapchain) {
+    return GetVtbl(device).DestroySwapchainKHR(device, swapchain);
+}
+
+__attribute__((visibility("default")))
+VkResult vkGetSwapchainImagesKHR(VkDevice device, VkSwapchainKHR swapchain, uint32_t* pCount, VkImage* pSwapchainImages) {
+    return GetVtbl(device).GetSwapchainImagesKHR(device, swapchain, pCount, pSwapchainImages);
+}
+
+__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);
+}
+
+__attribute__((visibility("default")))
+VkResult vkQueuePresentKHR(VkQueue queue, VkPresentInfoKHR* pPresentInfo) {
+    return GetVtbl(queue).QueuePresentKHR(queue, pPresentInfo);
+}
diff --git a/vulkan/libvulkan/entry.cpp.tmpl b/vulkan/libvulkan/entry.cpp.tmpl
index 352ac59..4e671e5 100644
--- a/vulkan/libvulkan/entry.cpp.tmpl
+++ b/vulkan/libvulkan/entry.cpp.tmpl
@@ -78,6 +78,53 @@
       {{end}}
     {{end}}
   {{end}}
+
+{{/* Extension functions aren't in the API file yet, so must be special-cased */}}
+__attribute__((visibility("default")))
+VkResult vkGetPhysicalDeviceSurfaceSupportKHR(VkPhysicalDevice physicalDevice, uint32_t queueFamilyIndex, const VkSurfaceDescriptionKHR* pSurfaceDescription, VkBool32* pSupported) {
+    return GetVtbl(physicalDevice).GetPhysicalDeviceSurfaceSupportKHR(physicalDevice, queueFamilyIndex, pSurfaceDescription, pSupported);
+}

+__attribute__((visibility("default")))
+VkResult vkGetSurfacePropertiesKHR(VkDevice device, const VkSurfaceDescriptionKHR* pSurfaceDescription, VkSurfacePropertiesKHR* pSurfaceProperties) {
+    return GetVtbl(device).GetSurfacePropertiesKHR(device, pSurfaceDescription, pSurfaceProperties);
+}

+__attribute__((visibility("default")))
+VkResult vkGetSurfaceFormatsKHR(VkDevice device, const VkSurfaceDescriptionKHR* pSurfaceDescription, uint32_t* pCount, VkSurfaceFormatKHR* pSurfaceFormats) {
+    return GetVtbl(device).GetSurfaceFormatsKHR(device, pSurfaceDescription, pCount, pSurfaceFormats);
+}

+__attribute__((visibility("default")))
+VkResult vkGetSurfacePresentModesKHR(VkDevice device, const VkSurfaceDescriptionKHR* pSurfaceDescription, uint32_t* pCount, VkPresentModeKHR* pPresentModes) {
+    return GetVtbl(device).GetSurfacePresentModesKHR(device, pSurfaceDescription, pCount, pPresentModes);
+}

+__attribute__((visibility("default")))
+VkResult vkCreateSwapchainKHR(VkDevice device, const VkSwapchainCreateInfoKHR* pCreateInfo, VkSwapchainKHR* pSwapchain) {
+    return GetVtbl(device).CreateSwapchainKHR(device, pCreateInfo, pSwapchain);
+}

+__attribute__((visibility("default")))
+VkResult vkDestroySwapchainKHR(VkDevice device, VkSwapchainKHR swapchain) {
+    return GetVtbl(device).DestroySwapchainKHR(device, swapchain);
+}

+__attribute__((visibility("default")))
+VkResult vkGetSwapchainImagesKHR(VkDevice device, VkSwapchainKHR swapchain, uint32_t* pCount, VkImage* pSwapchainImages) {
+    return GetVtbl(device).GetSwapchainImagesKHR(device, swapchain, pCount, pSwapchainImages);
+}

+__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);
+}

+__attribute__((visibility("default")))
+VkResult vkQueuePresentKHR(VkQueue queue, VkPresentInfoKHR* pPresentInfo) {
+    return GetVtbl(queue).QueuePresentKHR(queue, pPresentInfo);
+}

 {{end}}
 
 
diff --git a/vulkan/libvulkan/get_proc_addr.cpp b/vulkan/libvulkan/get_proc_addr.cpp
index 5f05f0e..ba38bff 100644
--- a/vulkan/libvulkan/get_proc_addr.cpp
+++ b/vulkan/libvulkan/get_proc_addr.cpp
@@ -370,21 +370,54 @@
 namespace vulkan {
 
 PFN_vkVoidFunction GetGlobalInstanceProcAddr(const char* name) {
+    const NameProcEntry* entry = FindProcEntry(kInstanceProcTbl, name);
+    if (entry)
+        return entry->proc;
+    // vkGetDeviceProcAddr must be available at the global/instance level for
+    // bootstrapping
     if (strcmp(name, "vkGetDeviceProcAddr") == 0)
         return reinterpret_cast<PFN_vkVoidFunction>(vkGetDeviceProcAddr);
-    const NameProcEntry* entry = FindProcEntry(kInstanceProcTbl, name);
-    return entry ? entry->proc : nullptr;
+    // special-case extension functions until they can be auto-generated
+    if (strcmp(name, "vkGetPhysicalDeviceSurfaceSupportKHR") == 0)
+        return reinterpret_cast<PFN_vkVoidFunction>(
+            vkGetPhysicalDeviceSurfaceSupportKHR);
+    return nullptr;
 }
 
 PFN_vkVoidFunction GetGlobalDeviceProcAddr(const char* name) {
     const NameProcEntry* entry = FindProcEntry(kDeviceProcTbl, name);
-    return entry ? entry->proc : nullptr;
+    if (entry)
+        return entry->proc;
+    // special-case extension functions until they can be auto-generated
+    if (strcmp(name, "vkGetSurfacePropertiesKHR") == 0)
+        return reinterpret_cast<PFN_vkVoidFunction>(vkGetSurfacePropertiesKHR);
+    if (strcmp(name, "vkGetSurfaceFormatsKHR") == 0)
+        return reinterpret_cast<PFN_vkVoidFunction>(vkGetSurfaceFormatsKHR);
+    if (strcmp(name, "vkGetSurfacePresentModesKHR") == 0)
+        return reinterpret_cast<PFN_vkVoidFunction>(
+            vkGetSurfacePresentModesKHR);
+    if (strcmp(name, "vkCreateSwapchainKHR") == 0)
+        return reinterpret_cast<PFN_vkVoidFunction>(vkCreateSwapchainKHR);
+    if (strcmp(name, "vkDestroySwapchainKHR") == 0)
+        return reinterpret_cast<PFN_vkVoidFunction>(vkDestroySwapchainKHR);
+    if (strcmp(name, "vkGetSwapchainImagesKHR") == 0)
+        return reinterpret_cast<PFN_vkVoidFunction>(vkGetSwapchainImagesKHR);
+    if (strcmp(name, "vkAcquireNextImageKHR") == 0)
+        return reinterpret_cast<PFN_vkVoidFunction>(vkAcquireNextImageKHR);
+    if (strcmp(name, "vkQueuePresentKHR") == 0)
+        return reinterpret_cast<PFN_vkVoidFunction>(vkQueuePresentKHR);
+    return nullptr;
 }
 
 PFN_vkVoidFunction GetSpecificInstanceProcAddr(const InstanceVtbl* vtbl,
                                                const char* name) {
+    size_t offset;
     const NameOffsetEntry* entry = FindProcEntry(kInstanceOffsetTbl, name);
-    if (!entry)
+    if (entry)
+        offset = entry->offset;
+    else if (strcmp(name, "vkGetPhysicalDeviceSurfaceSupportKHR") == 0)
+        offset = offsetof(InstanceVtbl, GetPhysicalDeviceSurfaceSupportKHR);
+    else
         return nullptr;
     const unsigned char* base = reinterpret_cast<const unsigned char*>(vtbl);
     return reinterpret_cast<PFN_vkVoidFunction>(
@@ -393,8 +426,27 @@
 
 PFN_vkVoidFunction GetSpecificDeviceProcAddr(const DeviceVtbl* vtbl,
                                              const char* name) {
+    size_t offset;
     const NameOffsetEntry* entry = FindProcEntry(kDeviceOffsetTbl, name);
-    if (!entry)
+    if (entry)
+        offset = entry->offset;
+    else if (strcmp(name, "vkGetSurfacePropertiesKHR") == 0)
+        offset = offsetof(DeviceVtbl, GetSurfacePropertiesKHR);
+    else if (strcmp(name, "vkGetSurfaceFormatsKHR") == 0)
+        offset = offsetof(DeviceVtbl, GetSurfaceFormatsKHR);
+    else if (strcmp(name, "vkGetSurfacePresentModesKHR") == 0)
+        offset = offsetof(DeviceVtbl, GetSurfacePresentModesKHR);
+    else if (strcmp(name, "vkCreateSwapchainKHR") == 0)
+        offset = offsetof(DeviceVtbl, CreateSwapchainKHR);
+    else if (strcmp(name, "vkDestroySwapchainKHR") == 0)
+        offset = offsetof(DeviceVtbl, DestroySwapchainKHR);
+    else if (strcmp(name, "vkGetSwapchainImagesKHR") == 0)
+        offset = offsetof(DeviceVtbl, GetSwapchainImagesKHR);
+    else if (strcmp(name, "vkAcquireNextImageKHR") == 0)
+        offset = offsetof(DeviceVtbl, AcquireNextImageKHR);
+    else if (strcmp(name, "vkQueuePresentKHR") == 0)
+        offset = offsetof(DeviceVtbl, QueuePresentKHR);
+    else
         return nullptr;
     const unsigned char* base = reinterpret_cast<const unsigned char*>(vtbl);
     return reinterpret_cast<PFN_vkVoidFunction>(
@@ -1160,6 +1212,16 @@
         ALOGE("missing device proc: %s", "vkCmdExecuteCommands");
         success = false;
     }
+    vtbl.ImportNativeFenceANDROID = reinterpret_cast<PFN_vkImportNativeFenceANDROID>(get_proc_addr(device, "vkImportNativeFenceANDROID"));
+    if (UNLIKELY(!vtbl.ImportNativeFenceANDROID)) {
+        ALOGE("missing device proc: %s", "vkImportNativeFenceANDROID");
+        success = false;
+    }
+    vtbl.QueueSignalNativeFenceANDROID = reinterpret_cast<PFN_vkQueueSignalNativeFenceANDROID>(get_proc_addr(device, "vkQueueSignalNativeFenceANDROID"));
+    if (UNLIKELY(!vtbl.QueueSignalNativeFenceANDROID)) {
+        ALOGE("missing device proc: %s", "vkQueueSignalNativeFenceANDROID");
+        success = false;
+    }
     // clang-format on
     return success;
 }
diff --git a/vulkan/libvulkan/get_proc_addr.cpp.tmpl b/vulkan/libvulkan/get_proc_addr.cpp.tmpl
index c3391e1..bbbb2a0 100644
--- a/vulkan/libvulkan/get_proc_addr.cpp.tmpl
+++ b/vulkan/libvulkan/get_proc_addr.cpp.tmpl
@@ -120,21 +120,51 @@
 namespace vulkan {

 PFN_vkVoidFunction GetGlobalInstanceProcAddr(const char* name) {
+    const NameProcEntry* entry = FindProcEntry(kInstanceProcTbl, name);
+    if (entry)
+        return entry->proc;
+    // vkGetDeviceProcAddr must be available at the global/instance level for bootstrapping
     if (strcmp(name, "vkGetDeviceProcAddr") == 0)
         return reinterpret_cast<PFN_vkVoidFunction>(vkGetDeviceProcAddr);
-    const NameProcEntry* entry = FindProcEntry(kInstanceProcTbl, name);
-    return entry ? entry->proc : nullptr;
+    // special-case extension functions until they can be auto-generated
+    if (strcmp(name, "vkGetPhysicalDeviceSurfaceSupportKHR") == 0)
+        return reinterpret_cast<PFN_vkVoidFunction>(vkGetPhysicalDeviceSurfaceSupportKHR);
+    return nullptr;
 }

 PFN_vkVoidFunction GetGlobalDeviceProcAddr(const char* name) {
     const NameProcEntry* entry = FindProcEntry(kDeviceProcTbl, name);
-    return entry ? entry->proc : nullptr;
+    if (entry)
+        return entry->proc;
+    // special-case extension functions until they can be auto-generated
+    if (strcmp(name, "vkGetSurfacePropertiesKHR") == 0)
+        return reinterpret_cast<PFN_vkVoidFunction>(vkGetSurfacePropertiesKHR);
+    if (strcmp(name, "vkGetSurfaceFormatsKHR") == 0)
+        return reinterpret_cast<PFN_vkVoidFunction>(vkGetSurfaceFormatsKHR);
+    if (strcmp(name, "vkGetSurfacePresentModesKHR") == 0)
+        return reinterpret_cast<PFN_vkVoidFunction>(vkGetSurfacePresentModesKHR);
+    if (strcmp(name, "vkCreateSwapchainKHR") == 0)
+        return reinterpret_cast<PFN_vkVoidFunction>(vkCreateSwapchainKHR);
+    if (strcmp(name, "vkDestroySwapchainKHR") == 0)
+        return reinterpret_cast<PFN_vkVoidFunction>(vkDestroySwapchainKHR);
+    if (strcmp(name, "vkGetSwapchainImagesKHR") == 0)
+        return reinterpret_cast<PFN_vkVoidFunction>(vkGetSwapchainImagesKHR);
+    if (strcmp(name, "vkAcquireNextImageKHR") == 0)
+        return reinterpret_cast<PFN_vkVoidFunction>(vkAcquireNextImageKHR);
+    if (strcmp(name, "vkQueuePresentKHR") == 0)
+        return reinterpret_cast<PFN_vkVoidFunction>(vkQueuePresentKHR);
+    return nullptr;
 }

 PFN_vkVoidFunction GetSpecificInstanceProcAddr(const InstanceVtbl* vtbl,
                                                const char* name) {
+    size_t offset;
     const NameOffsetEntry* entry = FindProcEntry(kInstanceOffsetTbl, name);
-    if (!entry)
+    if (entry)
+        offset = entry->offset;
+    else if (strcmp(name, "vkGetPhysicalDeviceSurfaceSupportKHR") == 0)
+        offset = offsetof(InstanceVtbl, GetPhysicalDeviceSurfaceSupportKHR);
+    else
         return nullptr;
     const unsigned char* base = reinterpret_cast<const unsigned char*>(vtbl);
     return reinterpret_cast<PFN_vkVoidFunction>(
@@ -143,8 +173,27 @@

 PFN_vkVoidFunction GetSpecificDeviceProcAddr(const DeviceVtbl* vtbl,
                                              const char* name) {
+    size_t offset;
     const NameOffsetEntry* entry = FindProcEntry(kDeviceOffsetTbl, name);
-    if (!entry)
+    if (entry)
+        offset = entry->offset;
+    else if (strcmp(name, "vkGetSurfacePropertiesKHR") == 0)
+        offset = offsetof(DeviceVtbl, GetSurfacePropertiesKHR);
+    else if (strcmp(name, "vkGetSurfaceFormatsKHR") == 0)
+        offset = offsetof(DeviceVtbl, GetSurfaceFormatsKHR);
+    else if (strcmp(name, "vkGetSurfacePresentModesKHR") == 0)
+        offset = offsetof(DeviceVtbl, GetSurfacePresentModesKHR);
+    else if (strcmp(name, "vkCreateSwapchainKHR") == 0)
+        offset = offsetof(DeviceVtbl, CreateSwapchainKHR);
+    else if (strcmp(name, "vkDestroySwapchainKHR") == 0)
+        offset = offsetof(DeviceVtbl, DestroySwapchainKHR);
+    else if (strcmp(name, "vkGetSwapchainImagesKHR") == 0)
+        offset = offsetof(DeviceVtbl, GetSwapchainImagesKHR);
+    else if (strcmp(name, "vkAcquireNextImageKHR") == 0)
+        offset = offsetof(DeviceVtbl, AcquireNextImageKHR);
+    else if (strcmp(name, "vkQueuePresentKHR") == 0)
+        offset = offsetof(DeviceVtbl, QueuePresentKHR);
+    else
         return nullptr;
     const unsigned char* base = reinterpret_cast<const unsigned char*>(vtbl);
     return reinterpret_cast<PFN_vkVoidFunction>(
@@ -187,6 +236,16 @@
     }
       {{end}}
     {{end}}
+    vtbl.ImportNativeFenceANDROID = reinterpret_cast<PFN_vkImportNativeFenceANDROID>(get_proc_addr(device, "vkImportNativeFenceANDROID"));
+    if (UNLIKELY(!vtbl.ImportNativeFenceANDROID)) {
+        ALOGE("missing device proc: %s", "vkImportNativeFenceANDROID");
+        success = false;
+    }
+    vtbl.QueueSignalNativeFenceANDROID = reinterpret_cast<PFN_vkQueueSignalNativeFenceANDROID>(get_proc_addr(device, "vkQueueSignalNativeFenceANDROID"));
+    if (UNLIKELY(!vtbl.QueueSignalNativeFenceANDROID)) {
+        ALOGE("missing device proc: %s", "vkQueueSignalNativeFenceANDROID");
+        success = false;
+    }
     // clang-format on
     return success;
 »}
diff --git a/vulkan/libvulkan/loader.cpp b/vulkan/libvulkan/loader.cpp
index 41425e9..26b1377 100644
--- a/vulkan/libvulkan/loader.cpp
+++ b/vulkan/libvulkan/loader.cpp
@@ -342,6 +342,15 @@
     }
     dispatch->vtbl = &device->vtbl_storage;
 
+    device->vtbl_storage.GetSurfacePropertiesKHR = GetSurfacePropertiesKHR;
+    device->vtbl_storage.GetSurfaceFormatsKHR = GetSurfaceFormatsKHR;
+    device->vtbl_storage.GetSurfacePresentModesKHR = GetSurfacePresentModesKHR;
+    device->vtbl_storage.CreateSwapchainKHR = CreateSwapchainKHR;
+    device->vtbl_storage.DestroySwapchainKHR = DestroySwapchainKHR;
+    device->vtbl_storage.GetSwapchainImagesKHR = GetSwapchainImagesKHR;
+    device->vtbl_storage.AcquireNextImageKHR = AcquireNextImageKHR;
+    device->vtbl_storage.QueuePresentKHR = QueuePresentKHR;
+
     // TODO: insert device layer entry points into device->vtbl_storage here?
 
     *out_device = drv_device;
@@ -402,6 +411,7 @@
     .GetPhysicalDeviceExtensionProperties = GetPhysicalDeviceExtensionPropertiesBottom,
     .GetPhysicalDeviceLayerProperties = GetPhysicalDeviceLayerPropertiesBottom,
     .GetPhysicalDeviceSparseImageFormatProperties = GetPhysicalDeviceSparseImageFormatPropertiesBottom,
+    .GetPhysicalDeviceSurfaceSupportKHR = GetPhysicalDeviceSurfaceSupportKHR,
     // clang-format on
 };
 
diff --git a/vulkan/libvulkan/loader.h b/vulkan/libvulkan/loader.h
index f3a59fd..db5c4d3 100644
--- a/vulkan/libvulkan/loader.h
+++ b/vulkan/libvulkan/loader.h
@@ -19,9 +19,18 @@
 
 #define VK_PROTOTYPES
 #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 {
 
+// TODO(jessehall): The InstanceVtbl and DeviceVtbl both have a set of
+// functions used in the app->layers/loader interface, and a different set of
+// functions used only in the loader->driver interface. We should probably
+// split them into two structures: one used for dispatch of application calls,
+// and one to hold the driver entry points.
+
 struct InstanceVtbl {
     // clang-format off
     VkInstance instance;
@@ -43,6 +52,9 @@
     PFN_vkGetPhysicalDeviceExtensionProperties GetPhysicalDeviceExtensionProperties;
     PFN_vkGetPhysicalDeviceLayerProperties GetPhysicalDeviceLayerProperties;
     PFN_vkGetPhysicalDeviceSparseImageFormatProperties GetPhysicalDeviceSparseImageFormatProperties;
+
+    // Layers and loader only, not implemented by drivers
+    PFN_vkGetPhysicalDeviceSurfaceSupportKHR GetPhysicalDeviceSurfaceSupportKHR;
     // clang-format on
 };
 
@@ -185,6 +197,20 @@
     PFN_vkCmdNextSubpass CmdNextSubpass;
     PFN_vkCmdEndRenderPass CmdEndRenderPass;
     PFN_vkCmdExecuteCommands CmdExecuteCommands;
+
+    // Layers and loader only, not implemented by drivers
+    PFN_vkGetSurfacePropertiesKHR GetSurfacePropertiesKHR;
+    PFN_vkGetSurfaceFormatsKHR GetSurfaceFormatsKHR;
+    PFN_vkGetSurfacePresentModesKHR GetSurfacePresentModesKHR;
+    PFN_vkCreateSwapchainKHR CreateSwapchainKHR;
+    PFN_vkDestroySwapchainKHR DestroySwapchainKHR;
+    PFN_vkGetSwapchainImagesKHR GetSwapchainImagesKHR;
+    PFN_vkAcquireNextImageKHR AcquireNextImageKHR;
+    PFN_vkQueuePresentKHR QueuePresentKHR;
+
+    // Implemented only by drivers, not by layers or the loader
+    PFN_vkImportNativeFenceANDROID ImportNativeFenceANDROID;
+    PFN_vkQueueSignalNativeFenceANDROID QueueSignalNativeFenceANDROID;
 };
 
 // -----------------------------------------------------------------------------
@@ -225,6 +251,40 @@
                     PFN_vkGetDeviceProcAddr get_proc_addr,
                     DeviceVtbl& vtbl);
 
+// -----------------------------------------------------------------------------
+// swapchain.cpp
+
+VkResult GetPhysicalDeviceSurfaceSupportKHR(
+    VkPhysicalDevice pdev,
+    uint32_t queue_family,
+    const VkSurfaceDescriptionKHR* surface_desc,
+    VkBool32* supported);
+VkResult GetSurfacePropertiesKHR(VkDevice device,
+                                 const VkSurfaceDescriptionKHR* surface_desc,
+                                 VkSurfacePropertiesKHR* properties);
+VkResult GetSurfaceFormatsKHR(VkDevice device,
+                              const VkSurfaceDescriptionKHR* surface_desc,
+                              uint32_t* count,
+                              VkSurfaceFormatKHR* formats);
+VkResult GetSurfacePresentModesKHR(VkDevice device,
+                                   const VkSurfaceDescriptionKHR* surface_desc,
+                                   uint32_t* count,
+                                   VkPresentModeKHR* modes);
+VkResult CreateSwapchainKHR(VkDevice device,
+                            const VkSwapchainCreateInfoKHR* create_info,
+                            VkSwapchainKHR* swapchain);
+VkResult DestroySwapchainKHR(VkDevice device, VkSwapchainKHR swapchain);
+VkResult GetSwapchainImagesKHR(VkDevice device,
+                               VkSwapchainKHR swapchain,
+                               uint32_t* count,
+                               VkImage* image);
+VkResult AcquireNextImageKHR(VkDevice device,
+                             VkSwapchainKHR swapchain,
+                             uint64_t timeout,
+                             VkSemaphore semaphore,
+                             uint32_t* image_index);
+VkResult QueuePresentKHR(VkQueue queue, VkPresentInfoKHR* present_info);
+
 }  // namespace vulkan
 
 #endif  // LIBVULKAN_LOADER_H
diff --git a/vulkan/libvulkan/swapchain.cpp b/vulkan/libvulkan/swapchain.cpp
new file mode 100644
index 0000000..7db5869
--- /dev/null
+++ b/vulkan/libvulkan/swapchain.cpp
@@ -0,0 +1,110 @@
+/*
+ * Copyright 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "loader.h"
+#define LOG_NDEBUG 0
+#include <log/log.h>
+
+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"
+    if (surface_desc->sType != VK_STRUCTURE_TYPE_SURFACE_DESCRIPTION_WINDOW_KHR)
+        return VK_ERROR_INVALID_VALUE;
+#pragma clang diagnostic pop
+
+    const VkSurfaceDescriptionWindowKHR* window_desc =
+        reinterpret_cast<const VkSurfaceDescriptionWindowKHR*>(surface_desc);
+
+    // 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);
+
+    return VK_SUCCESS;
+}
+
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wunused-parameter"
+VkResult GetSurfacePropertiesKHR(VkDevice device,
+                                 const VkSurfaceDescriptionKHR* surface_desc,
+                                 VkSurfacePropertiesKHR* properties) {
+    ALOGV("TODO: %s", __FUNCTION__);
+    return VK_SUCCESS;
+}
+
+VkResult GetSurfaceFormatsKHR(VkDevice device,
+                              const VkSurfaceDescriptionKHR* surface_desc,
+                              uint32_t* count,
+                              VkSurfaceFormatKHR* formats) {
+    ALOGV("TODO: %s", __FUNCTION__);
+    return VK_SUCCESS;
+}
+
+VkResult GetSurfacePresentModesKHR(VkDevice device,
+                                   const VkSurfaceDescriptionKHR* surface_desc,
+                                   uint32_t* count,
+                                   VkPresentModeKHR* modes) {
+    ALOGV("TODO: %s", __FUNCTION__);
+    return VK_SUCCESS;
+}
+
+VkResult CreateSwapchainKHR(VkDevice device,
+                            const VkSwapchainCreateInfoKHR* create_info,
+                            VkSwapchainKHR* swapchain) {
+    ALOGV("TODO: %s", __FUNCTION__);
+    return VK_SUCCESS;
+}
+
+VkResult DestroySwapchainKHR(VkDevice device, VkSwapchainKHR swapchain) {
+    ALOGV("TODO: %s", __FUNCTION__);
+    return VK_SUCCESS;
+}
+
+VkResult GetSwapchainImagesKHR(VkDevice device,
+                               VkSwapchainKHR swapchain,
+                               uint32_t* count,
+                               VkImage* image) {
+    ALOGV("TODO: %s", __FUNCTION__);
+    return VK_SUCCESS;
+}
+
+VkResult AcquireNextImageKHR(VkDevice device,
+                             VkSwapchainKHR swapchain,
+                             uint64_t timeout,
+                             VkSemaphore semaphore,
+                             uint32_t* image_index) {
+    ALOGV("TODO: %s", __FUNCTION__);
+    return VK_SUCCESS;
+}
+
+VkResult QueuePresentKHR(VkQueue queue, VkPresentInfoKHR* present_info) {
+    ALOGV("TODO: %s", __FUNCTION__);
+    return VK_SUCCESS;
+}
+#pragma clang diagnostic pop
+
+}  // namespace vulkan