Add back render-ahead support

Currently only supported in the EGL path.
Vulkan support Coming Soon

Bug: 127822449
Test: trace of hwuimacro
Change-Id: Iac2b039e11d964aab5b8ca1bdf2a5430b187e2ea
diff --git a/libs/hwui/renderthread/CanvasContext.cpp b/libs/hwui/renderthread/CanvasContext.cpp
index baa41c1..4808d68 100644
--- a/libs/hwui/renderthread/CanvasContext.cpp
+++ b/libs/hwui/renderthread/CanvasContext.cpp
@@ -111,6 +111,7 @@
     rootRenderNode->makeRoot();
     mRenderNodes.emplace_back(rootRenderNode);
     mProfiler.setDensity(mRenderThread.mainDisplayInfo().density);
+    setRenderAheadDepth(Properties::defaultRenderAhead);
 }
 
 CanvasContext::~CanvasContext() {
@@ -159,6 +160,7 @@
     if (hasSurface) {
         mHaveNewSurface = true;
         mSwapHistory.clear();
+        applyRenderAheadSettings();
     } else {
         mRenderThread.removeFrameCallback(this);
         mGenerationID++;
@@ -423,6 +425,12 @@
 
     waitOnFences();
 
+    if (mRenderAheadDepth) {
+        auto presentTime = mCurrentFrameInfo->get(FrameInfoIndex::Vsync) +
+                (mRenderThread.timeLord().frameIntervalNanos() * (mRenderAheadDepth + 1));
+        native_window_set_buffers_timestamp(mNativeSurface.get(), presentTime);
+    }
+
     bool requireSwap = false;
     bool didSwap =
             mRenderPipeline->swapBuffers(frame, drew, windowDirty, mCurrentFrameInfo, &requireSwap);
@@ -636,6 +644,28 @@
     return width == mLastFrameWidth && height == mLastFrameHeight;
 }
 
+void CanvasContext::applyRenderAheadSettings() {
+    if (Properties::getRenderPipelineType() == RenderPipelineType::SkiaVulkan) {
+        // TODO: Fix SkiaVulkan's assumptions on buffer counts. And SIGBUS crashes.
+        mRenderAheadDepth = 0;
+        return;
+    }
+    if (mNativeSurface) {
+        native_window_set_buffer_count(mNativeSurface.get(), 3 + mRenderAheadDepth);
+        if (!mRenderAheadDepth) {
+            native_window_set_buffers_timestamp(mNativeSurface.get(), NATIVE_WINDOW_TIMESTAMP_AUTO);
+        }
+    }
+}
+
+void CanvasContext::setRenderAheadDepth(int renderAhead) {
+    if (renderAhead < 0 || renderAhead > 2 || renderAhead == mRenderAheadDepth) {
+        return;
+    }
+    mRenderAheadDepth = renderAhead;
+    applyRenderAheadSettings();
+}
+
 SkRect CanvasContext::computeDirtyRect(const Frame& frame, SkRect* dirty) {
     if (frame.width() != mLastFrameWidth || frame.height() != mLastFrameHeight) {
         // can't rely on prior content of window if viewport size changes
diff --git a/libs/hwui/renderthread/CanvasContext.h b/libs/hwui/renderthread/CanvasContext.h
index abca342..4a3119a 100644
--- a/libs/hwui/renderthread/CanvasContext.h
+++ b/libs/hwui/renderthread/CanvasContext.h
@@ -204,6 +204,8 @@
         return mUseForceDark;
     }
 
+    void setRenderAheadDepth(int renderAhead);
+
 private:
     CanvasContext(RenderThread& thread, bool translucent, RenderNode* rootRenderNode,
                   IContextFactory* contextFactory, std::unique_ptr<IRenderPipeline> renderPipeline);
@@ -217,6 +219,7 @@
 
     bool isSwapChainStuffed();
     bool surfaceRequiresRedraw();
+    void applyRenderAheadSettings();
 
     SkRect computeDirtyRect(const Frame& frame, SkRect* dirty);
 
@@ -235,6 +238,7 @@
     // painted onto its surface.
     bool mIsDirty = false;
     SwapBehavior mSwapBehavior = SwapBehavior::kSwap_default;
+    int mRenderAheadDepth = 0;
     struct SwapHistory {
         SkRect damage;
         nsecs_t vsyncTime;
diff --git a/libs/hwui/renderthread/RenderProxy.cpp b/libs/hwui/renderthread/RenderProxy.cpp
index 16240b4..b58bab1 100644
--- a/libs/hwui/renderthread/RenderProxy.cpp
+++ b/libs/hwui/renderthread/RenderProxy.cpp
@@ -312,6 +312,12 @@
     mRenderThread.queue().post([this, enable]() { mContext->setForceDark(enable); });
 }
 
+void RenderProxy::setRenderAheadDepth(int renderAhead) {
+    mRenderThread.queue().post([ context = mContext, renderAhead ] {
+        context->setRenderAheadDepth(renderAhead);
+    });
+}
+
 int RenderProxy::copySurfaceInto(sp<Surface>& surface, int left, int top, int right, int bottom,
                                  SkBitmap* bitmap) {
     auto& thread = RenderThread::getInstance();
diff --git a/libs/hwui/renderthread/RenderProxy.h b/libs/hwui/renderthread/RenderProxy.h
index a1a5551..a0f08cb 100644
--- a/libs/hwui/renderthread/RenderProxy.h
+++ b/libs/hwui/renderthread/RenderProxy.h
@@ -123,6 +123,23 @@
     ANDROID_API void removeFrameMetricsObserver(FrameMetricsObserver* observer);
     ANDROID_API void setForceDark(bool enable);
 
+    /**
+     * Sets a render-ahead depth on the backing renderer. This will increase latency by
+     * <swapInterval> * renderAhead and increase memory usage by (3 + renderAhead) * <resolution>.
+     * In return the renderer will be less susceptible to jitter, resulting in a smoother animation.
+     *
+     * Not recommended to use in response to anything touch driven, but for canned animations
+     * where latency is not a concern careful use may be beneficial.
+     *
+     * Note that when increasing this there will be a frame gap of N frames where N is
+     * renderAhead - <current renderAhead>. When decreasing this if there are any pending
+     * frames they will retain their prior renderAhead value, so it will take a few frames
+     * for the decrease to flush through.
+     *
+     * @param renderAhead How far to render ahead, must be in the range [0..2]
+     */
+    ANDROID_API void setRenderAheadDepth(int renderAhead);
+
     ANDROID_API static int copySurfaceInto(sp<Surface>& surface, int left, int top, int right,
                                            int bottom, SkBitmap* bitmap);
     ANDROID_API static void prepareToDraw(Bitmap& bitmap);