Directly manage buffer presentation in Vulkan using AHardwareBuffers.

Instead of relying on Vulkan swapchains this CL enables HWUI to directly
manage the native window.  This allows us to preallocate buffers using
our own strategy as well as having no longer having to jump through an
unecessary translation layer that resulted in code that was hard to
reason about and also introduced inefficiencies.

Bug: 123541940
Bug: 119687951
Test: CtsUiRenderingTestCases and CtsGraphicsTestCases
Change-Id: I7e5930748795e7ca4a998ab2c608c3c9b6363037
diff --git a/libs/hwui/Android.bp b/libs/hwui/Android.bp
index 793dd8d..4f1b2a4 100644
--- a/libs/hwui/Android.bp
+++ b/libs/hwui/Android.bp
@@ -69,6 +69,7 @@
         "libminikin",
         "libandroidfw",
         "libcrypto",
+        "libsync",
     ],
     static_libs: [
         "libEGL_blobCache",
@@ -180,6 +181,7 @@
         "renderthread/EglManager.cpp",
         "renderthread/ReliableSurface.cpp",
         "renderthread/VulkanManager.cpp",
+        "renderthread/VulkanSurface.cpp",
         "renderthread/RenderProxy.cpp",
         "renderthread/RenderTask.cpp",
         "renderthread/RenderThread.cpp",
diff --git a/libs/hwui/pipeline/skia/SkiaVulkanPipeline.cpp b/libs/hwui/pipeline/skia/SkiaVulkanPipeline.cpp
index 87cffb5..edde6d3 100644
--- a/libs/hwui/pipeline/skia/SkiaVulkanPipeline.cpp
+++ b/libs/hwui/pipeline/skia/SkiaVulkanPipeline.cpp
@@ -55,20 +55,8 @@
 }
 
 Frame SkiaVulkanPipeline::getFrame() {
-    LOG_ALWAYS_FATAL_IF(mVkSurface == nullptr,
-                        "drawRenderNode called on a context with no surface!");
-
-    SkSurface* backBuffer = mVkManager.getBackbufferSurface(&mVkSurface);
-    LOG_ALWAYS_FATAL_IF(mVkSurface == nullptr,
-                        "drawRenderNode called on a context with an invalid surface");
-    if (backBuffer == nullptr) {
-        SkDebugf("failed to get backbuffer");
-        return Frame(-1, -1, 0);
-    }
-
-    Frame frame(mVkSurface->windowWidth(), mVkSurface->windowHeight(),
-                mVkManager.getAge(mVkSurface));
-    return frame;
+    LOG_ALWAYS_FATAL_IF(mVkSurface == nullptr, "getFrame() called on a context with no surface!");
+    return mVkManager.dequeueNextBuffer(mVkSurface);
 }
 
 bool SkiaVulkanPipeline::draw(const Frame& frame, const SkRect& screenDirty, const SkRect& dirty,
@@ -77,13 +65,13 @@
                               bool opaque, const LightInfo& lightInfo,
                               const std::vector<sp<RenderNode>>& renderNodes,
                               FrameInfoVisualizer* profiler) {
-    sk_sp<SkSurface> backBuffer = mVkSurface->getBackBufferSurface();
+    sk_sp<SkSurface> backBuffer = mVkSurface->getCurrentSkSurface();
     if (backBuffer.get() == nullptr) {
         return false;
     }
     SkiaPipeline::updateLighting(lightGeometry, lightInfo);
     renderFrame(*layerUpdateQueue, dirty, renderNodes, opaque, contentDrawBounds,
-            backBuffer, mVkSurface->preTransform());
+            backBuffer, mVkSurface->getCurrentPreTransform());
     ShaderCache::get().onVkFrameFlushed(mRenderThread.getGrContext());
     layerUpdateQueue->clear();
 
@@ -113,7 +101,7 @@
     currentFrameInfo->markSwapBuffers();
 
     if (*requireSwap) {
-        mVkManager.swapBuffers(mVkSurface);
+        mVkManager.swapBuffers(mVkSurface, screenDirty);
     }
 
     return *requireSwap;
diff --git a/libs/hwui/pipeline/skia/SkiaVulkanPipeline.h b/libs/hwui/pipeline/skia/SkiaVulkanPipeline.h
index 2c24edd..77a7ab1 100644
--- a/libs/hwui/pipeline/skia/SkiaVulkanPipeline.h
+++ b/libs/hwui/pipeline/skia/SkiaVulkanPipeline.h
@@ -18,6 +18,7 @@
 
 #include "SkiaPipeline.h"
 #include "renderthread/VulkanManager.h"
+#include "renderthread/VulkanSurface.h"
 
 #include "renderstate/RenderState.h"
 
diff --git a/libs/hwui/renderthread/VulkanManager.cpp b/libs/hwui/renderthread/VulkanManager.cpp
index 5af660c..d4c6eae 100644
--- a/libs/hwui/renderthread/VulkanManager.cpp
+++ b/libs/hwui/renderthread/VulkanManager.cpp
@@ -16,6 +16,7 @@
 
 #include "VulkanManager.h"
 
+#include <android/sync.h>
 #include <gui/Surface.h>
 
 #include "Properties.h"
@@ -23,6 +24,7 @@
 #include "renderstate/RenderState.h"
 #include "utils/FatVector.h"
 
+#include <GrBackendSemaphore.h>
 #include <GrBackendSurface.h>
 #include <GrContext.h>
 #include <GrTypes.h>
@@ -142,6 +144,7 @@
     GET_INST_PROC(GetPhysicalDeviceProperties);
     GET_INST_PROC(GetPhysicalDeviceQueueFamilyProperties);
     GET_INST_PROC(GetPhysicalDeviceFeatures2);
+    GET_INST_PROC(GetPhysicalDeviceImageFormatProperties2);
     GET_INST_PROC(CreateDevice);
     GET_INST_PROC(EnumerateDeviceExtensionProperties);
     GET_INST_PROC(CreateAndroidSurfaceKHR);
@@ -318,11 +321,6 @@
     GET_DEV_PROC(GetDeviceQueue);
     GET_DEV_PROC(DeviceWaitIdle);
     GET_DEV_PROC(DestroyDevice);
-    GET_DEV_PROC(CreateSwapchainKHR);
-    GET_DEV_PROC(DestroySwapchainKHR);
-    GET_DEV_PROC(GetSwapchainImagesKHR);
-    GET_DEV_PROC(AcquireNextImageKHR);
-    GET_DEV_PROC(QueuePresentKHR);
     GET_DEV_PROC(CreateCommandPool);
     GET_DEV_PROC(DestroyCommandPool);
     GET_DEV_PROC(AllocateCommandBuffers);
@@ -426,201 +424,102 @@
     };
 }
 
