Make RenderNodeAnimator and WebView play nice

Change-Id: Ifaefcf510b2d377663fc86f60608d6ec9be8329a
diff --git a/core/java/android/view/ThreadedRenderer.java b/core/java/android/view/ThreadedRenderer.java
index 89a2ea2..9c7fa25 100644
--- a/core/java/android/view/ThreadedRenderer.java
+++ b/core/java/android/view/ThreadedRenderer.java
@@ -53,6 +53,12 @@
 
     private static final long NANOS_PER_MS = 1000000;
 
+    // Keep in sync with DrawFrameTask.h SYNC_* flags
+    // Nothing interesting to report
+    private static final int SYNC_OK = 0x0;
+    // Needs a ViewRoot invalidate
+    private static final int SYNC_INVALIDATE_REQUIRED = 0x1;
+
     private int mWidth, mHeight;
     private long mNativeProxy;
     private boolean mInitialized = false;
@@ -201,8 +207,11 @@
         if (dirty == null) {
             dirty = NULL_RECT;
         }
-        nSyncAndDrawFrame(mNativeProxy, frameTimeNanos,
+        int syncResult = nSyncAndDrawFrame(mNativeProxy, frameTimeNanos,
                 dirty.left, dirty.top, dirty.right, dirty.bottom);
+        if ((syncResult & SYNC_INVALIDATE_REQUIRED) != 0) {
+            attachInfo.mViewRootImpl.invalidate();
+        }
     }
 
     @Override
@@ -304,7 +313,7 @@
     private static native void nSetup(long nativeProxy, int width, int height);
     private static native void nSetDisplayListData(long nativeProxy, long displayList,
             long newData);
-    private static native void nSyncAndDrawFrame(long nativeProxy, long frameTimeNanos,
+    private static native int nSyncAndDrawFrame(long nativeProxy, long frameTimeNanos,
             int dirtyLeft, int dirtyTop, int dirtyRight, int dirtyBottom);
     private static native void nRunWithGlContext(long nativeProxy, Runnable runnable);
     private static native void nDestroyCanvasAndSurface(long nativeProxy);
diff --git a/core/jni/android_view_ThreadedRenderer.cpp b/core/jni/android_view_ThreadedRenderer.cpp
index 6a048672..cd0ed8c 100644
--- a/core/jni/android_view_ThreadedRenderer.cpp
+++ b/core/jni/android_view_ThreadedRenderer.cpp
@@ -191,11 +191,11 @@
     proxy->setup(width, height);
 }
 
