Make buffer age work in Vulkan

Test: manual testing in skiavk mode

Change-Id: I5b9d8af7d9cecf2f022ef104ec33a5b7477e9e0c
diff --git a/libs/hwui/Android.mk b/libs/hwui/Android.mk
index cf571e9..21ebe5f 100644
--- a/libs/hwui/Android.mk
+++ b/libs/hwui/Android.mk
@@ -46,6 +46,7 @@
     renderthread/RenderTask.cpp \
     renderthread/RenderThread.cpp \
     renderthread/TimeLord.cpp \
+    renderthread/Frame.cpp \
     thread/TaskManager.cpp \
     utils/Blur.cpp \
     utils/GLUtils.cpp \
diff --git a/libs/hwui/hwui/Bitmap.cpp b/libs/hwui/hwui/Bitmap.cpp
index d3e765d..64ef866 100644
--- a/libs/hwui/hwui/Bitmap.cpp
+++ b/libs/hwui/hwui/Bitmap.cpp
@@ -16,6 +16,7 @@
 #include "Bitmap.h"
 
 #include "Caches.h"
+#include "renderthread/EglManager.h"
 #include "renderthread/RenderThread.h"
 #include "renderthread/RenderProxy.h"
 
diff --git a/libs/hwui/pipeline/skia/SkiaOpenGLPipeline.cpp b/libs/hwui/pipeline/skia/SkiaOpenGLPipeline.cpp
index 7f3474a..c8258f7 100644
--- a/libs/hwui/pipeline/skia/SkiaOpenGLPipeline.cpp
+++ b/libs/hwui/pipeline/skia/SkiaOpenGLPipeline.cpp
@@ -19,6 +19,7 @@
 #include "DeferredLayerUpdater.h"
 #include "LayerDrawable.h"
 #include "renderthread/EglManager.h"
+#include "renderthread/Frame.h"
 #include "renderstate/RenderState.h"
 #include "SkiaPipeline.h"
 #include "SkiaProfileRenderer.h"
diff --git a/libs/hwui/pipeline/skia/SkiaVulkanPipeline.cpp b/libs/hwui/pipeline/skia/SkiaVulkanPipeline.cpp
index ca394b2..0d3f4ef 100644
--- a/libs/hwui/pipeline/skia/SkiaVulkanPipeline.cpp
+++ b/libs/hwui/pipeline/skia/SkiaVulkanPipeline.cpp
@@ -17,7 +17,7 @@
 #include "SkiaVulkanPipeline.h"
 
 #include "DeferredLayerUpdater.h"
-#include "renderthread/EglManager.h" // needed for Frame
+#include "renderthread/Frame.h"
 #include "Readback.h"
 #include "renderstate/RenderState.h"
 #include "SkiaPipeline.h"
@@ -58,8 +58,7 @@
         return Frame(-1, -1, 0);
     }
 
-    // TODO: support buffer age if Vulkan API can do it
-    Frame frame(backBuffer->width(), backBuffer->height(), 0);
+    Frame frame(backBuffer->width(), backBuffer->height(), mVkManager.getAge(mVkSurface));
     return frame;
 }
 
diff --git a/libs/hwui/renderthread/CanvasContext.cpp b/libs/hwui/renderthread/CanvasContext.cpp
index c561c86..1b3bf96 100644
--- a/libs/hwui/renderthread/CanvasContext.cpp
+++ b/libs/hwui/renderthread/CanvasContext.cpp
@@ -20,6 +20,7 @@
 #include "AnimationContext.h"
 #include "Caches.h"
 #include "EglManager.h"
+#include "Frame.h"
 #include "LayerUpdateQueue.h"
 #include "Properties.h"
 #include "RenderThread.h"
diff --git a/libs/hwui/renderthread/EglManager.cpp b/libs/hwui/renderthread/EglManager.cpp
index de95bee..02021fc 100644
--- a/libs/hwui/renderthread/EglManager.cpp
+++ b/libs/hwui/renderthread/EglManager.cpp
@@ -19,6 +19,7 @@
 #include "Texture.h"
 #include "Caches.h"
 #include "DeviceInfo.h"
+#include "Frame.h"
 #include "Properties.h"
 #include "RenderThread.h"
 #include "renderstate/RenderState.h"
