Home | History | Annotate | Download | only in uibench
      1 /*
      2  * Copyright (C) 2016 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.test.uibench;
     18 
     19 import android.app.Activity;
     20 import android.content.Context;
     21 import android.graphics.Canvas;
     22 import android.graphics.Color;
     23 import android.graphics.ColorFilter;
     24 import android.graphics.Paint;
     25 import android.graphics.PixelFormat;
     26 import android.graphics.Rect;
     27 import android.graphics.drawable.Drawable;
     28 import android.os.Bundle;
     29 import android.os.Handler;
     30 import android.os.HandlerThread;
     31 import android.os.Message;
     32 import android.util.AttributeSet;
     33 import android.view.FrameMetrics;
     34 import android.view.View;
     35 import android.view.Window;
     36 import android.view.Window.OnFrameMetricsAvailableListener;
     37 import android.view.animation.AnimationUtils;
     38 import android.widget.TextView;
     39 
     40 public class RenderingJitter extends Activity {
     41     private TextView mJitterReport;
     42     private TextView mUiFrameTimeReport;
     43     private TextView mRenderThreadTimeReport;
     44     private TextView mTotalFrameTimeReport;
     45     private TextView mMostlyTotalFrameTimeReport;
     46     private PointGraphView mGraph;
     47 
     48     private static Handler sMetricsHandler;
     49     static {
     50         HandlerThread thread = new HandlerThread("frameMetricsListener");
     51         thread.start();
     52         sMetricsHandler = new Handler(thread.getLooper());
     53     }
     54 
     55     private Handler mUpdateHandler = new Handler() {
     56         @Override
     57         public void handleMessage(Message msg) {
     58             switch (msg.what) {
     59                 case R.id.jitter_mma:
     60                     mJitterReport.setText((CharSequence) msg.obj);
     61                     break;
     62                 case R.id.totalish_mma:
     63                     mMostlyTotalFrameTimeReport.setText((CharSequence) msg.obj);
     64                     break;
     65                 case R.id.ui_frametime_mma:
     66                     mUiFrameTimeReport.setText((CharSequence) msg.obj);
     67                     break;
     68                 case R.id.rt_frametime_mma:
     69                     mRenderThreadTimeReport.setText((CharSequence) msg.obj);
     70                     break;
     71                 case R.id.total_mma:
     72                     mTotalFrameTimeReport.setText((CharSequence) msg.obj);
     73                     break;
     74                 case R.id.graph:
     75                     mGraph.addJitterSample(msg.arg1, msg.arg2);
     76                     break;
     77             }
     78         }
     79     };
     80 
     81     @Override
     82     protected void onCreate(Bundle savedInstanceState) {
     83         super.onCreate(savedInstanceState);
     84         setContentView(R.layout.rendering_jitter);
     85         View content = findViewById(android.R.id.content);
     86         content.setBackground(new AnimatedBackgroundDrawable());
     87         content.setKeepScreenOn(true);
     88         mJitterReport = findViewById(R.id.jitter_mma);
     89         mMostlyTotalFrameTimeReport = findViewById(R.id.totalish_mma);
     90         mUiFrameTimeReport = findViewById(R.id.ui_frametime_mma);
     91         mRenderThreadTimeReport = findViewById(R.id.rt_frametime_mma);
     92         mTotalFrameTimeReport = findViewById(R.id.total_mma);
     93         mGraph = findViewById(R.id.graph);
     94         mJitterReport.setText("abcdefghijklmnopqrstuvwxyz");
     95         mMostlyTotalFrameTimeReport.setText("ABCDEFGHIJKLMNOPQRSTUVWXYZ");
     96         mUiFrameTimeReport.setText("0123456789");
     97         mRenderThreadTimeReport.setText(",.!()[]{};");
     98         getWindow().addOnFrameMetricsAvailableListener(mMetricsListener, sMetricsHandler);
     99     }
    100 
    101     public static final class PointGraphView extends View {
    102         private static final float[] JITTER_LINES_MS = {
    103                 .5f, 1.0f, 1.5f, 2.0f, 3.0f, 4.0f, 5.0f
    104         };
    105         private static final String[] JITTER_LINES_LABELS = makeLabels(JITTER_LINES_MS);
    106         private static final int[] JITTER_LINES_COLORS = new int[] {
    107                 0xFF00E676, 0xFFFFF176, 0xFFFDD835, 0xFFFBC02D, 0xFFF9A825,
    108                 0xFFF57F17, 0xFFDD2C00
    109         };
    110         private Paint mPaint = new Paint();
    111         private float[] mJitterYs = new float[JITTER_LINES_MS.length];
    112         private float mLabelWidth;
    113         private float mLabelHeight;
    114         private float mDensity;
    115         private float mGraphScale;
    116         private float mGraphMaxMs;
    117 
    118         private float[] mJitterPoints;
    119         private float[] mJitterAvgPoints;
    120 
    121         public PointGraphView(Context context, AttributeSet attrs) {
    122             super(context, attrs);
    123             setWillNotDraw(false);
    124             mDensity = context.getResources().getDisplayMetrics().density;
    125             mPaint.setTextSize(dp(10));
    126             Rect textBounds = new Rect();
    127             mPaint.getTextBounds("8.8", 0, 3, textBounds);
    128             mLabelWidth = textBounds.width() + dp(2);
    129             mLabelHeight = textBounds.height();
    130         }
    131 
    132         public void addJitterSample(int jitterUs, int jitterUsAvg) {
    133             for (int i = 1; i < mJitterPoints.length - 2; i += 2) {
    134                 mJitterPoints[i] = mJitterPoints[i + 2];
    135                 mJitterAvgPoints[i] = mJitterAvgPoints[i + 2];
    136             }
    137             mJitterPoints[mJitterPoints.length - 1] =
    138                     getHeight() - mGraphScale * (jitterUs / 1000.0f);
    139             mJitterAvgPoints[mJitterAvgPoints.length - 1] =
    140                     getHeight() - mGraphScale * (jitterUsAvg / 1000.0f);
    141             invalidate();
    142         }
    143 
    144         private float dp(float dp) {
    145             return mDensity * dp;
    146         }
    147 
    148         @Override
    149         protected void onDraw(Canvas canvas) {
    150             canvas.drawColor(0x90000000);
    151             int h = getHeight();
    152             int w = getWidth();
    153             mPaint.setColor(Color.WHITE);
    154             mPaint.setStrokeWidth(dp(1));
    155             canvas.drawLine(mLabelWidth, 0, mLabelWidth, h, mPaint);
    156             for (int i = 0; i < JITTER_LINES_LABELS.length; i++) {
    157                 canvas.drawText(JITTER_LINES_LABELS[i],
    158                         0, (float) Math.floor(mJitterYs[i] + mLabelHeight * .5f), mPaint);
    159             }
    160             for (int i = 0; i < JITTER_LINES_LABELS.length; i++) {
    161                 mPaint.setColor(JITTER_LINES_COLORS[i]);
    162                 canvas.drawLine(mLabelWidth, mJitterYs[i], w, mJitterYs[i], mPaint);
    163             }
    164             mPaint.setStrokeWidth(dp(2));
    165             mPaint.setColor(Color.WHITE);
    166             canvas.drawPoints(mJitterPoints, mPaint);
    167             mPaint.setColor(0xFF2196F3);
    168             canvas.drawPoints(mJitterAvgPoints, mPaint);
    169         }
    170 
    171         @Override
    172         protected void onSizeChanged(int w, int h, int oldw, int oldh) {
    173             super.onSizeChanged(w, h, oldw, oldh);
    174             int graphWidth = (int) ((w - mLabelWidth - dp(1)) / mDensity);
    175             float[] oldJitterPoints = mJitterPoints;
    176             float[] oldJitterAvgPoints = mJitterAvgPoints;
    177             mJitterPoints = new float[graphWidth * 2];
    178             mJitterAvgPoints = new float[graphWidth * 2];
    179             for (int i = 0; i < mJitterPoints.length; i += 2) {
    180                 mJitterPoints[i] = mLabelWidth + (i / 2 + 1) * mDensity;
    181                 mJitterAvgPoints[i] = mJitterPoints[i];
    182             }
    183             if (oldJitterPoints != null) {
    184                 int newIndexShift = Math.max(mJitterPoints.length - oldJitterPoints.length, 0);
    185                 int oldIndexShift = oldJitterPoints.length - mJitterPoints.length;
    186                 for (int i = 1 + newIndexShift; i < mJitterPoints.length; i += 2) {
    187                     mJitterPoints[i] = oldJitterPoints[i + oldIndexShift];
    188                     mJitterAvgPoints[i] = oldJitterAvgPoints[i + oldIndexShift];
    189                 }
    190             }
    191             mGraphMaxMs = JITTER_LINES_MS[JITTER_LINES_MS.length - 1] + .5f;
    192             mGraphScale = (h / mGraphMaxMs);
    193             for (int i = 0; i < JITTER_LINES_MS.length; i++) {
    194                 mJitterYs[i] = (float) Math.floor(h - mGraphScale * JITTER_LINES_MS[i]);
    195             }
    196         }
    197 
    198         private static String[] makeLabels(float[] divisions) {
    199             String[] ret = new String[divisions.length];
    200             for (int i = 0; i < divisions.length; i++) {
    201                 ret[i] = Float.toString(divisions[i]);
    202             }
    203             return ret;
    204         }
    205     }
    206 
    207     private final OnFrameMetricsAvailableListener mMetricsListener = new OnFrameMetricsAvailableListener() {
    208         private final static double WEIGHT = 40;
    209         private long mPreviousFrameTotal;
    210         private double mJitterMma;
    211         private double mUiFrametimeMma;
    212         private double mRtFrametimeMma;
    213         private double mTotalFrametimeMma;
    214         private double mMostlyTotalFrametimeMma;
    215         private boolean mNeedsFirstValues = true;
    216 
    217         @Override
    218         public void onFrameMetricsAvailable(Window window, FrameMetrics frameMetrics,
    219                 int dropCountSinceLastInvocation) {
    220             if (frameMetrics.getMetric(FrameMetrics.FIRST_DRAW_FRAME) == 1) {
    221                 return;
    222             }
    223 
    224             long uiDuration = frameMetrics.getMetric(FrameMetrics.INPUT_HANDLING_DURATION)
    225                     + frameMetrics.getMetric(FrameMetrics.ANIMATION_DURATION)
    226                     + frameMetrics.getMetric(FrameMetrics.LAYOUT_MEASURE_DURATION)
    227                     + frameMetrics.getMetric(FrameMetrics.DRAW_DURATION);
    228             long rtDuration = frameMetrics.getMetric(FrameMetrics.SYNC_DURATION)
    229                     + frameMetrics.getMetric(FrameMetrics.COMMAND_ISSUE_DURATION);
    230             long totalDuration = frameMetrics.getMetric(FrameMetrics.TOTAL_DURATION);
    231             long jitter = Math.abs(totalDuration - mPreviousFrameTotal);
    232             if (mNeedsFirstValues) {
    233                 mJitterMma = 0;
    234                 mUiFrametimeMma = uiDuration;
    235                 mRtFrametimeMma = rtDuration;
    236                 mTotalFrametimeMma = totalDuration;
    237                 mMostlyTotalFrametimeMma = uiDuration + rtDuration;
    238                 mNeedsFirstValues = false;
    239             } else {
    240                 mJitterMma = add(mJitterMma, jitter);
    241                 mUiFrametimeMma = add(mUiFrametimeMma, uiDuration);
    242                 mRtFrametimeMma = add(mRtFrametimeMma, rtDuration);
    243                 mTotalFrametimeMma = add(mTotalFrametimeMma, totalDuration);
    244                 mMostlyTotalFrametimeMma = add(mMostlyTotalFrametimeMma, uiDuration + rtDuration);
    245             }
    246             mPreviousFrameTotal = totalDuration;
    247             mUpdateHandler.obtainMessage(R.id.jitter_mma,
    248                     String.format("Jitter: %.3fms", toMs(mJitterMma))).sendToTarget();
    249             mUpdateHandler.obtainMessage(R.id.totalish_mma,
    250                     String.format("CPU-total duration: %.3fms", toMs(mMostlyTotalFrametimeMma))).sendToTarget();
    251             mUpdateHandler.obtainMessage(R.id.ui_frametime_mma,
    252                     String.format("UI duration: %.3fms", toMs(mUiFrametimeMma))).sendToTarget();
    253             mUpdateHandler.obtainMessage(R.id.rt_frametime_mma,
    254                     String.format("RT duration: %.3fms", toMs(mRtFrametimeMma))).sendToTarget();
    255             mUpdateHandler.obtainMessage(R.id.total_mma,
    256                     String.format("Total duration: %.3fms", toMs(mTotalFrametimeMma))).sendToTarget();
    257             mUpdateHandler.obtainMessage(R.id.graph, (int) (jitter / 1000),
    258                     (int) (mJitterMma / 1000)).sendToTarget();
    259         }
    260 
    261         double add(double previous, double today) {
    262             return (((WEIGHT - 1) * previous) + today) / WEIGHT;
    263         }
    264 
    265         double toMs(double val) {
    266             return val / 1000000;
    267         }
    268     };
    269 
    270     private static final class AnimatedBackgroundDrawable extends Drawable {
    271         private static final int FROM_COLOR = 0xFF18FFFF;
    272         private static final int TO_COLOR = 0xFF40C4FF;
    273         private static final int DURATION = 1400;
    274 
    275         private final Paint mPaint;
    276         private boolean mReverse;
    277         private long mStartTime;
    278         private int mColor;
    279 
    280         private boolean mReverseX;
    281         private boolean mReverseY;
    282         private float mX;
    283         private float mY;
    284         private float mRadius;
    285         private float mMoveStep = 10.0f;
    286 
    287         public AnimatedBackgroundDrawable() {
    288             mPaint = new Paint();
    289             mPaint.setColor(0xFFFFFF00);
    290             mPaint.setAntiAlias(true);
    291         }
    292 
    293         @Override
    294         public void draw(Canvas canvas) {
    295             stepColor();
    296             canvas.drawColor(mColor);
    297 
    298             mX += (mReverseX ? -mMoveStep : mMoveStep);
    299             mY += (mReverseY ? -mMoveStep : mMoveStep);
    300             clampXY();
    301             canvas.drawCircle(mX, mY, mRadius, mPaint);
    302 
    303             invalidateSelf();
    304         }
    305 
    306         private void clampXY() {
    307             if (mX <= mRadius) {
    308                 mReverseX = false;
    309                 mX = mRadius;
    310             }
    311             if (mY <= mRadius) {
    312                 mReverseY = false;
    313                 mY = mRadius;
    314             }
    315             float maxX = getBounds().width() - mRadius;
    316             if (mX >= maxX) {
    317                 mReverseX = true;
    318                 mX = maxX;
    319             }
    320             float maxY = getBounds().height() - mRadius;
    321             if (mY >= maxY) {
    322                 mReverseY = true;
    323                 mY = maxY;
    324             }
    325         }
    326 
    327         @Override
    328         protected void onBoundsChange(Rect bounds) {
    329             super.onBoundsChange(bounds);
    330             mMoveStep = Math.min(bounds.width(), bounds.height()) / 130.0f;
    331             mRadius = Math.min(bounds.width(), bounds.height()) / 20.0f;
    332         }
    333 
    334         @Override
    335         public void setAlpha(int alpha) {
    336         }
    337 
    338         @Override
    339         public void setColorFilter(ColorFilter colorFilter) {
    340         }
    341 
    342         @Override
    343         public int getOpacity() {
    344             return PixelFormat.OPAQUE;
    345         }
    346 
    347         private void stepColor() {
    348             if (mStartTime == 0) {
    349                 mStartTime = AnimationUtils.currentAnimationTimeMillis();
    350             }
    351             float frac = (AnimationUtils.currentAnimationTimeMillis() - mStartTime)
    352                     / (float) DURATION;
    353             if (frac > 1.0f) frac = 1.0f;
    354             int dest = mReverse ? FROM_COLOR : TO_COLOR;
    355             int src = mReverse ? TO_COLOR : FROM_COLOR;
    356             int r = (int) (Color.red(src) + (Color.red(dest) - Color.red(src)) * frac);
    357             int g = (int) (Color.green(src) + (Color.green(dest) - Color.green(src)) * frac);
    358             int b = (int) (Color.blue(src) + (Color.blue(dest) - Color.blue(src)) * frac);
    359             mColor = Color.rgb(r, g, b);
    360             if (frac == 1.0f) {
    361                 mStartTime = 0;
    362                 mReverse = !mReverse;
    363             }
    364         }
    365     }
    366 }
    367