| 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;
|
| + }
|
| }
|
|
|