RenderThread animator support

Change-Id: Icf29098edfdaf7ed550bbe9d49e9eaefb4167084
diff --git a/libs/hwui/renderthread/CanvasContext.cpp b/libs/hwui/renderthread/CanvasContext.cpp
index 5ce7ba6..63f4b95 100644
--- a/libs/hwui/renderthread/CanvasContext.cpp
+++ b/libs/hwui/renderthread/CanvasContext.cpp
@@ -308,18 +308,20 @@
     return value == EGL_BUFFER_PRESERVED;
 }
 
-CanvasContext::CanvasContext(bool translucent)
+CanvasContext::CanvasContext(bool translucent, RenderNode* rootRenderNode)
         : mRenderThread(RenderThread::getInstance())
         , mEglSurface(EGL_NO_SURFACE)
         , mDirtyRegionsEnabled(false)
         , mOpaque(!translucent)
         , mCanvas(0)
-        , mHaveNewSurface(false) {
+        , mHaveNewSurface(false)
+        , mRootRenderNode(rootRenderNode) {
     mGlobalContext = GlobalContext::get();
 }
 
 CanvasContext::~CanvasContext() {
     destroyCanvasAndSurface();
+    mRenderThread.removeFrameCallback(this);
 }
 
 void CanvasContext::destroyCanvasAndSurface() {
@@ -403,7 +405,16 @@
     }
 }
 
-void CanvasContext::drawDisplayList(RenderNode* displayList, Rect* dirty) {
+void CanvasContext::prepareTree(TreeInfo& info) {
+    mRootRenderNode->prepareTree(info);
+
+    if (info.hasAnimations && !info.hasFunctors) {
+        // TODO: Functors
+        mRenderThread.postFrameCallback(this);
+    }
+}
+
+void CanvasContext::draw(Rect* dirty) {
     LOG_ALWAYS_FATAL_IF(!mCanvas || mEglSurface == EGL_NO_SURFACE,
             "drawDisplayList called on a context with no canvas or surface!");
 
@@ -417,7 +428,7 @@
     }
 
     status_t status;
-    if (dirty) {
+    if (dirty && !dirty->isEmpty()) {
         status = mCanvas->prepareDirty(dirty->left, dirty->top,
                 dirty->right, dirty->bottom, mOpaque);
     } else {
@@ -425,7 +436,7 @@
     }
 
     Rect outBounds;
-    status |= mCanvas->drawDisplayList(displayList, outBounds);
+    status |= mCanvas->drawDisplayList(mRootRenderNode.get(), outBounds);
 
     // TODO: Draw debug info
     // TODO: Performance tracking
@@ -437,6 +448,20 @@
     }
 }
 
