Avoid excessive KGSL maps

Hook MIN_UNDEQUEUED_BUFFERS if possible to avoid thrashing kgsl
maps when render_ahead is being used

Bug: 143555869
Test: verified kgsl maps only happened once per buffer
Change-Id: I985fae0a9a7635be3a1cf6177186e5541a1169df
diff --git a/libs/hwui/pipeline/skia/SkiaOpenGLPipeline.cpp b/libs/hwui/pipeline/skia/SkiaOpenGLPipeline.cpp
index 8d5acc6..24a6228 100644
--- a/libs/hwui/pipeline/skia/SkiaOpenGLPipeline.cpp
+++ b/libs/hwui/pipeline/skia/SkiaOpenGLPipeline.cpp
@@ -157,21 +157,7 @@
     }
 }
 
-static void setBufferCount(ANativeWindow* window, uint32_t extraBuffers) {
-    int query_value;
-    int err = window->query(window, NATIVE_WINDOW_MIN_UNDEQUEUED_BUFFERS, &query_value);
-    if (err != 0 || query_value < 0) {
-        ALOGE("window->query failed: %s (%d) value=%d", strerror(-err), err, query_value);
-        return;
-    }
-    auto min_undequeued_buffers = static_cast<uint32_t>(query_value);
-
-    int bufferCount = min_undequeued_buffers + 2 + extraBuffers;
-    native_window_set_buffer_count(window, bufferCount);
-}
-
-bool SkiaOpenGLPipeline::setSurface(ANativeWindow* surface, SwapBehavior swapBehavior,
-                                    uint32_t extraBuffers) {
+bool SkiaOpenGLPipeline::setSurface(ANativeWindow* surface, SwapBehavior swapBehavior) {
     if (mEglSurface != EGL_NO_SURFACE) {
         mEglManager.destroySurface(mEglSurface);
         mEglSurface = EGL_NO_SURFACE;
@@ -189,7 +175,6 @@
     if (mEglSurface != EGL_NO_SURFACE) {
         const bool preserveBuffer = (swapBehavior != SwapBehavior::kSwap_discardBuffer);
         mBufferPreserved = mEglManager.setPreserveBuffer(mEglSurface, preserveBuffer);
-        setBufferCount(surface, extraBuffers);
         return true;
     }
 
diff --git a/libs/hwui/pipeline/skia/SkiaOpenGLPipeline.h b/libs/hwui/pipeline/skia/SkiaOpenGLPipeline.h
index fc6e114..fddd97f 100644
--- a/libs/hwui/pipeline/skia/SkiaOpenGLPipeline.h
+++ b/libs/hwui/pipeline/skia/SkiaOpenGLPipeline.h
@@ -45,8 +45,7 @@
     bool swapBuffers(const renderthread::Frame& frame, bool drew, const SkRect& screenDirty,
                      FrameInfo* currentFrameInfo, bool* requireSwap) override;
     DeferredLayerUpdater* createTextureLayer() override;
-    bool setSurface(ANativeWindow* surface, renderthread::SwapBehavior swapBehavior,
-                    uint32_t extraBuffers) override;
+    bool setSurface(ANativeWindow* surface, renderthread::SwapBehavior swapBehavior) override;
     void onStop() override;
     bool isSurfaceReady() override;
     bool isContextReady() override;
diff --git a/libs/hwui/pipeline/skia/SkiaVulkanPipeline.cpp b/libs/hwui/pipeline/skia/SkiaVulkanPipeline.cpp
index 535a199..212a428 100644
--- a/libs/hwui/pipeline/skia/SkiaVulkanPipeline.cpp
+++ b/libs/hwui/pipeline/skia/SkiaVulkanPipeline.cpp
@@ -116,8 +116,7 @@
 
 void SkiaVulkanPipeline::onStop() {}
 
-bool SkiaVulkanPipeline::setSurface(ANativeWindow* surface, SwapBehavior swapBehavior,
-                                    uint32_t extraBuffers) {
+bool SkiaVulkanPipeline::setSurface(ANativeWindow* surface, SwapBehavior swapBehavior) {
     if (mVkSurface) {
         mVkManager.destroySurface(mVkSurface);
         mVkSurface = nullptr;
@@ -127,7 +126,7 @@
         mRenderThread.requireVkContext();
         mVkSurface =
                 mVkManager.createSurface(surface, mColorMode, mSurfaceColorSpace, mSurfaceColorType,
-                                         mRenderThread.getGrContext(), extraBuffers);
+                                         mRenderThread.getGrContext(), 0);
     }
 
     return mVkSurface != nullptr;
diff --git a/libs/hwui/pipeline/skia/SkiaVulkanPipeline.h b/libs/hwui/pipeline/skia/SkiaVulkanPipeline.h
index c8bf233..6268daa 100644
--- a/libs/hwui/pipeline/skia/SkiaVulkanPipeline.h
+++ b/libs/hwui/pipeline/skia/SkiaVulkanPipeline.h
@@ -42,8 +42,7 @@
     bool swapBuffers(const renderthread::Frame& frame, bool drew, const SkRect& screenDirty,
                      FrameInfo* currentFrameInfo, bool* requireSwap) override;
     DeferredLayerUpdater* createTextureLayer() override;
-    bool setSurface(ANativeWindow* surface, renderthread::SwapBehavior swapBehavior,
-                    uint32_t extraBuffers) override;
+    bool setSurface(ANativeWindow* surface, renderthread::SwapBehavior swapBehavior) override;
     void onStop() override;
     bool isSurfaceReady() override;
     bool isContextReady() override;
diff --git a/libs/hwui/renderthread/CanvasContext.cpp b/libs/hwui/renderthread/CanvasContext.cpp
index 335bcdc..a362bd2 100644
--- a/libs/hwui/renderthread/CanvasContext.cpp
+++ b/libs/hwui/renderthread/CanvasContext.cpp
@@ -139,20 +139,22 @@
     mAnimationContext->destroy();
 }
 
+static void setBufferCount(ANativeWindow* window, uint32_t extraBuffers) {
+    int query_value;
+    int err = window->query(window, NATIVE_WINDOW_MIN_UNDEQUEUED_BUFFERS, &query_value);
+    if (err != 0 || query_value < 0) {
+        ALOGE("window->query failed: %s (%d) value=%d", strerror(-err), err, query_value);
+        return;
+    }
+    auto min_undequeued_buffers = static_cast<uint32_t>(query_value);
+
+    int bufferCount = min_undequeued_buffers + 2 + extraBuffers;
+    native_window_set_buffer_count(window, bufferCount);
+}
+
 void CanvasContext::setSurface(ANativeWindow* window, bool enableTimeout) {
     ATRACE_CALL();
 
-    if (window) {
-        mNativeSurface = std::make_unique<ReliableSurface>(window);
-        mNativeSurface->init();
-        if (enableTimeout) {
-            // TODO: Fix error handling & re-shorten timeout
-            ANativeWindow_setDequeueTimeout(window, 4000_ms);
-        }
-    } else {
-        mNativeSurface = nullptr;
-    }
-
     if (mRenderAheadDepth == 0 && DeviceInfo::get()->getMaxRefreshRate() > 66.6f) {
         mFixedRenderAhead = false;
         mRenderAheadCapacity = 1;
@@ -161,9 +163,24 @@
         mRenderAheadCapacity = mRenderAheadDepth;
     }
 
+    if (window) {
+        mNativeSurface = std::make_unique<ReliableSurface>(window);
+        mNativeSurface->init();
+        if (enableTimeout) {
+            // TODO: Fix error handling & re-shorten timeout
+            ANativeWindow_setDequeueTimeout(window, 4000_ms);
+        }
+        mNativeSurface->setExtraBufferCount(mRenderAheadCapacity);
+    } else {
+        mNativeSurface = nullptr;
+    }
+
     bool hasSurface = mRenderPipeline->setSurface(
-            mNativeSurface ? mNativeSurface->getNativeWindow() : nullptr, mSwapBehavior,
-            mRenderAheadCapacity);
+            mNativeSurface ? mNativeSurface->getNativeWindow() : nullptr, mSwapBehavior);
+
+    if (mNativeSurface && !mNativeSurface->didSetExtraBuffers()) {
+        setBufferCount(mNativeSurface->getNativeWindow(), mRenderAheadCapacity);
+    }
 
     mFrameNumber = -1;
 
diff --git a/libs/hwui/renderthread/IRenderPipeline.h b/libs/hwui/renderthread/IRenderPipeline.h
index ba0d64c..c3c2286 100644
--- a/libs/hwui/renderthread/IRenderPipeline.h
+++ b/libs/hwui/renderthread/IRenderPipeline.h
@@ -66,8 +66,7 @@
     virtual bool swapBuffers(const Frame& frame, bool drew, const SkRect& screenDirty,
                              FrameInfo* currentFrameInfo, bool* requireSwap) = 0;
     virtual DeferredLayerUpdater* createTextureLayer() = 0;
-    virtual bool setSurface(ANativeWindow* window, SwapBehavior swapBehavior,
-                            uint32_t extraBuffers) = 0;
+    virtual bool setSurface(ANativeWindow* window, SwapBehavior swapBehavior) = 0;
     virtual void onStop() = 0;
     virtual bool isSurfaceReady() = 0;
     virtual bool isContextReady() = 0;
diff --git a/libs/hwui/renderthread/ReliableSurface.cpp b/libs/hwui/renderthread/ReliableSurface.cpp
index 8a0b4e8..dcf1fc1 100644
--- a/libs/hwui/renderthread/ReliableSurface.cpp
+++ b/libs/hwui/renderthread/ReliableSurface.cpp
@@ -19,6 +19,7 @@
 #include <log/log_main.h>
 #include <private/android/AHardwareBufferHelpers.h>
 // TODO: this should be including apex instead.
+#include <system/window.h>
 #include <vndk/window.h>
 
 namespace android::uirenderer::renderthread {
@@ -44,6 +45,7 @@
     ANativeWindow_setDequeueBufferInterceptor(mWindow, nullptr, nullptr);
     ANativeWindow_setQueueBufferInterceptor(mWindow, nullptr, nullptr);
     ANativeWindow_setPerformInterceptor(mWindow, nullptr, nullptr);
+    ANativeWindow_setQueryInterceptor(mWindow, nullptr, nullptr);
     ANativeWindow_release(mWindow);
 }
 
@@ -63,6 +65,10 @@
     result = ANativeWindow_setPerformInterceptor(mWindow, hook_perform, this);
     LOG_ALWAYS_FATAL_IF(result != NO_ERROR, "Failed to set perform interceptor: error = %d",
                         result);
+
+    result = ANativeWindow_setQueryInterceptor(mWindow, hook_query, this);
+    LOG_ALWAYS_FATAL_IF(result != NO_ERROR, "Failed to set query interceptor: error = %d",
+                        result);
 }
 
 int ReliableSurface::reserveNext() {
@@ -249,9 +255,29 @@
             case ANATIVEWINDOW_PERFORM_SET_BUFFERS_FORMAT:
                 rs->mFormat = static_cast<AHardwareBuffer_Format>(va_arg(args, int32_t));
                 break;
+            case NATIVE_WINDOW_SET_BUFFER_COUNT:
+                size_t bufferCount = va_arg(args, size_t);
+                if (bufferCount >= rs->mExpectedBufferCount) {
+                    rs->mDidSetExtraBuffers = true;
+                } else {
+                    ALOGD("HOOK FAILED! Expected %zd got = %zd", rs->mExpectedBufferCount, bufferCount);
+                }
+                break;
         }
     }
     return result;
 }
 