@@ -74,24 +75,6 @@
     bool setDamage = false;
 } EglExtensions;
 
-void Frame::map(const SkRect& in, EGLint* out) const {
-    /* The rectangles are specified relative to the bottom-left of the surface
-     * and the x and y components of each rectangle specify the bottom-left
-     * position of that rectangle.
-     *
-     * HWUI does everything with 0,0 being top-left, so need to map
-     * the rect
-     */
-    SkIRect idirty;
-    in.roundOut(&idirty);
-    EGLint y = mHeight - (idirty.y() + idirty.height());
-    // layout: {x, y, width, height}
-    out[0] = idirty.x();
-    out[1] = y;
-    out[2] = idirty.width();
-    out[3] = idirty.height();
-}
-
 EglManager::EglManager(RenderThread& thread)
         : mRenderThread(thread)
         , mEglDisplay(EGL_NO_DISPLAY)
diff --git a/libs/hwui/renderthread/EglManager.h b/libs/hwui/renderthread/EglManager.h
index b12522e..0251925 100644
--- a/libs/hwui/renderthread/EglManager.h
+++ b/libs/hwui/renderthread/EglManager.h
@@ -26,36 +26,8 @@
 namespace uirenderer {
 namespace renderthread {
 
+class Frame;
 class RenderThread;
-class EglManager;
-
-class Frame {
-public:
-    Frame(EGLint width, EGLint height, EGLint bufferAge)
-            : mWidth(width)
-            , mHeight(height)
-            , mBufferAge(bufferAge) { }
-
-    EGLint width() const { return mWidth; }
-    EGLint height() const { return mHeight; }
-
-    // See: https://www.khronos.org/registry/egl/extensions/EXT/EGL_EXT_buffer_age.txt
-    // for what this means
-    EGLint bufferAge() const { return mBufferAge; }
-
-private:
-    Frame() {}
-    friend class EglManager;
-
-    EGLSurface mSurface;
-    EGLint mWidth;
-    EGLint mHeight;
-    EGLint mBufferAge;
-
-    // Maps from 0,0 in top-left to 0,0 in bottom-left
-    // If out is not an EGLint[4] you're going to have a bad time
-    void map(const SkRect& in, EGLint* out) const;
-};
 
 // This class contains the shared global EGL objects, such as EGLDisplay
 // and EGLConfig, which are re-used by CanvasContext
diff --git a/libs/hwui/renderthread/Frame.cpp b/libs/hwui/renderthread/Frame.cpp
new file mode 100644
index 0000000..126bb09
--- /dev/null
+++ b/libs/hwui/renderthread/Frame.cpp
@@ -0,0 +1,44 @@
+/*
+ * Copyright (C) 2016 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 "Frame.h"
+#include <SkRect.h>
+
+namespace android {
+namespace uirenderer {
+namespace renderthread {
+
+void Frame::map(const SkRect& in, int32_t* out) const {
+    /* The rectangles are specified relative to the bottom-left of the surface
+     * and the x and y components of each rectangle specify the bottom-left
+     * position of that rectangle.
+     *
+     * HWUI does everything with 0,0 being top-left, so need to map
+     * the rect
+     */
+    SkIRect idirty;
+    in.roundOut(&idirty);
+    int32_t y = mHeight - (idirty.y() + idirty.height());
+    // layout: {x, y, width, height}
+    out[0] = idirty.x();
+    out[1] = y;
+    out[2] = idirty.width();
+    out[3] = idirty.height();
+}
+
+} /* namespace renderthread */
+} /* namespace uirenderer */
+} /* namespace android */
diff --git a/libs/hwui/renderthread/Frame.h b/libs/hwui/renderthread/Frame.h
new file mode 100644
index 0000000..99996fe
--- /dev/null
+++ b/libs/hwui/renderthread/Frame.h
@@ -0,0 +1,60 @@
+/*
+ * Copyright (C) 2016 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 <stdint.h>
+
+struct SkRect;
+typedef void *EGLSurface;
+
+namespace android {
+namespace uirenderer {
+namespace renderthread {
+
+class Frame {
+public:
+    Frame(int32_t width, int32_t height, int32_t bufferAge)
+            : mWidth(width)
+            , mHeight(height)
+            , mBufferAge(bufferAge) { }
+
+    int32_t width() const { return mWidth; }
+    int32_t height() const { return mHeight; }
+
+    // See: https://www.khronos.org/registry/egl/extensions/EXT/EGL_EXT_buffer_age.txt
+    // for what this means
+    int32_t bufferAge() const { return mBufferAge; }
+
+private:
+    Frame() {}
+    friend class EglManager;
+
+    int32_t mWidth;
+    int32_t mHeight;
+    int32_t mBufferAge;
+
+    EGLSurface mSurface;
+
+    // Maps from 0,0 in top-left to 0,0 in bottom-left
+    // If out is not an int32_t[4] you're going to have a bad time
+    void map(const SkRect& in, int32_t* out) const;
+};
+
+} /* namespace renderthread */
+} /* namespace uirenderer */
+} /* namespace android */
+
diff --git a/libs/hwui/renderthread/IRenderPipeline.h b/libs/hwui/renderthread/IRenderPipeline.h
index 0e4000b..45f6718 100644
--- a/libs/hwui/renderthread/IRenderPipeline.h
+++ b/libs/hwui/renderthread/IRenderPipeline.h
@@ -17,7 +17,6 @@
 #pragma once
 
 #include "FrameInfoVisualizer.h"
