Index: ui/android/java/src/org/chromium/ui/VSyncMonitor.java |
diff --git a/ui/android/java/src/org/chromium/ui/VSyncMonitor.java b/ui/android/java/src/org/chromium/ui/VSyncMonitor.java |
index 281ed143c3f9299d42878f97486f7119d7292708..0396135687390d44800967eec5e0b901c357820a 100644 |
--- a/ui/android/java/src/org/chromium/ui/VSyncMonitor.java |
+++ b/ui/android/java/src/org/chromium/ui/VSyncMonitor.java |
@@ -51,6 +51,8 @@ |
// If the monitor is activated after having been idle, we synthesize the first vsync to reduce |
// latency. |
private final Handler mHandler = new Handler(); |
+ private final Runnable mSyntheticVSyncRunnable; |
+ private long mLastVSyncCpuTimeNano; |
/** |
* Constructs a VSyncMonitor |
@@ -71,6 +73,7 @@ |
@Override |
public void doFrame(long frameTimeNanos) { |
TraceEvent.begin("VSync"); |
+ mHandler.removeCallbacks(mSyntheticVSyncRunnable); |
if (useEstimatedRefreshPeriod && mConsecutiveVSync) { |
// Display.getRefreshRate() is unreliable on some platforms. |
// Adjust refresh period- initial value is based on Display.getRefreshRate() |
@@ -83,6 +86,16 @@ |
mGoodStartingPointNano = frameTimeNanos; |
onVSyncCallback(frameTimeNanos, getCurrentNanoTime()); |
TraceEvent.end("VSync"); |
+ } |
+ }; |
+ mSyntheticVSyncRunnable = new Runnable() { |
+ @Override |
+ public void run() { |
+ TraceEvent.begin("VSyncSynthetic"); |
+ mChoreographer.removeFrameCallback(mVSyncFrameCallback); |
+ final long currentTime = getCurrentNanoTime(); |
+ onVSyncCallback(estimateLastVSyncTime(currentTime), currentTime); |
+ TraceEvent.end("VSyncSynthetic"); |
} |
}; |
mGoodStartingPointNano = getCurrentNanoTime(); |
@@ -124,6 +137,7 @@ |
assert mHaveRequestInFlight; |
mInsideVSync = true; |
mHaveRequestInFlight = false; |
+ mLastVSyncCpuTimeNano = currentTimeNanos; |
try { |
if (mListener != null) { |
mListener.onVSync(this, frameTimeNanos / NANOSECONDS_PER_MICROSECOND); |
@@ -137,6 +151,31 @@ |
if (mHaveRequestInFlight) return; |
mHaveRequestInFlight = true; |
mConsecutiveVSync = mInsideVSync; |
+ // There's no way to tell if we're currently in the scope of a |
+ // choregrapher frame callback, which might in turn allow us to honor |
+ // the vsync callback in the current frame. Thus, we eagerly post the |
+ // frame callback even when we post a synthetic frame callback. If the |
+ // frame callback is honored before the synthetic callback, we simply |
+ // remove the synthetic callback. |
+ postSyntheticVSyncIfNecessary(); |
mChoreographer.postFrameCallback(mVSyncFrameCallback); |
} |
+ |
+ private void postSyntheticVSyncIfNecessary() { |
+ // TODO(jdduke): Consider removing synthetic vsyncs altogether if |
+ // they're found to be no longer necessary. |
+ final long currentTime = getCurrentNanoTime(); |
+ // Only trigger a synthetic vsync if we've been idle for long enough and the upcoming real |
+ // vsync is more than half a frame away. |
+ if (currentTime - mLastVSyncCpuTimeNano < 2 * mRefreshPeriodNano) return; |
+ if (currentTime - estimateLastVSyncTime(currentTime) > mRefreshPeriodNano / 2) return; |
+ mHandler.post(mSyntheticVSyncRunnable); |
+ } |
+ |
+ private long estimateLastVSyncTime(long currentTime) { |
+ final long lastRefreshTime = mGoodStartingPointNano |
+ + ((currentTime - mGoodStartingPointNano) / mRefreshPeriodNano) |
+ * mRefreshPeriodNano; |
+ return lastRefreshTime; |
+ } |
} |