Upstream version 7.35.144.0
[platform/framework/web/crosswalk.git] / src / content / public / android / java / src / org / chromium / content / browser / VSyncMonitor.java
1 // Copyright 2012 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.content.browser;
6
7 import android.content.Context;
8 import android.os.Build;
9 import android.os.Handler;
10 import android.view.Choreographer;
11 import android.view.WindowManager;
12
13 import org.chromium.base.TraceEvent;
14
15 /**
16  * Notifies clients of the default displays's vertical sync pulses.
17  * This class works in "burst" mode: once the update is requested, the listener will be
18  * called MAX_VSYNC_COUNT times on the vertical sync pulses (on JB) or on every refresh
19  * period (on ICS, see below), unless stop() is called.
20  * On ICS, VSyncMonitor relies on setVSyncPointForICS() being called to set a reasonable
21  * approximation of a vertical sync starting point; see also http://crbug.com/156397.
22  */
23 public class VSyncMonitor {
24     private static final long NANOSECONDS_PER_SECOND = 1000000000;
25     private static final long NANOSECONDS_PER_MILLISECOND = 1000000;
26     private static final long NANOSECONDS_PER_MICROSECOND = 1000;
27     public static final int MAX_AUTO_ONVSYNC_COUNT = 5;
28
29     /**
30      * VSync listener class
31      */
32     public interface Listener {
33         /**
34          * Called very soon after the start of the display's vertical sync period.
35          * @param monitor The VSyncMonitor that triggered the signal.
36          * @param vsyncTimeMicros Absolute frame time in microseconds.
37          */
38         public void onVSync(VSyncMonitor monitor, long vsyncTimeMicros);
39     }
40
41     private Listener mListener;
42
43     // Display refresh rate as reported by the system.
44     private final long mRefreshPeriodNano;
45
46     private boolean mHaveRequestInFlight;
47     private int mTriggerNextVSyncCount;
48
49     // Choreographer is used to detect vsync on >= JB.
50     private final Choreographer mChoreographer;
51     private final Choreographer.FrameCallback mVSyncFrameCallback;
52
53     // On ICS we just post a task through the handler (http://crbug.com/156397)
54     private final Runnable mVSyncRunnableCallback;
55     private long mGoodStartingPointNano;
56     private long mLastPostedNano;
57
58     // If the monitor is activated after having been idle, we synthesize the first vsync to reduce
59     // latency.
60     private final Handler mHandler = new Handler();
61     private final Runnable mSyntheticVSyncRunnable;
62     private long mLastVSyncCpuTimeNano;
63
64     public VSyncMonitor(Context context, VSyncMonitor.Listener listener) {
65         this(context, listener, true);
66     }
67
68     VSyncMonitor(Context context, VSyncMonitor.Listener listener, boolean enableJBVSync) {
69         mListener = listener;
70         float refreshRate = ((WindowManager) context.getSystemService(Context.WINDOW_SERVICE))
71                 .getDefaultDisplay().getRefreshRate();
72         if (refreshRate <= 0) refreshRate = 60;
73         mRefreshPeriodNano = (long) (NANOSECONDS_PER_SECOND / refreshRate);
74         mTriggerNextVSyncCount = 0;
75
76         if (enableJBVSync && Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) {
77             // Use Choreographer on JB+ to get notified of vsync.
78             mChoreographer = Choreographer.getInstance();
79             mVSyncFrameCallback = new Choreographer.FrameCallback() {
80                 @Override
81                 public void doFrame(long frameTimeNanos) {
82                     TraceEvent.begin("VSync");
83                     mGoodStartingPointNano = frameTimeNanos;
84                     onVSyncCallback(frameTimeNanos, getCurrentNanoTime());
85                     TraceEvent.end("VSync");
86                 }
87             };
88             mVSyncRunnableCallback = null;
89         } else {
90             // On ICS we just hope that running tasks is relatively predictable.
91             mChoreographer = null;
92             mVSyncFrameCallback = null;
93             mVSyncRunnableCallback = new Runnable() {
94                 @Override
95                 public void run() {
96                     TraceEvent.begin("VSyncTimer");
97                     final long currentTime = getCurrentNanoTime();
98                     onVSyncCallback(currentTime, currentTime);
99                     TraceEvent.end("VSyncTimer");
100                 }
101             };
102             mLastPostedNano = 0;
103         }
104         mSyntheticVSyncRunnable = new Runnable() {
105             @Override
106             public void run() {
107                 TraceEvent.begin("VSyncSynthetic");
108                 final long currentTime = getCurrentNanoTime();
109                 onVSyncCallback(estimateLastVSyncTime(currentTime), currentTime);
110                 TraceEvent.end("VSyncSynthetic");
111             }
112         };
113         mGoodStartingPointNano = getCurrentNanoTime();
114     }
115
116     /**
117      * Returns the time interval between two consecutive vsync pulses in microseconds.
118      */
119     public long getVSyncPeriodInMicroseconds() {
120         return mRefreshPeriodNano / NANOSECONDS_PER_MICROSECOND;
121     }
122
123     /**
124      * Determine whether a true vsync signal is available on this platform.
125      */
126     public boolean isVSyncSignalAvailable() {
127         return mChoreographer != null;
128     }
129
130     /**
131      * Stop reporting vsync events. Note that at most one pending vsync event can still be delivered
132      * after this function is called.
133      */
134     public void stop() {
135         mTriggerNextVSyncCount = 0;
136     }
137
138     /**
139      * Unregister the listener.
140      * No vsync events will be reported afterwards.
141      */
142     public void unregisterListener() {
143         stop();
144         mListener = null;
145     }
146
147     /**
148      * Request to be notified of the closest display vsync events.
149      * Listener.onVSync() will be called soon after the upcoming vsync pulses.
150      * It will be called at most MAX_AUTO_ONVSYNC_COUNT times unless requestUpdate() is called.
151      */
152     public void requestUpdate() {
153         mTriggerNextVSyncCount = MAX_AUTO_ONVSYNC_COUNT;
154         postCallback();
155     }
156
157     /**
158      * Set the best guess of the point in the past when the vsync has happened.
159      * @param goodStartingPointNano Known vsync point in the past.
160      */
161     public void setVSyncPointForICS(long goodStartingPointNano) {
162         mGoodStartingPointNano = goodStartingPointNano;
163     }
164
165     private long getCurrentNanoTime() {
166         return System.nanoTime();
167     }
168
169     private void onVSyncCallback(long frameTimeNanos, long currentTimeNanos) {
170         assert mHaveRequestInFlight;
171         mHaveRequestInFlight = false;
172         mLastVSyncCpuTimeNano = currentTimeNanos;
173         if (mTriggerNextVSyncCount >= 0) {
174             mTriggerNextVSyncCount--;
175             postCallback();
176         }
177         if (mListener != null) {
178             mListener.onVSync(this, frameTimeNanos / NANOSECONDS_PER_MICROSECOND);
179         }
180     }
181
182     private void postCallback() {
183         if (mHaveRequestInFlight) return;
184         mHaveRequestInFlight = true;
185         if (postSyntheticVSync()) return;
186         if (isVSyncSignalAvailable()) {
187             mChoreographer.postFrameCallback(mVSyncFrameCallback);
188         } else {
189             postRunnableCallback();
190         }
191     }
192
193     private boolean postSyntheticVSync() {
194         final long currentTime = getCurrentNanoTime();
195         // Only trigger a synthetic vsync if we've been idle for long enough and the upcoming real
196         // vsync is more than half a frame away.
197         if (currentTime - mLastVSyncCpuTimeNano < 2 * mRefreshPeriodNano) return false;
198         if (currentTime - estimateLastVSyncTime(currentTime) > mRefreshPeriodNano / 2) return false;
199         mHandler.post(mSyntheticVSyncRunnable);
200         return true;
201     }
202
203     private long estimateLastVSyncTime(long currentTime) {
204         final long lastRefreshTime = mGoodStartingPointNano +
205                 ((currentTime - mGoodStartingPointNano) / mRefreshPeriodNano) * mRefreshPeriodNano;
206         return lastRefreshTime;
207     }
208
209     private void postRunnableCallback() {
210         assert !isVSyncSignalAvailable();
211         final long currentTime = getCurrentNanoTime();
212         final long lastRefreshTime = estimateLastVSyncTime(currentTime);
213         long delay = (lastRefreshTime + mRefreshPeriodNano) - currentTime;
214         assert delay > 0 && delay <= mRefreshPeriodNano;
215
216         if (currentTime + delay <= mLastPostedNano + mRefreshPeriodNano / 2) {
217             delay += mRefreshPeriodNano;
218         }
219
220         mLastPostedNano = currentTime + delay;
221         if (delay == 0) mHandler.post(mVSyncRunnableCallback);
222         else mHandler.postDelayed(mVSyncRunnableCallback, delay / NANOSECONDS_PER_MILLISECOND);
223     }
224 }