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 39 private static double viewportCoverage(TileData view, TileData tile) { 40 if (tile.left < view.right 41 && tile.right >= view.left 42 && tile.top < view.bottom 43 && tile.bottom >= view.top) { 44 return 1.0f; 45 } 46 return 0.0f; 47 } 48 49 protected interface MetricGen { 50 public double getValue(TileData[] frame); 51 52 public double getMax(); 53 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 ? 1 : 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 1; 95 } 96 97 @Override 98 public int getLabelId() { 99 return R.string.viewport_coverage; 100 } 101 } 102 }; 103 104 protected interface StatGen { 105 public double getValue(double sortedValues[]); 106 107 public int getLabelId(); 108 } 109 110 public static double getPercentile(double sortedValues[], double ratioAbove) { 111 double index = ratioAbove * (sortedValues.length - 1); 112 int intIndex = (int) Math.floor(index); 113 if (index == intIndex) { 114 return sortedValues[intIndex]; 115 } 116 double alpha = index - intIndex; 117 return sortedValues[intIndex] * (1 - alpha) 118 + sortedValues[intIndex + 1] * (alpha); 119 } 120 121 protected static StatGen[] Stats = new StatGen[] { 122 new StatGen() { 123 @Override 124 public double getValue(double[] sortedValues) { 125 return getPercentile(sortedValues, 0.25); 126 } 127 128 @Override 129 public int getLabelId() { 130 return R.string.percentile_25; 131 } 132 }, new StatGen() { 133 @Override 134 public double getValue(double[] sortedValues) { 135 return getPercentile(sortedValues, 0.5); 136 } 137 138 @Override 139 public int getLabelId() { 140 return R.string.percentile_50; 141 } 142 }, new StatGen() { 143 @Override 144 public double getValue(double[] sortedValues) { 145 return getPercentile(sortedValues, 0.75); 146 } 147 148 @Override 149 public int getLabelId() { 150 return R.string.percentile_75; 151 } 152 }, 153 }; 154 155 public PlaybackGraphs() { 156 whiteLabels = new Paint(); 157 whiteLabels.setColor(Color.WHITE); 158 whiteLabels.setTextSize(PlaybackView.TILE_SCALE / 3); 159 } 160 161 private ArrayList<ShapeDrawable> mShapes = new ArrayList<ShapeDrawable>(); 162 protected double[][] mStats = new double[Metrics.length][Stats.length]; 163 protected HashMap<String, Double> mSingleStats; 164 165 public void setData(RunData data) { 166 mShapes.clear(); 167 double metricValues[] = new double[data.frames.length]; 168 169 if (data.frames.length == 0) { 170 return; 171 } 172 173 for (int metricIndex = 0; metricIndex < Metrics.length; metricIndex++) { 174 // create graph out of rectangles, one per frame 175 int lastBar = 0; 176 for (int frameIndex = 0; frameIndex < data.frames.length; frameIndex++) { 177 TileData frame[] = data.frames[frameIndex]; 178 int newBar = (frame[0].top + frame[0].bottom) / 2; 179 180 MetricGen s = Metrics[metricIndex]; 181 double absoluteValue = s.getValue(frame); 182 double relativeValue = absoluteValue / s.getMax(); 183 relativeValue = Math.min(1,relativeValue); 184 relativeValue = Math.max(0,relativeValue); 185 int rightPos = (int) (-BAR_WIDTH * metricIndex); 186 int leftPos = (int) (-BAR_WIDTH * (metricIndex + relativeValue)); 187 188 ShapeDrawable graphBar = new ShapeDrawable(); 189 graphBar.getPaint().setColor(Color.BLUE); 190 graphBar.setBounds(leftPos, lastBar, rightPos, newBar); 191 192 mShapes.add(graphBar); 193 metricValues[frameIndex] = absoluteValue; 194 lastBar = newBar; 195 } 196 197 // store aggregate statistics per metric (median, and similar) 198 Arrays.sort(metricValues); 199 for (int statIndex = 0; statIndex < Stats.length; statIndex++) { 200 mStats[metricIndex][statIndex] = 201 Stats[statIndex].getValue(metricValues); 202 } 203 204 mSingleStats = data.singleStats; 205 } 206 } 207 208 public void drawVerticalShiftedShapes(Canvas canvas, 209 ArrayList<ShapeDrawable> shapes) { 210 // Shapes drawn here are drawn relative to the viewRect 211 Rect viewRect = shapes.get(shapes.size() - 1).getBounds(); 212 canvas.translate(0, 5 * PlaybackView.TILE_SCALE - viewRect.top); 213 214 for (ShapeDrawable shape : mShapes) { 215 shape.draw(canvas); 216 } 217 for (ShapeDrawable shape : shapes) { 218 shape.draw(canvas); 219 } 220 } 221 222 public void draw(Canvas canvas, ArrayList<ShapeDrawable> shapes, 223 ArrayList<String> strings, Resources resources) { 224 canvas.scale(CANVAS_SCALE, CANVAS_SCALE); 225 226 canvas.translate(BAR_WIDTH * Metrics.length, 0); 227 228 canvas.save(); 229 drawVerticalShiftedShapes(canvas, shapes); 230 canvas.restore(); 231 232 for (int metricIndex = 0; metricIndex < Metrics.length; metricIndex++) { 233 String label = resources.getString( 234 Metrics[metricIndex].getLabelId()); 235 int xPos = (metricIndex + 1) * -BAR_WIDTH; 236 int yPos = LABELOFFSET; 237 canvas.drawText(label, xPos, yPos, whiteLabels); 238 for (int statIndex = 0; statIndex < Stats.length; statIndex++) { 239 String statLabel = resources.getString( 240 Stats[statIndex].getLabelId()).substring(0,3); 241 label = statLabel + " " + resources.getString( 242 R.string.format_stat, mStats[metricIndex][statIndex]); 243 yPos = LABELOFFSET + (1 + statIndex) * PlaybackView.TILE_SCALE 244 / 2; 245 canvas.drawText(label, xPos, yPos, whiteLabels); 246 } 247 } 248 for (int stringIndex = 0; stringIndex < strings.size(); stringIndex++) { 249 int yPos = LABELOFFSET + stringIndex * PlaybackView.TILE_SCALE / 2; 250 canvas.drawText(strings.get(stringIndex), 0, yPos, whiteLabels); 251 } 252 } 253 } 254