Home | History | Annotate | Download | only in graphicsstats
      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.server.cts.device.graphicsstats;
     18 
     19 import android.app.Activity;
     20 import android.graphics.Canvas;
     21 import android.graphics.Color;
     22 import android.os.Bundle;
     23 import android.os.Handler;
     24 import android.view.Choreographer;
     25 import android.view.FrameMetrics;
     26 import android.view.View;
     27 import android.view.Window;
     28 
     29 import java.util.concurrent.CountDownLatch;
     30 import java.util.concurrent.TimeUnit;
     31 import java.util.concurrent.TimeoutException;
     32 
     33 public class DrawFramesActivity extends Activity implements Window.OnFrameMetricsAvailableListener {
     34 
     35     public static final int FRAME_JANK_RECORD_DRAW = 1 << 0;
     36     public static final int FRAME_JANK_ANIMATION = 1 << 1;
     37     public static final int FRAME_JANK_LAYOUT = 1 << 2;
     38     public static final int FRAME_JANK_DAVEY_JR = 1 << 3;
     39     public static final int FRAME_JANK_DAVEY = 1 << 4;
     40     public static final int FRAME_JANK_MISS_VSYNC = 1 << 5;
     41 
     42     private static final String TAG = "GraphicsStatsDeviceTest";
     43 
     44     private static final int[] COLORS = new int[] {
     45             Color.RED,
     46             Color.GREEN,
     47             Color.BLUE,
     48     };
     49 
     50     private View mColorView;
     51     private int mColorIndex;
     52     private final CountDownLatch mReady = new CountDownLatch(1);
     53     private Choreographer mChoreographer;
     54     private CountDownLatch mFramesFinishedFence = mReady;
     55     private int mFrameIndex;
     56     private int[] mFramesToDraw;
     57     private int mDroppedReportsCount = 0;
     58     private int mRenderedFrames = 0;
     59 
     60     @Override
     61     public void onCreate(Bundle bundle) {
     62         super.onCreate(bundle);
     63         getWindow().addOnFrameMetricsAvailableListener(this, new Handler());
     64 
     65         mChoreographer = Choreographer.getInstance();
     66         mColorView = new View(this) {
     67             {
     68                 setWillNotDraw(false);
     69             }
     70 
     71             @Override
     72             protected void onDraw(Canvas canvas) {
     73                 jankIf(FRAME_JANK_RECORD_DRAW);
     74             }
     75 
     76             @Override
     77             public void layout(int l, int t, int r, int b) {
     78                 super.layout(l, t, r, b);
     79                 jankIf(FRAME_JANK_LAYOUT);
     80             }
     81         };
     82         updateColor();
     83         setContentView(mColorView);
     84     }
     85 
     86     private void setupFrame() {
     87         updateColor();
     88         if (isFrameFlagSet(FRAME_JANK_LAYOUT)) {
     89             mColorView.requestLayout();
     90         }
     91         if (isFrameFlagSet(FRAME_JANK_DAVEY_JR)) {
     92             spinSleep(150);
     93         }
     94         if (isFrameFlagSet(FRAME_JANK_DAVEY)) {
     95             spinSleep(700);
     96         }
     97     }
     98 
     99     private void updateColor() {
    100         mColorView.setBackgroundColor(COLORS[mColorIndex]);
    101         // allow COLORs to be length == 1 or have duplicates without breaking the test
    102         mColorView.invalidate();
    103         mColorIndex = (mColorIndex + 1) % COLORS.length;
    104     }
    105 
    106     private void jankIf(int flagIsSet) {
    107         if (isFrameFlagSet(flagIsSet)) {
    108             jank();
    109         }
    110     }
    111 
    112     private boolean isFrameFlagSet(int flag) {
    113         return mFramesToDraw != null && (mFramesToDraw[mFrameIndex] & flag) != 0;
    114     }
    115 
    116     private void jank() {
    117         spinSleep(24);
    118     }
    119 
    120     private void spinSleep(int durationMs) {
    121         long until = System.currentTimeMillis() + durationMs;
    122         while (System.currentTimeMillis() < until) {}
    123     }
    124 
    125     private void scheduleDraw() {
    126         mChoreographer.postFrameCallback((long timestamp) -> {
    127             setupFrame();
    128             jankIf(FRAME_JANK_ANIMATION);
    129         });
    130         if (isFrameFlagSet(FRAME_JANK_MISS_VSYNC)) {
    131             spinSleep(45);
    132         }
    133     }
    134 
    135     private void onDrawFinished() {
    136         if (mFramesToDraw != null && mFrameIndex < mFramesToDraw.length - 1) {
    137             mFrameIndex++;
    138             scheduleDraw();
    139         } else if (mFramesFinishedFence != null) {
    140             mFramesFinishedFence.countDown();
    141             mFramesFinishedFence = null;
    142             mFramesToDraw = null;
    143         }
    144     }
    145 
    146     public void drawFrames(final int frameCount) throws InterruptedException, TimeoutException {
    147         drawFrames(new int[frameCount]);
    148     }
    149 
    150     public void waitForReady() throws InterruptedException, TimeoutException {
    151         if (!mReady.await(4, TimeUnit.SECONDS)) {
    152             throw new TimeoutException();
    153         }
    154     }
    155 
    156     public void drawFrames(final int[] framesToDraw) throws InterruptedException, TimeoutException {
    157         if (!mReady.await(4, TimeUnit.SECONDS)) {
    158             throw new TimeoutException();
    159         }
    160         final CountDownLatch fence = new CountDownLatch(1);
    161         long timeoutDurationMs = 0;
    162         for (int frame : framesToDraw) {
    163             // 50ms base time + 20ms for every extra jank event
    164             timeoutDurationMs += 50 + (24 * Integer.bitCount(frame));
    165             if ((frame & FRAME_JANK_DAVEY_JR) != 0) {
    166                 timeoutDurationMs += 150;
    167             }
    168             if ((frame & FRAME_JANK_DAVEY) != 0) {
    169                 timeoutDurationMs += 700;
    170             }
    171         }
    172         runOnUiThread(() -> {
    173             mFramesToDraw = framesToDraw;
    174             mFrameIndex = 0;
    175             mFramesFinishedFence = fence;
    176             scheduleDraw();
    177         });
    178         if (!fence.await(timeoutDurationMs, TimeUnit.MILLISECONDS)) {
    179             throw new TimeoutException("Drawing " + framesToDraw.length + " frames timed out after "
    180                     + timeoutDurationMs + "ms");
    181         }
    182     }
    183 
    184     public int getRenderedFramesCount() {
    185         return mRenderedFrames;
    186     }
    187 
    188     public int getDroppedReportsCount() {
    189         return mDroppedReportsCount;
    190     }
    191 
    192     @Override
    193     public void onFrameMetricsAvailable(Window window, FrameMetrics frameMetrics,
    194             int dropCountSinceLastInvocation) {
    195         mDroppedReportsCount += dropCountSinceLastInvocation;
    196         mRenderedFrames++;
    197         onDrawFinished();
    198     }
    199 }
    200