-#include "EglManager.h"
 
 #include <SkRect.h>
 #include <utils/RefBase.h>
diff --git a/libs/hwui/renderthread/OpenGLPipeline.cpp b/libs/hwui/renderthread/OpenGLPipeline.cpp
index 9dc2b59..df08599 100644
--- a/libs/hwui/renderthread/OpenGLPipeline.cpp
+++ b/libs/hwui/renderthread/OpenGLPipeline.cpp
@@ -18,6 +18,7 @@
 
 #include "DeferredLayerUpdater.h"
 #include "EglManager.h"
+#include "Frame.h"
 #include "ProfileRenderer.h"
 #include "renderstate/RenderState.h"
 #include "OpenGLReadback.h"
diff --git a/libs/hwui/renderthread/VulkanManager.cpp b/libs/hwui/renderthread/VulkanManager.cpp
index 4d239bc..68c04af 100644
--- a/libs/hwui/renderthread/VulkanManager.cpp
+++ b/libs/hwui/renderthread/VulkanManager.cpp
@@ -17,6 +17,7 @@
 #include "VulkanManager.h"
 
 #include "DeviceInfo.h"
+#include "Properties.h"
 #include "RenderThread.h"
 
 #include <GrContext.h>
@@ -100,6 +101,10 @@
     mRenderThread.setGrContext(GrContext::Create(kVulkan_GrBackend,
             (GrBackendContext) mBackendContext.get()));
     DeviceInfo::initialize(mRenderThread.getGrContext()->caps()->maxRenderTargetSize());
+
+    if (Properties::enablePartialUpdates && Properties::useBufferAge) {
+        mSwapBehavior = SwapBehavior::BufferAge;
+    }
 }
 
 // Returns the next BackbufferInfo to use for the next draw. The function will make sure all
@@ -162,7 +167,7 @@
     }
 
     // set up layout transfer from initial to color attachment
-    VkImageLayout layout = surface->mImageLayouts[backbuffer->mImageIndex];
+    VkImageLayout layout = surface->mImageInfos[backbuffer->mImageIndex].mImageLayout;
     SkASSERT(VK_IMAGE_LAYOUT_UNDEFINED == layout || VK_IMAGE_LAYOUT_PRESENT_SRC_KHR == layout);
     VkPipelineStageFlags srcStageMask = (VK_IMAGE_LAYOUT_UNDEFINED == layout) ?
                                         VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT :
@@ -215,7 +220,7 @@
 
     // We need to notify Skia that we changed the layout of the wrapped VkImage
     GrVkImageInfo* imageInfo;
-    sk_sp<SkSurface> skSurface = surface->mSurfaces[backbuffer->mImageIndex];
+    sk_sp<SkSurface> skSurface = surface->mImageInfos[backbuffer->mImageIndex].mSurface;
     skSurface->getRenderTargetHandle((GrBackendObject*)&imageInfo,
             SkSurface::kFlushRead_BackendHandleAccess);
     imageInfo->updateImageLayout(VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL);