+// Called by choreographer to do an RT-driven animation
+void CanvasContext::doFrame(nsecs_t frameTimeNanos) {
+    ATRACE_CALL();
+
+    TreeInfo info;
+    info.evaluateAnimations = true;
+    info.frameTimeMs = nanoseconds_to_milliseconds(frameTimeNanos);
+    info.performStagingPush = false;
+    info.prepareTextures = false;
+
+    prepareTree(info);
+    draw(NULL);
+}
+
 void CanvasContext::invokeFunctor(Functor* functor) {
     ATRACE_CALL();
     DrawGlInfo::Mode mode = DrawGlInfo::kModeProcessNoContext;
diff --git a/libs/hwui/renderthread/CanvasContext.h b/libs/hwui/renderthread/CanvasContext.h
index a3fe591..0873ad4 100644
--- a/libs/hwui/renderthread/CanvasContext.h
+++ b/libs/hwui/renderthread/CanvasContext.h
@@ -25,6 +25,7 @@
 
 #include "../RenderNode.h"
 #include "RenderTask.h"
+#include "RenderThread.h"
 
 #define FUNCTOR_PROCESS_DELAY 4
 
@@ -39,15 +40,13 @@
 namespace renderthread {
 
 class GlobalContext;
-class CanvasContext;
-class RenderThread;
 
 // This per-renderer class manages the bridge between the global EGL context
 // and the render surface.
-class CanvasContext {
+class CanvasContext : public IFrameCallback {
 public:
-    CanvasContext(bool translucent);
-    ~CanvasContext();
+    CanvasContext(bool translucent, RenderNode* rootRenderNode);
+    virtual ~CanvasContext();
 
     bool initialize(EGLNativeWindowType window);
     void updateSurface(EGLNativeWindowType window);
@@ -55,9 +54,13 @@
     void setup(int width, int height);
     void makeCurrent();
     void processLayerUpdates(const Vector<DeferredLayerUpdater*>* layerUpdaters, TreeInfo& info);
-    void drawDisplayList(RenderNode* displayList, Rect* dirty);
+    void prepareTree(TreeInfo& info);
+    void draw(Rect* dirty);
     void destroyCanvasAndSurface();
 
+    // IFrameCallback, Chroreographer-driven frame callback entry point
+    virtual void doFrame(nsecs_t frameTimeNanos);
+
     bool copyLayerInto(DeferredLayerUpdater* layer, SkBitmap* bitmap);
 
     void invokeFunctor(Functor* functor);
@@ -82,6 +85,8 @@
     bool mOpaque;
     OpenGLRenderer* mCanvas;
     bool mHaveNewSurface;
+
+    const sp<RenderNode> mRootRenderNode;
 };
 
 } /* namespace renderthread */
diff --git a/libs/hwui/renderthread/DrawFrameTask.cpp b/libs/hwui/renderthread/DrawFrameTask.cpp
index f542d43..ff4be71 100644
--- a/libs/hwui/renderthread/DrawFrameTask.cpp
+++ b/libs/hwui/renderthread/DrawFrameTask.cpp
@@ -30,7 +30,7 @@
 namespace uirenderer {
 namespace renderthread {
 
-DrawFrameTask::DrawFrameTask() : mContext(0), mRenderNode(0) {
+DrawFrameTask::DrawFrameTask() : mContext(0) {
 }
 
 DrawFrameTask::~DrawFrameTask() {
@@ -55,25 +55,17 @@
     }
 }
 
-void DrawFrameTask::setRenderNode(RenderNode* renderNode) {
-    LOG_ALWAYS_FATAL_IF(!mContext, "Lifecycle violation, there's no context to setRenderNode with!");
-
-    mRenderNode = renderNode;
-}
-
 void DrawFrameTask::setDirty(int left, int top, int right, int bottom) {
     mDirty.set(left, top, right, bottom);
 }
 
 void DrawFrameTask::drawFrame(RenderThread* renderThread) {
-    LOG_ALWAYS_FATAL_IF(!mRenderNode.get(), "Cannot drawFrame with no render node!");
     LOG_ALWAYS_FATAL_IF(!mContext, "Cannot drawFrame with no CanvasContext!");
 
     postAndWait(renderThread);
 
     // Reset the single-frame data
     mDirty.setEmpty();
-    mRenderNode = 0;
 }
 
 void DrawFrameTask::postAndWait(RenderThread* renderThread) {
@@ -88,8 +80,7 @@
     bool canUnblockUiThread = syncFrameState();
 
     // Grab a copy of everything we need
-    Rect dirtyCopy(mDirty);
-    sp<RenderNode> renderNode = mRenderNode;
+    Rect dirty(mDirty);
     CanvasContext* context = mContext;
 
     // From this point on anything in "this" is *UNSAFE TO ACCESS*
@@ -97,15 +88,20 @@
         unblockUiThread();
     }
 
-    drawRenderNode(context, renderNode.get(), &dirtyCopy);
+    context->draw(&dirty);
 
     if (!canUnblockUiThread) {
         unblockUiThread();
     }
 }
 
-static void prepareTreeInfo(TreeInfo& info) {
+static void initTreeInfo(TreeInfo& info) {
     info.prepareTextures = true;
+    info.performStagingPush = true;
+    info.evaluateAnimations = true;
+    // TODO: Get this from Choreographer
+    nsecs_t frameTimeNs = systemTime(CLOCK_MONOTONIC);
+    info.frameTimeMs = nanoseconds_to_milliseconds(frameTimeNs);
 }
 
 bool DrawFrameTask::syncFrameState() {
@@ -113,9 +109,9 @@
     mContext->makeCurrent();
     Caches::getInstance().textureCache.resetMarkInUse();
     TreeInfo info;
-    prepareTreeInfo(info);
+    initTreeInfo(info);
     mContext->processLayerUpdates(&mLayers, info);
-    mRenderNode->prepareTree(info);
+    mContext->prepareTree(info);
     // If prepareTextures is false, we ran out of texture cache space
     return !info.hasFunctors && info.prepareTextures;
 }
@@ -125,16 +121,6 @@
     mSignal.signal();
 }
 
-void DrawFrameTask::drawRenderNode(CanvasContext* context, RenderNode* renderNode, Rect* dirty) {
-    ATRACE_CALL();
-
-    if (dirty->bottom == -1 && dirty->left == -1
-            && dirty->top == -1 && dirty->right == -1) {
-        dirty = 0;
-    }
-    context->drawDisplayList(renderNode, dirty);
-}
-
 } /* namespace renderthread */
 } /* namespace uirenderer */
 } /* namespace android */
