1 /*
2  * Copyright (C) 2017 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License
15  */
16 
17 package com.android.internal.view;
18 
19 import android.os.Handler;
20 import android.os.Message;
21 import android.view.Choreographer;
22 import android.view.Display;
23 
24 /**
25  * Utility class to schedule things at vsync-sf instead of vsync-app
26  * @hide
27  */
28 public class SurfaceFlingerVsyncChoreographer {
29 
30     private static final long ONE_MS_IN_NS = 1000000;
31     private static final long ONE_S_IN_NS = ONE_MS_IN_NS * 1000;
32 
33     private final Handler mHandler;
34     private final Choreographer mChoreographer;
35 
36     /**
37      * The offset between vsync-app and vsync-surfaceflinger. See
38      * {@link #calculateAppSurfaceFlingerVsyncOffsetMs} why this is necessary.
39      */
40     private long mSurfaceFlingerOffsetMs;
41 
SurfaceFlingerVsyncChoreographer(Handler handler, Display display, Choreographer choreographer)42     public SurfaceFlingerVsyncChoreographer(Handler handler, Display display,
43             Choreographer choreographer) {
44         mHandler = handler;
45         mChoreographer = choreographer;
46         mSurfaceFlingerOffsetMs = calculateAppSurfaceFlingerVsyncOffsetMs(display);
47     }
48 
getSurfaceFlingerOffsetMs()49     public long getSurfaceFlingerOffsetMs() {
50         return mSurfaceFlingerOffsetMs;
51     }
52 
53     /**
54      * This method calculates the offset between vsync-surfaceflinger and vsync-app. If vsync-app
55      * is a couple of milliseconds before vsync-sf, a touch or animation event that causes a surface
56      * flinger transaction are sometimes processed before the vsync-sf tick, and sometimes after,
57      * which leads to jank. Figure out this difference here and then post all the touch/animation
58      * events to start being processed at vsync-sf.
59      *
60      * @return The offset between vsync-app and vsync-sf, or 0 if vsync app happens after vsync-sf.
61      */
calculateAppSurfaceFlingerVsyncOffsetMs(Display display)62     private long calculateAppSurfaceFlingerVsyncOffsetMs(Display display) {
63 
64         // Calculate vsync offset from SurfaceFlinger.
65         // See frameworks/native/services/surfaceflinger/SurfaceFlinger.cpp:getDisplayConfigs
66         long vsyncPeriod = (long) (ONE_S_IN_NS / display.getRefreshRate());
67         long sfVsyncOffset = vsyncPeriod - (display.getPresentationDeadlineNanos() - ONE_MS_IN_NS);
68         return Math.max(0, (sfVsyncOffset - display.getAppVsyncOffsetNanos()) / ONE_MS_IN_NS);
69     }
70 
scheduleAtSfVsync(Runnable r)71     public void scheduleAtSfVsync(Runnable r) {
72         final long delay = calculateDelay();
73         if (delay <= 0) {
74             r.run();
75         } else {
76             mHandler.postDelayed(r, delay);
77         }
78     }
79 
scheduleAtSfVsync(Handler h, Message m)80     public void scheduleAtSfVsync(Handler h, Message m) {
81         final long delay = calculateDelay();
82         if (delay <= 0) {
83             h.handleMessage(m);
84         } else {
85             m.setAsynchronous(true);
86             h.sendMessageDelayed(m, delay);
87         }
88     }
89 
calculateDelay()90     private long calculateDelay() {
91         final long sinceFrameStart = System.nanoTime() - mChoreographer.getLastFrameTimeNanos();
92         return mSurfaceFlingerOffsetMs - sinceFrameStart / 1000000;
93     }
94 }
95