Fix root RenderNode damage calculation

 Bug: 15888445

Change-Id: I281ec9271c9889673dcdfcb6d31e341a7b47b7de
diff --git a/core/java/android/view/HardwareRenderer.java b/core/java/android/view/HardwareRenderer.java
index f2f363a..8c9b819 100644
--- a/core/java/android/view/HardwareRenderer.java
+++ b/core/java/android/view/HardwareRenderer.java
@@ -327,6 +327,12 @@
     }
 
     /**
+     *  Indicates that the content drawn by HardwareDrawCallbacks needs to
+     *  be updated, which will be done by the next call to draw()
+     */
+    abstract void invalidateRoot();
+
+    /**
      * Draws the specified view.
      *
      * @param view The view to draw.
diff --git a/core/java/android/view/ThreadedRenderer.java b/core/java/android/view/ThreadedRenderer.java
index e2ebf6e..b033780 100644
--- a/core/java/android/view/ThreadedRenderer.java
+++ b/core/java/android/view/ThreadedRenderer.java
@@ -96,6 +96,7 @@
     private RenderNode mRootNode;
     private Choreographer mChoreographer;
     private boolean mProfilingEnabled;
+    private boolean mRootNodeNeedsUpdate;
 
     ThreadedRenderer(Context context, boolean translucent) {
         final TypedArray a = context.obtainStyledAttributes(
@@ -255,30 +256,41 @@
         return changed;
     }
 
-    private void updateRootDisplayList(View view, HardwareDrawCallbacks callbacks) {
+    private void updateViewTreeDisplayList(View view) {
         view.mPrivateFlags |= View.PFLAG_DRAWN;
-
         view.mRecreateDisplayList = (view.mPrivateFlags & View.PFLAG_INVALIDATED)
                 == View.PFLAG_INVALIDATED;
         view.mPrivateFlags &= ~View.PFLAG_INVALIDATED;
-
-        Trace.traceBegin(Trace.TRACE_TAG_VIEW, "getDisplayList");
-        HardwareCanvas canvas = mRootNode.start(mSurfaceWidth, mSurfaceHeight);
-        try {
-            canvas.save();
-            canvas.translate(mInsetLeft, mInsetTop);
-            callbacks.onHardwarePreDraw(canvas);
-            canvas.drawRenderNode(view.getDisplayList());
-            callbacks.onHardwarePostDraw(canvas);
-            canvas.restore();
-        } finally {
-            mRootNode.end(canvas);
-            Trace.traceEnd(Trace.TRACE_TAG_VIEW);
-        }
-
+        view.getDisplayList();
         view.mRecreateDisplayList = false;
     }
 
+    private void updateRootDisplayList(View view, HardwareDrawCallbacks callbacks) {
+        Trace.traceBegin(Trace.TRACE_TAG_VIEW, "getDisplayList");
+        updateViewTreeDisplayList(view);
+
+        if (mRootNodeNeedsUpdate || !mRootNode.isValid()) {
+            HardwareCanvas canvas = mRootNode.start(mSurfaceWidth, mSurfaceHeight);
+            try {
+                canvas.save();
+                canvas.translate(mInsetLeft, mInsetTop);
+                callbacks.onHardwarePreDraw(canvas);
+                canvas.drawRenderNode(view.getDisplayList());
+                callbacks.onHardwarePostDraw(canvas);
+                canvas.restore();
+                mRootNodeNeedsUpdate = false;
+            } finally {
+                mRootNode.end(canvas);
+            }
+        }
+        Trace.traceEnd(Trace.TRACE_TAG_VIEW);
+    }
+
+    @Override
+    void invalidateRoot() {
+        mRootNodeNeedsUpdate = true;
+    }
+
     @Override
     void draw(View view, AttachInfo attachInfo, HardwareDrawCallbacks callbacks) {
         attachInfo.mIgnoreDirtyState = true;
diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java
index b554548..341419c 100644
--- a/core/java/android/view/ViewRootImpl.java
+++ b/core/java/android/view/ViewRootImpl.java
@@ -2440,8 +2440,11 @@
             if (attachInfo.mHardwareRenderer != null && attachInfo.mHardwareRenderer.isEnabled()) {
                 // Draw with hardware renderer.
                 mIsAnimating = false;
-                mHardwareYOffset = yOffset;
-                mHardwareXOffset = xOffset;
+                if (mHardwareYOffset != yOffset || mHardwareXOffset != xOffset) {
+                    mHardwareYOffset = yOffset;
+                    mHardwareXOffset = xOffset;
+                    mAttachInfo.mHardwareRenderer.invalidateRoot();
+                }
                 mResizeAlpha = resizeAlpha;
 
                 dirty.setEmpty();
@@ -2827,6 +2830,10 @@
         // Set the new focus host and node.
         mAccessibilityFocusedHost = view;
         mAccessibilityFocusedVirtualView = node;
+
+        if (mAttachInfo.mHardwareRenderer != null) {
+            mAttachInfo.mHardwareRenderer.invalidateRoot();
+        }
     }
 
     @Override
diff --git a/core/jni/android_view_ThreadedRenderer.cpp b/core/jni/android_view_ThreadedRenderer.cpp
index aab48b3..988d461 100644
--- a/core/jni/android_view_ThreadedRenderer.cpp
+++ b/core/jni/android_view_ThreadedRenderer.cpp
@@ -138,13 +138,6 @@
         }
     }
 
-protected:
-    virtual void damageSelf(TreeInfo& info) {
-        // Intentionally a no-op. As RootRenderNode gets a new DisplayListData
-        // every frame this would result in every draw push being a full inval,
-        // which is wrong. Only RootRenderNode has this issue.
-    }
-
 private:
     sp<Looper> mLooper;
     std::vector<OnFinishedEvent> mOnFinishedEvents;
diff --git a/libs/hwui/RenderNode.h b/libs/hwui/RenderNode.h
index 54fa143..8cc65b2 100644
--- a/libs/hwui/RenderNode.h
+++ b/libs/hwui/RenderNode.h
@@ -173,9 +173,6 @@
     // UI thread only!
     ANDROID_API void addAnimator(const sp<BaseRenderNodeAnimator>& animator);
 
-protected:
-    virtual void damageSelf(TreeInfo& info);
-
 private:
     typedef key_value_pair_t<float, DrawRenderNodeOp*> ZDrawRenderNodeOpPair;
 
@@ -250,6 +247,7 @@
     void prepareLayer(TreeInfo& info);
     void pushLayerUpdate(TreeInfo& info);
     void deleteDisplayListData();
+    void damageSelf(TreeInfo& info);
 
     void incParentRefCount() { mParentCount++; }
     void decParentRefCount();
diff --git a/libs/hwui/renderthread/CanvasContext.cpp b/libs/hwui/renderthread/CanvasContext.cpp
index 57279b7..a4ac262 100644
--- a/libs/hwui/renderthread/CanvasContext.cpp
+++ b/libs/hwui/renderthread/CanvasContext.cpp
@@ -185,6 +185,11 @@
     } else if (!mDirtyRegionsEnabled || mHaveNewSurface) {
         dirty.setEmpty();
     } else {
+        if (!dirty.intersect(0, 0, width, height)) {
+            ALOGW("Dirty " RECT_STRING " doesn't intersect with 0 0 %d %d ?",
+                    SK_RECT_ARGS(dirty), width, height);
+            dirty.setEmpty();
+        }
         profiler().unionDirty(&dirty);
     }