| Index: content/public/android/java/src/org/chromium/content/browser/VSyncMonitor.java
|
| diff --git a/content/public/android/java/src/org/chromium/content/browser/VSyncMonitor.java b/content/public/android/java/src/org/chromium/content/browser/VSyncMonitor.java
|
| index 0aca7f310aff0942117f9308a63c16de86e24e6e..961e870990f55774deeb4c3c564f587611f4026b 100644
|
| --- a/content/public/android/java/src/org/chromium/content/browser/VSyncMonitor.java
|
| +++ b/content/public/android/java/src/org/chromium/content/browser/VSyncMonitor.java
|
| @@ -6,15 +6,23 @@ package org.chromium.content.browser;
|
|
|
| import android.content.Context;
|
| import android.os.Build;
|
| -import android.util.Log;
|
| +import android.os.Handler;
|
| import android.view.Choreographer;
|
| -import android.view.View;
|
| import android.view.WindowManager;
|
|
|
| +import org.chromium.content.common.TraceEvent;
|
| +
|
| /**
|
| * Notifies clients of the default displays's vertical sync pulses.
|
| + * This class works in "burst" mode: once the update is requested, the listener will be
|
| + * called MAX_VSYNC_COUNT times on the vertical sync pulses (on JB) or on every refresh
|
| + * period (on ICS, see below), unless stop() is called.
|
| + * On ICS, VSyncMonitor relies on setVSyncPointForICS() being called to set a reasonable
|
| + * approximation of a vertical sync starting point; see also http://crbug.com/156397.
|
| */
|
| public class VSyncMonitor {
|
| + private static final String TAG = VSyncMonitor.class.getSimpleName();
|
| +
|
| public interface Listener {
|
| /**
|
| * Called very soon after the start of the display's vertical sync period.
|
| @@ -24,79 +32,71 @@ public class VSyncMonitor {
|
| public void onVSync(VSyncMonitor monitor, long vsyncTimeMicros);
|
| }
|
|
|
| - /** Time source used for test the timeout functionality. */
|
| - interface TestTimeSource {
|
| - public long currentTimeMillis();
|
| - }
|
| -
|
| - // To save battery, we don't run vsync more than this time unless requestUpdate() is called.
|
| - public static final long VSYNC_TIMEOUT_MILLISECONDS = 3000;
|
| -
|
| - private static final long MICROSECONDS_PER_SECOND = 1000000;
|
| + private static final long NANOSECONDS_PER_SECOND = 1000000000;
|
| + private static final long NANOSECONDS_PER_MILLISECOND = 1000000;
|
| private static final long NANOSECONDS_PER_MICROSECOND = 1000;
|
|
|
| - private static final String TAG = VSyncMonitor.class.getName();
|
| + private Listener mListener;
|
|
|
| // Display refresh rate as reported by the system.
|
| - private long mRefreshPeriodMicros;
|
| + private final long mRefreshPeriodNano;
|
|
|
| // Last time requestUpdate() was called.
|
| - private long mLastUpdateRequestMillis;
|
| + private long mLastUpdateRequestNano;
|
|
|
| - // Whether we are currently getting notified of vsync events.
|
| - private boolean mVSyncCallbackActive = false;
|
| + private boolean mHaveRequestInFlight;
|
| +
|
| + private int mTriggerNextVSyncCount;
|
| + private static final int MAX_VSYNC_COUNT = 5;
|
|
|
| // Choreographer is used to detect vsync on >= JB.
|
| - private Choreographer mChoreographer;
|
| - private Choreographer.FrameCallback mFrameCallback;
|
| + private final Choreographer mChoreographer;
|
| + private final Choreographer.FrameCallback mVSyncFrameCallback;
|
|
|
| - private Listener mListener;
|
| - private View mView;
|
| - private VSyncTerminator mVSyncTerminator;
|
| - private TestTimeSource mTestTimeSource;
|
| -
|
| - // VSyncTerminator keeps vsync running for a period of VSYNC_TIMEOUT_MILLISECONDS. If there is
|
| - // no update request during this period, the monitor will be stopped automatically.
|
| - private class VSyncTerminator implements Runnable {
|
| - public void run() {
|
| - if (currentTimeMillis() > mLastUpdateRequestMillis +
|
| - VSYNC_TIMEOUT_MILLISECONDS) {
|
| - stop();
|
| - } else if (mVSyncTerminator == this) {
|
| - mView.postDelayed(this, VSYNC_TIMEOUT_MILLISECONDS);
|
| - }
|
| - }
|
| + // On ICS we just post a task through the handler (http://crbug.com/156397)
|
| + private final Handler mHandler;
|
| + private final Runnable mVSyncRunnableCallback;
|
| + private long mGoodStartingPointNano;
|
| + private long mLastPostedNano;
|
| +
|
| + public VSyncMonitor(Context context, VSyncMonitor.Listener listener) {
|
| + this(context, listener, true);
|
| }
|
|
|
| - /**
|
| - * Constructor.
|
| - *
|
| - * @param context Resource context.
|
| - * @param view View to be used for timeout scheduling.
|
| - * @param listener Listener to be notified of vsync events.
|
| - */
|
| - public VSyncMonitor(Context context, View view, Listener listener) {
|
| + VSyncMonitor(Context context, VSyncMonitor.Listener listener, boolean enableJBVSync) {
|
| mListener = listener;
|
| - mView = view;
|
| -
|
| float refreshRate = ((WindowManager) context.getSystemService(Context.WINDOW_SERVICE))
|
| .getDefaultDisplay().getRefreshRate();
|
| - if (refreshRate <= 0) {
|
| - refreshRate = 60;
|
| - }
|
| - mRefreshPeriodMicros = (long) (MICROSECONDS_PER_SECOND / refreshRate);
|
| + if (refreshRate <= 0) refreshRate = 60;
|
| + mRefreshPeriodNano = (long) (NANOSECONDS_PER_SECOND / refreshRate);
|
| + mTriggerNextVSyncCount = 0;
|
|
|
| - // Use Choreographer on JB+ to get notified of vsync.
|
| - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) {
|
| + if (enableJBVSync && Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) {
|
| + // Use Choreographer on JB+ to get notified of vsync.
|
| mChoreographer = Choreographer.getInstance();
|
| - mFrameCallback = new Choreographer.FrameCallback() {
|
| + mVSyncFrameCallback = new Choreographer.FrameCallback() {
|
| @Override
|
| public void doFrame(long frameTimeNanos) {
|
| - postVSyncCallback();
|
| - mListener.onVSync(VSyncMonitor.this,
|
| - frameTimeNanos / NANOSECONDS_PER_MICROSECOND);
|
| + TraceEvent.instant("VSync");
|
| + onVSyncCallback(frameTimeNanos);
|
| + }
|
| + };
|
| + mHandler = null;
|
| + mVSyncRunnableCallback = null;
|
| + } else {
|
| + // On ICS we just hope that running tasks is relatively predictable.
|
| + mChoreographer = null;
|
| + mVSyncFrameCallback = null;
|
| + mHandler = new Handler();
|
| + mVSyncRunnableCallback = new Runnable() {
|
| + @Override
|
| + public void run() {
|
| + TraceEvent.instant("VSyncTimer");
|
| + onVSyncCallback(System.nanoTime());
|
| }
|
| };
|
| + mGoodStartingPointNano = getCurrentNanoTime();
|
| + mLastPostedNano = 0;
|
| }
|
| }
|
|
|
| @@ -104,7 +104,7 @@ public class VSyncMonitor {
|
| * Returns the time interval between two consecutive vsync pulses in microseconds.
|
| */
|
| public long getVSyncPeriodInMicroseconds() {
|
| - return mRefreshPeriodMicros;
|
| + return mRefreshPeriodNano / NANOSECONDS_PER_MICROSECOND;
|
| }
|
|
|
| /**
|
| @@ -115,54 +115,81 @@ public class VSyncMonitor {
|
| }
|
|
|
| /**
|
| - * Request to be notified of display vsync events. Listener.onVSync() will be called soon after
|
| - * the upcoming vsync pulses. If VSYNC_TIMEOUT_MILLISECONDS passes after the last time this
|
| - * function is called, the updates will cease automatically.
|
| - *
|
| - * This function throws an IllegalStateException if isVSyncSignalAvailable() returns false.
|
| + * Stop reporting vsync events. Note that at most one pending vsync event can still be delivered
|
| + * after this function is called.
|
| */
|
| - public void requestUpdate() {
|
| - if (!isVSyncSignalAvailable()) {
|
| - throw new IllegalStateException("VSync signal not available");
|
| - }
|
| - mLastUpdateRequestMillis = currentTimeMillis();
|
| - if (!mVSyncCallbackActive) {
|
| - mVSyncCallbackActive = true;
|
| - mVSyncTerminator = new VSyncTerminator();
|
| - mView.postDelayed(mVSyncTerminator, VSYNC_TIMEOUT_MILLISECONDS);
|
| - postVSyncCallback();
|
| - }
|
| + public void stop() {
|
| + mTriggerNextVSyncCount = 0;
|
| }
|
|
|
| - private void postVSyncCallback() {
|
| - if (!mVSyncCallbackActive) {
|
| - return;
|
| - }
|
| - mChoreographer.postFrameCallback(mFrameCallback);
|
| + /**
|
| + * Unregister the listener.
|
| + * No vsync events will be reported afterwards.
|
| + */
|
| + public void unregisterListener() {
|
| + stop();
|
| + mListener = null;
|
| }
|
|
|
| /**
|
| - * Stop reporting vsync events. Note that at most one pending vsync event can still be delivered
|
| - * after this function is called.
|
| + * Request to be notified of the closest display vsync events.
|
| + * Listener.onVSync() will be called soon after the upcoming vsync pulses.
|
| + * It will be called at most MAX_VSYNC_COUNT times unless requestUpdate() is called again.
|
| */
|
| - public void stop() {
|
| - mVSyncCallbackActive = false;
|
| - mVSyncTerminator = null;
|
| + public void requestUpdate() {
|
| + mTriggerNextVSyncCount = MAX_VSYNC_COUNT;
|
| + mLastUpdateRequestNano = getCurrentNanoTime();
|
| + postCallback();
|
| + }
|
| +
|
| + /**
|
| + * Set the best guess of the point in the past when the vsync has happened.
|
| + * @param goodStartingPointNano Known vsync point in the past.
|
| + */
|
| + public void setVSyncPointForICS(long goodStartingPointNano) {
|
| + mGoodStartingPointNano = goodStartingPointNano;
|
| + }
|
| +
|
| + private long getCurrentNanoTime() {
|
| + return System.nanoTime();
|
| }
|
|
|
| - public boolean isActive() {
|
| - return mVSyncCallbackActive;
|
| + private void onVSyncCallback(long frameTimeNanos) {
|
| + assert mHaveRequestInFlight;
|
| + mHaveRequestInFlight = false;
|
| + if (mTriggerNextVSyncCount > 0) {
|
| + mTriggerNextVSyncCount--;
|
| + postCallback();
|
| + }
|
| + if (mListener != null) {
|
| + mListener.onVSync(this, frameTimeNanos / NANOSECONDS_PER_MICROSECOND);
|
| + }
|
| }
|
|
|
| - void setTestDependencies(View testView, TestTimeSource testTimeSource) {
|
| - mView = testView;
|
| - mTestTimeSource = testTimeSource;
|
| + private void postCallback() {
|
| + if (mHaveRequestInFlight) return;
|
| + mHaveRequestInFlight = true;
|
| + if (isVSyncSignalAvailable()) {
|
| + mChoreographer.postFrameCallback(mVSyncFrameCallback);
|
| + } else {
|
| + postRunnableCallback();
|
| + }
|
| }
|
|
|
| - private long currentTimeMillis() {
|
| - if (mTestTimeSource != null) {
|
| - return mTestTimeSource.currentTimeMillis();
|
| + private void postRunnableCallback() {
|
| + assert !isVSyncSignalAvailable();
|
| + final long currentTime = mLastUpdateRequestNano;
|
| + final long lastRefreshTime = mGoodStartingPointNano +
|
| + ((currentTime - mGoodStartingPointNano) / mRefreshPeriodNano) * mRefreshPeriodNano;
|
| + long delay = (lastRefreshTime + mRefreshPeriodNano) - currentTime;
|
| + assert delay >= 0 && delay < mRefreshPeriodNano;
|
| +
|
| + if (currentTime + delay <= mLastPostedNano + mRefreshPeriodNano / 2) {
|
| + delay += mRefreshPeriodNano;
|
| }
|
| - return System.currentTimeMillis();
|
| +
|
| + mLastPostedNano = currentTime + delay;
|
| + if (delay == 0) mHandler.post(mVSyncRunnableCallback);
|
| + else mHandler.postDelayed(mVSyncRunnableCallback, delay / NANOSECONDS_PER_MILLISECOND);
|
| }
|
| }
|
|
|