Update To 11.40.268.0
[platform/framework/web/crosswalk.git] / src / ui / android / java / src / org / chromium / ui / VSyncMonitor.java
1 // Copyright 2014 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 package org.chromium.ui;
6
7 import android.annotation.SuppressLint;
8 import android.content.Context;
9 import android.os.Build;
10 import android.os.Handler;
11 import android.view.Choreographer;
12 import android.view.WindowManager;
13
14 import org.chromium.base.TraceEvent;
15
16 /**
17  * Notifies clients of the default displays's vertical sync pulses.
18  * On ICS, VSyncMonitor relies on setVSyncPointForICS() being called to set a reasonable
19  * approximation of a vertical sync starting point; see also http://crbug.com/156397.
20  */
21 @SuppressLint("NewApi")
22 public class VSyncMonitor {
23     private static final long NANOSECONDS_PER_SECOND = 1000000000;
24     private static final long NANOSECONDS_PER_MILLISECOND = 1000000;
25     private static final long NANOSECONDS_PER_MICROSECOND = 1000;
26
27     private boolean mInsideVSync = false;
28
29     // Conservative guess about vsync's consecutivity.
30     // If true, next tick is guaranteed to be consecutive.
31     private boolean mConsecutiveVSync = false;
32
33     /**
34      * VSync listener class
35      */
36     public interface Listener {
37         /**
38          * Called very soon after the start of the display's vertical sync period.
39          * @param monitor The VSyncMonitor that triggered the signal.
40          * @param vsyncTimeMicros Absolute frame time in microseconds.
41          */
42         public void onVSync(VSyncMonitor monitor, long vsyncTimeMicros);
43     }
44
45     private Listener mListener;
46
47     // Display refresh rate as reported by the system.
48     private long mRefreshPeriodNano;
49
50     private boolean mHaveRequestInFlight;
51
52     // Choreographer is used to detect vsync on >= JB.
53     private final Choreographer mChoreographer;
54     private final Choreographer.FrameCallback mVSyncFrameCallback;
55
56     // On ICS we just post a task through the handler (http://crbug.com/156397)
57     private final Runnable mVSyncRunnableCallback;
58     private long mGoodStartingPointNano;
59     private long mLastPostedNano;
60
61     // If the monitor is activated after having been idle, we synthesize the first vsync to reduce
62     // latency.
63     private final Handler mHandler = new Handler();
64     private final Runnable mSyntheticVSyncRunnable;
65     private long mLastVSyncCpuTimeNano;
66
67     /**
68      * Constructs a VSyncMonitor
69      * @param context The application context.
70      * @param listener The listener receiving VSync notifications.
71      */
72     public VSyncMonitor(Context context, VSyncMonitor.Listener listener) {
73         this(context, listener, true);
74     }
75
76     /**
77      * Constructs a VSyncMonitor
78      * @param context The application context.
79      * @param listener The listener receiving VSync notifications.
80      * @param enableJBVsync Whether to allow Choreographer-based notifications on JB and up.
81      */
82     public VSyncMonitor(Context context, VSyncMonitor.Listener listener, boolean enableJBVSync) {
83         mListener = listener;
84         float refreshRate = ((WindowManager) context.getSystemService(Context.WINDOW_SERVICE))
85                 .getDefaultDisplay().getRefreshRate();
86         final boolean useEstimatedRefreshPeriod = refreshRate < 30;
87
88         if (refreshRate <= 0) refreshRate = 60;
89         mRefreshPeriodNano = (long) (NANOSECONDS_PER_SECOND / refreshRate);
90
91         if (enableJBVSync && Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) {
92             // Use Choreographer on JB+ to get notified of vsync.
93             mChoreographer = Choreographer.getInstance();
94             mVSyncFrameCallback = new Choreographer.FrameCallback() {
95                 @Override
96                 public void doFrame(long frameTimeNanos) {
97                     TraceEvent.begin("VSync");
98                     if (useEstimatedRefreshPeriod && mConsecutiveVSync) {
99                         // Display.getRefreshRate() is unreliable on some platforms.
100                         // Adjust refresh period- initial value is based on Display.getRefreshRate()
101                         // after that it asymptotically approaches the real value.
102                         long lastRefreshDurationNano = frameTimeNanos - mGoodStartingPointNano;
103                         float lastRefreshDurationWeight = 0.1f;
104                         mRefreshPeriodNano += (long) (lastRefreshDurationWeight *
105                                 (lastRefreshDurationNano - mRefreshPeriodNano));
106                     }
107                     mGoodStartingPointNano = frameTimeNanos;
108                     onVSyncCallback(frameTimeNanos, getCurrentNanoTime());
109                     TraceEvent.end("VSync");
110                 }
111             };
112             mVSyncRunnableCallback = null;
113         } else {
114             // On ICS we just hope that running tasks is relatively predictable.
115             mChoreographer = null;
116             mVSyncFrameCallback = null;
117             mVSyncRunnableCallback = new Runnable() {
118                 @Override
119                 public void run() {
120                     TraceEvent.begin("VSyncTimer");
121                     final long currentTime = getCurrentNanoTime();
122                     onVSyncCallback(currentTime, currentTime);
123                     TraceEvent.end("VSyncTimer");
124                 }
125             };
126             mLastPostedNano = 0;
127         }
128         mSyntheticVSyncRunnable = new Runnable() {
129             @Override
130             public void run() {
131                 TraceEvent.begin("VSyncSynthetic");
132                 final long currentTime = getCurrentNanoTime();
133                 onVSyncCallback(estimateLastVSyncTime(currentTime), currentTime);
134                 TraceEvent.end("VSyncSynthetic");
135             }
136         };
137         mGoodStartingPointNano = getCurrentNanoTime();
138     }
139
140     /**
141      * Returns the time interval between two consecutive vsync pulses in microseconds.
142      */
143     public long getVSyncPeriodInMicroseconds() {
144         return mRefreshPeriodNano / NANOSECONDS_PER_MICROSECOND;
145     }
146
147     /**
148      * Determine whether a true vsync signal is available on this platform.
149      */
150     private boolean isVSyncSignalAvailable() {
151         return mChoreographer != null;
152     }
153
154     /**
155      * Request to be notified of the closest display vsync events.
156      * Listener.onVSync() will be called soon after the upcoming vsync pulses.
157      */
158     public void requestUpdate() {
159         postCallback();
160     }
161
162     /**
163      * Set the best guess of the point in the past when the vsync has happened.
164      * @param goodStartingPointNano Known vsync point in the past.
165      */
166     public void setVSyncPointForICS(long goodStartingPointNano) {
167         mGoodStartingPointNano = goodStartingPointNano;
168     }
169
170     /**
171      * @return true if onVSync handler is executing. If onVSync handler
172      * introduces invalidations, View#invalidate() should be called. If
173      * View#postInvalidateOnAnimation is called instead, the corresponding onDraw
174      * will be delayed by one frame. The embedder of VSyncMonitor should check
175      * this value if it wants to post an invalidation.
176      */
177     public boolean isInsideVSync() {
178         return mInsideVSync;
179     }
180
181     private long getCurrentNanoTime() {
182         return System.nanoTime();
183     }
184
185     private void onVSyncCallback(long frameTimeNanos, long currentTimeNanos) {
186         assert mHaveRequestInFlight;
187         mInsideVSync = true;
188         mHaveRequestInFlight = false;
189         mLastVSyncCpuTimeNano = currentTimeNanos;
190         try {
191             if (mListener != null) {
192                 mListener.onVSync(this, frameTimeNanos / NANOSECONDS_PER_MICROSECOND);
193             }
194         } finally {
195             mInsideVSync = false;
196         }
197     }
198
199     private void postCallback() {
200         if (mHaveRequestInFlight) return;
201         mHaveRequestInFlight = true;
202         if (postSyntheticVSync()) return;
203         if (isVSyncSignalAvailable()) {
204             mConsecutiveVSync = mInsideVSync;
205             mChoreographer.postFrameCallback(mVSyncFrameCallback);
206         } else {
207             postRunnableCallback();
208         }
209     }
210
211     private boolean postSyntheticVSync() {
212         final long currentTime = getCurrentNanoTime();
213         // Only trigger a synthetic vsync if we've been idle for long enough and the upcoming real
214         // vsync is more than half a frame away.
215         if (currentTime - mLastVSyncCpuTimeNano < 2 * mRefreshPeriodNano) return false;
216         if (currentTime - estimateLastVSyncTime(currentTime) > mRefreshPeriodNano / 2) return false;
217         mHandler.post(mSyntheticVSyncRunnable);
218         return true;
219     }
220
221     private long estimateLastVSyncTime(long currentTime) {
222         final long lastRefreshTime = mGoodStartingPointNano +
223                 ((currentTime - mGoodStartingPointNano) / mRefreshPeriodNano) * mRefreshPeriodNano;
224         return lastRefreshTime;
225     }
226
227     private void postRunnableCallback() {
228         assert !isVSyncSignalAvailable();
229         final long currentTime = getCurrentNanoTime();
230         final long lastRefreshTime = estimateLastVSyncTime(currentTime);
231         long delay = (lastRefreshTime + mRefreshPeriodNano) - currentTime;
232         assert delay > 0 && delay <= mRefreshPeriodNano;
233
234         if (currentTime + delay <= mLastPostedNano + mRefreshPeriodNano / 2) {
235             delay += mRefreshPeriodNano;
236         }
237
238         mLastPostedNano = currentTime + delay;
239         if (delay == 0) mHandler.post(mVSyncRunnableCallback);
240         else mHandler.postDelayed(mVSyncRunnableCallback, delay / NANOSECONDS_PER_MILLISECOND);
241     }
242 }