+int ReliableSurface::hook_query(const ANativeWindow *window, ANativeWindow_queryFn query,
+        void *data, int what, int *value) {
+    ReliableSurface* rs = reinterpret_cast<ReliableSurface*>(data);
+    int result = query(window, what, value);
+    if (what == ANATIVEWINDOW_QUERY_MIN_UNDEQUEUED_BUFFERS && result == OK) {
+        std::lock_guard _lock{rs->mMutex};
+        *value += rs->mExtraBuffers;
+        rs->mExpectedBufferCount = *value + 2;
+    }
+    return result;
+}
+
 };  // namespace android::uirenderer::renderthread
diff --git a/libs/hwui/renderthread/ReliableSurface.h b/libs/hwui/renderthread/ReliableSurface.h
index 58cd067..f699eb1 100644
--- a/libs/hwui/renderthread/ReliableSurface.h
+++ b/libs/hwui/renderthread/ReliableSurface.h
@@ -17,6 +17,7 @@
 #pragma once
 
 #include <android-base/unique_fd.h>
+#include <system/window.h>
 #include <apex/window.h>
 #include <utils/Errors.h>
 #include <utils/Macros.h>
@@ -49,6 +50,16 @@
         return ret;
     }
 
+    void setExtraBufferCount(size_t extraBuffers) {
+        std::lock_guard _lock{mMutex};
+        mExtraBuffers = extraBuffers;
+    }
+
+    bool didSetExtraBuffers() const {
+        std::lock_guard _lock{mMutex};
+        return mDidSetExtraBuffers;
+    }
+
 private:
     ANativeWindow* mWindow;
 
