Upstream version 10.38.208.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     /**
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
48     // Choreographer is used to detect vsync on >= JB.
49     private final Choreographer mChoreographer;
50     private final Choreographer.FrameCallback mVSyncFrameCallback;
51
52     // On ICS we just post a task through the handler (http://crbug.com/156397)
53     private final Runnable mVSyncRunnableCallback;
54     private long mGoodStartingPointNano;
55     private long mLastPostedNano;
56
57     // If the monitor is activated after having been idle, we synthesize the first vsync to reduce
58     // latency.
59     private final Handler mHandler = new Handler();
60     private final Runnable mSyntheticVSyncRunnable;
61     private long mLastVSyncCpuTimeNano;
62
63     /**
64      * Constructs a VSyncMonitor
65      * @param context The application context.
66      * @param listener The listener receiving VSync notifications.
67      */
68     public VSyncMonitor(Context context, VSyncMonitor.Listener listener) {
69         this(context, listener, true);
70     }
71
72     /**
73      * Constructs a VSyncMonitor
74      * @param context The application context.
75      * @param listener The listener receiving VSync notifications.
76      * @param enableJBVsync Whether to allow Choreographer-based notifications on JB and up.
77      */
78     public VSyncMonitor(Context context, VSyncMonitor.Listener listener, boolean enableJBVSync) {
79         mListener = listener;
80         float refreshRate = ((WindowManager) context.getSystemService(Context.WINDOW_SERVICE))
81                 .getDefaultDisplay().getRefreshRate();
82         if (refreshRate <= 0) refreshRate = 60;
83         mRefreshPeriodNano = (long) (NANOSECONDS_PER_SECOND / refreshRate);
84
85         if (enableJBVSync && Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) {
86             // Use Choreographer on JB+ to get notified of vsync.
87             mChoreographer = Choreographer.getInstance();
88             mVSyncFrameCallback = new Choreographer.FrameCallback() {
89                 @Override
90                 public void doFrame(long frameTimeNanos) {
91                     TraceEvent.begin("VSync");
92                     mGoodStartingPointNano = frameTimeNanos;
93                     onVSyncCallback(frameTimeNanos, getCurrentNanoTime());
94                     TraceEvent.end("VSync");
95                 }
96             };
97             mVSyncRunnableCallback = null;
98         } else {
99             // On ICS we just hope that running tasks is relatively predictable.
100             mChoreographer = null;
101             mVSyncFrameCallback = null;
102             mVSyncRunnableCallback = new Runnable() {
103                 @Override
104                 public void run() {
105                     TraceEvent.begin("VSyncTimer");
106                     final long currentTime = getCurrentNanoTime();
107                     onVSyncCallback(currentTime, currentTime);
108                     TraceEvent.end("VSyncTimer");
109                 }
110             };
111             mLastPostedNano = 0;
112         }
113         mSyntheticVSyncRunnable = new Runnable() {
114             @Override
115             public void run() {
116                 TraceEvent.begin("VSyncSynthetic");
117                 final long currentTime = getCurrentNanoTime();
118                 onVSyncCallback(estimateLastVSyncTime(currentTime), currentTime);
119                 TraceEvent.end("VSyncSynthetic");
120             }
121         };
122         mGoodStartingPointNano = getCurrentNanoTime();
123     }
124
125     /**
126      * Returns the time interval between two consecutive vsync pulses in microseconds.
127      */
128     public long getVSyncPeriodInMicroseconds() {
129         return mRefreshPeriodNano / NANOSECONDS_PER_MICROSECOND;
130     }
131
132     /**
133      * Determine whether a true vsync signal is available on this platform.
134      */
135     private boolean isVSyncSignalAvailable() {
136         return mChoreographer != null;
137     }
138
139     /**
140      * Request to be notified of the closest display vsync events.
141      * Listener.onVSync() will be called soon after the upcoming vsync pulses.
142      */
143     public void requestUpdate() {
144         postCallback();
145     }
146
147     /**
148      * Set the best guess of the point in the past when the vsync has happened.
149      * @param goodStartingPointNano Known vsync point in the past.
150      */
151     public void setVSyncPointForICS(long goodStartingPointNano) {
152         mGoodStartingPointNano = goodStartingPointNano;
153     }
154
155     /**
156      * @return true if onVSync handler is executing. If onVSync handler
157      * introduces invalidations, View#invalidate() should be called. If
158      * View#postInvalidateOnAnimation is called instead, the corresponding onDraw
159      * will be delayed by one frame. The embedder of VSyncMonitor should check
160      * this value if it wants to post an invalidation.
161      */
162     public boolean isInsideVSync() {
163         return mInsideVSync;
164     }
165
166     private long getCurrentNanoTime() {
167         return System.nanoTime();
168     }
169
170     private void onVSyncCallback(long frameTimeNanos, long currentTimeNanos) {
171         assert mHaveRequestInFlight;
172         mInsideVSync = true;
173         mHaveRequestInFlight = false;
174         mLastVSyncCpuTimeNano = currentTimeNanos;
175         try {
176             if (mListener != null) {
177                 mListener.onVSync(this, frameTimeNanos / NANOSECONDS_PER_MICROSECOND);
178             }
179         } finally {
180             mInsideVSync = false;
181         }
182     }
183
184     private void postCallback() {
185         if (mHaveRequestInFlight) return;
186         mHaveRequestInFlight = true;
187         if (postSyntheticVSync()) return;
188         if (isVSyncSignalAvailable()) {
189             mChoreographer.postFrameCallback(mVSyncFrameCallback);
190         } else {
191             postRunnableCallback();
192         }
193     }
194
195     private boolean postSyntheticVSync() {
196         final long currentTime = getCurrentNanoTime();
197         // Only trigger a synthetic vsync if we've been idle for long enough and the upcoming real
198         // vsync is more than half a frame away.
199         if (currentTime - mLastVSyncCpuTimeNano < 2 * mRefreshPeriodNano) return false;
200         if (currentTime - estimateLastVSyncTime(currentTime) > mRefreshPeriodNano / 2) return false;
201         mHandler.post(mSyntheticVSyncRunnable);
202         return true;
203     }
204
205     private long estimateLastVSyncTime(long currentTime) {
206         final long lastRefreshTime = mGoodStartingPointNano +
207                 ((currentTime - mGoodStartingPointNano) / mRefreshPeriodNano) * mRefreshPeriodNano;
208         return lastRefreshTime;
209     }
210
211     private void postRunnableCallback() {
212         assert !isVSyncSignalAvailable();
213         final long currentTime = getCurrentNanoTime();
214         final long lastRefreshTime = estimateLastVSyncTime(currentTime);
215         long delay = (lastRefreshTime + mRefreshPeriodNano) - currentTime;
216         assert delay > 0 && delay <= mRefreshPeriodNano;
217
218         if (currentTime + delay <= mLastPostedNano + mRefreshPeriodNano / 2) {
219             delay += mRefreshPeriodNano;
220         }
221
222         mLastPostedNano = currentTime + delay;
223         if (delay == 0) mHandler.post(mVSyncRunnableCallback);
224         else mHandler.postDelayed(mVSyncRunnableCallback, delay / NANOSECONDS_PER_MILLISECOND);
225     }
226 }