• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright 2023 The Chromium Authors
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.base.jank_tracker;
6 
7 import android.content.Context;
8 import android.hardware.display.DisplayManager;
9 import android.hardware.display.DisplayManager.DisplayListener;
10 import android.os.Build.VERSION_CODES;
11 import android.view.Display;
12 import android.view.FrameMetrics;
13 import android.view.Window;
14 import android.view.Window.OnFrameMetricsAvailableListener;
15 
16 import androidx.annotation.RequiresApi;
17 
18 import org.chromium.base.ContextUtils;
19 import org.chromium.base.TimeUtils;
20 import org.chromium.base.TraceEvent;
21 
22 import java.util.concurrent.atomic.AtomicBoolean;
23 
24 /**
25  * This class receives OnFrameMetricsAvailableListener.onFrameMetricsAvailable() callbacks and
26  * records frame durations in a FrameMetricsStore instance.
27  */
28 @RequiresApi(api = VERSION_CODES.N)
29 public class FrameMetricsListener implements OnFrameMetricsAvailableListener {
30     private class DisplayListenerBackend implements DisplayListener {
startListening()31         public void startListening() {
32             Context appCtx = ContextUtils.getApplicationContext();
33             DisplayManager displayManager =
34                     (DisplayManager) appCtx.getSystemService(Context.DISPLAY_SERVICE);
35             displayManager.registerDisplayListener(this, /* handler= */ null);
36         }
37 
38         @Override
onDisplayAdded(int sdkDisplayId)39         public void onDisplayAdded(int sdkDisplayId) {}
40 
41         @Override
onDisplayRemoved(int sdkDisplayId)42         public void onDisplayRemoved(int sdkDisplayId) {}
43 
44         @Override
onDisplayChanged(int sdkDisplayId)45         public void onDisplayChanged(int sdkDisplayId) {
46             maybeUpdateRefreshRate();
47         }
48     }
49 
50     private DisplayListenerBackend mBackend = new DisplayListenerBackend();
51 
52     private final FrameMetricsStore mFrameMetricsStore;
53     private AtomicBoolean mIsRecording = new AtomicBoolean(false);
54     // Microseconds between each frame.
55     private long mVsyncInterval;
56 
FrameMetricsListener(FrameMetricsStore frameMetricsStore)57     public FrameMetricsListener(FrameMetricsStore frameMetricsStore) {
58         mFrameMetricsStore = frameMetricsStore;
59         mBackend.startListening();
60         maybeUpdateRefreshRate();
61     }
62 
maybeUpdateRefreshRate()63     private void maybeUpdateRefreshRate() {
64         Context appCtx = ContextUtils.getApplicationContext();
65         DisplayManager displayManager =
66                 (DisplayManager) appCtx.getSystemService(Context.DISPLAY_SERVICE);
67         Display display = displayManager.getDisplay(Display.DEFAULT_DISPLAY);
68         if (display == null) {
69             return;
70         }
71         float refreshRate = display.getRefreshRate();
72         final long kMicrosecondsPerSecond = 1000_000L;
73         mVsyncInterval = kMicrosecondsPerSecond / ((long) refreshRate);
74         TraceEvent.instant(
75                 "FrameMetricsListener.maybeUpdateRefreshRate", Long.toString(mVsyncInterval));
76     }
77 
78     /**
79      * Toggles recording into FrameMetricsStore. When recording is stopped, reports accumulated
80      * metrics.
81      * @param isRecording
82      */
setIsListenerRecording(boolean isRecording)83     public void setIsListenerRecording(boolean isRecording) {
84         mIsRecording.set(isRecording);
85     }
86 
87     @RequiresApi(api = VERSION_CODES.N)
88     @Override
onFrameMetricsAvailable( Window window, FrameMetrics frameMetrics, int dropCountSinceLastInvocation)89     public void onFrameMetricsAvailable(
90             Window window, FrameMetrics frameMetrics, int dropCountSinceLastInvocation) {
91         if (!mIsRecording.get()) {
92             return;
93         }
94 
95         long frameTotalDurationNs = frameMetrics.getMetric(FrameMetrics.TOTAL_DURATION);
96         long frame_start_vsync_ts = frameMetrics.getMetric(FrameMetrics.VSYNC_TIMESTAMP);
97 
98         try (TraceEvent e =
99                 TraceEvent.scoped("onFrameMetricsAvailable", Long.toString(frameTotalDurationNs))) {
100             // FrameMetrics.DEADLINE was added in API level 31(S).
101             // TODO(b/311139161): Update RequiresApi level to Android S.
102             long deadlineNs = frameMetrics.getMetric(FrameMetrics.DEADLINE);
103             int missedVsyncs = 0;
104             if (frameTotalDurationNs >= deadlineNs) {
105                 long frameDeadlineDeltaUs =
106                         (frameTotalDurationNs - deadlineNs) / TimeUtils.NANOSECONDS_PER_MICROSECOND;
107                 missedVsyncs = (int) ((frameDeadlineDeltaUs + mVsyncInterval) / mVsyncInterval);
108             }
109             mFrameMetricsStore.addFrameMeasurement(
110                     frameTotalDurationNs, missedVsyncs, frame_start_vsync_ts);
111         }
112     }
113 }
114