Refactor pin/unpinImages to work across pipelines.

Test: existing CTS tests still pass
Change-Id: Ib2607e9853396bad42f298829b5c5da0d210af32
diff --git a/libs/hwui/DisplayList.cpp b/libs/hwui/DisplayList.cpp
index 5213d48..5e4a7f7 100644
--- a/libs/hwui/DisplayList.cpp
+++ b/libs/hwui/DisplayList.cpp
@@ -25,6 +25,7 @@
 #include "RecordedOp.h"
 #include "RenderNode.h"
 #include "VectorDrawable.h"
+#include "renderthread/CanvasContext.h"
 
 namespace android {
 namespace uirenderer {
@@ -105,11 +106,8 @@
 
 bool DisplayList::prepareListAndChildren(TreeInfo& info, bool functorsNeedLayer,
         std::function<void(RenderNode*, TreeInfo&, bool)> childFn) {
-    TextureCache& cache = Caches::getInstance().textureCache;
-    for (auto& bitmapResource : bitmapResources) {
-        void* ownerToken = &info.canvasContext;
-        info.prepareTextures = cache.prefetchAndMarkInUse(ownerToken, bitmapResource.get());
-    }
+    info.prepareTextures = info.canvasContext.pinImages(bitmapResources);
+
     for (auto&& op : children) {
         RenderNode* childNode = op->renderNode;
         info.damageAccumulator->pushTransform(&op->localMatrix);
diff --git a/libs/hwui/pipeline/skia/SkiaDisplayList.cpp b/libs/hwui/pipeline/skia/SkiaDisplayList.cpp
index c734097..4abaa90 100644
--- a/libs/hwui/pipeline/skia/SkiaDisplayList.cpp
+++ b/libs/hwui/pipeline/skia/SkiaDisplayList.cpp
@@ -20,7 +20,7 @@
 #include "VectorDrawable.h"
 
 #include <SkImagePriv.h>
-#include <SkMutex.h>
+
 
 namespace android {
 namespace uirenderer {
@@ -40,7 +40,7 @@
 }
 
 bool SkiaDisplayList::reuseDisplayList(RenderNode* node, renderthread::CanvasContext* context) {
-    reset(context ? context->getGrContext() : nullptr, SkRect::MakeEmpty());
+    reset(SkRect::MakeEmpty());
     node->attachAvailableList(this);
     return true;
 }
@@ -53,9 +53,12 @@
 
 bool SkiaDisplayList::prepareListAndChildren(TreeInfo& info, bool functorsNeedLayer,
         std::function<void(RenderNode*, TreeInfo&, bool)> childFn) {
-    // force all mutable images to be pinned in the GPU cache for the duration
-    // of this frame
-    pinImages(info.canvasContext.getGrContext());
+    // If the prepare tree is triggered by the UI thread then we must force all
+    // mutable images to be pinned in the GPU cache until the next UI thread
+    // draw
+    if (info.mode == TreeInfo::MODE_FULL) {
+        info.prepareTextures = info.canvasContext.pinImages(mMutableImages);
+    }
 
     for (auto& child : mChildNodes) {
         RenderNode* childNode = child.getRenderNode();
@@ -78,45 +81,7 @@
     return isDirty;
 }
 
-static std::vector<sk_sp<SkImage>> gPinnedImages;
-static SkBaseMutex gLock;
-
-void SkiaDisplayList::pinImages(GrContext* context) {
-    if (mPinnedImages) return;
-    for (SkImage* image : mMutableImages) {
-        SkImage_pinAsTexture(image, context);
-    }
-    mPinnedImages = true;
-}
-
-void SkiaDisplayList::unpinImages(GrContext* context) {
-    if (!mPinnedImages) return;
-    if (context) {
-        for (SkImage* image : mMutableImages) {
-            SkImage_unpinAsTexture(image, context);
-        }
-    } else {
-        gLock.acquire();
-        for (SkImage* image : mMutableImages) {
-            gPinnedImages.emplace_back(sk_ref_sp(image));
-        }
-        gLock.release();
-    }
-    mPinnedImages = false;
-}
-
-void SkiaDisplayList::cleanupImages(GrContext* context) {
-    gLock.acquire();
-    for (auto& image : gPinnedImages) {
-        SkImage_unpinAsTexture(image.get(), context);
-    }
-    gPinnedImages.clear();
-    gLock.release();
-}
-
-void SkiaDisplayList::reset(GrContext* context, SkRect bounds) {
-    unpinImages(context);
-    SkASSERT(!mPinnedImages);
+void SkiaDisplayList::reset(SkRect bounds) {
     mIsProjectionReceiver = false;
 
     mDrawable->reset(bounds);
diff --git a/libs/hwui/pipeline/skia/SkiaDisplayList.h b/libs/hwui/pipeline/skia/SkiaDisplayList.h
index 734aae4a..f34b485 100644
--- a/libs/hwui/pipeline/skia/SkiaDisplayList.h
+++ b/libs/hwui/pipeline/skia/SkiaDisplayList.h
@@ -51,7 +51,7 @@
      * constructed with the provided bounds.  The reuse avoids any overhead
      * associated with destroying the SkLiteDL as well as the deques and vectors.
      */
-    void reset(GrContext* context, SkRect bounds);
+    void reset(SkRect bounds);
 
     /**
      * Use the linear allocator to create any SkDrawables needed by the display
@@ -119,21 +119,6 @@
     void updateChildren(std::function<void(RenderNode*)> updateFn) override;
 
     /**
-     * Pin/Unpin any mutable images to the GPU cache. A pinned images is
-     * guaranteed to be remain in the cache until it has been unpinned which
-     * we leverage to avoid making a CPU copy of the pixels.
-     */
-    void pinImages(GrContext* context);
-    void unpinImages(GrContext* context);
-
-    /**
-     * If a SkiaDisplayList is deleted on the UI thread we cache a list of any
-     * images that need unpinned from the GPU cache and call this function on
-     * a subsequent frame to perform that cleanup.
-     */
-    static void cleanupImages(GrContext* context);
-
-    /**
      * We use std::deque here because (1) we need to iterate through these
      * elements and (2) mDrawable holds pointers to the elements, so they cannot
      * relocate.
@@ -145,9 +130,6 @@
     sk_sp<SkLiteDL> mDrawable;
 
     bool mIsProjectionReceiver = false;
-
-private:
-    bool mPinnedImages = false;
 };
 
 }; // namespace skiapipeline
diff --git a/libs/hwui/pipeline/skia/SkiaPipeline.cpp b/libs/hwui/pipeline/skia/SkiaPipeline.cpp
index 03fa266..69e603b 100644
--- a/libs/hwui/pipeline/skia/SkiaPipeline.cpp
+++ b/libs/hwui/pipeline/skia/SkiaPipeline.cpp
@@ -46,6 +46,22 @@
     // which will flush temporary resources over time.
 }
 
+bool SkiaPipeline::pinImages(std::vector<SkImage*>& mutableImages) {
+    for (SkImage* image : mutableImages) {
+        mPinnedImages.emplace_back(sk_ref_sp(image));
+        // TODO: return false if texture creation fails (see b/32691999)
+        SkImage_pinAsTexture(image, mRenderThread.getGrContext());
+    }
+    return true;
+}
+
+void SkiaPipeline::unpinImages() {
+    for (auto& image : mPinnedImages) {
+        SkImage_unpinAsTexture(image.get(), mRenderThread.getGrContext());
+    }
+    mPinnedImages.clear();
+}
+
 void SkiaPipeline::renderLayers(const FrameBuilder::LightGeometry& lightGeometry,
         LayerUpdateQueue* layerUpdateQueue, bool opaque,
         const BakedOpRenderer::LightInfo& lightInfo) {
@@ -154,9 +170,6 @@
         const std::vector<sp<RenderNode>>& nodes, bool opaque, const Rect &contentDrawBounds,
         sk_sp<SkSurface> surface) {
 
-    // unpin all mutable images that were attached to nodes deleted while on the UI thread
-    SkiaDisplayList::cleanupImages(surface->getCanvas()->getGrContext());
-
     // draw all layers up front
     renderLayersImpl(layers, opaque);
 
diff --git a/libs/hwui/pipeline/skia/SkiaPipeline.h b/libs/hwui/pipeline/skia/SkiaPipeline.h
index 160046a..877a353 100644
--- a/libs/hwui/pipeline/skia/SkiaPipeline.h
+++ b/libs/hwui/pipeline/skia/SkiaPipeline.h
@@ -34,6 +34,10 @@
 
     void onDestroyHardwareResources() override;
 
+    bool pinImages(std::vector<SkImage*>& mutableImages) override;
+    bool pinImages(LsaVector<sk_sp<Bitmap>>& images) override { return false; }
+    void unpinImages() override;
+
     void renderLayers(const FrameBuilder::LightGeometry& lightGeometry,
             LayerUpdateQueue* layerUpdateQueue, bool opaque,
             const BakedOpRenderer::LightInfo& lightInfo) override;
@@ -101,6 +105,7 @@
 
 private:
     TaskManager mTaskManager;
+    std::vector<sk_sp<SkImage>> mPinnedImages;
     static float mLightRadius;
     static uint8_t mAmbientShadowAlpha;
     static uint8_t mSpotShadowAlpha;
diff --git a/libs/hwui/pipeline/skia/SkiaRecordingCanvas.cpp b/libs/hwui/pipeline/skia/SkiaRecordingCanvas.cpp
index ecc6d51..621816a 100644
--- a/libs/hwui/pipeline/skia/SkiaRecordingCanvas.cpp
+++ b/libs/hwui/pipeline/skia/SkiaRecordingCanvas.cpp
@@ -41,7 +41,7 @@
     }
     SkRect bounds = SkRect::MakeWH(width, height);
     if (mDisplayList) {
-        mDisplayList->reset(nullptr, bounds);
+        mDisplayList->reset(bounds);
     } else {
         mDisplayList.reset(new SkiaDisplayList(bounds));
     }
diff --git a/libs/hwui/renderthread/CanvasContext.h b/libs/hwui/renderthread/CanvasContext.h
index b61eef2..c322efb 100644
--- a/libs/hwui/renderthread/CanvasContext.h
+++ b/libs/hwui/renderthread/CanvasContext.h
@@ -80,6 +80,26 @@
     }
 
     /**
+     * Pin any mutable images to the GPU cache. A pinned images is guaranteed to
+     * remain in the cache until it has been unpinned. We leverage this feature
+     * to avoid making a CPU copy of the pixels.
+     *
+     * @return true if the images have been successfully pinned to the GPU cache
+     *         and false otherwise (e.g. cache limits have been exceeded).
+     */
+    bool pinImages(std::vector<SkImage*>& mutableImages) {
+        return mRenderPipeline->pinImages(mutableImages);
+    }
+    bool pinImages(LsaVector<sk_sp<Bitmap>>& images) {
+        return mRenderPipeline->pinImages(images);
+    }
+
+    /**
+     * Unpin any image that had be previously pinned to the GPU cache
+     */
+    void unpinImages() { mRenderPipeline->unpinImages(); }
+
+    /**
      * Destroy any layers that have been attached to the provided RenderNode removing
      * any state that may have been set during createOrUpdateLayer().
      */
diff --git a/libs/hwui/renderthread/DrawFrameTask.cpp b/libs/hwui/renderthread/DrawFrameTask.cpp
index e3b6dc6..4ff54a5 100644
--- a/libs/hwui/renderthread/DrawFrameTask.cpp
+++ b/libs/hwui/renderthread/DrawFrameTask.cpp
@@ -119,7 +119,7 @@
     int64_t vsync = mFrameInfo[static_cast<int>(FrameInfoIndex::Vsync)];
     mRenderThread->timeLord().vsyncReceived(vsync);
     bool canDraw = mContext->makeCurrent();
-    Caches::getInstance().textureCache.resetMarkInUse(mContext);
+    mContext->unpinImages();
 
     for (size_t i = 0; i < mLayers.size(); i++) {
         mLayers[i]->apply();
diff --git a/libs/hwui/renderthread/IRenderPipeline.h b/libs/hwui/renderthread/IRenderPipeline.h
index 52894ad..0e4000b 100644
--- a/libs/hwui/renderthread/IRenderPipeline.h
+++ b/libs/hwui/renderthread/IRenderPipeline.h
@@ -73,6 +73,9 @@
     virtual TaskManager* getTaskManager() = 0;
     virtual bool createOrUpdateLayer(RenderNode* node,
             const DamageAccumulator& damageAccumulator) = 0;
+    virtual bool pinImages(std::vector<SkImage*>& mutableImages) = 0;
+    virtual bool pinImages(LsaVector<sk_sp<Bitmap>>& images) = 0;
+    virtual void unpinImages() = 0;
 
     virtual ~IRenderPipeline() {}
 };
diff --git a/libs/hwui/renderthread/OpenGLPipeline.cpp b/libs/hwui/renderthread/OpenGLPipeline.cpp
index cca0fca..afeeef8 100644
--- a/libs/hwui/renderthread/OpenGLPipeline.cpp
+++ b/libs/hwui/renderthread/OpenGLPipeline.cpp
@@ -31,7 +31,8 @@
 namespace renderthread {
 
 OpenGLPipeline::OpenGLPipeline(RenderThread& thread)
-        :  mEglManager(thread.eglManager()), mRenderThread(thread) {
+        :  mEglManager(thread.eglManager())
+        , mRenderThread(thread) {
 }
 
 MakeCurrentResult OpenGLPipeline::makeCurrent() {
@@ -222,6 +223,19 @@
     return transformUpdateNeeded;
 }
 
+bool OpenGLPipeline::pinImages(LsaVector<sk_sp<Bitmap>>& images) {
+    TextureCache& cache = Caches::getInstance().textureCache;
+    bool prefetchSucceeded = true;
+    for (auto& bitmapResource : images) {
+        prefetchSucceeded &= cache.prefetchAndMarkInUse(this, bitmapResource.get());
+    }
+    return prefetchSucceeded;
+}
+
+void OpenGLPipeline::unpinImages() {
+    Caches::getInstance().textureCache.resetMarkInUse(this);
+}
+
 void OpenGLPipeline::destroyLayer(RenderNode* node) {
     if (OffscreenBuffer* layer = node->getLayer()) {
         layer->renderState.layerPool().putOrDelete(layer);
diff --git a/libs/hwui/renderthread/OpenGLPipeline.h b/libs/hwui/renderthread/OpenGLPipeline.h
index 8722d59..6df8be4 100644
--- a/libs/hwui/renderthread/OpenGLPipeline.h
+++ b/libs/hwui/renderthread/OpenGLPipeline.h
@@ -55,6 +55,9 @@
     TaskManager* getTaskManager() override;
     bool createOrUpdateLayer(RenderNode* node,
             const DamageAccumulator& damageAccumulator) override;
+    bool pinImages(std::vector<SkImage*>& mutableImages) override { return false; }
+    bool pinImages(LsaVector<sk_sp<Bitmap>>& images) override;
+    void unpinImages() override;
     static void destroyLayer(RenderNode* node);
     static void prepareToDraw(const RenderThread& thread, Bitmap* bitmap);
     static void invokeFunctor(const RenderThread& thread, Functor* functor);
diff --git a/libs/hwui/tests/unit/SkiaDisplayListTests.cpp b/libs/hwui/tests/unit/SkiaDisplayListTests.cpp
index fe6cea6..67fb78a 100644
--- a/libs/hwui/tests/unit/SkiaDisplayListTests.cpp
+++ b/libs/hwui/tests/unit/SkiaDisplayListTests.cpp
@@ -58,7 +58,7 @@
     ASSERT_TRUE(skiaDL.mIsProjectionReceiver);
 
     bounds = SkRect::MakeWH(100, 100);
-    skiaDL.reset(nullptr, bounds);
+    skiaDL.reset(bounds);
 
     ASSERT_EQ(skiaDL.mDrawable->getBounds(), bounds);
     ASSERT_TRUE(skiaDL.mChildNodes.empty());