@@ -62,6 +73,9 @@
     base::unique_fd mReservedFenceFd;
     bool mHasDequeuedBuffer = false;
     int mBufferQueueState = OK;
+    size_t mExtraBuffers = 0;
+    size_t mExpectedBufferCount = 0;
+    bool mDidSetExtraBuffers = false;
 
     bool isFallbackBuffer(const ANativeWindowBuffer* windowBuffer) const;
     ANativeWindowBuffer* acquireFallbackBuffer(int error);
@@ -81,6 +95,8 @@
 
     static int hook_perform(ANativeWindow* window, ANativeWindow_performFn perform, void* data,
                             int operation, va_list args);
+    static int hook_query(const ANativeWindow* window, ANativeWindow_queryFn query, void* data,
+            int what, int* value);
 };
 
 };  // namespace android::uirenderer::renderthread
diff --git a/libs/hwui/tests/unit/SkiaPipelineTests.cpp b/libs/hwui/tests/unit/SkiaPipelineTests.cpp
index 1208062..e7a889d 100644
--- a/libs/hwui/tests/unit/SkiaPipelineTests.cpp
+++ b/libs/hwui/tests/unit/SkiaPipelineTests.cpp
@@ -398,7 +398,7 @@
     auto surface = context.surface();
     auto pipeline = std::make_unique<SkiaOpenGLPipeline>(renderThread);
     EXPECT_FALSE(pipeline->isSurfaceReady());
-    EXPECT_TRUE(pipeline->setSurface(surface.get(), SwapBehavior::kSwap_default, 0));
+    EXPECT_TRUE(pipeline->setSurface(surface.get(), SwapBehavior::kSwap_default));
     EXPECT_TRUE(pipeline->isSurfaceReady());
     renderThread.destroyRenderingContext();
     EXPECT_FALSE(pipeline->isSurfaceReady());