-static void android_view_ThreadedRenderer_syncAndDrawFrame(JNIEnv* env, jobject clazz,
+static int android_view_ThreadedRenderer_syncAndDrawFrame(JNIEnv* env, jobject clazz,
         jlong proxyPtr, jlong frameTimeNanos, jint dirtyLeft, jint dirtyTop,
         jint dirtyRight, jint dirtyBottom) {
     RenderProxy* proxy = reinterpret_cast<RenderProxy*>(proxyPtr);
-    proxy->syncAndDrawFrame(frameTimeNanos, dirtyLeft, dirtyTop, dirtyRight, dirtyBottom);
+    return proxy->syncAndDrawFrame(frameTimeNanos, dirtyLeft, dirtyTop, dirtyRight, dirtyBottom);
 }
 
 static void android_view_ThreadedRenderer_destroyCanvasAndSurface(JNIEnv* env, jobject clazz,
@@ -272,7 +272,7 @@
     { "nUpdateSurface", "(JLandroid/view/Surface;)V", (void*) android_view_ThreadedRenderer_updateSurface },
     { "nPauseSurface", "(JLandroid/view/Surface;)V", (void*) android_view_ThreadedRenderer_pauseSurface },
     { "nSetup", "(JII)V", (void*) android_view_ThreadedRenderer_setup },
-    { "nSyncAndDrawFrame", "(JJIIII)V", (void*) android_view_ThreadedRenderer_syncAndDrawFrame },
+    { "nSyncAndDrawFrame", "(JJIIII)I", (void*) android_view_ThreadedRenderer_syncAndDrawFrame },
     { "nDestroyCanvasAndSurface", "(J)V", (void*) android_view_ThreadedRenderer_destroyCanvasAndSurface },
     { "nInvokeFunctor", "(JJZ)V", (void*) android_view_ThreadedRenderer_invokeFunctor },
     { "nRunWithGlContext", "(JLjava/lang/Runnable;)V", (void*) android_view_ThreadedRenderer_runWithGlContext },
diff --git a/libs/hwui/RenderNode.cpp b/libs/hwui/RenderNode.cpp
index 2c29985..9902ff1 100644
--- a/libs/hwui/RenderNode.cpp
+++ b/libs/hwui/RenderNode.cpp
@@ -160,13 +160,13 @@
     newEnd = std::remove_if(mAnimators.begin(), mAnimators.end(), functor);
     mAnimators.erase(newEnd, mAnimators.end());
     mProperties.updateMatrix();
-    info.hasAnimations |= mAnimators.size();
+    info.out.hasAnimations |= mAnimators.size();
 }
 
 void RenderNode::prepareSubTree(TreeInfo& info, DisplayListData* subtree) {
     if (subtree) {
         TextureCache& cache = Caches::getInstance().textureCache;
-        info.hasFunctors |= subtree->functorCount;
+        info.out.hasFunctors |= subtree->functorCount;
         // TODO: Fix ownedBitmapResources to not require disabling prepareTextures
         // and thus falling out of async drawing path.
         if (subtree->ownedBitmapResources.size()) {
diff --git a/libs/hwui/TreeInfo.h b/libs/hwui/TreeInfo.h
index a383fbf..fc5994c 100644
--- a/libs/hwui/TreeInfo.h
+++ b/libs/hwui/TreeInfo.h
@@ -34,25 +34,33 @@
 struct TreeInfo {
     // The defaults here should be safe for everyone but DrawFrameTask to use as-is.
     TreeInfo()
-            : hasFunctors(false)
-            , prepareTextures(false)
-            , performStagingPush(true)
-            , frameTimeMs(0)
-            , evaluateAnimations(false)
-            , hasAnimations(false)
-            , animationHook(0)
+        : frameTimeMs(0)
+        , animationHook(NULL)
+        , prepareTextures(false)
+        , performStagingPush(true)
+        , evaluateAnimations(false)
     {}
 
-    bool hasFunctors;
+    nsecs_t frameTimeMs;
+    AnimationHook* animationHook;
     bool prepareTextures;
     bool performStagingPush;
-
-    // Animations
-    nsecs_t frameTimeMs;
     bool evaluateAnimations;
-    // This is only updated if evaluateAnimations is true
-    bool hasAnimations;
-    AnimationHook* animationHook;
+
+    struct Out {
+        Out()
+            : hasFunctors(false)
+            , hasAnimations(false)
+            , requiresUiRedraw(false)
+        {}
+        bool hasFunctors;
+        // This is only updated if evaluateAnimations is true
+        bool hasAnimations;
+        // This is set to true if there is an animation that RenderThread cannot
+        // animate itself, such as if hasFunctors is true
+        // This is only set if hasAnimations is true
+        bool requiresUiRedraw;
+    } out;
 
     // TODO: Damage calculations
 };
diff --git a/libs/hwui/renderthread/CanvasContext.cpp b/libs/hwui/renderthread/CanvasContext.cpp
index f199236..fc3548c 100644
--- a/libs/hwui/renderthread/CanvasContext.cpp
+++ b/libs/hwui/renderthread/CanvasContext.cpp
@@ -391,10 +391,20 @@
     mHaveNewSurface |= mGlobalContext->makeCurrent(mEglSurface);
 }
 
+void CanvasContext::prepareDraw(const Vector<DeferredLayerUpdater*>* layerUpdaters,
+        TreeInfo& info) {
+    LOG_ALWAYS_FATAL_IF(!mCanvas, "Cannot prepareDraw without a canvas!");
+    makeCurrent();
+
+    processLayerUpdates(layerUpdaters, info);
+    if (info.out.hasAnimations) {
+        // TODO: Uh... crap?
+    }
+    prepareTree(info);
+}
+
 void CanvasContext::processLayerUpdates(const Vector<DeferredLayerUpdater*>* layerUpdaters,
         TreeInfo& info) {
-    LOG_ALWAYS_FATAL_IF(!mCanvas, "Cannot process layer updates without a canvas!");
-    makeCurrent();
     for (size_t i = 0; i < layerUpdaters->size(); i++) {
         DeferredLayerUpdater* update = layerUpdaters->itemAt(i);
         bool success = update->apply(info);
@@ -406,13 +416,19 @@
 }
 
 void CanvasContext::prepareTree(TreeInfo& info) {
-    info.frameTimeMs = mRenderThread.timeLord().frameTimeMs();
+    mRenderThread.removeFrameCallback(this);
 
+    info.frameTimeMs = mRenderThread.timeLord().frameTimeMs();
     mRootRenderNode->prepareTree(info);
 
-    if (info.hasAnimations && !info.hasFunctors) {
-        // TODO: Functors
-        mRenderThread.postFrameCallback(this);
+    if (info.out.hasAnimations) {
+        if (info.out.hasFunctors) {
+            info.out.requiresUiRedraw = true;
+        } else if (!info.out.requiresUiRedraw) {
+            // If animationsNeedsRedraw is set don't bother posting for an RT anim
+            // as we will just end up fighting the UI thread.
+            mRenderThread.postFrameCallback(this);
+        }
     }
 }
 
diff --git a/libs/hwui/renderthread/CanvasContext.h b/libs/hwui/renderthread/CanvasContext.h
index 8022c9e..a95e27a 100644
--- a/libs/hwui/renderthread/CanvasContext.h
+++ b/libs/hwui/renderthread/CanvasContext.h
@@ -53,8 +53,7 @@
     void pauseSurface(EGLNativeWindowType window);
     void setup(int width, int height);
     void makeCurrent();
-    void processLayerUpdates(const Vector<DeferredLayerUpdater*>* layerUpdaters, TreeInfo& info);
-    void prepareTree(TreeInfo& info);
+    void prepareDraw(const Vector<DeferredLayerUpdater*>* layerUpdaters, TreeInfo& info);
     void draw(Rect* dirty);
     void destroyCanvasAndSurface();
 
@@ -71,6 +70,9 @@
     Layer* createTextureLayer();
 
 private:
+    void processLayerUpdates(const Vector<DeferredLayerUpdater*>* layerUpdaters, TreeInfo& info);
+    void prepareTree(TreeInfo& info);
+
     void setSurface(EGLNativeWindowType window);
     void swapBuffers();
     void requireSurface();
diff --git a/libs/hwui/renderthread/DrawFrameTask.cpp b/libs/hwui/renderthread/DrawFrameTask.cpp
index a7b781a..3b8786c 100644
--- a/libs/hwui/renderthread/DrawFrameTask.cpp
+++ b/libs/hwui/renderthread/DrawFrameTask.cpp
@@ -33,7 +33,8 @@
 DrawFrameTask::DrawFrameTask()
         : mRenderThread(NULL)
         , mContext(NULL)
-        , mFrameTimeNanos(NULL) {
+        , mFrameTimeNanos(0)
+        , mSyncResult(kSync_OK) {
 }
 
 DrawFrameTask::~DrawFrameTask() {
@@ -63,15 +64,18 @@
     mDirty.set(left, top, right, bottom);
 }
 
-void DrawFrameTask::drawFrame(nsecs_t frameTimeNanos) {
+int DrawFrameTask::drawFrame(nsecs_t frameTimeNanos) {
     LOG_ALWAYS_FATAL_IF(!mContext, "Cannot drawFrame with no CanvasContext!");
 
+    mSyncResult = kSync_OK;
     mFrameTimeNanos = frameTimeNanos;
     postAndWait();
 
     // Reset the single-frame data
     mFrameTimeNanos = 0;
     mDirty.setEmpty();
+
+    return mSyncResult;
 }
 
 void DrawFrameTask::postAndWait() {
@@ -114,14 +118,16 @@
     Caches::getInstance().textureCache.resetMarkInUse();
     TreeInfo info;
     initTreeInfo(info);
-    mContext->processLayerUpdates(&mLayers, info);
-    mContext->prepareTree(info);
-    if (info.hasAnimations) {
+    mContext->prepareDraw(&mLayers, info);
+    if (info.out.hasAnimations) {
         // TODO: dirty calculations, for now just do a full-screen inval
         mDirty.setEmpty();
+        if (info.out.requiresUiRedraw) {
+            mSyncResult |= kSync_UIRedrawRequired;
+        }
     }
     // If prepareTextures is false, we ran out of texture cache space
-    return !info.hasFunctors && info.prepareTextures;
+    return !info.out.hasFunctors && info.prepareTextures;
 }
 
 void DrawFrameTask::unblockUiThread() {
diff --git a/libs/hwui/renderthread/DrawFrameTask.h b/libs/hwui/renderthread/DrawFrameTask.h
index ea00900..b9307e1 100644
--- a/libs/hwui/renderthread/DrawFrameTask.h
+++ b/libs/hwui/renderthread/DrawFrameTask.h
@@ -37,6 +37,11 @@
 class CanvasContext;
 class RenderThread;
 
+enum SyncResult {
+    kSync_OK = 0,
+    kSync_UIRedrawRequired = 1 << 1,
+};
+
 /*
  * This is a special Super Task. It is re-used multiple times by RenderProxy,
  * and contains state (such as layer updaters & new DisplayListDatas) that is
@@ -54,7 +59,7 @@
     void removeLayer(DeferredLayerUpdater* layer);
 
     void setDirty(int left, int top, int right, int bottom);
-    void drawFrame(nsecs_t frameTimeNanos);
+    int drawFrame(nsecs_t frameTimeNanos);
 
     virtual void run();
 
@@ -75,6 +80,8 @@
     Rect mDirty;
     nsecs_t mFrameTimeNanos;
 
+    int mSyncResult;
+
     /*********************************************
      *  Multi frame data
      *********************************************/
diff --git a/libs/hwui/renderthread/RenderProxy.cpp b/libs/hwui/renderthread/RenderProxy.cpp
index 7b7c019..31bb61a 100644
--- a/libs/hwui/renderthread/RenderProxy.cpp
+++ b/libs/hwui/renderthread/RenderProxy.cpp
@@ -146,10 +146,10 @@
     post(task);
 }
 
-void RenderProxy::syncAndDrawFrame(nsecs_t frameTimeNanos,
+int RenderProxy::syncAndDrawFrame(nsecs_t frameTimeNanos,
         int dirtyLeft, int dirtyTop, int dirtyRight, int dirtyBottom) {
     mDrawFrameTask.setDirty(dirtyLeft, dirtyTop, dirtyRight, dirtyBottom);
-    mDrawFrameTask.drawFrame(frameTimeNanos);
+    return mDrawFrameTask.drawFrame(frameTimeNanos);
 }
 
 CREATE_BRIDGE1(destroyCanvasAndSurface, CanvasContext* context) {
diff --git a/libs/hwui/renderthread/RenderProxy.h b/libs/hwui/renderthread/RenderProxy.h
index bfa2b8d..984cc65 100644
--- a/libs/hwui/renderthread/RenderProxy.h
+++ b/libs/hwui/renderthread/RenderProxy.h
@@ -66,7 +66,7 @@
     ANDROID_API void updateSurface(const sp<ANativeWindow>& window);
     ANDROID_API void pauseSurface(const sp<ANativeWindow>& window);
     ANDROID_API void setup(int width, int height);
-    ANDROID_API void syncAndDrawFrame(nsecs_t frameTimeNanos,
+    ANDROID_API int syncAndDrawFrame(nsecs_t frameTimeNanos,
             int dirtyLeft, int dirtyTop, int dirtyRight, int dirtyBottom);
     ANDROID_API void destroyCanvasAndSurface();
 
diff --git a/tests/HwAccelerationTest/src/com/android/test/hwui/CirclePropActivity.java b/tests/HwAccelerationTest/src/com/android/test/hwui/CirclePropActivity.java
index f060bc8..5c273de1 100644
--- a/tests/HwAccelerationTest/src/com/android/test/hwui/CirclePropActivity.java
+++ b/tests/HwAccelerationTest/src/com/android/test/hwui/CirclePropActivity.java
@@ -27,6 +27,9 @@
 import android.view.HardwareCanvas;
 import android.view.RenderNodeAnimator;
 import android.view.View;
+import android.webkit.WebChromeClient;
+import android.webkit.WebView;
+import android.webkit.WebViewClient;
 import android.widget.LinearLayout;
 import android.widget.LinearLayout.LayoutParams;
 import android.widget.ProgressBar;
@@ -43,6 +46,12 @@
 
         ProgressBar spinner = new ProgressBar(this, null, android.R.attr.progressBarStyleLarge);
         layout.addView(spinner, new LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT));
+        // For testing with a functor in the tree
+//        WebView wv = new WebView(this);
+//        wv.setWebViewClient(new WebViewClient());
+//        wv.setWebChromeClient(new WebChromeClient());
+//        wv.loadUrl("http://theverge.com");
+//        layout.addView(wv, new LayoutParams(LayoutParams.MATCH_PARENT, 100));
 
         layout.addView(new CircleView(this),
                 new LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT));