Merge "Tune scheduling a bit, avoid a binder ipc"
diff --git a/libs/hwui/renderthread/CanvasContext.cpp b/libs/hwui/renderthread/CanvasContext.cpp
index c199a71..73af4c4 100644
--- a/libs/hwui/renderthread/CanvasContext.cpp
+++ b/libs/hwui/renderthread/CanvasContext.cpp
@@ -27,6 +27,7 @@
 #include "renderstate/RenderState.h"
 #include "renderstate/Stencil.h"
 #include "protos/hwui.pb.h"
+#include "utils/TimeUtils.h"
 
 #if HWUI_NEW_OPS
 #include "BakedOpRenderer.h"
@@ -108,6 +109,7 @@
         const bool preserveBuffer = (mSwapBehavior != kSwap_discardBuffer);
         mBufferPreserved = mEglManager.setPreserveBuffer(mEglSurface, preserveBuffer);
         mHaveNewSurface = true;
+        mSwapHistory.clear();
         makeCurrent();
     } else {
         mRenderThread.removeFrameCallback(this);
@@ -217,13 +219,30 @@
         return;
     }
 
-    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
-    // last vsync time. Or something.
-    mNativeWindow->query(mNativeWindow.get(),
-            NATIVE_WINDOW_CONSUMER_RUNNING_BEHIND, &runningBehind);
-    info.out.canDrawThisFrame = !runningBehind;
+    if (CC_LIKELY(mSwapHistory.size())) {
+        nsecs_t latestVsync = mRenderThread.timeLord().latestVsync();
+        const SwapHistory& lastSwap = mSwapHistory.back();
+        int vsyncDelta = std::abs(lastSwap.vsyncTime - latestVsync);
+        // The slight fudge-factor is to deal with cases where
+        // the vsync was estimated due to being slow handling the signal.
+        // See the logic in TimeLord#computeFrameTimeNanos or in
+        // Choreographer.java for details on when this happens
+        if (vsyncDelta < 2_ms) {
+            // Already drew for this vsync pulse, UI draw request missed
+            // the deadline for RT animations
+            info.out.canDrawThisFrame = false;
+        } else if (lastSwap.swapTime < latestVsync) {
+            info.out.canDrawThisFrame = true;
+        } else {
+            // We're maybe behind? Find out for sure
+            int runningBehind = 0;
+            mNativeWindow->query(mNativeWindow.get(),
+                    NATIVE_WINDOW_CONSUMER_RUNNING_BEHIND, &runningBehind);
+            info.out.canDrawThisFrame = !runningBehind;
+        }
+    } else {
+        info.out.canDrawThisFrame = true;
+    }
 
     if (!info.out.canDrawThisFrame) {
         mCurrentFrameInfo->addFlag(FrameInfoFlags::SkippedFrame);
@@ -297,7 +316,7 @@
     // last frame so there's nothing to union() against
     // Therefore we only care about the > 1 case.
     if (frame.bufferAge() > 1) {
-        if (frame.bufferAge() > (int) mDamageHistory.size()) {
+        if (frame.bufferAge() > (int) mSwapHistory.size()) {
             // We don't have enough history to handle this old of a buffer
             // Just do a full-draw
             dirty.set(0, 0, frame.width(), frame.height());
@@ -305,16 +324,13 @@
             // At this point we haven't yet added the latest frame
             // to the damage history (happens below)
             // So we need to damage
-            for (int i = mDamageHistory.size() - 1;
-                    i > ((int) mDamageHistory.size()) - frame.bufferAge(); i--) {
-                dirty.join(mDamageHistory[i]);
+            for (int i = mSwapHistory.size() - 1;
+                    i > ((int) mSwapHistory.size()) - frame.bufferAge(); i--) {
+                dirty.join(mSwapHistory[i].damage);
             }
         }
     }
 
-    // Add the screen damage to the ring buffer.
-    mDamageHistory.next() = screenDirty;
-
     mEglManager.damageFrame(frame, dirty);
 
 #if HWUI_NEW_OPS
@@ -445,6 +461,10 @@
         if (CC_UNLIKELY(!mEglManager.swapBuffers(frame, screenDirty))) {
             setSurface(nullptr);
         }
+        SwapHistory& swap = mSwapHistory.next();
+        swap.damage = screenDirty;
+        swap.swapTime = systemTime(CLOCK_MONOTONIC);
+        swap.vsyncTime = mRenderThread.timeLord().latestVsync();
         mHaveNewSurface = false;
     }
 
diff --git a/libs/hwui/renderthread/CanvasContext.h b/libs/hwui/renderthread/CanvasContext.h
index 533d693..f362584 100644
--- a/libs/hwui/renderthread/CanvasContext.h
+++ b/libs/hwui/renderthread/CanvasContext.h
@@ -150,7 +150,13 @@
     EGLSurface mEglSurface = EGL_NO_SURFACE;
     bool mBufferPreserved = false;
     SwapBehavior mSwapBehavior = kSwap_default;
-    RingBuffer<SkRect, 3> mDamageHistory;
+    struct SwapHistory {
+        SkRect damage;
+        nsecs_t vsyncTime;
+        nsecs_t swapTime;
+    };
+
+    RingBuffer<SwapHistory, 3> mSwapHistory;
 
     bool mOpaque;
     OpenGLRenderer* mCanvas = nullptr;
diff --git a/libs/hwui/utils/TimeUtils.h b/libs/hwui/utils/TimeUtils.h
new file mode 100644
index 0000000..8d42d7e
--- /dev/null
+++ b/libs/hwui/utils/TimeUtils.h
@@ -0,0 +1,31 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+#ifndef UTILS_TIMEUTILS_H
+#define UTILS_TIMEUTILS_H
+
+#include <utils/Timers.h>
+
+namespace android {
+namespace uirenderer {
+
+constexpr nsecs_t operator"" _ms (unsigned long long ms) {
+    return milliseconds_to_nanoseconds(ms);
+}
+
+} /* namespace uirenderer */
+} /* namespace android */
+
+#endif /* UTILS_TIMEUTILS_H */