-// Returns the next BackbufferInfo to use for the next draw. The function will make sure all
-// previous uses have finished before returning.
-VulkanSurface::BackbufferInfo* VulkanManager::getAvailableBackbuffer(VulkanSurface* surface) {
-    SkASSERT(surface->mBackbuffers);
+Frame VulkanManager::dequeueNextBuffer(VulkanSurface* surface) {
 
-    ++surface->mCurrentBackbufferIndex;
-    if (surface->mCurrentBackbufferIndex > surface->mImageCount) {
-        surface->mCurrentBackbufferIndex = 0;
+    VulkanSurface::NativeBufferInfo* bufferInfo = surface->dequeueNativeBuffer();
+
+    if (bufferInfo == nullptr) {
+        ALOGE("VulkanSurface::dequeueNativeBuffer called with an invalid surface!");
+        return Frame(-1, -1, 0);
     }
 
-    VulkanSurface::BackbufferInfo* backbuffer =
-            surface->mBackbuffers + surface->mCurrentBackbufferIndex;
+    LOG_ALWAYS_FATAL_IF(!bufferInfo->dequeued);
 
-    // Before we reuse a backbuffer, make sure its fences have all signaled so that we can safely
-    // reuse its commands buffers.
-    VkResult res = mWaitForFences(mDevice, 2, backbuffer->mUsageFences, true, UINT64_MAX);
-    if (res != VK_SUCCESS) {
-        return nullptr;
+    if (bufferInfo->dequeue_fence != -1) {
+        int fence_clone = dup(bufferInfo->dequeue_fence);
+        if (fence_clone == -1) {
+            ALOGE("dup(fence) failed, stalling until signalled: %s (%d)", strerror(errno), errno);
+            sync_wait(bufferInfo->dequeue_fence, -1 /* forever */);
+        } else {
+            VkSemaphoreCreateInfo semaphoreInfo;
+            semaphoreInfo.sType = VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO;
+            semaphoreInfo.pNext = nullptr;
+            semaphoreInfo.flags = 0;
+            VkSemaphore semaphore;
+            VkResult err = mCreateSemaphore(mDevice, &semaphoreInfo, nullptr, &semaphore);
+            LOG_ALWAYS_FATAL_IF(VK_SUCCESS != err, "Failed to create import semaphore, err: %d",
+                                err);
+
+            VkImportSemaphoreFdInfoKHR importInfo;
+            importInfo.sType = VK_STRUCTURE_TYPE_IMPORT_SEMAPHORE_FD_INFO_KHR;
+            importInfo.pNext = nullptr;
+            importInfo.semaphore = semaphore;
+            importInfo.flags = VK_SEMAPHORE_IMPORT_TEMPORARY_BIT;
+            importInfo.handleType = VK_EXTERNAL_SEMAPHORE_HANDLE_TYPE_SYNC_FD_BIT;
+            importInfo.fd = fence_clone;
+
+            err = mImportSemaphoreFdKHR(mDevice, &importInfo);
+            LOG_ALWAYS_FATAL_IF(VK_SUCCESS != err, "Failed to import semaphore, err: %d", err);
+
+            GrBackendSemaphore backendSemaphore;
+            backendSemaphore.initVulkan(semaphore);
+            bufferInfo->skSurface->wait(1, &backendSemaphore);
+        }
     }
 
-    return backbuffer;
+    int bufferAge = (mSwapBehavior == SwapBehavior::Discard) ? 0 : surface->getCurrentBuffersAge();
+    return Frame(surface->logicalWidth(), surface->logicalHeight(), bufferAge);
 }
 
-static SkMatrix getPreTransformMatrix(int width, int height,
-                                      VkSurfaceTransformFlagBitsKHR transform) {
-    switch (transform) {
-        case VK_SURFACE_TRANSFORM_IDENTITY_BIT_KHR:
-            return SkMatrix::I();
-        case VK_SURFACE_TRANSFORM_ROTATE_90_BIT_KHR:
-            return SkMatrix::MakeAll(0, -1, height, 1, 0, 0, 0, 0, 1);
-        case VK_SURFACE_TRANSFORM_ROTATE_180_BIT_KHR:
-            return SkMatrix::MakeAll(-1, 0, width, 0, -1, height, 0, 0, 1);
-        case VK_SURFACE_TRANSFORM_ROTATE_270_BIT_KHR:
-            return SkMatrix::MakeAll(0, 1, 0, -1, 0, width, 0, 0, 1);
-        case VK_SURFACE_TRANSFORM_HORIZONTAL_MIRROR_BIT_KHR:
-            return SkMatrix::MakeAll(-1, 0, width, 0, 1, 0, 0, 0, 1);
-        case VK_SURFACE_TRANSFORM_HORIZONTAL_MIRROR_ROTATE_90_BIT_KHR:
-            return SkMatrix::MakeAll(0, -1, height, -1, 0, width, 0, 0, 1);
-        case VK_SURFACE_TRANSFORM_HORIZONTAL_MIRROR_ROTATE_180_BIT_KHR:
-            return SkMatrix::MakeAll(1, 0, 0, 0, -1, height, 0, 0, 1);
-        case VK_SURFACE_TRANSFORM_HORIZONTAL_MIRROR_ROTATE_270_BIT_KHR:
-            return SkMatrix::MakeAll(0, 1, 0, 1, 0, 0, 0, 0, 1);
-        default:
-            LOG_ALWAYS_FATAL("Unsupported pre transform of swapchain.");
-    }
-    return SkMatrix::I();
-}
-
-
-SkSurface* VulkanManager::getBackbufferSurface(VulkanSurface** surfaceOut) {
-    // Recreate VulkanSurface, if ANativeWindow has been resized.
-    VulkanSurface* surface = *surfaceOut;
-    int windowWidth = 0, windowHeight = 0;
-    ANativeWindow* window = surface->mNativeWindow;
-    window->query(window, NATIVE_WINDOW_WIDTH, &windowWidth);
-    window->query(window, NATIVE_WINDOW_HEIGHT, &windowHeight);
-    if (windowWidth != surface->mWindowWidth || windowHeight != surface->mWindowHeight) {
-        ColorMode colorMode = surface->mColorMode;
-        sk_sp<SkColorSpace> colorSpace = surface->mColorSpace;
-        SkColorType colorType = surface->mColorType;
-        GrContext* grContext = surface->mGrContext;
-        destroySurface(surface);
-        *surfaceOut = createSurface(window, colorMode, colorSpace, colorType, grContext);
-        surface = *surfaceOut;
-        if (!surface) {
-            return nullptr;
-        }
+void VulkanManager::swapBuffers(VulkanSurface* surface, const SkRect& dirtyRect) {
+    if (CC_UNLIKELY(Properties::waitForGpuCompletion)) {
+        ATRACE_NAME("Finishing GPU work");
+        mDeviceWaitIdle(mDevice);
     }
 
-    VulkanSurface::BackbufferInfo* backbuffer = getAvailableBackbuffer(surface);
-    SkASSERT(backbuffer);
+    VkExportSemaphoreCreateInfo exportInfo;
+    exportInfo.sType = VK_STRUCTURE_TYPE_EXPORT_SEMAPHORE_CREATE_INFO;
+    exportInfo.pNext = nullptr;
+    exportInfo.handleTypes = VK_EXTERNAL_SEMAPHORE_HANDLE_TYPE_SYNC_FD_BIT;
 
-    VkResult res;
+    VkSemaphoreCreateInfo semaphoreInfo;
+    semaphoreInfo.sType = VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO;
+    semaphoreInfo.pNext = &exportInfo;
+    semaphoreInfo.flags = 0;
+    VkSemaphore semaphore;
+    VkResult err = mCreateSemaphore(mDevice, &semaphoreInfo, nullptr, &semaphore);
+    ALOGE_IF(VK_SUCCESS != err, "VulkanManager::swapBuffers(): Failed to create semaphore");
 
-    res = mResetFences(mDevice, 2, backbuffer->mUsageFences);
-    SkASSERT(VK_SUCCESS == res);
+    GrBackendSemaphore backendSemaphore;
+    backendSemaphore.initVulkan(semaphore);
 
-    // The acquire will signal the attached mAcquireSemaphore. We use this to know the image has
-    // finished presenting and that it is safe to begin sending new commands to the returned image.
-    res = mAcquireNextImageKHR(mDevice, surface->mSwapchain, UINT64_MAX,
-                               backbuffer->mAcquireSemaphore, VK_NULL_HANDLE,
-                               &backbuffer->mImageIndex);
+    VulkanSurface::NativeBufferInfo* bufferInfo = surface->getCurrentBufferInfo();
 
-    if (VK_ERROR_SURFACE_LOST_KHR == res) {
-        // need to figure out how to create a new vkSurface without the platformData*
-        // maybe use attach somehow? but need a Window
-        return nullptr;
-    }
-    if (VK_ERROR_OUT_OF_DATE_KHR == res || VK_SUBOPTIMAL_KHR == res) {
-        // tear swapchain down and try again
-        if (!createSwapchain(surface)) {
-            return nullptr;
-        }
-        backbuffer = getAvailableBackbuffer(surface);
-        res = mResetFences(mDevice, 2, backbuffer->mUsageFences);
-        SkASSERT(VK_SUCCESS == res);
+    int fenceFd = -1;
+    GrSemaphoresSubmitted submitted =
+            bufferInfo->skSurface->flush(SkSurface::BackendSurfaceAccess::kPresent,
+                                         SkSurface::kNone_FlushFlags, 1, &backendSemaphore);
+    if (submitted == GrSemaphoresSubmitted::kYes) {
+        VkSemaphoreGetFdInfoKHR getFdInfo;
+        getFdInfo.sType = VK_STRUCTURE_TYPE_SEMAPHORE_GET_FD_INFO_KHR;
+        getFdInfo.pNext = nullptr;
+        getFdInfo.semaphore = semaphore;
+        getFdInfo.handleType = VK_EXTERNAL_SEMAPHORE_HANDLE_TYPE_SYNC_FD_BIT;
 
-        // acquire the image
-        res = mAcquireNextImageKHR(mDevice, surface->mSwapchain, UINT64_MAX,
-                                   backbuffer->mAcquireSemaphore, VK_NULL_HANDLE,
-                                   &backbuffer->mImageIndex);
-
-        if (VK_SUCCESS != res) {
-            return nullptr;
-        }
+        err = mGetSemaphoreFdKHR(mDevice, &getFdInfo, &fenceFd);
+        ALOGE_IF(VK_SUCCESS != err, "VulkanManager::swapBuffers(): Failed to get semaphore Fd");
+    } else {
+        ALOGE("VulkanManager::swapBuffers(): Semaphore submission failed");
+        mQueueWaitIdle(mGraphicsQueue);
     }
 
-    // set up layout transfer from initial to color attachment
-    VkImageLayout layout = surface->mImageInfos[backbuffer->mImageIndex].mImageLayout;
-    SkASSERT(VK_IMAGE_LAYOUT_UNDEFINED == layout || VK_IMAGE_LAYOUT_PRESENT_SRC_KHR == layout);
-    VkPipelineStageFlags srcStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT;
-    VkPipelineStageFlags dstStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT;
-    VkAccessFlags srcAccessMask = 0;
-    VkAccessFlags dstAccessMask = VK_ACCESS_COLOR_ATTACHMENT_READ_BIT |
-                                  VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT;
+    surface->presentCurrentBuffer(dirtyRect, fenceFd);
 
-    VkImageMemoryBarrier imageMemoryBarrier = {
-            VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER,     // sType
-            NULL,                                       // pNext
-            srcAccessMask,                              // outputMask
-            dstAccessMask,                              // inputMask
-            layout,                                     // oldLayout
-            VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL,   // newLayout
-            mPresentQueueIndex,                         // srcQueueFamilyIndex
-            mGraphicsQueueIndex,       // dstQueueFamilyIndex
-            surface->mImages[backbuffer->mImageIndex],  // image
-            {VK_IMAGE_ASPECT_COLOR_BIT, 0, 1, 0, 1}     // subresourceRange
-    };
-    mResetCommandBuffer(backbuffer->mTransitionCmdBuffers[0], 0);
-
-    VkCommandBufferBeginInfo info;
-    memset(&info, 0, sizeof(VkCommandBufferBeginInfo));
-    info.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO;
-    info.flags = 0;
-    mBeginCommandBuffer(backbuffer->mTransitionCmdBuffers[0], &info);
-
-    mCmdPipelineBarrier(backbuffer->mTransitionCmdBuffers[0], srcStageMask, dstStageMask, 0, 0,
-                        nullptr, 0, nullptr, 1, &imageMemoryBarrier);
-
-    mEndCommandBuffer(backbuffer->mTransitionCmdBuffers[0]);
-
-    VkPipelineStageFlags waitDstStageFlags = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT;
-    // insert the layout transfer into the queue and wait on the acquire
-    VkSubmitInfo submitInfo;
-    memset(&submitInfo, 0, sizeof(VkSubmitInfo));
-    submitInfo.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO;
-    submitInfo.waitSemaphoreCount = 1;
-    // Wait to make sure aquire semaphore set above has signaled.
-    submitInfo.pWaitSemaphores = &backbuffer->mAcquireSemaphore;
-    submitInfo.pWaitDstStageMask = &waitDstStageFlags;
-    submitInfo.commandBufferCount = 1;
-    submitInfo.pCommandBuffers = &backbuffer->mTransitionCmdBuffers[0];
-    submitInfo.signalSemaphoreCount = 0;
-
-    // Attach first fence to submission here so we can track when the command buffer finishes.
-    mQueueSubmit(mGraphicsQueue, 1, &submitInfo, backbuffer->mUsageFences[0]);
-
-    // We need to notify Skia that we changed the layout of the wrapped VkImage
-    sk_sp<SkSurface> skSurface = surface->mImageInfos[backbuffer->mImageIndex].mSurface;
-    GrBackendRenderTarget backendRT = skSurface->getBackendRenderTarget(
-            SkSurface::kFlushRead_BackendHandleAccess);
-    if (!backendRT.isValid()) {
-        SkASSERT(backendRT.isValid());
-        return nullptr;
-    }
-    backendRT.setVkImageLayout(VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL);
-
-    surface->mPreTransform = getPreTransformMatrix(surface->windowWidth(),
-                                                   surface->windowHeight(),
-                                                   surface->mTransform);
-
-    surface->mBackbuffer = std::move(skSurface);
-    return surface->mBackbuffer.get();
-}
-
-void VulkanManager::destroyBuffers(VulkanSurface* surface) {
-    if (surface->mBackbuffers) {
-        for (uint32_t i = 0; i < surface->mImageCount + 1; ++i) {
-            mWaitForFences(mDevice, 2, surface->mBackbuffers[i].mUsageFences, true, UINT64_MAX);
-            surface->mBackbuffers[i].mImageIndex = -1;
-            mDestroySemaphore(mDevice, surface->mBackbuffers[i].mAcquireSemaphore, nullptr);
-            mDestroySemaphore(mDevice, surface->mBackbuffers[i].mRenderSemaphore, nullptr);
-            mFreeCommandBuffers(mDevice, mCommandPool, 2,
-                    surface->mBackbuffers[i].mTransitionCmdBuffers);
-            mDestroyFence(mDevice, surface->mBackbuffers[i].mUsageFences[0], 0);
-            mDestroyFence(mDevice, surface->mBackbuffers[i].mUsageFences[1], 0);
-        }
-    }
-
-    delete[] surface->mBackbuffers;
-    surface->mBackbuffers = nullptr;
-    delete[] surface->mImageInfos;
-    surface->mImageInfos = nullptr;
-    delete[] surface->mImages;
-    surface->mImages = nullptr;
+    // Exporting a semaphore with copy transference via vkGetSemaphoreFdKHR, has the same effect of
+    // destroying the semaphore and creating a new one with the same handle, and the payloads
+    // ownership is move to the Fd we created. Thus the semaphore is in a state that we can delete
+    // it and we don't need to wait on the command buffer we submitted to finish.
+    mDestroySemaphore(mDevice, semaphore, nullptr);
 }
 
 void VulkanManager::destroySurface(VulkanSurface* surface) {
@@ -630,271 +529,9 @@
     }
     mDeviceWaitIdle(mDevice);
 
-    destroyBuffers(surface);
-
-    if (VK_NULL_HANDLE != surface->mSwapchain) {
-        mDestroySwapchainKHR(mDevice, surface->mSwapchain, nullptr);
-        surface->mSwapchain = VK_NULL_HANDLE;
-    }
-
-    if (VK_NULL_HANDLE != surface->mVkSurface) {
-        mDestroySurfaceKHR(mInstance, surface->mVkSurface, nullptr);
-        surface->mVkSurface = VK_NULL_HANDLE;
-    }
     delete surface;
 }
 
-void VulkanManager::createBuffers(VulkanSurface* surface, VkFormat format, VkExtent2D extent) {
-    mGetSwapchainImagesKHR(mDevice, surface->mSwapchain, &surface->mImageCount, nullptr);
-    SkASSERT(surface->mImageCount);
-    surface->mImages = new VkImage[surface->mImageCount];
-    mGetSwapchainImagesKHR(mDevice, surface->mSwapchain, &surface->mImageCount, surface->mImages);
-
-    SkSurfaceProps props(0, kUnknown_SkPixelGeometry);
-
-    // set up initial image layouts and create surfaces
-    surface->mImageInfos = new VulkanSurface::ImageInfo[surface->mImageCount];
-    for (uint32_t i = 0; i < surface->mImageCount; ++i) {
-        GrVkImageInfo info;
-        info.fImage = surface->mImages[i];
-        info.fAlloc = GrVkAlloc();
-        info.fImageLayout = VK_IMAGE_LAYOUT_UNDEFINED;
-        info.fImageTiling = VK_IMAGE_TILING_OPTIMAL;
-        info.fFormat = format;
-        info.fLevelCount = 1;
-
-        GrBackendRenderTarget backendRT(extent.width, extent.height, 0, 0, info);
-
-        VulkanSurface::ImageInfo& imageInfo = surface->mImageInfos[i];
-        imageInfo.mSurface = SkSurface::MakeFromBackendRenderTarget(
-                surface->mGrContext, backendRT, kTopLeft_GrSurfaceOrigin,
-                surface->mColorType, surface->mColorSpace, &props);
-    }
-
-    SkASSERT(mCommandPool != VK_NULL_HANDLE);
-
-    // set up the backbuffers
-    VkSemaphoreCreateInfo semaphoreInfo;
-    memset(&semaphoreInfo, 0, sizeof(VkSemaphoreCreateInfo));
-    semaphoreInfo.sType = VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO;
-    semaphoreInfo.pNext = nullptr;
-    semaphoreInfo.flags = 0;
-    VkCommandBufferAllocateInfo commandBuffersInfo;
-    memset(&commandBuffersInfo, 0, sizeof(VkCommandBufferAllocateInfo));
-    commandBuffersInfo.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO;
-    commandBuffersInfo.pNext = nullptr;
-    commandBuffersInfo.commandPool = mCommandPool;
-    commandBuffersInfo.level = VK_COMMAND_BUFFER_LEVEL_PRIMARY;
-    commandBuffersInfo.commandBufferCount = 2;
-    VkFenceCreateInfo fenceInfo;
-    memset(&fenceInfo, 0, sizeof(VkFenceCreateInfo));
-    fenceInfo.sType = VK_STRUCTURE_TYPE_FENCE_CREATE_INFO;
-    fenceInfo.pNext = nullptr;
-    fenceInfo.flags = VK_FENCE_CREATE_SIGNALED_BIT;
-
-    // we create one additional backbuffer structure here, because we want to
-    // give the command buffers they contain a chance to finish before we cycle back
-    surface->mBackbuffers = new VulkanSurface::BackbufferInfo[surface->mImageCount + 1];
-    for (uint32_t i = 0; i < surface->mImageCount + 1; ++i) {
-        SkDEBUGCODE(VkResult res);
-        surface->mBackbuffers[i].mImageIndex = -1;
-        SkDEBUGCODE(res =) mCreateSemaphore(mDevice, &semaphoreInfo, nullptr,
-                                            &surface->mBackbuffers[i].mAcquireSemaphore);
-        SkDEBUGCODE(res =) mCreateSemaphore(mDevice, &semaphoreInfo, nullptr,
-                                            &surface->mBackbuffers[i].mRenderSemaphore);
-        SkDEBUGCODE(res =) mAllocateCommandBuffers(mDevice, &commandBuffersInfo,
-                                                   surface->mBackbuffers[i].mTransitionCmdBuffers);
-        SkDEBUGCODE(res =) mCreateFence(mDevice, &fenceInfo, nullptr,
-                                        &surface->mBackbuffers[i].mUsageFences[0]);
-        SkDEBUGCODE(res =) mCreateFence(mDevice, &fenceInfo, nullptr,
-                                        &surface->mBackbuffers[i].mUsageFences[1]);
-        SkASSERT(VK_SUCCESS == res);
-    }
-    surface->mCurrentBackbufferIndex = surface->mImageCount;
-}
-
-bool VulkanManager::createSwapchain(VulkanSurface* surface) {
-    // check for capabilities
-    VkSurfaceCapabilitiesKHR caps;
-    VkResult res = mGetPhysicalDeviceSurfaceCapabilitiesKHR(mPhysicalDevice,
-                                                            surface->mVkSurface, &caps);
-    if (VK_SUCCESS != res) {
-        return false;
-    }
-
-    uint32_t surfaceFormatCount;
-    res = mGetPhysicalDeviceSurfaceFormatsKHR(mPhysicalDevice, surface->mVkSurface,
-                                              &surfaceFormatCount, nullptr);
-    if (VK_SUCCESS != res) {
-        return false;
-    }
-
-    FatVector<VkSurfaceFormatKHR, 4> surfaceFormats(surfaceFormatCount);
-    res = mGetPhysicalDeviceSurfaceFormatsKHR(mPhysicalDevice, surface->mVkSurface,
-                                              &surfaceFormatCount, surfaceFormats.data());
-    if (VK_SUCCESS != res) {
-        return false;
-    }
-
-    uint32_t presentModeCount;
-    res = mGetPhysicalDeviceSurfacePresentModesKHR(mPhysicalDevice,
-                                                   surface->mVkSurface, &presentModeCount, nullptr);
-    if (VK_SUCCESS != res) {
-        return false;
-    }
-
-    FatVector<VkPresentModeKHR, VK_PRESENT_MODE_RANGE_SIZE_KHR> presentModes(presentModeCount);
-    res = mGetPhysicalDeviceSurfacePresentModesKHR(mPhysicalDevice,
-                                                   surface->mVkSurface, &presentModeCount,
-                                                   presentModes.data());
-    if (VK_SUCCESS != res) {
-        return false;
-    }
-
-    if (!SkToBool(caps.supportedTransforms & VK_SURFACE_TRANSFORM_IDENTITY_BIT_KHR)) {
-        return false;
-    }
-    VkSurfaceTransformFlagBitsKHR transform;
-    if (SkToBool(caps.supportedTransforms & caps.currentTransform) &&
-        !SkToBool(caps.currentTransform & VK_SURFACE_TRANSFORM_INHERIT_BIT_KHR)) {
-        transform = caps.currentTransform;
-    } else {
-        transform = VK_SURFACE_TRANSFORM_IDENTITY_BIT_KHR;
-    }
-
-    VkExtent2D extent = caps.currentExtent;
-    // clamp width; to handle currentExtent of -1 and  protect us from broken hints
-    if (extent.width < caps.minImageExtent.width) {
-        extent.width = caps.minImageExtent.width;
-    }
-    SkASSERT(extent.width <= caps.maxImageExtent.width);
-    // clamp height
-    if (extent.height < caps.minImageExtent.height) {
-        extent.height = caps.minImageExtent.height;
-    }
-    SkASSERT(extent.height <= caps.maxImageExtent.height);
-
-    VkExtent2D swapExtent = extent;
-    if (transform == VK_SURFACE_TRANSFORM_ROTATE_90_BIT_KHR ||
-        transform == VK_SURFACE_TRANSFORM_ROTATE_270_BIT_KHR ||
-        transform == VK_SURFACE_TRANSFORM_HORIZONTAL_MIRROR_ROTATE_90_BIT_KHR ||
-        transform == VK_SURFACE_TRANSFORM_HORIZONTAL_MIRROR_ROTATE_270_BIT_KHR) {
-        swapExtent.width = extent.height;
-        swapExtent.height = extent.width;
-    }
-
-    surface->mWindowWidth = extent.width;
-    surface->mWindowHeight = extent.height;
-
-    uint32_t imageCount = std::max<uint32_t>(3, caps.minImageCount);
-    if (caps.maxImageCount > 0 && imageCount > caps.maxImageCount) {
-        // Application must settle for fewer images than desired:
-        imageCount = caps.maxImageCount;
-    }
-
-    // Currently Skia requires the images to be color attchments and support all transfer
-    // operations.
-    VkImageUsageFlags usageFlags = VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT |
-                                   VK_IMAGE_USAGE_TRANSFER_SRC_BIT |
-                                   VK_IMAGE_USAGE_TRANSFER_DST_BIT;
-    SkASSERT((caps.supportedUsageFlags & usageFlags) == usageFlags);
-
-    SkASSERT(caps.supportedCompositeAlpha &
-             (VK_COMPOSITE_ALPHA_OPAQUE_BIT_KHR | VK_COMPOSITE_ALPHA_INHERIT_BIT_KHR));
-    VkCompositeAlphaFlagBitsKHR composite_alpha =
-            (caps.supportedCompositeAlpha & VK_COMPOSITE_ALPHA_INHERIT_BIT_KHR)
-                    ? VK_COMPOSITE_ALPHA_INHERIT_BIT_KHR
-                    : VK_COMPOSITE_ALPHA_OPAQUE_BIT_KHR;
-
-    VkFormat surfaceFormat = VK_FORMAT_R8G8B8A8_UNORM;
-    VkColorSpaceKHR colorSpace = VK_COLORSPACE_SRGB_NONLINEAR_KHR;
-    if (surface->mColorType == SkColorType::kRGBA_F16_SkColorType) {
-        surfaceFormat = VK_FORMAT_R16G16B16A16_SFLOAT;
-    }
-
-    if (surface->mColorMode == ColorMode::WideColorGamut) {
-        skcms_Matrix3x3 surfaceGamut;
-        LOG_ALWAYS_FATAL_IF(!surface->mColorSpace->toXYZD50(&surfaceGamut),
-                            "Could not get gamut matrix from color space");
-        if (memcmp(&surfaceGamut, &SkNamedGamut::kSRGB, sizeof(surfaceGamut)) == 0) {
-            colorSpace = VK_COLOR_SPACE_EXTENDED_SRGB_NONLINEAR_EXT;
-        } else if (memcmp(&surfaceGamut, &SkNamedGamut::kDCIP3, sizeof(surfaceGamut)) == 0) {
-            colorSpace = VK_COLOR_SPACE_DISPLAY_P3_NONLINEAR_EXT;
-        } else {
-            LOG_ALWAYS_FATAL("Unreachable: unsupported wide color space.");
-        }
-    }
-
-    bool foundSurfaceFormat = false;
-    for (uint32_t i = 0; i < surfaceFormatCount; ++i) {
-        if (surfaceFormat == surfaceFormats[i].format
-                && colorSpace == surfaceFormats[i].colorSpace) {
-            foundSurfaceFormat = true;
-            break;
-        }
-    }
-
-    if (!foundSurfaceFormat) {
-        return false;
-    }
-
-    // FIFO is always available and will match what we do on GL so just pick that here.
-    VkPresentModeKHR mode = VK_PRESENT_MODE_FIFO_KHR;
-
-    VkSwapchainCreateInfoKHR swapchainCreateInfo;
-    memset(&swapchainCreateInfo, 0, sizeof(VkSwapchainCreateInfoKHR));
-    swapchainCreateInfo.sType = VK_STRUCTURE_TYPE_SWAPCHAIN_CREATE_INFO_KHR;
-    swapchainCreateInfo.surface = surface->mVkSurface;
-    swapchainCreateInfo.minImageCount = imageCount;
-    swapchainCreateInfo.imageFormat = surfaceFormat;
-    swapchainCreateInfo.imageColorSpace = colorSpace;
-    swapchainCreateInfo.imageExtent = swapExtent;
-    swapchainCreateInfo.imageArrayLayers = 1;
-    swapchainCreateInfo.imageUsage = usageFlags;
-
-    uint32_t queueFamilies[] = {mGraphicsQueueIndex, mPresentQueueIndex};
-    if (mGraphicsQueueIndex != mPresentQueueIndex) {
-        swapchainCreateInfo.imageSharingMode = VK_SHARING_MODE_CONCURRENT;
-        swapchainCreateInfo.queueFamilyIndexCount = 2;
-        swapchainCreateInfo.pQueueFamilyIndices = queueFamilies;
-    } else {
-        swapchainCreateInfo.imageSharingMode = VK_SHARING_MODE_EXCLUSIVE;
-        swapchainCreateInfo.queueFamilyIndexCount = 0;
-        swapchainCreateInfo.pQueueFamilyIndices = nullptr;
-    }
-
-    swapchainCreateInfo.preTransform = transform;
-    swapchainCreateInfo.compositeAlpha = composite_alpha;
-    swapchainCreateInfo.presentMode = mode;
-    swapchainCreateInfo.clipped = true;
-    swapchainCreateInfo.oldSwapchain = surface->mSwapchain;
-
-    res = mCreateSwapchainKHR(mDevice, &swapchainCreateInfo, nullptr, &surface->mSwapchain);
-    if (VK_SUCCESS != res) {
-        return false;
-    }
-
-    surface->mTransform = transform;
-
-    // destroy the old swapchain
-    if (swapchainCreateInfo.oldSwapchain != VK_NULL_HANDLE) {
-        mDeviceWaitIdle(mDevice);
-
-        destroyBuffers(surface);
-
-        mDestroySwapchainKHR(mDevice, swapchainCreateInfo.oldSwapchain, nullptr);
-    }
-
-    createBuffers(surface, surfaceFormat, swapExtent);
-
-    // The window content is not updated (frozen) until a buffer of the window size is received.
-    // This prevents temporary stretching of the window after it is resized, but before the first
-    // buffer with new size is enqueued.
-    native_window_set_scaling_mode(surface->mNativeWindow, NATIVE_WINDOW_SCALING_MODE_FREEZE);
-
-    return true;
-}
-
 VulkanSurface* VulkanManager::createSurface(ANativeWindow* window, ColorMode colorMode,
                                             sk_sp<SkColorSpace> surfaceColorSpace,
                                             SkColorType surfaceColorType,
@@ -904,185 +541,8 @@
         return nullptr;
     }
 
-    VulkanSurface* surface = new VulkanSurface(colorMode, window, surfaceColorSpace,
-                                               surfaceColorType, grContext);
-
-    VkAndroidSurfaceCreateInfoKHR surfaceCreateInfo;
-    memset(&surfaceCreateInfo, 0, sizeof(VkAndroidSurfaceCreateInfoKHR));
-    surfaceCreateInfo.sType = VK_STRUCTURE_TYPE_ANDROID_SURFACE_CREATE_INFO_KHR;
-    surfaceCreateInfo.pNext = nullptr;
-    surfaceCreateInfo.flags = 0;
-    surfaceCreateInfo.window = window;
-
-    VkResult res = mCreateAndroidSurfaceKHR(mInstance, &surfaceCreateInfo, nullptr,
-            &surface->mVkSurface);
-    if (VK_SUCCESS != res) {
-        delete surface;
-        return nullptr;
-    }
-
-    SkDEBUGCODE(VkBool32 supported; res = mGetPhysicalDeviceSurfaceSupportKHR(
-            mPhysicalDevice, mPresentQueueIndex, surface->mVkSurface, &supported);
-    // All physical devices and queue families on Android must be capable of
-    // presentation with any native window.
-    SkASSERT(VK_SUCCESS == res && supported););
-
-    if (!createSwapchain(surface)) {
-        destroySurface(surface);
-        return nullptr;
-    }
-
-    return surface;
-}
-
-// Helper to know which src stage flags we need to set when transitioning to the present layout
-static VkPipelineStageFlags layoutToPipelineSrcStageFlags(const VkImageLayout layout) {
-    if (VK_IMAGE_LAYOUT_GENERAL == layout) {
-        return VK_PIPELINE_STAGE_ALL_COMMANDS_BIT;
-    } else if (VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL == layout ||
-               VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL == layout) {
-        return VK_PIPELINE_STAGE_TRANSFER_BIT;
-    } else if (VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL == layout) {
-        return VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT;
-    } else if (VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL == layout ||
-               VK_IMAGE_LAYOUT_DEPTH_STENCIL_READ_ONLY_OPTIMAL == layout) {
-        return VK_PIPELINE_STAGE_LATE_FRAGMENT_TESTS_BIT;
-    } else if (VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL == layout) {
-        return VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT;
-    } else if (VK_IMAGE_LAYOUT_PREINITIALIZED == layout) {
-        return VK_PIPELINE_STAGE_HOST_BIT;
-    }
-
-    SkASSERT(VK_IMAGE_LAYOUT_UNDEFINED == layout);
-    return VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT;
-}
-
-// Helper to know which src access mask we need to set when transitioning to the present layout
-static VkAccessFlags layoutToSrcAccessMask(const VkImageLayout layout) {
-    VkAccessFlags flags = 0;
-    if (VK_IMAGE_LAYOUT_GENERAL == layout) {
-        flags = VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT |
-                VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT | VK_ACCESS_TRANSFER_WRITE_BIT |
-                VK_ACCESS_TRANSFER_READ_BIT | VK_ACCESS_SHADER_READ_BIT | VK_ACCESS_HOST_WRITE_BIT |
-                VK_ACCESS_HOST_READ_BIT;
-    } else if (VK_IMAGE_LAYOUT_PREINITIALIZED == layout) {
-        flags = VK_ACCESS_HOST_WRITE_BIT;
-    } else if (VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL == layout) {
-        flags = VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT;
-    } else if (VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL == layout) {
-        flags = VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT;
-    } else if (VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL == layout) {
-        flags = VK_ACCESS_TRANSFER_WRITE_BIT;
-    } else if (VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL == layout) {
-        flags = VK_ACCESS_TRANSFER_READ_BIT;
-    } else if (VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL == layout) {
-        flags = VK_ACCESS_SHADER_READ_BIT;
-    }
-    return flags;
-}
-
-void VulkanManager::swapBuffers(VulkanSurface* surface) {
-    if (CC_UNLIKELY(Properties::waitForGpuCompletion)) {
-        ATRACE_NAME("Finishing GPU work");
-        mDeviceWaitIdle(mDevice);
-    }
-
-    SkASSERT(surface->mBackbuffers);
-    VulkanSurface::BackbufferInfo* backbuffer =
-            surface->mBackbuffers + surface->mCurrentBackbufferIndex;
-
-    SkSurface* skSurface = surface->mImageInfos[backbuffer->mImageIndex].mSurface.get();
-    GrBackendRenderTarget backendRT = skSurface->getBackendRenderTarget(
-            SkSurface::kFlushRead_BackendHandleAccess);
-    SkASSERT(backendRT.isValid());
-
-    GrVkImageInfo imageInfo;
-    SkAssertResult(backendRT.getVkImageInfo(&imageInfo));
-
-    // Check to make sure we never change the actually wrapped image
-    SkASSERT(imageInfo.fImage == surface->mImages[backbuffer->mImageIndex]);
-
-    // We need to transition the image to VK_IMAGE_LAYOUT_PRESENT_SRC_KHR and make sure that all
-    // previous work is complete for before presenting. So we first add the necessary barrier here.
-    VkImageLayout layout = imageInfo.fImageLayout;
-    VkPipelineStageFlags srcStageMask = layoutToPipelineSrcStageFlags(layout);
-    VkPipelineStageFlags dstStageMask = VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT;
-    VkAccessFlags srcAccessMask = layoutToSrcAccessMask(layout);
-    VkAccessFlags dstAccessMask = 0;
-
-    VkImageMemoryBarrier imageMemoryBarrier = {
-            VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER,     // sType
-            NULL,                                       // pNext
-            srcAccessMask,                              // outputMask
-            dstAccessMask,                              // inputMask
-            layout,                                     // oldLayout
-            VK_IMAGE_LAYOUT_PRESENT_SRC_KHR,            // newLayout
-            mGraphicsQueueIndex,                        // srcQueueFamilyIndex
-            mPresentQueueIndex,                         // dstQueueFamilyIndex
-            surface->mImages[backbuffer->mImageIndex],  // image
-            {VK_IMAGE_ASPECT_COLOR_BIT, 0, 1, 0, 1}     // subresourceRange
-    };
-
-    mResetCommandBuffer(backbuffer->mTransitionCmdBuffers[1], 0);
-    VkCommandBufferBeginInfo info;
-    memset(&info, 0, sizeof(VkCommandBufferBeginInfo));
-    info.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO;
-    info.flags = 0;
-    mBeginCommandBuffer(backbuffer->mTransitionCmdBuffers[1], &info);
-    mCmdPipelineBarrier(backbuffer->mTransitionCmdBuffers[1], srcStageMask, dstStageMask, 0, 0,
-                        nullptr, 0, nullptr, 1, &imageMemoryBarrier);
-    mEndCommandBuffer(backbuffer->mTransitionCmdBuffers[1]);
-
-    surface->mImageInfos[backbuffer->mImageIndex].mImageLayout = VK_IMAGE_LAYOUT_PRESENT_SRC_KHR;
-
-    // insert the layout transfer into the queue and wait on the acquire
-    VkSubmitInfo submitInfo;
-    memset(&submitInfo, 0, sizeof(VkSubmitInfo));
-    submitInfo.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO;
-    submitInfo.waitSemaphoreCount = 0;
-    submitInfo.pWaitDstStageMask = 0;
-    submitInfo.commandBufferCount = 1;
-    submitInfo.pCommandBuffers = &backbuffer->mTransitionCmdBuffers[1];
-    submitInfo.signalSemaphoreCount = 1;
-    // When this command buffer finishes we will signal this semaphore so that we know it is now
-    // safe to present the image to the screen.
-    submitInfo.pSignalSemaphores = &backbuffer->mRenderSemaphore;
-
-    // Attach second fence to submission here so we can track when the command buffer finishes.
-    mQueueSubmit(mGraphicsQueue, 1, &submitInfo, backbuffer->mUsageFences[1]);
-
-    // Submit present operation to present queue. We use a semaphore here to make sure all rendering
-    // to the image is complete and that the layout has been change to present on the graphics
-    // queue.
-    const VkPresentInfoKHR presentInfo = {
-            VK_STRUCTURE_TYPE_PRESENT_INFO_KHR,  // sType
-            NULL,                                // pNext
-            1,                                   // waitSemaphoreCount
-            &backbuffer->mRenderSemaphore,       // pWaitSemaphores
-            1,                                   // swapchainCount
-            &surface->mSwapchain,                // pSwapchains
-            &backbuffer->mImageIndex,            // pImageIndices
-            NULL                                 // pResults
-    };
-
-    mQueuePresentKHR(mPresentQueue, &presentInfo);
-
-    surface->mBackbuffer.reset();
-    surface->mImageInfos[backbuffer->mImageIndex].mLastUsed = surface->mCurrentTime;
-    surface->mImageInfos[backbuffer->mImageIndex].mInvalid = false;
-    surface->mCurrentTime++;
-}
-
-int VulkanManager::getAge(VulkanSurface* surface) {
-    SkASSERT(surface->mBackbuffers);
-    VulkanSurface::BackbufferInfo* backbuffer =
-            surface->mBackbuffers + surface->mCurrentBackbufferIndex;
-    if (mSwapBehavior == SwapBehavior::Discard ||
-        surface->mImageInfos[backbuffer->mImageIndex].mInvalid) {
-        return 0;
-    }
-    uint16_t lastUsed = surface->mImageInfos[backbuffer->mImageIndex].mLastUsed;
-    return surface->mCurrentTime - lastUsed;
+    return VulkanSurface::Create(window, colorMode, surfaceColorType, surfaceColorSpace, grContext,
+                                  *this);
 }
 
 bool VulkanManager::setupDummyCommandBuffer() {
diff --git a/libs/hwui/renderthread/VulkanManager.h b/libs/hwui/renderthread/VulkanManager.h
index 95c9630..c3d2891 100644
--- a/libs/hwui/renderthread/VulkanManager.h
+++ b/libs/hwui/renderthread/VulkanManager.h
@@ -28,7 +28,9 @@
 #include <ui/Fence.h>
 #include <utils/StrongPointer.h>
 #include <vk/GrVkBackendContext.h>
+#include "Frame.h"
 #include "IRenderPipeline.h"
+#include "VulkanSurface.h"
 
 class GrVkExtensions;
 
@@ -38,66 +40,6 @@
 
 class RenderThread;
 
-class VulkanSurface {
-public:
-    VulkanSurface(ColorMode colorMode, ANativeWindow* window, sk_sp<SkColorSpace> colorSpace,
-                  SkColorType colorType, GrContext* grContext)
-            : mColorMode(colorMode), mNativeWindow(window), mColorSpace(colorSpace),
-              mColorType(colorType), mGrContext(grContext) {}
-
-    sk_sp<SkSurface> getBackBufferSurface() { return mBackbuffer; }
-
-    // The width and height are are the logical width and height for when submitting draws to the
-    // surface. In reality if the window is rotated the underlying VkImage may have the width and
-    // height swapped.
-    int windowWidth() const { return mWindowWidth; }
-    int windowHeight() const { return mWindowHeight; }
-
-    SkMatrix& preTransform() { return mPreTransform; }
-
-private:
-    friend class VulkanManager;
-    struct BackbufferInfo {
-        uint32_t mImageIndex;           // image this is associated with
-        VkSemaphore mAcquireSemaphore;  // we signal on this for acquisition of image
-        VkSemaphore mRenderSemaphore;   // we wait on this for rendering to be done
-        VkCommandBuffer
-                mTransitionCmdBuffers[2];  // to transition layout between present and render
-        // We use these fences to make sure the above Command buffers have finished their work
-        // before attempting to reuse them or destroy them.
-        VkFence mUsageFences[2];
-    };
-
-    struct ImageInfo {
-        VkImageLayout mImageLayout = VK_IMAGE_LAYOUT_UNDEFINED;
-        sk_sp<SkSurface> mSurface;
-        uint16_t mLastUsed = 0;
-        bool mInvalid = true;
-    };
-
-    sk_sp<SkSurface> mBackbuffer;
-
-    VkSurfaceKHR mVkSurface = VK_NULL_HANDLE;
-    VkSwapchainKHR mSwapchain = VK_NULL_HANDLE;
-
-    BackbufferInfo* mBackbuffers = nullptr;
-    uint32_t mCurrentBackbufferIndex;
-
-    uint32_t mImageCount;
-    VkImage* mImages = nullptr;
-    ImageInfo* mImageInfos;
-    uint16_t mCurrentTime = 0;
-    ColorMode mColorMode;
-    ANativeWindow* mNativeWindow;
-    int mWindowWidth = 0;
-    int mWindowHeight = 0;
-    sk_sp<SkColorSpace> mColorSpace;
-    SkColorType mColorType;
-    VkSurfaceTransformFlagBitsKHR mTransform = VK_SURFACE_TRANSFORM_IDENTITY_BIT_KHR;
-    SkMatrix mPreTransform;
-    GrContext* mGrContext;
-};
-
 // This class contains the shared global Vulkan objects, such as VkInstance, VkDevice and VkQueue,
 // which are re-used by CanvasContext. This class is created once and should be used by all vulkan
 // windowing contexts. The VulkanManager must be initialized before use.
@@ -114,33 +56,19 @@
     // Quick check to see if the VulkanManager has been initialized.
     bool hasVkContext() { return mDevice != VK_NULL_HANDLE; }
 
-    // Given a window this creates a new VkSurfaceKHR and VkSwapchain and stores them inside a new
-    // VulkanSurface object which is returned.
+    // Create and destroy functions for wrapping an ANativeWindow in a VulkanSurface
     VulkanSurface* createSurface(ANativeWindow* window, ColorMode colorMode,
                                  sk_sp<SkColorSpace> surfaceColorSpace,
                                  SkColorType surfaceColorType,
                                  GrContext* grContext);
-
-    // Destroy the VulkanSurface and all associated vulkan objects.
     void destroySurface(VulkanSurface* surface);
 
+    Frame dequeueNextBuffer(VulkanSurface* surface);
+    void swapBuffers(VulkanSurface* surface, const SkRect& dirtyRect);
+
     // Cleans up all the global state in the VulkanManger.
     void destroy();
 
-    // No work is needed to make a VulkanSurface current, and all functions require that a
-    // VulkanSurface is passed into them so we just return true here.
-    bool isCurrent(VulkanSurface* surface) { return true; }
-
-    int getAge(VulkanSurface* surface);
-
-    // Returns an SkSurface which wraps the next image returned from vkAcquireNextImageKHR. It also
-    // will transition the VkImage from a present layout to color attachment so that it can be used
-    // by the client for drawing.
-    SkSurface* getBackbufferSurface(VulkanSurface** surface);
-
-    // Presents the current VkImage.
-    void swapBuffers(VulkanSurface* surface);
-
     // Inserts a wait on fence command into the Vulkan command buffer.
     status_t fenceWait(sp<Fence>& fence);
 
@@ -153,17 +81,10 @@
     sk_sp<GrContext> createContext(const GrContextOptions& options);
 
 private:
+    friend class VulkanSurface;
     // Sets up the VkInstance and VkDevice objects. Also fills out the passed in
     // VkPhysicalDeviceFeatures struct.
     void setupDevice(GrVkExtensions&, VkPhysicalDeviceFeatures2&);
-
-    void destroyBuffers(VulkanSurface* surface);
-
-    bool createSwapchain(VulkanSurface* surface);
-    void createBuffers(VulkanSurface* surface, VkFormat format, VkExtent2D extent);
-
-    VulkanSurface::BackbufferInfo* getAvailableBackbuffer(VulkanSurface* surface);
-
     bool setupDummyCommandBuffer();
 
     // simple wrapper class that exists only to initialize a pointer to NULL
@@ -190,13 +111,6 @@
     VkPtr<PFN_vkGetPhysicalDeviceSurfaceFormatsKHR> mGetPhysicalDeviceSurfaceFormatsKHR;
     VkPtr<PFN_vkGetPhysicalDeviceSurfacePresentModesKHR> mGetPhysicalDeviceSurfacePresentModesKHR;
 
-    VkPtr<PFN_vkCreateSwapchainKHR> mCreateSwapchainKHR;
-    VkPtr<PFN_vkDestroySwapchainKHR> mDestroySwapchainKHR;
-    VkPtr<PFN_vkGetSwapchainImagesKHR> mGetSwapchainImagesKHR;
-    VkPtr<PFN_vkAcquireNextImageKHR> mAcquireNextImageKHR;
-    VkPtr<PFN_vkQueuePresentKHR> mQueuePresentKHR;
-    VkPtr<PFN_vkCreateSharedSwapchainsKHR> mCreateSharedSwapchainsKHR;
-
     // Instance Functions
     VkPtr<PFN_vkEnumerateInstanceVersion> mEnumerateInstanceVersion;
     VkPtr<PFN_vkEnumerateInstanceExtensionProperties> mEnumerateInstanceExtensionProperties;
@@ -207,6 +121,7 @@
     VkPtr<PFN_vkGetPhysicalDeviceProperties> mGetPhysicalDeviceProperties;
     VkPtr<PFN_vkGetPhysicalDeviceQueueFamilyProperties> mGetPhysicalDeviceQueueFamilyProperties;
     VkPtr<PFN_vkGetPhysicalDeviceFeatures2> mGetPhysicalDeviceFeatures2;
+    VkPtr<PFN_vkGetPhysicalDeviceImageFormatProperties2> mGetPhysicalDeviceImageFormatProperties2;
     VkPtr<PFN_vkCreateDevice> mCreateDevice;
     VkPtr<PFN_vkEnumerateDeviceExtensionProperties> mEnumerateDeviceExtensionProperties;
 
diff --git a/libs/hwui/renderthread/VulkanSurface.cpp b/libs/hwui/renderthread/VulkanSurface.cpp
new file mode 100644
index 0000000..c03c3a8
--- /dev/null
+++ b/libs/hwui/renderthread/VulkanSurface.cpp
@@ -0,0 +1,536 @@
+/*
+ * Copyright (C) 2019 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 "VulkanSurface.h"
+
+#include <algorithm>
+#include <SkSurface.h>
+
+#include "VulkanManager.h"
+#include "utils/TraceUtils.h"
+#include "utils/Color.h"
+
+namespace android {
+namespace uirenderer {
+namespace renderthread {
+
+static bool IsTransformSupported(int transform) {
+    // For now, only support pure rotations, not flip or flip-and-rotate, until we have
+    // more time to test them and build sample code. As far as I know we never actually
+    // use anything besides pure rotations anyway.
+    return transform == 0
+        || transform == NATIVE_WINDOW_TRANSFORM_ROT_90
+        || transform == NATIVE_WINDOW_TRANSFORM_ROT_180
+        || transform == NATIVE_WINDOW_TRANSFORM_ROT_270;
+}
+
+static int InvertTransform(int transform) {
+    switch (transform) {
+        case NATIVE_WINDOW_TRANSFORM_ROT_90:
+            return NATIVE_WINDOW_TRANSFORM_ROT_270;
+        case NATIVE_WINDOW_TRANSFORM_ROT_180:
+            return NATIVE_WINDOW_TRANSFORM_ROT_180;
+        case NATIVE_WINDOW_TRANSFORM_ROT_270:
+            return NATIVE_WINDOW_TRANSFORM_ROT_90;
+        default:
+            return 0;
+    }
+}
+
+static int ConvertVkTransformToNative(VkSurfaceTransformFlagsKHR transform) {
+    switch (transform) {
+        case VK_SURFACE_TRANSFORM_ROTATE_90_BIT_KHR:
+            return NATIVE_WINDOW_TRANSFORM_ROT_270;
+        case VK_SURFACE_TRANSFORM_ROTATE_180_BIT_KHR:
+            return NATIVE_WINDOW_TRANSFORM_ROT_180;
+        case VK_SURFACE_TRANSFORM_ROTATE_270_BIT_KHR:
+            return NATIVE_WINDOW_TRANSFORM_ROT_90;
+        case VK_SURFACE_TRANSFORM_IDENTITY_BIT_KHR:
+        case VK_SURFACE_TRANSFORM_INHERIT_BIT_KHR:
+        default:
+            return 0;
+    }
+}
+
+static SkMatrix GetPreTransformMatrix(SkISize windowSize, int transform) {
+    const int width = windowSize.width();
+    const int height = windowSize.height();
+
+    switch (transform) {
+        case 0:
+            return SkMatrix::I();
+        case NATIVE_WINDOW_TRANSFORM_ROT_90:
+            return SkMatrix::MakeAll(0, -1, height, 1, 0, 0, 0, 0, 1);
+        case NATIVE_WINDOW_TRANSFORM_ROT_180:
+            return SkMatrix::MakeAll(-1, 0, width, 0, -1, height, 0, 0, 1);
+        case NATIVE_WINDOW_TRANSFORM_ROT_270:
+            return SkMatrix::MakeAll(0, 1, 0, -1, 0, width, 0, 0, 1);
+        default:
+            LOG_ALWAYS_FATAL("Unsupported Window Transform (%d)", transform);
+    }
+    return SkMatrix::I();
+}
+
+void VulkanSurface::ComputeWindowSizeAndTransform(WindowInfo* windowInfo, const SkISize& minSize,
+                                                   const SkISize& maxSize) {
+    SkISize& windowSize = windowInfo->size;
+
+    // clamp width & height to handle currentExtent of -1 and  protect us from broken hints
+    if (windowSize.width() < minSize.width() || windowSize.width() > maxSize.width()
+        || windowSize.height() < minSize.height() || windowSize.height() > maxSize.height()) {
+        int width = std::min(maxSize.width(), std::max(minSize.width(), windowSize.width()));
+        int height = std::min(maxSize.height(), std::max(minSize.height(), windowSize.height()));
+        ALOGE("Invalid Window Dimensions [%d, %d]; clamping to [%d, %d]",
+              windowSize.width(), windowSize.height(), width, height);
+        windowSize.set(width, height);
+    }
+
+    windowInfo->actualSize = windowSize;
+    if (windowInfo->transform & HAL_TRANSFORM_ROT_90) {
+        windowInfo->actualSize.set(windowSize.height(), windowSize.width());
+    }
+
+    windowInfo->preTransform = GetPreTransformMatrix(windowInfo->size, windowInfo->transform);
+}
+
+static bool ResetNativeWindow(ANativeWindow* window) {
+    // -- Reset the native window --
+    // The native window might have been used previously, and had its properties
+    // changed from defaults. That will affect the answer we get for queries
+    // like MIN_UNDEQUEUED_BUFFERS. Reset to a known/default state before we
+    // attempt such queries.
+
+    int err = native_window_api_connect(window, NATIVE_WINDOW_API_EGL);
+    if (err != 0) {
+        ALOGW("native_window_api_connect failed: %s (%d)", strerror(-err), err);
+        return false;
+    }
+
+    // this will match what we do on GL so pick that here.
+    err = window->setSwapInterval(window, 1);
+    if (err != 0) {
+        ALOGW("native_window->setSwapInterval(1) failed: %s (%d)", strerror(-err), err);
+        return false;
+    }
+
+    err = native_window_set_shared_buffer_mode(window, false);
+    if (err != 0) {
+        ALOGW("native_window_set_shared_buffer_mode(false) failed: %s (%d)", strerror(-err), err);
+        return false;
+    }
+
+    err = native_window_set_auto_refresh(window, false);
+    if (err != 0) {
+        ALOGW("native_window_set_auto_refresh(false) failed: %s (%d)", strerror(-err), err);
+        return false;
+    }
+
+    return true;
+}
+
+class VkSurfaceAutoDeleter {
+public:
+    VkSurfaceAutoDeleter(VkInstance instance, VkSurfaceKHR surface,
+                         PFN_vkDestroySurfaceKHR destroySurfaceKHR)
+            : mInstance(instance)
+            , mSurface(surface)
+            , mDestroySurfaceKHR(destroySurfaceKHR) {}
+    ~VkSurfaceAutoDeleter() {
+        destroy();
+    }
+
+    void destroy() {
+        if (mSurface != VK_NULL_HANDLE) {
+            mDestroySurfaceKHR(mInstance, mSurface, nullptr);
+            mSurface = VK_NULL_HANDLE;
+        }
+    }
+
+private:
+    VkInstance mInstance;
+    VkSurfaceKHR mSurface;
+    PFN_vkDestroySurfaceKHR mDestroySurfaceKHR;
+};
+
+VulkanSurface* VulkanSurface::Create(ANativeWindow* window, ColorMode colorMode,
+                                       SkColorType colorType, sk_sp<SkColorSpace> colorSpace,
+                                       GrContext* grContext, const VulkanManager& vkManager) {
+
+    VkAndroidSurfaceCreateInfoKHR surfaceCreateInfo;
+    memset(&surfaceCreateInfo, 0, sizeof(VkAndroidSurfaceCreateInfoKHR));
+    surfaceCreateInfo.sType = VK_STRUCTURE_TYPE_ANDROID_SURFACE_CREATE_INFO_KHR;
+    surfaceCreateInfo.pNext = nullptr;
+    surfaceCreateInfo.flags = 0;
+    surfaceCreateInfo.window = window;
+
+    VkSurfaceKHR vkSurface = VK_NULL_HANDLE;
+    VkResult res = vkManager.mCreateAndroidSurfaceKHR(vkManager.mInstance, &surfaceCreateInfo,
+                                                      nullptr, &vkSurface);
+    if (VK_SUCCESS != res) {
+        ALOGE("VulkanSurface::Create() vkCreateAndroidSurfaceKHR failed (%d)", res);
+        return nullptr;
+    }
+
+    VkSurfaceAutoDeleter vkSurfaceDeleter(vkManager.mInstance, vkSurface,
+                                          vkManager.mDestroySurfaceKHR);
+
+    SkDEBUGCODE(VkBool32 supported; res = vkManager.mGetPhysicalDeviceSurfaceSupportKHR(
+            vkManager.mPhysicalDevice, vkManager.mPresentQueueIndex, vkSurface, &supported);
+    // All physical devices and queue families on Android must be capable of
+    // presentation with any native window.
+    SkASSERT(VK_SUCCESS == res && supported););
+
+    // check for capabilities
+    VkSurfaceCapabilitiesKHR caps;
+    res = vkManager.mGetPhysicalDeviceSurfaceCapabilitiesKHR(vkManager.mPhysicalDevice, vkSurface,
+                                                             &caps);
+    if (VK_SUCCESS != res) {
+        ALOGE("VulkanSurface::Create() vkGetPhysicalDeviceSurfaceCapabilitiesKHR failed (%d)", res);
+        return nullptr;
+    }
+
+    LOG_ALWAYS_FATAL_IF(0 == (caps.supportedTransforms & VK_SURFACE_TRANSFORM_IDENTITY_BIT_KHR));
+
+    /*
+     * We must destroy the VK Surface before attempting to update the window as doing so after
+     * will cause the native window to be modified in unexpected ways.
+     */
+    vkSurfaceDeleter.destroy();
+
+    /*
+     * Populate Window Info struct
+     */
+    WindowInfo windowInfo;
+
+    windowInfo.transform = ConvertVkTransformToNative(caps.supportedTransforms);
+    windowInfo.size = SkISize::Make(caps.currentExtent.width, caps.currentExtent.height);
+
+    const SkISize minSize = SkISize::Make(caps.minImageExtent.width, caps.minImageExtent.height);
+    const SkISize maxSize = SkISize::Make(caps.maxImageExtent.width, caps.maxImageExtent.height);
+    ComputeWindowSizeAndTransform(&windowInfo, minSize, maxSize);
+
+    windowInfo.bufferCount = std::max<uint32_t>(VulkanSurface::sMaxBufferCount, caps.minImageCount);
+    if (caps.maxImageCount > 0 && windowInfo.bufferCount > caps.maxImageCount) {
+        // Application must settle for fewer images than desired:
+        windowInfo.bufferCount = caps.maxImageCount;
+    }
+
+    // Currently Skia requires the images to be color attachments and support all transfer
+    // operations.
+    VkImageUsageFlags usageFlags = VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT |
+                                   VK_IMAGE_USAGE_SAMPLED_BIT |
+                                   VK_IMAGE_USAGE_TRANSFER_SRC_BIT |
+                                   VK_IMAGE_USAGE_TRANSFER_DST_BIT;
+    LOG_ALWAYS_FATAL_IF((caps.supportedUsageFlags & usageFlags) != usageFlags);
+
+    windowInfo.dataspace = HAL_DATASPACE_V0_SRGB;
+    if (colorMode == ColorMode::WideColorGamut) {
+        skcms_Matrix3x3 surfaceGamut;
+        LOG_ALWAYS_FATAL_IF(!colorSpace->toXYZD50(&surfaceGamut),
+                            "Could not get gamut matrix from color space");
+        if (memcmp(&surfaceGamut, &SkNamedGamut::kSRGB, sizeof(surfaceGamut)) == 0) {
+            windowInfo.dataspace = HAL_DATASPACE_V0_SCRGB;
+        } else if (memcmp(&surfaceGamut, &SkNamedGamut::kDCIP3, sizeof(surfaceGamut)) == 0) {
+            windowInfo.dataspace = HAL_DATASPACE_DISPLAY_P3;
+        } else {
+            LOG_ALWAYS_FATAL("Unreachable: unsupported wide color space.");
+        }
+    }
+
+    windowInfo.pixelFormat = ColorTypeToPixelFormat(colorType);
+    VkFormat vkPixelFormat = VK_FORMAT_R8G8B8A8_UNORM;
+    if (windowInfo.pixelFormat == PIXEL_FORMAT_RGBA_FP16) {
+        vkPixelFormat = VK_FORMAT_R16G16B16A16_SFLOAT;
+    }
+
+    uint64_t producerUsage =
+            AHARDWAREBUFFER_USAGE_GPU_FRAMEBUFFER | AHARDWAREBUFFER_USAGE_GPU_SAMPLED_IMAGE;
+    uint64_t consumerUsage;
+    native_window_get_consumer_usage(window, &consumerUsage);
+    windowInfo.windowUsageFlags = consumerUsage | producerUsage;
+
+    /*
+     * Now we attempt to modify the window!
+     */
+    if (!UpdateWindow(window, windowInfo)) {
+        return nullptr;
+    }
+
+    return new VulkanSurface(window, windowInfo, minSize, maxSize, grContext);
+}
+
+bool VulkanSurface::UpdateWindow(ANativeWindow* window, const WindowInfo& windowInfo) {
+    ATRACE_CALL();
+
+    if (!ResetNativeWindow(window)) {
+        return false;
+    }
+
+    // -- Configure the native window --
+    int err = native_window_set_buffers_format(window, windowInfo.pixelFormat);
+    if (err != 0) {
+        ALOGE("VulkanSurface::UpdateWindow() native_window_set_buffers_format(%d) failed: %s (%d)",
+              windowInfo.pixelFormat, strerror(-err), err);
+        return false;
+    }
+
+    err = native_window_set_buffers_data_space(window, windowInfo.dataspace);
+    if (err != 0) {
+        ALOGE("VulkanSurface::UpdateWindow() native_window_set_buffers_data_space(%d) "
+              "failed: %s (%d)", windowInfo.dataspace, strerror(-err), err);
+        return false;
+    }
+
+    const SkISize& size = windowInfo.actualSize;
+    err = native_window_set_buffers_dimensions(window, size.width(), size.height());
+    if (err != 0) {
+        ALOGE("VulkanSurface::UpdateWindow() native_window_set_buffers_dimensions(%d,%d) "
+              "failed: %s (%d)", size.width(), size.height(), strerror(-err), err);
+        return false;
+    }
+
+    // native_window_set_buffers_transform() expects the transform the app is requesting that
+    // the compositor perform during composition. With native windows, pre-transform works by
+    // rendering with the same transform the compositor is applying (as in Vulkan), but
+    // then requesting the inverse transform, so that when the compositor does
+    // it's job the two transforms cancel each other out and the compositor ends
+    // up applying an identity transform to the app's buffer.
+    err = native_window_set_buffers_transform(window, InvertTransform(windowInfo.transform));
+    if (err != 0) {
+        ALOGE("VulkanSurface::UpdateWindow() native_window_set_buffers_transform(%d) "
+              "failed: %s (%d)", windowInfo.transform, strerror(-err), err);
+        return false;
+    }
+
+    // Vulkan defaults to NATIVE_WINDOW_SCALING_MODE_SCALE_TO_WINDOW, but this is different than
+    // HWUI's expectation
+    err = native_window_set_scaling_mode(window, NATIVE_WINDOW_SCALING_MODE_FREEZE);
+    if (err != 0) {
+        ALOGE("VulkanSurface::UpdateWindow() native_window_set_scaling_mode(SCALE_TO_WINDOW) "
+              "failed: %s (%d)", strerror(-err), err);
+        return false;
+    }
+
+    // Lower layer insists that we have at least two buffers.
+    err = native_window_set_buffer_count(window, std::max(2, windowInfo.bufferCount));
+    if (err != 0) {
+        ALOGE("VulkanSurface::UpdateWindow() native_window_set_buffer_count(%d) failed: %s (%d)",
+              windowInfo.bufferCount, strerror(-err), err);
+        return false;
+    }
+
+    err = native_window_set_usage(window, windowInfo.windowUsageFlags);
+    if (err != 0) {
+        ALOGE("VulkanSurface::UpdateWindow() native_window_set_usage failed: %s (%d)",
+              strerror(-err), err);
+        return false;
+    }
+
+    return err == 0;
+}
+
+VulkanSurface::VulkanSurface(ANativeWindow* window, const WindowInfo& windowInfo,
+                               SkISize minWindowSize, SkISize maxWindowSize, GrContext* grContext)
+        : mNativeWindow(window)
+        , mWindowInfo(windowInfo)
+        , mGrContext(grContext)
+        , mMinWindowSize(minWindowSize)
+        , mMaxWindowSize(maxWindowSize) { }
+
+VulkanSurface::~VulkanSurface() {
+    releaseBuffers();
+
+    // release the native window to be available for use by other clients
+    int err = native_window_api_disconnect(mNativeWindow.get(), NATIVE_WINDOW_API_EGL);
+    ALOGW_IF(err != 0, "native_window_api_disconnect failed: %s (%d)", strerror(-err), err);
+}
+
+void VulkanSurface::releaseBuffers() {
+    for (uint32_t i = 0; i < VulkanSurface::sMaxBufferCount; i++) {
+        VulkanSurface::NativeBufferInfo& bufferInfo = mNativeBuffers[i];
+
+        if (bufferInfo.buffer.get() != nullptr && bufferInfo.dequeued) {
+            int err = mNativeWindow->cancelBuffer(mNativeWindow.get(), bufferInfo.buffer.get(),
+                                                  bufferInfo.dequeue_fence);
+            if (err != 0) {
+                ALOGE("cancelBuffer[%u] failed during destroy: %s (%d)", i, strerror(-err), err);
+            }
+            bufferInfo.dequeued = false;
+
+            if (bufferInfo.dequeue_fence >= 0) {
+                close(bufferInfo.dequeue_fence);
+                bufferInfo.dequeue_fence = -1;
+            }
+        }
+
+        LOG_ALWAYS_FATAL_IF(bufferInfo.dequeued);
+        LOG_ALWAYS_FATAL_IF(bufferInfo.dequeue_fence != -1);
+
+        bufferInfo.skSurface.reset();
+        bufferInfo.buffer.clear();
+        bufferInfo.hasValidContents = false;
+        bufferInfo.lastPresentedCount = 0;
+    }
+}
+
+VulkanSurface::NativeBufferInfo* VulkanSurface::dequeueNativeBuffer() {
+    // Set the dequeue index to invalid in case of error and only reset it to the correct
+    // value at the end of the function if everything dequeued correctly.
+    mDequeuedIndex = -1;
+
+    //check if the native window has been resized or rotated and update accordingly
+    SkISize newSize = SkISize::MakeEmpty();
+    int transformHint = 0;
+    mNativeWindow->query(mNativeWindow.get(), NATIVE_WINDOW_WIDTH, &newSize.fWidth);
+    mNativeWindow->query(mNativeWindow.get(), NATIVE_WINDOW_HEIGHT, &newSize.fHeight);
+    mNativeWindow->query(mNativeWindow.get(), NATIVE_WINDOW_TRANSFORM_HINT, &transformHint);
+    if (newSize != mWindowInfo.actualSize || transformHint != mWindowInfo.transform) {
+        WindowInfo newWindowInfo = mWindowInfo;
+        newWindowInfo.size = newSize;
+        newWindowInfo.transform = IsTransformSupported(transformHint) ? transformHint : 0;
+        ComputeWindowSizeAndTransform(&newWindowInfo, mMinWindowSize, mMaxWindowSize);
+
+        int err = 0;
+        if (newWindowInfo.actualSize != mWindowInfo.actualSize) {
+            // reset the native buffers and update the window
+            err = native_window_set_buffers_dimensions(mNativeWindow.get(),
+                                                       newWindowInfo.actualSize.width(),
+                                                       newWindowInfo.actualSize.height());
+            if (err != 0) {
+                ALOGE("native_window_set_buffers_dimensions(%d,%d) failed: %s (%d)",
+                      newWindowInfo.actualSize.width(),
+                      newWindowInfo.actualSize.height(), strerror(-err), err);
+                return nullptr;
+            }
+            // reset the NativeBufferInfo (including SkSurface) associated with the old buffers. The
+            // new NativeBufferInfo storage will be populated lazily as we dequeue each new buffer.
+            releaseBuffers();
+            // TODO should we ask the nativewindow to allocate buffers?
+        }
+
+        if (newWindowInfo.transform != mWindowInfo.transform) {
+            err = native_window_set_buffers_transform(mNativeWindow.get(),
+                    InvertTransform(newWindowInfo.transform));
+            if (err != 0) {
+                ALOGE("native_window_set_buffers_transform(%d) failed: %s (%d)",
+                      newWindowInfo.transform, strerror(-err), err);
+                newWindowInfo.transform = mWindowInfo.transform;
+                ComputeWindowSizeAndTransform(&newWindowInfo, mMinWindowSize, mMaxWindowSize);
+            }
+        }
+
+        mWindowInfo = newWindowInfo;
+    }
+
+    ANativeWindowBuffer* buffer;
+    int fence_fd;
+    int err = mNativeWindow->dequeueBuffer(mNativeWindow.get(), &buffer, &fence_fd);
+    if (err != 0) {
+        ALOGE("dequeueBuffer failed: %s (%d)", strerror(-err), err);
+        return nullptr;
+    }
+
+    uint32_t idx;
+    for (idx = 0; idx < mWindowInfo.bufferCount; idx++) {
+        if (mNativeBuffers[idx].buffer.get() == buffer) {
+            mNativeBuffers[idx].dequeued = true;
+            mNativeBuffers[idx].dequeue_fence = fence_fd;
+            break;
+        } else if (mNativeBuffers[idx].buffer.get() == nullptr) {
+            // increasing the number of buffers we have allocated
+            mNativeBuffers[idx].buffer = buffer;
+            mNativeBuffers[idx].dequeued = true;
+            mNativeBuffers[idx].dequeue_fence = fence_fd;
+            break;
+        }
+    }
+    if (idx == mWindowInfo.bufferCount) {
+        ALOGE("dequeueBuffer returned unrecognized buffer");
+        mNativeWindow->cancelBuffer(mNativeWindow.get(), buffer, fence_fd);
+        return nullptr;
+    }
+
+    VulkanSurface::NativeBufferInfo* bufferInfo = &mNativeBuffers[idx];
+
+    if (bufferInfo->skSurface.get() == nullptr) {
+        bufferInfo->skSurface =
+                SkSurface::MakeFromAHardwareBuffer(mGrContext,
+                        ANativeWindowBuffer_getHardwareBuffer(bufferInfo->buffer.get()),
+                        kTopLeft_GrSurfaceOrigin, DataSpaceToColorSpace(mWindowInfo.dataspace),
+                        nullptr);
+        if (bufferInfo->skSurface.get() == nullptr) {
+            ALOGE("SkSurface::MakeFromAHardwareBuffer failed");
+            mNativeWindow->cancelBuffer(mNativeWindow.get(), buffer, fence_fd);
+            return nullptr;
+        }
+    }
+
+    mDequeuedIndex = idx;
+    return bufferInfo;
+}
+
+bool VulkanSurface::presentCurrentBuffer(const SkRect& dirtyRect, int semaphoreFd) {
+    if (!dirtyRect.isEmpty()) {
+        SkRect transformedRect;
+        mWindowInfo.preTransform.mapRect(&transformedRect, dirtyRect);
+
+        SkIRect transformedIRect;
+        transformedRect.roundOut(&transformedIRect);
+        transformedIRect.intersect(0, 0, mWindowInfo.size.fWidth, mWindowInfo.size.fHeight);
+
+        // map to bottom-left coordinate system
+        android_native_rect_t aRect;
+        aRect.left = transformedIRect.x();
+        aRect.top = mWindowInfo.size.fHeight - (transformedIRect.y() + transformedIRect.height());
+        aRect.right = aRect.left + transformedIRect.width();
+        aRect.bottom = aRect.top - transformedIRect.height();
+
+        int err = native_window_set_surface_damage(mNativeWindow.get(), &aRect, 1);
+        ALOGE_IF(err != 0, "native_window_set_surface_damage failed: %s (%d)", strerror(-err), err);
+    }
+
+    VulkanSurface::NativeBufferInfo& currentBuffer = mNativeBuffers[mDequeuedIndex];
+    int queuedFd = (semaphoreFd != -1) ? semaphoreFd : currentBuffer.dequeue_fence;
+    int err = mNativeWindow->queueBuffer(mNativeWindow.get(), currentBuffer.buffer.get(), queuedFd);
+
+    currentBuffer.dequeued = false;
+    // queueBuffer always closes fence, even on error
+    if (err != 0) {
+        ALOGE("queueBuffer failed: %s (%d)", strerror(-err), err);
+        mNativeWindow->cancelBuffer(mNativeWindow.get(), currentBuffer.buffer.get(),
+                                    currentBuffer.dequeue_fence);
+    } else {
+        currentBuffer.hasValidContents = true;
+        currentBuffer.lastPresentedCount = mPresentCount;
+        mPresentCount++;
+    }
+
+    if (currentBuffer.dequeue_fence >= 0) {
+        close(currentBuffer.dequeue_fence);
+        currentBuffer.dequeue_fence = -1;
+    }
+
+    return err == 0;
+}
+
+int VulkanSurface::getCurrentBuffersAge() {
+    VulkanSurface::NativeBufferInfo& currentBuffer = mNativeBuffers[mDequeuedIndex];
+    return currentBuffer.hasValidContents ? (mPresentCount - currentBuffer.lastPresentedCount) : 0;
+}
+
+} /* namespace renderthread */
+} /* namespace uirenderer */
+} /* namespace android */
diff --git a/libs/hwui/renderthread/VulkanSurface.h b/libs/hwui/renderthread/VulkanSurface.h
new file mode 100644
index 0000000..4fd9cd2
--- /dev/null
+++ b/libs/hwui/renderthread/VulkanSurface.h
@@ -0,0 +1,127 @@
+/*
+ * Copyright (C) 2019 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.
+ */
+#pragma once
+
+#include <system/graphics.h>
+#include <system/window.h>
+#include <vulkan/vulkan.h>
+
+#include <SkSize.h>
+#include <SkRefCnt.h>
+
+#include "IRenderPipeline.h"
+
+class SkSurface;
+
+namespace android {
+namespace uirenderer {
+namespace renderthread {
+
+class VulkanManager;
+
+class VulkanSurface {
+public:
+    static VulkanSurface* Create(ANativeWindow* window,
+                                  ColorMode colorMode,
+                                  SkColorType colorType,
+                                  sk_sp<SkColorSpace> colorSpace,
+                                  GrContext* grContext,
+                                  const VulkanManager& vkManager);
+    ~VulkanSurface();
+
+    sk_sp<SkSurface> getCurrentSkSurface() { return mNativeBuffers[mDequeuedIndex].skSurface; }
+    const SkMatrix& getCurrentPreTransform() { return mWindowInfo.preTransform; }
+
+private:
+    /*
+     * All structs/methods in this private section are specifically for use by the VulkanManager
+     *
+     */
+    friend VulkanManager;
+    struct NativeBufferInfo {
+        sk_sp<SkSurface> skSurface;
+        sp<ANativeWindowBuffer> buffer;
+        // The fence is only valid when the buffer is dequeued, and should be
+        // -1 any other time. When valid, we own the fd, and must ensure it is
+        // closed: either by closing it explicitly when queueing the buffer,
+        // or by passing ownership e.g. to ANativeWindow::cancelBuffer().
+        int dequeue_fence = -1;
+        bool dequeued = false;
+        uint32_t lastPresentedCount = 0;
+        bool hasValidContents = false;
+    };
+
+    NativeBufferInfo* dequeueNativeBuffer();
+    NativeBufferInfo* getCurrentBufferInfo() { return &mNativeBuffers[mDequeuedIndex]; }
+    bool presentCurrentBuffer(const SkRect& dirtyRect, int semaphoreFd);
+
+    // The width and height are are the logical width and height for when submitting draws to the
+    // surface. In reality if the window is rotated the underlying window may have the width and
+    // height swapped.
+    int logicalWidth() const { return mWindowInfo.size.width(); }
+    int logicalHeight() const { return mWindowInfo.size.height(); }
+    int getCurrentBuffersAge();
+
+private:
+    /*
+     * All code below this line while logically available to VulkanManager should not be treated
+     * as private to this class.
+     *
+     */
+    static constexpr int sMaxBufferCount = 3;
+
+    struct WindowInfo {
+        SkISize size;
+        PixelFormat pixelFormat;
+        android_dataspace dataspace;
+        int transform;
+        int bufferCount;
+        uint64_t windowUsageFlags;
+
+        // size of the ANativeWindow if the inverse of transform requires us to swap width/height
+        SkISize actualSize;
+        // transform to be applied to the SkSurface to map the coordinates to the provided transform
+        SkMatrix preTransform;
+    };
+
+    VulkanSurface(ANativeWindow* window,
+                  const WindowInfo& windowInfo,
+                  SkISize minWindowSize,
+                  SkISize maxWindowSize,
+                  GrContext* grContext);
+    static bool UpdateWindow(ANativeWindow* window,
+                             const WindowInfo& windowInfo);
+    static void ComputeWindowSizeAndTransform(WindowInfo* windowInfo,
+                                              const SkISize& minSize,
+                                              const SkISize& maxSize);
+    void releaseBuffers();
+
+    NativeBufferInfo mNativeBuffers[VulkanSurface::sMaxBufferCount];
+
+    sp<ANativeWindow> mNativeWindow;
+    WindowInfo mWindowInfo;
+    GrContext* mGrContext;
+
+    int mDequeuedIndex = -1;
+    uint32_t mPresentCount = 0;
+
+    const SkISize mMinWindowSize;
+    const SkISize mMaxWindowSize;
+};
+
+} /* namespace renderthread */
+} /* namespace uirenderer */
+} /* namespace android */
\ No newline at end of file