Track buildLayer calls, destroy if unused

 Bug: 17208461

Change-Id: Ibdb104a493285d77a6891c5e74e38a52c7014da9
diff --git a/libs/hwui/RenderNode.cpp b/libs/hwui/RenderNode.cpp
index a79875e..cbb794e 100644
--- a/libs/hwui/RenderNode.cpp
+++ b/libs/hwui/RenderNode.cpp
@@ -34,6 +34,7 @@
 #include "LayerRenderer.h"
 #include "OpenGLRenderer.h"
 #include "utils/MathUtils.h"
+#include "renderthread/CanvasContext.h"
 
 namespace android {
 namespace uirenderer {
@@ -208,6 +209,13 @@
     if (info.renderer && mLayer->deferredUpdateScheduled) {
         info.renderer->pushLayerUpdate(mLayer);
     }
+
+    if (CC_UNLIKELY(info.canvasContext)) {
+        // If canvasContext is not null that means there are prefetched layers
+        // that need to be accounted for. That might be us, so tell CanvasContext
+        // that this layer is in the tree and should not be destroyed.
+        info.canvasContext->markLayerInUse(this);
+    }
 }
 
 void RenderNode::prepareTreeImpl(TreeInfo& info) {
diff --git a/libs/hwui/TreeInfo.h b/libs/hwui/TreeInfo.h
index e78d8bd..ae6ea94 100644
--- a/libs/hwui/TreeInfo.h
+++ b/libs/hwui/TreeInfo.h
@@ -26,6 +26,10 @@
 namespace android {
 namespace uirenderer {
 
+namespace renderthread {
+class CanvasContext;
+}
+
 class OpenGLRenderer;
 class RenderState;
 
@@ -59,6 +63,7 @@
         , renderState(renderState)
         , renderer(NULL)
         , errorHandler(NULL)
+        , canvasContext(NULL)
     {}
 
     explicit TreeInfo(TraversalMode mode, const TreeInfo& clone)
@@ -69,6 +74,7 @@
         , renderState(clone.renderState)
         , renderer(clone.renderer)
         , errorHandler(clone.errorHandler)
+        , canvasContext(clone.canvasContext)
     {}
 
     const TraversalMode mode;
@@ -89,6 +95,8 @@
     // layer updates or similar. May be NULL.
     OpenGLRenderer* renderer;
     ErrorHandler* errorHandler;
+    // TODO: Remove this? May be NULL
+    renderthread::CanvasContext* canvasContext;
 
     struct Out {
         Out()
diff --git a/libs/hwui/renderthread/CanvasContext.cpp b/libs/hwui/renderthread/CanvasContext.cpp
index d9fa0bc..ecfedf6 100644
--- a/libs/hwui/renderthread/CanvasContext.cpp
+++ b/libs/hwui/renderthread/CanvasContext.cpp
@@ -16,6 +16,7 @@
 
 #include "CanvasContext.h"
 
+#include <algorithm>
 #include <private/hwui/DrawGlInfo.h>
 #include <strings.h>
 
@@ -53,6 +54,7 @@
     destroyCanvasAndSurface();
     mRenderThread.removeFrameCallback(this);
     delete mAnimationContext;
+    freePrefetechedLayers();
 }
 
 void CanvasContext::destroyCanvasAndSurface() {
@@ -142,10 +144,17 @@
 
     info.damageAccumulator = &mDamageAccumulator;
     info.renderer = mCanvas;
+    if (mPrefetechedLayers.size() && info.mode == TreeInfo::MODE_FULL) {
+        info.canvasContext = this;
+    }
     mAnimationContext->startFrame();
     mRootRenderNode->prepareTree(info);
     mAnimationContext->runRemainingAnimations(info);
 
+    if (info.canvasContext) {
+        freePrefetechedLayers();
+    }
+
     int runningBehind = 0;
     // TODO: This query is moderately expensive, investigate adding some sort
     // of fast-path based off when we last called eglSwapBuffers() as well as
@@ -249,6 +258,26 @@
     thread.renderState().invokeFunctor(functor, mode, NULL);
 }
 
+void CanvasContext::markLayerInUse(RenderNode* node) {
+    if (mPrefetechedLayers.erase(node)) {
+        node->decStrong(0);
+    }
+}
+
+static void destroyPrefetechedNode(RenderNode* node) {
+    ALOGW("Incorrectly called buildLayer on View: %s, destroying layer...", node->getName());
+    node->destroyHardwareResources();
+    node->decStrong(0);
+}
+
+void CanvasContext::freePrefetechedLayers() {
+    if (mPrefetechedLayers.size()) {
+        requireGlContext();
+        std::for_each(mPrefetechedLayers.begin(), mPrefetechedLayers.end(), destroyPrefetechedNode);
+        mPrefetechedLayers.clear();
+    }
+}
+
 void CanvasContext::buildLayer(RenderNode* node) {
     ATRACE_CALL();
     if (!mEglManager.hasEglContext() || !mCanvas) {
@@ -270,6 +299,9 @@
     node->setPropertyFieldsDirty(RenderNode::GENERIC);
 
     mCanvas->flushLayerUpdates();
+
+    node->incStrong(0);
+    mPrefetechedLayers.insert(node);
 }
 
 bool CanvasContext::copyLayerInto(DeferredLayerUpdater* layer, SkBitmap* bitmap) {
diff --git a/libs/hwui/renderthread/CanvasContext.h b/libs/hwui/renderthread/CanvasContext.h
index 749da1b..7c27190 100644
--- a/libs/hwui/renderthread/CanvasContext.h
+++ b/libs/hwui/renderthread/CanvasContext.h
@@ -17,6 +17,8 @@
 #ifndef CANVASCONTEXT_H_
 #define CANVASCONTEXT_H_
 
+#include <set>
+
 #include <cutils/compiler.h>
 #include <EGL/egl.h>
 #include <SkBitmap.h>
@@ -71,6 +73,7 @@
 
     void buildLayer(RenderNode* node);
     bool copyLayerInto(DeferredLayerUpdater* layer, SkBitmap* bitmap);
+    void markLayerInUse(RenderNode* node);
 
     void destroyHardwareResources();
     static void trimMemory(RenderThread& thread, int level);
@@ -99,6 +102,8 @@
 
     void requireGlContext();
 
+    void freePrefetechedLayers();
+
     RenderThread& mRenderThread;
     EglManager& mEglManager;
     sp<ANativeWindow> mNativeWindow;
@@ -114,6 +119,8 @@
     const sp<RenderNode> mRootRenderNode;
 
     DrawProfiler mProfiler;
+
+    std::set<RenderNode*> mPrefetechedLayers;
 };
 
 } /* namespace renderthread */
diff --git a/libs/hwui/renderthread/EglManager.cpp b/libs/hwui/renderthread/EglManager.cpp
index 37f8e60..e030cdb 100644
--- a/libs/hwui/renderthread/EglManager.cpp
+++ b/libs/hwui/renderthread/EglManager.cpp
@@ -14,8 +14,6 @@
  * limitations under the License.
  */
 
-#define LOG_TAG "EglContext"
-
 #include "EglManager.h"
 
 #include <cutils/log.h>