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