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->getBaseLayerSize() != fSize) { 110 fSize = canvas->getBaseLayerSize(); 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 SkPath plotPath; 136 SkPath fillPath; 137 138 static const SkScalar kStrokeWidth = SkIntToScalar(2); 139 SkPaint plotPaint; 140 SkPaint fillPaint; 141 plotPaint.setAntiAlias(fAA); 142 plotPaint.setStyle(SkPaint::kStroke_Style); 143 plotPaint.setStrokeWidth(kStrokeWidth); 144 plotPaint.setStrokeCap(SkPaint::kRound_Cap); 145 plotPaint.setStrokeJoin(SkPaint::kRound_Join); 146 fillPaint.setAntiAlias(fAA); 147 fillPaint.setStyle(SkPaint::kFill_Style); 148 149 SkTDArray<SkScalar>* prevData = nullptr; 150 for (int i = 0; i < kNumGraphs; ++i) { 151 gen_paths(fData[i], 152 prevData, 153 height, 154 0, 155 SkIntToScalar(kPixelsPerTick), 156 fShift, 157 &plotPath, 158 &fillPath); 159 160 // Make the fills partially transparent 161 fillPaint.setColor((colors[i] & 0x00ffffff) | 0x80000000); 162 canvas->drawPath(fillPath, fillPaint); 163 164 plotPaint.setColor(colors[i]); 165 canvas->drawPath(plotPath, plotPaint); 166 167 prevData = fData + i; 168 } 169 170 fShift += kShiftPerFrame; 171 } 172 } 173 174 private: 175 enum { 176 kNumGraphs = 5, 177 kPixelsPerTick = 3, 178 kShiftPerFrame = 1, 179 }; 180 int fShift; 181 SkISize fSize; 182 SkTDArray<SkScalar> fData[kNumGraphs]; 183 bool fAA; 184 185 typedef Benchmark INHERITED; 186 }; 187 188 ////////////////////////////////////////////////////////////////////////////// 189 190 DEF_BENCH( return new ChartBench(true); ) 191 DEF_BENCH( return new ChartBench(false); ) 192