• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2011 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.test.tilebenchmark;
18 
19 import android.content.res.Resources;
20 import android.graphics.Canvas;
21 import android.graphics.Color;
22 import android.graphics.Paint;
23 import android.graphics.Rect;
24 import android.graphics.drawable.ShapeDrawable;
25 
26 import com.test.tilebenchmark.RunData.TileData;
27 
28 import java.util.ArrayList;
29 import java.util.Arrays;
30 import java.util.HashMap;
31 
32 public class PlaybackGraphs {
33     private static final int BAR_WIDTH = PlaybackView.TILE_SCALE * 3;
34     private static final float CANVAS_SCALE = 0.2f;
35     private static final double IDEAL_FRAMES = 60;
36     private static final int LABELOFFSET = 100;
37     private static Paint whiteLabels;
38 
viewportCoverage(TileData view, TileData tile)39     private static double viewportCoverage(TileData view, TileData tile) {
40         if (tile.left < (view.right * view.scale)
41                 && tile.right >= (view.left * view.scale)
42                 && tile.top < (view.bottom * view.scale)
43                 && tile.bottom >= (view.top * view.scale)) {
44             return 1.0f;
45         }
46         return 0.0f;
47     }
48 
49     protected interface MetricGen {
getValue(TileData[] frame)50         public double getValue(TileData[] frame);
51 
getMax()52         public double getMax();
53 
getLabelId()54         public int getLabelId();
55     };
56 
57     protected static MetricGen[] Metrics = new MetricGen[] {
58             new MetricGen() {
59                 // framerate graph
60                 @Override
61                 public double getValue(TileData[] frame) {
62                     int renderTimeUS = frame[0].level;
63                     return 1.0e6f / renderTimeUS;
64                 }
65 
66                 @Override
67                 public double getMax() {
68                     return IDEAL_FRAMES;
69                 }
70 
71                 @Override
72                 public int getLabelId() {
73                     return R.string.frames_per_second;
74                 }
75             }, new MetricGen() {
76                 // coverage graph
77                 @Override
78                 public double getValue(TileData[] frame) {
79                     double total = 0, totalCount = 0;
80                     for (int tileID = 1; tileID < frame.length; tileID++) {
81                         TileData data = frame[tileID];
82                         double coverage = viewportCoverage(frame[0], data);
83                         total += coverage * (data.isReady ? 100 : 0);
84                         totalCount += coverage;
85                     }
86                     if (totalCount == 0) {
87                         return -1;
88                     }
89                     return total / totalCount;
90                 }
91 
92                 @Override
93                 public double getMax() {
94                     return 100;
95                 }
96 
97                 @Override
98                 public int getLabelId() {
99                     return R.string.viewport_coverage;
100                 }
101             }
102     };
103 
104     protected interface StatGen {
getValue(double sortedValues[])105         public double getValue(double sortedValues[]);
106 
getLabelId()107         public int getLabelId();
108     }
109 
getPercentile(double sortedValues[], double ratioAbove)110     public static double getPercentile(double sortedValues[], double ratioAbove) {
111         if (sortedValues.length == 0)
112             return -1;
113 
114         double index = ratioAbove * (sortedValues.length - 1);
115         int intIndex = (int) Math.floor(index);
116         if (index == intIndex) {
117             return sortedValues[intIndex];
118         }
119         double alpha = index - intIndex;
120         return sortedValues[intIndex] * (1 - alpha)
121                 + sortedValues[intIndex + 1] * (alpha);
122     }
123 
getMean(double sortedValues[])124     public static double getMean(double sortedValues[]) {
125         if (sortedValues.length == 0)
126             return -1;
127 
128         double agg = 0;
129         for (double val : sortedValues) {
130             agg += val;
131         }
132         return agg / sortedValues.length;
133     }
134 
getStdDev(double sortedValues[])135     public static double getStdDev(double sortedValues[]) {
136         if (sortedValues.length == 0)
137             return -1;
138 
139         double agg = 0;
140         double sqrAgg = 0;
141         for (double val : sortedValues) {
142             agg += val;
143             sqrAgg += val*val;
144         }
145         double mean = agg / sortedValues.length;
146         return Math.sqrt((sqrAgg / sortedValues.length) - (mean * mean));
147     }
148 
149     protected static StatGen[] Stats = new StatGen[] {
150             new StatGen() {
151                 @Override
152                 public double getValue(double[] sortedValues) {
153                     return getPercentile(sortedValues, 0.25);
154                 }
155 
156                 @Override
157                 public int getLabelId() {
158                     return R.string.percentile_25;
159                 }
160             }, new StatGen() {
161                 @Override
162                 public double getValue(double[] sortedValues) {
163                     return getPercentile(sortedValues, 0.5);
164                 }
165 
166                 @Override
167                 public int getLabelId() {
168                     return R.string.percentile_50;
169                 }
170             }, new StatGen() {
171                 @Override
172                 public double getValue(double[] sortedValues) {
173                     return getPercentile(sortedValues, 0.75);
174                 }
175 
176                 @Override
177                 public int getLabelId() {
178                     return R.string.percentile_75;
179                 }
180             }, new StatGen() {
181                 @Override
182                 public double getValue(double[] sortedValues) {
183                     return getStdDev(sortedValues);
184                 }
185 
186                 @Override
187                 public int getLabelId() {
188                     return R.string.std_dev;
189                 }
190             }, new StatGen() {
191                 @Override
192                 public double getValue(double[] sortedValues) {
193                     return getMean(sortedValues);
194                 }
195 
196                 @Override
197                 public int getLabelId() {
198                     return R.string.mean;
199                 }
200             },
201     };
202 
PlaybackGraphs()203     public PlaybackGraphs() {
204         whiteLabels = new Paint();
205         whiteLabels.setColor(Color.WHITE);
206         whiteLabels.setTextSize(PlaybackView.TILE_SCALE / 3);
207     }
208 
209     private ArrayList<ShapeDrawable> mShapes = new ArrayList<ShapeDrawable>();
210     protected final double[][] mStats = new double[Metrics.length][Stats.length];
211     protected HashMap<String, Double> mSingleStats;
212 
gatherFrameMetric(int metricIndex, double metricValues[], RunData data)213     private void gatherFrameMetric(int metricIndex, double metricValues[], RunData data) {
214         // create graph out of rectangles, one per frame
215         int lastBar = 0;
216         for (int frameIndex = 0; frameIndex < data.frames.length; frameIndex++) {
217             TileData frame[] = data.frames[frameIndex];
218             int newBar = (int)((frame[0].top + frame[0].bottom) * frame[0].scale / 2.0f);
219 
220             MetricGen s = Metrics[metricIndex];
221             double absoluteValue = s.getValue(frame);
222             double relativeValue = absoluteValue / s.getMax();
223             relativeValue = Math.min(1,relativeValue);
224             relativeValue = Math.max(0,relativeValue);
225             int rightPos = (int) (-BAR_WIDTH * metricIndex);
226             int leftPos = (int) (-BAR_WIDTH * (metricIndex + relativeValue));
227 
228             ShapeDrawable graphBar = new ShapeDrawable();
229             graphBar.getPaint().setColor(Color.BLUE);
230             graphBar.setBounds(leftPos, lastBar, rightPos, newBar);
231 
232             mShapes.add(graphBar);
233             metricValues[frameIndex] = absoluteValue;
234             lastBar = newBar;
235         }
236     }
237 
setData(RunData data)238     public void setData(RunData data) {
239         mShapes.clear();
240         double metricValues[] = new double[data.frames.length];
241 
242         mSingleStats = data.singleStats;
243 
244         if (data.frames.length == 0) {
245             return;
246         }
247 
248         for (int metricIndex = 0; metricIndex < Metrics.length; metricIndex++) {
249             // calculate metric based on list of frames
250             gatherFrameMetric(metricIndex, metricValues, data);
251 
252             // store aggregate statistics per metric (median, and similar)
253             Arrays.sort(metricValues);
254             for (int statIndex = 0; statIndex < Stats.length; statIndex++) {
255                 mStats[metricIndex][statIndex] =
256                         Stats[statIndex].getValue(metricValues);
257             }
258         }
259     }
260 
drawVerticalShiftedShapes(Canvas canvas, ArrayList<ShapeDrawable> shapes)261     public void drawVerticalShiftedShapes(Canvas canvas,
262             ArrayList<ShapeDrawable> shapes) {
263         // Shapes drawn here are drawn relative to the viewRect
264         Rect viewRect = shapes.get(shapes.size() - 1).getBounds();
265         canvas.translate(0, 5 * PlaybackView.TILE_SCALE - viewRect.top);
266 
267         for (ShapeDrawable shape : mShapes) {
268             shape.draw(canvas);
269         }
270         for (ShapeDrawable shape : shapes) {
271             shape.draw(canvas);
272         }
273     }
274 
draw(Canvas canvas, ArrayList<ShapeDrawable> shapes, ArrayList<String> strings, Resources resources)275     public void draw(Canvas canvas, ArrayList<ShapeDrawable> shapes,
276             ArrayList<String> strings, Resources resources) {
277         canvas.scale(CANVAS_SCALE, CANVAS_SCALE);
278 
279         canvas.translate(BAR_WIDTH * Metrics.length, 0);
280 
281         canvas.save();
282         drawVerticalShiftedShapes(canvas, shapes);
283         canvas.restore();
284 
285         for (int metricIndex = 0; metricIndex < Metrics.length; metricIndex++) {
286             String label = resources.getString(
287                     Metrics[metricIndex].getLabelId());
288             int xPos = (metricIndex + 1) * -BAR_WIDTH;
289             int yPos = LABELOFFSET;
290             canvas.drawText(label, xPos, yPos, whiteLabels);
291             for (int statIndex = 0; statIndex < Stats.length; statIndex++) {
292                 String statLabel = resources.getString(
293                         Stats[statIndex].getLabelId()).substring(0,3);
294                 label = statLabel + " " + resources.getString(
295                         R.string.format_stat, mStats[metricIndex][statIndex]);
296                 yPos = LABELOFFSET + (1 + statIndex) * PlaybackView.TILE_SCALE
297                         / 2;
298                 canvas.drawText(label, xPos, yPos, whiteLabels);
299             }
300         }
301         for (int stringIndex = 0; stringIndex < strings.size(); stringIndex++) {
302             int yPos = LABELOFFSET + stringIndex * PlaybackView.TILE_SCALE / 2;
303             canvas.drawText(strings.get(stringIndex), 0, yPos, whiteLabels);
304         }
305     }
306 }
307