1 /* 2 * Copyright 2013 Google Inc. 3 * 4 * Use of this source code is governed by a BSD-style license that can be 5 * found in the LICENSE file. 6 */ 7 8 #include "Benchmark.h" 9 #include "SkCanvas.h" 10 #include "SkPaint.h" 11 #include "SkPath.h" 12 #include "SkRandom.h" 13 #include "SkTDArray.h" 14 15 /** 16 * This is a conversion of samplecode/SampleChart.cpp into a bench. It sure would be nice to be able 17 * to write one subclass that can be a GM, bench, and/or Sample. 18 */ 19 20 // Generates y values for the chart plots. 21 static void gen_data(SkScalar yAvg, SkScalar ySpread, int count, 22 SkRandom* random, SkTDArray<SkScalar>* dataPts) { 23 dataPts->setCount(count); 24 for (int i = 0; i < count; ++i) { 25 (*dataPts)[i] = random->nextRangeScalar(yAvg - SkScalarHalf(ySpread), 26 yAvg + SkScalarHalf(ySpread)); 27 } 28 } 29 30 // Generates a path to stroke along the top of each plot and a fill path for the area below each 31 // plot. The fill path is bounded below by the bottomData plot points or a horizontal line at 32 // yBase if bottomData == nullptr. 33 // The plots are animated by rotating the data points by leftShift. 34 static void gen_paths(const SkTDArray<SkScalar>& topData, 35 const SkTDArray<SkScalar>* bottomData, 36 SkScalar yBase, 37 SkScalar xLeft, SkScalar xDelta, 38 int leftShift, 39 SkPath* plot, SkPath* fill) { 40 plot->rewind(); 41 fill->rewind(); 42 plot->incReserve(topData.count()); 43 if (nullptr == bottomData) { 44 fill->incReserve(topData.count() + 2); 45 } else { 46 fill->incReserve(2 * topData.count()); 47 } 48 49 leftShift %= topData.count(); 50 SkScalar x = xLeft; 51 52 // Account for the leftShift using two loops 53 int shiftToEndCount = topData.count() - leftShift; 54 plot->moveTo(x, topData[leftShift]); 55 fill->moveTo(x, topData[leftShift]); 56 57 for (int i = 1; i < shiftToEndCount; ++i) { 58 plot->lineTo(x, topData[i + leftShift]); 59 fill->lineTo(x, topData[i + leftShift]); 60 x += xDelta; 61 } 62 63 for (int i = 0; i < leftShift; ++i) { 64 plot->lineTo(x, topData[i]); 65 fill->lineTo(x, topData[i]); 66 x += xDelta; 67 } 68 69 if (bottomData) { 70 SkASSERT(bottomData->count() == topData.count()); 71 // iterate backwards over the previous graph's data to generate the bottom of the filled 72 // area (and account for leftShift). 73 for (int i = 0; i < leftShift; ++i) { 74 x -= xDelta; 75 fill->lineTo(x, (*bottomData)[leftShift - 1 - i]); 76 } 77 for (int i = 0; i < shiftToEndCount; ++i) { 78 x -= xDelta; 79 fill->lineTo(x, (*bottomData)[bottomData->count() - 1 - i]); 80 } 81 } else { 82 fill->lineTo(x - xDelta, yBase); 83 fill->lineTo(xLeft, yBase); 84 } 85 } 86 87 // A set of scrolling line plots with the area between each plot filled. Stresses out GPU path 88 // filling 89 class ChartBench : public Benchmark { 90 public: 91 ChartBench(bool aa) { 92 fShift = 0; 93 fAA = aa; 94 fSize.fWidth = -1; 95 fSize.fHeight = -1; 96 } 97 98 protected: 99 const char* onGetName() override { 100 if (fAA) { 101 return "chart_aa"; 102 } else { 103 return "chart_bw"; 104 } 105 } 106 107 void onDraw(int loops, SkCanvas* canvas) override { 108 bool sizeChanged = false; 109 if (canvas->getDeviceSize() != fSize) { 110 fSize = canvas->getDeviceSize(); 111 sizeChanged = true; 112 } 113 114 SkScalar ySpread = SkIntToScalar(fSize.fHeight / 20); 115 116 SkScalar height = SkIntToScalar(fSize.fHeight); 117 if (sizeChanged) { 118 int dataPointCount = SkMax32(fSize.fWidth / kPixelsPerTick + 1, 2); 119 120 SkRandom random; 121 for (int i = 0; i < kNumGraphs; ++i) { 122 SkScalar y = (kNumGraphs - i) * (height - ySpread) / (kNumGraphs + 1); 123 fData[i].reset(); 124 gen_data(y, ySpread, dataPointCount, &random, fData + i); 125 } 126 } 127 128 SkRandom colorRand; 129 SkColor colors[kNumGraphs]; 130 for (int i = 0; i < kNumGraphs; ++i) { 131 colors[i] = colorRand.nextU() | 0xff000000; 132 } 133 134 for (int frame = 0; frame < loops; ++frame) { 135 136 canvas->clear(0xFFE0F0E0); 137 138 SkPath plotPath; 139 SkPath fillPath; 140 141 static const SkScalar kStrokeWidth = SkIntToScalar(2); 142 SkPaint plotPaint; 143 SkPaint fillPaint; 144 plotPaint.setAntiAlias(fAA); 145 plotPaint.setStyle(SkPaint::kStroke_Style); 146 plotPaint.setStrokeWidth(kStrokeWidth); 147 plotPaint.setStrokeCap(SkPaint::kRound_Cap); 148 plotPaint.setStrokeJoin(SkPaint::kRound_Join); 149 fillPaint.setAntiAlias(fAA); 150 fillPaint.setStyle(SkPaint::kFill_Style); 151 152 SkTDArray<SkScalar>* prevData = nullptr; 153 for (int i = 0; i < kNumGraphs; ++i) { 154 gen_paths(fData[i], 155 prevData, 156 height, 157 0, 158 SkIntToScalar(kPixelsPerTick), 159 fShift, 160 &plotPath, 161 &fillPath); 162 163 // Make the fills partially transparent 164 fillPaint.setColor((colors[i] & 0x00ffffff) | 0x80000000); 165 canvas->drawPath(fillPath, fillPaint); 166 167 plotPaint.setColor(colors[i]); 168 canvas->drawPath(plotPath, plotPaint); 169 170 prevData = fData + i; 171 } 172 173 fShift += kShiftPerFrame; 174 } 175 } 176 177 private: 178 enum { 179 kNumGraphs = 5, 180 kPixelsPerTick = 3, 181 kShiftPerFrame = 1, 182 }; 183 int fShift; 184 SkISize fSize; 185 SkTDArray<SkScalar> fData[kNumGraphs]; 186 bool fAA; 187 188 typedef Benchmark INHERITED; 189 }; 190 191 ////////////////////////////////////////////////////////////////////////////// 192 193 DEF_BENCH( return new ChartBench(true); ) 194 DEF_BENCH( return new ChartBench(false); ) 195