@@ -243,10 +248,8 @@
 
     delete[] surface->mBackbuffers;
     surface->mBackbuffers = nullptr;
-    delete[] surface->mSurfaces;
-    surface->mSurfaces = nullptr;
-    delete[] surface->mImageLayouts;
-    surface->mImageLayouts = nullptr;
+    delete[] surface->mImageInfos;
+    surface->mImageInfos = nullptr;
     delete[] surface->mImages;
     surface->mImages = nullptr;
 }
@@ -286,8 +289,7 @@
     GrPixelConfig config = wantSRGB ? kSRGBA_8888_GrPixelConfig : kRGBA_8888_GrPixelConfig;
 
     // set up initial image layouts and create surfaces
-    surface->mImageLayouts = new VkImageLayout[surface->mImageCount];
-    surface->mSurfaces = new sk_sp<SkSurface>[surface->mImageCount];
+    surface->mImageInfos = new VulkanSurface::ImageInfo[surface->mImageCount];
     for (uint32_t i = 0; i < surface->mImageCount; ++i) {
         GrBackendRenderTargetDesc desc;
         GrVkImageInfo info;
@@ -306,9 +308,9 @@
         desc.fStencilBits = 0;
         desc.fRenderTargetHandle = (GrBackendObject) &info;
 
-        surface->mSurfaces[i] = SkSurface::MakeFromBackendRenderTarget(mRenderThread.getGrContext(),
+        VulkanSurface::ImageInfo& imageInfo = surface->mImageInfos[i];
+        imageInfo.mSurface = SkSurface::MakeFromBackendRenderTarget(mRenderThread.getGrContext(),
                 desc, &props);
-        surface->mImageLayouts[i] = VK_IMAGE_LAYOUT_UNDEFINED;
     }
 
     SkASSERT(mCommandPool != VK_NULL_HANDLE);
@@ -595,7 +597,7 @@
     VulkanSurface::BackbufferInfo* backbuffer = surface->mBackbuffers +
             surface->mCurrentBackbufferIndex;
     GrVkImageInfo* imageInfo;
-    SkSurface* skSurface = surface->mSurfaces[backbuffer->mImageIndex].get();
+    SkSurface* skSurface = surface->mImageInfos[backbuffer->mImageIndex].mSurface.get();
     skSurface->getRenderTargetHandle((GrBackendObject*)&imageInfo,
             SkSurface::kFlushRead_BackendHandleAccess);
     // Check to make sure we never change the actually wrapped image
@@ -632,7 +634,7 @@
             0, nullptr, 0, nullptr, 1, &imageMemoryBarrier);
     mEndCommandBuffer(backbuffer->mTransitionCmdBuffers[1]);
 
-    surface->mImageLayouts[backbuffer->mImageIndex] = VK_IMAGE_LAYOUT_PRESENT_SRC_KHR;
+    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;
@@ -668,6 +670,20 @@
     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) {
+    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;
 }
 
 } /* namespace renderthread */
diff --git a/libs/hwui/renderthread/VulkanManager.h b/libs/hwui/renderthread/VulkanManager.h
index f0e3320..d225b3f 100644
--- a/libs/hwui/renderthread/VulkanManager.h
+++ b/libs/hwui/renderthread/VulkanManager.h
@@ -46,6 +46,13 @@
         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;
@@ -56,8 +63,8 @@
 
     uint32_t mImageCount;
     VkImage* mImages;
-    VkImageLayout* mImageLayouts;
-    sk_sp<SkSurface>* mSurfaces;
+    ImageInfo* mImageInfos;
+    uint16_t mCurrentTime = 0;
 };
 
 // This class contains the shared global Vulkan objects, such as VkInstance, VkDevice and VkQueue,
@@ -87,6 +94,8 @@
     // 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.
@@ -161,6 +170,12 @@
     uint32_t mPresentQueueIndex;
     VkQueue mPresentQueue = VK_NULL_HANDLE;
     VkCommandPool mCommandPool = VK_NULL_HANDLE;
+
+    enum class SwapBehavior {
+        Discard,
+        BufferAge,
+    };
+    SwapBehavior mSwapBehavior = SwapBehavior::Discard;
 };
 
 } /* namespace renderthread */