blob: e1df7d3e28ac2c52a6091fe0ceaa12ad0fdec044 [file] [log] [blame]
John Reck03a1edf2016-10-06 14:46:19 -07001/*
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
17package com.android.test.uibench;
18
19import android.app.Activity;
John Recke692f312016-10-06 17:39:49 -070020import android.content.Context;
John Reck03a1edf2016-10-06 14:46:19 -070021import android.graphics.Canvas;
22import android.graphics.Color;
23import android.graphics.ColorFilter;
24import android.graphics.Paint;
25import android.graphics.PixelFormat;
26import android.graphics.Rect;
27import android.graphics.drawable.Drawable;
28import android.os.Bundle;
29import android.os.Handler;
30import android.os.HandlerThread;
31import android.os.Message;
John Recke692f312016-10-06 17:39:49 -070032import android.util.AttributeSet;
John Reck03a1edf2016-10-06 14:46:19 -070033import android.view.FrameMetrics;
34import android.view.View;
35import android.view.Window;
36import android.view.Window.OnFrameMetricsAvailableListener;
37import android.view.animation.AnimationUtils;
38import android.widget.TextView;
39
40public class RenderingJitter extends Activity {
41 private TextView mJitterReport;
42 private TextView mUiFrameTimeReport;
43 private TextView mRenderThreadTimeReport;
44 private TextView mTotalFrameTimeReport;
45 private TextView mMostlyTotalFrameTimeReport;
John Recke692f312016-10-06 17:39:49 -070046 private PointGraphView mGraph;
John Reck03a1edf2016-10-06 14:46:19 -070047
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;
John Recke692f312016-10-06 17:39:49 -070074 case R.id.graph:
75 mGraph.addJitterSample(msg.arg1, msg.arg2);
76 break;
John Reck03a1edf2016-10-06 14:46:19 -070077 }
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);
Alan Viverette51efddb2017-04-05 10:00:01 -040088 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);
John Reck03a1edf2016-10-06 14:46:19 -070094 mJitterReport.setText("abcdefghijklmnopqrstuvwxyz");
95 mMostlyTotalFrameTimeReport.setText("ABCDEFGHIJKLMNOPQRSTUVWXYZ");
96 mUiFrameTimeReport.setText("0123456789");
97 mRenderThreadTimeReport.setText(",.!()[]{};");
98 getWindow().addOnFrameMetricsAvailableListener(mMetricsListener, sMetricsHandler);
99 }
100
John Recke692f312016-10-06 17:39:49 -0700101 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
John Reck03a1edf2016-10-06 14:46:19 -0700207 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();
John Recke692f312016-10-06 17:39:49 -0700257 mUpdateHandler.obtainMessage(R.id.graph, (int) (jitter / 1000),
258 (int) (mJitterMma / 1000)).sendToTarget();
John Reck03a1edf2016-10-06 14:46:19 -0700259 }
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}