diff --git a/libs/hwui/renderthread/DrawFrameTask.h b/libs/hwui/renderthread/DrawFrameTask.h
index 055d4cf..c280868 100644
--- a/libs/hwui/renderthread/DrawFrameTask.h
+++ b/libs/hwui/renderthread/DrawFrameTask.h
@@ -53,7 +53,6 @@
     void addLayer(DeferredLayerUpdater* layer);
     void removeLayer(DeferredLayerUpdater* layer);
 
-    void setRenderNode(RenderNode* renderNode);
     void setDirty(int left, int top, int right, int bottom);
     void drawFrame(RenderThread* renderThread);
 
@@ -63,7 +62,6 @@
     void postAndWait(RenderThread* renderThread);
     bool syncFrameState();
     void unblockUiThread();
-    static void drawRenderNode(CanvasContext* context, RenderNode* renderNode, Rect* dirty);
 
     Mutex mLock;
     Condition mSignal;
@@ -73,7 +71,6 @@
     /*********************************************
      *  Single frame data
      *********************************************/
-    sp<RenderNode> mRenderNode;
     Rect mDirty;
 
     /*********************************************
diff --git a/libs/hwui/renderthread/RenderProxy.cpp b/libs/hwui/renderthread/RenderProxy.cpp
index ce490f1..87886e6 100644
--- a/libs/hwui/renderthread/RenderProxy.cpp
+++ b/libs/hwui/renderthread/RenderProxy.cpp
@@ -51,15 +51,16 @@
     MethodInvokeRenderTask* task = new MethodInvokeRenderTask((RunnableMethod) Bridge_ ## method); \
     ARGS(method) *args = (ARGS(method) *) task->payload()
 
-CREATE_BRIDGE1(createContext, bool translucent) {
-    return new CanvasContext(args->translucent);
+CREATE_BRIDGE2(createContext, bool translucent, RenderNode* rootRenderNode) {
+    return new CanvasContext(args->translucent, args->rootRenderNode);
 }
 
-RenderProxy::RenderProxy(bool translucent)
+RenderProxy::RenderProxy(bool translucent, RenderNode* rootRenderNode)
         : mRenderThread(RenderThread::getInstance())
         , mContext(0) {
     SETUP_TASK(createContext);
     args->translucent = translucent;
+    args->rootRenderNode = rootRenderNode;
     mContext = (CanvasContext*) postAndWait(task);
     mDrawFrameTask.setContext(mContext);
 }
@@ -133,9 +134,8 @@
     post(task);
 }
 
-void RenderProxy::drawDisplayList(RenderNode* displayList,
+void RenderProxy::syncAndDrawFrame(
         int dirtyLeft, int dirtyTop, int dirtyRight, int dirtyBottom) {
-    mDrawFrameTask.setRenderNode(displayList);
     mDrawFrameTask.setDirty(dirtyLeft, dirtyTop, dirtyRight, dirtyBottom);
     mDrawFrameTask.drawFrame(&mRenderThread);
 }
diff --git a/libs/hwui/renderthread/RenderProxy.h b/libs/hwui/renderthread/RenderProxy.h
index a112493..eab1395 100644
--- a/libs/hwui/renderthread/RenderProxy.h
+++ b/libs/hwui/renderthread/RenderProxy.h
@@ -56,14 +56,14 @@
  */
 class ANDROID_API RenderProxy {
 public:
-    ANDROID_API RenderProxy(bool translucent);
+    ANDROID_API RenderProxy(bool translucent, RenderNode* rootNode);
     ANDROID_API virtual ~RenderProxy();
 
     ANDROID_API bool initialize(const sp<ANativeWindow>& window);
     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 drawDisplayList(RenderNode* displayList,
+    ANDROID_API void syncAndDrawFrame(
             int dirtyLeft, int dirtyTop, int dirtyRight, int dirtyBottom);
     ANDROID_API void destroyCanvasAndSurface();
 
diff --git a/libs/hwui/renderthread/RenderThread.cpp b/libs/hwui/renderthread/RenderThread.cpp
index 212f475..e95707a 100644
--- a/libs/hwui/renderthread/RenderThread.cpp
+++ b/libs/hwui/renderthread/RenderThread.cpp
@@ -18,9 +18,11 @@
 
 #include "RenderThread.h"
 
+#include <gui/DisplayEventReceiver.h>
+#include <utils/Log.h>
+
 #include "CanvasContext.h"
 #include "RenderProxy.h"
-#include <utils/Log.h>
 
 namespace android {
 using namespace uirenderer::renderthread;
@@ -29,6 +31,14 @@
 namespace uirenderer {
 namespace renderthread {
 
+// Number of events to read at a time from the DisplayEventReceiver pipe.
+// The value should be large enough that we can quickly drain the pipe
+// using just a few large reads.
+static const size_t EVENT_BUFFER_SIZE = 100;
+
+// Slight delay to give the UI time to push us a new frame before we replay
+static const int DISPATCH_FRAME_CALLBACKS_DELAY = 0;
+
 TaskQueue::TaskQueue() : mHead(0), mTail(0) {}
 
 RenderTask* TaskQueue::next() {
@@ -103,8 +113,25 @@
     }
 }
 
+class DispatchFrameCallbacks : public RenderTask {
+private:
+    RenderThread* mRenderThread;
+public:
+    DispatchFrameCallbacks(RenderThread* rt) : mRenderThread(rt) {}
+
+    virtual void run() {
+        mRenderThread->dispatchFrameCallbacks();
+    }
+};
+
 RenderThread::RenderThread() : Thread(true), Singleton<RenderThread>()
-        , mNextWakeup(LLONG_MAX) {
+        , mNextWakeup(LLONG_MAX)
+        , mDisplayEventReceiver(0)
+        , mVsyncRequested(false)
+        , mFrameCallbackTaskPending(false)
+        , mFrameCallbackTask(0)
+        , mFrameTime(0) {
+    mFrameCallbackTask = new DispatchFrameCallbacks(this);
     mLooper = new Looper(false);
     run("RenderThread");
 }
@@ -112,10 +139,86 @@
 RenderThread::~RenderThread() {
 }
 
+void RenderThread::initializeDisplayEventReceiver() {
+    LOG_ALWAYS_FATAL_IF(mDisplayEventReceiver, "Initializing a second DisplayEventReceiver?");
+    mDisplayEventReceiver = new DisplayEventReceiver();
+    status_t status = mDisplayEventReceiver->initCheck();
+    LOG_ALWAYS_FATAL_IF(status != NO_ERROR, "Initialization of DisplayEventReceiver "
+            "failed with status: %d", status);
+
+    // Register the FD
+    mLooper->addFd(mDisplayEventReceiver->getFd(), 0,
+            Looper::EVENT_INPUT, RenderThread::displayEventReceiverCallback, this);
+}
+
+int RenderThread::displayEventReceiverCallback(int fd, int events, void* data) {
+    if (events & (Looper::EVENT_ERROR | Looper::EVENT_HANGUP)) {
+        ALOGE("Display event receiver pipe was closed or an error occurred.  "
+                "events=0x%x", events);
+        return 0; // remove the callback
+    }
+
+    if (!(events & Looper::EVENT_INPUT)) {
+        ALOGW("Received spurious callback for unhandled poll event.  "
+                "events=0x%x", events);
+        return 1; // keep the callback
+    }
+
+    reinterpret_cast<RenderThread*>(data)->drainDisplayEventQueue();
+
+    return 1; // keep the callback
+}
+
+static nsecs_t latestVsyncEvent(DisplayEventReceiver* receiver) {
+    DisplayEventReceiver::Event buf[EVENT_BUFFER_SIZE];
+    nsecs_t latest = 0;
+    ssize_t n;
+    while ((n = receiver->getEvents(buf, EVENT_BUFFER_SIZE)) > 0) {
+        for (ssize_t i = 0; i < n; i++) {
+            const DisplayEventReceiver::Event& ev = buf[i];
+            switch (ev.header.type) {
+            case DisplayEventReceiver::DISPLAY_EVENT_VSYNC:
+                latest = ev.header.timestamp;
+                break;
+            }
+        }
+    }
+    if (n < 0) {
+        ALOGW("Failed to get events from display event receiver, status=%d", status_t(n));
+    }
+    return latest;
+}
+
+void RenderThread::drainDisplayEventQueue() {
+    nsecs_t vsyncEvent = latestVsyncEvent(mDisplayEventReceiver);
+    if (vsyncEvent > 0) {
+        mVsyncRequested = false;
+        mFrameTime = vsyncEvent;
+        if (!mFrameCallbackTaskPending) {
+            mFrameCallbackTaskPending = true;
+            //queueDelayed(mFrameCallbackTask, DISPATCH_FRAME_CALLBACKS_DELAY);
+            queue(mFrameCallbackTask);
+        }
+    }
+}
+
+void RenderThread::dispatchFrameCallbacks() {
+    mFrameCallbackTaskPending = false;
+
+    std::set<IFrameCallback*> callbacks;
+    mFrameCallbacks.swap(callbacks);
+
+    for (std::set<IFrameCallback*>::iterator it = callbacks.begin(); it != callbacks.end(); it++) {
+        (*it)->doFrame(mFrameTime);
+    }
+}
+
 bool RenderThread::threadLoop() {
+    initializeDisplayEventReceiver();
+
     int timeoutMillis = -1;
     for (;;) {
-        int result = mLooper->pollAll(timeoutMillis);
+        int result = mLooper->pollOnce(timeoutMillis);
         LOG_ALWAYS_FATAL_IF(result == Looper::POLL_ERROR,
                 "RenderThread Looper POLL_ERROR!");
 
@@ -159,6 +262,20 @@
     mQueue.remove(task);
 }
 
+void RenderThread::postFrameCallback(IFrameCallback* callback) {
+    mFrameCallbacks.insert(callback);
+    if (!mVsyncRequested) {
+        mVsyncRequested = true;
+        status_t status = mDisplayEventReceiver->requestNextVsync();
+        LOG_ALWAYS_FATAL_IF(status != NO_ERROR,
+                "requestNextVsync failed with status: %d", status);
+    }
+}
+
+void RenderThread::removeFrameCallback(IFrameCallback* callback) {
+    mFrameCallbacks.erase(callback);
+}
+
 RenderTask* RenderThread::nextTask(nsecs_t* nextWakeup) {
     AutoMutex _lock(mLock);
     RenderTask* next = mQueue.peek();
diff --git a/libs/hwui/renderthread/RenderThread.h b/libs/hwui/renderthread/RenderThread.h
index e444aa0..b93dfd6 100644
--- a/libs/hwui/renderthread/RenderThread.h
+++ b/libs/hwui/renderthread/RenderThread.h
@@ -18,6 +18,10 @@
 #define RENDERTHREAD_H_
 
 #include "RenderTask.h"
+
+#include <memory>
+#include <set>
+
 #include <cutils/compiler.h>
 #include <utils/Looper.h>
 #include <utils/Mutex.h>
@@ -25,9 +29,13 @@
 #include <utils/Thread.h>
 
 namespace android {
+class DisplayEventReceiver;
+
 namespace uirenderer {
 namespace renderthread {
 
+class DispatchFrameCallbacks;
+
 class TaskQueue {
 public:
     TaskQueue();
@@ -42,6 +50,15 @@
     RenderTask* mTail;
 };
 
+// Mimics android.view.Choreographer.FrameCallback
+class IFrameCallback {
+public:
+    virtual void doFrame(nsecs_t frameTimeNanos) = 0;
+
+protected:
+    ~IFrameCallback() {}
+};
+
 class ANDROID_API RenderThread : public Thread, public Singleton<RenderThread> {
 public:
     // RenderThread takes complete ownership of tasks that are queued
@@ -50,15 +67,25 @@
     void queueDelayed(RenderTask* task, int delayMs);
     void remove(RenderTask* task);
 
+    // Mimics android.view.Choreographer
+    void postFrameCallback(IFrameCallback* callback);
+    void removeFrameCallback(IFrameCallback* callback);
+
 protected:
     virtual bool threadLoop();
 
 private:
     friend class Singleton<RenderThread>;
+    friend class DispatchFrameCallbacks;
 
     RenderThread();
     virtual ~RenderThread();
 
+    void initializeDisplayEventReceiver();
+    static int displayEventReceiverCallback(int fd, int events, void* data);
+    void drainDisplayEventQueue();
+    void dispatchFrameCallbacks();
+
     // Returns the next task to be run. If this returns NULL nextWakeup is set
     // to the time to requery for the nextTask to run. mNextWakeup is also
     // set to this time
@@ -69,6 +96,13 @@
 
     nsecs_t mNextWakeup;
     TaskQueue mQueue;
+
+    DisplayEventReceiver* mDisplayEventReceiver;
+    bool mVsyncRequested;
+    std::set<IFrameCallback*> mFrameCallbacks;
+    bool mFrameCallbackTaskPending;
+    DispatchFrameCallbacks* mFrameCallbackTask;
+    nsecs_t mFrameTime;
 };
 
 } /* namespace renderthread */