• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright 2012 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 "Timer.h"
9 #include "PictureBenchmark.h"
10 #include "SkCanvas.h"
11 #include "SkPicture.h"
12 #include "SkString.h"
13 #include "picture_utils.h"
14 
15 namespace sk_tools {
16 
PictureBenchmark()17 PictureBenchmark::PictureBenchmark()
18     : fRepeats(1)
19     , fRenderer(NULL)
20     , fTimerResult(TimerData::kAvg_Result)
21     , fTimerTypes(0)
22     , fTimeIndividualTiles(false)
23     , fPurgeDecodedTex(false)
24     , fWriter(NULL) {
25 }
26 
~PictureBenchmark()27 PictureBenchmark::~PictureBenchmark() {
28     SkSafeUnref(fRenderer);
29 }
30 
setTimersToShow(bool wall,bool truncatedWall,bool cpu,bool truncatedCpu,bool gpu)31 void PictureBenchmark::setTimersToShow(bool wall,
32                                        bool truncatedWall,
33                                        bool cpu,
34                                        bool truncatedCpu,
35                                        bool gpu) {
36     fTimerTypes = 0;
37     fTimerTypes |= wall ? TimerData::kWall_Flag : 0;
38     fTimerTypes |= truncatedWall ? TimerData::kTruncatedWall_Flag : 0;
39     fTimerTypes |= cpu ? TimerData::kCpu_Flag : 0;
40     fTimerTypes |= truncatedCpu ? TimerData::kTruncatedCpu_Flag : 0;
41     fTimerTypes |= gpu ? TimerData::kGpu_Flag : 0;
42 }
43 
setupTimer(bool useGLTimer)44 Timer* PictureBenchmark::setupTimer(bool useGLTimer) {
45 #if SK_SUPPORT_GPU
46     if (useGLTimer && fRenderer != NULL && fRenderer->isUsingGpuDevice()) {
47         return SkNEW_ARGS(Timer, (fRenderer->getGLContext()));
48     }
49 #endif
50     return SkNEW_ARGS(Timer, (NULL));
51 }
52 
setRenderer(sk_tools::PictureRenderer * renderer)53 PictureRenderer* PictureBenchmark::setRenderer(sk_tools::PictureRenderer* renderer) {
54     SkRefCnt_SafeAssign(fRenderer, renderer);
55     return renderer;
56 }
57 
run(SkPicture * pict,bool useMultiPictureDraw)58 void PictureBenchmark::run(SkPicture* pict, bool useMultiPictureDraw) {
59     SkASSERT(pict);
60     if (NULL == pict) {
61         return;
62     }
63 
64     SkASSERT(fRenderer != NULL);
65     if (NULL == fRenderer) {
66         return;
67     }
68 
69     fRenderer->init(pict, NULL, NULL, NULL, false, useMultiPictureDraw);
70 
71     // We throw this away to remove first time effects (such as paging in this program)
72     fRenderer->setup();
73 
74     fRenderer->render(NULL);
75     fRenderer->resetState(true);   // flush, swapBuffers and Finish
76 
77     if (fPurgeDecodedTex) {
78         fRenderer->purgeTextures();
79     }
80 
81     bool usingGpu = false;
82 #if SK_SUPPORT_GPU
83     usingGpu = fRenderer->isUsingGpuDevice();
84 #endif
85 
86     uint32_t timerTypes = fTimerTypes;
87     if (!usingGpu) {
88         timerTypes &= ~TimerData::kGpu_Flag;
89     }
90 
91     SkString timeFormat;
92     if (TimerData::kPerIter_Result == fTimerResult) {
93         timeFormat = fRenderer->getPerIterTimeFormat();
94     } else {
95         timeFormat = fRenderer->getNormalTimeFormat();
96     }
97 
98     static const int kNumInnerLoops = 10;
99     int numOuterLoops = 1;
100     int numInnerLoops = fRepeats;
101 
102     if (TimerData::kPerIter_Result == fTimerResult && fRepeats > 1) {
103         // interpret this flag combination to mean: generate 'fRepeats'
104         // numbers by averaging each rendering 'kNumInnerLoops' times
105         numOuterLoops = fRepeats;
106         numInnerLoops = kNumInnerLoops;
107     }
108 
109     if (fTimeIndividualTiles) {
110         TiledPictureRenderer* tiledRenderer = fRenderer->getTiledRenderer();
111         SkASSERT(tiledRenderer && tiledRenderer->supportsTimingIndividualTiles());
112         if (NULL == tiledRenderer || !tiledRenderer->supportsTimingIndividualTiles()) {
113             return;
114         }
115         int xTiles, yTiles;
116         if (!tiledRenderer->tileDimensions(xTiles, yTiles)) {
117             return;
118         }
119 
120         int x, y;
121         while (tiledRenderer->nextTile(x, y)) {
122             // There are two timers, which will behave slightly differently:
123             // 1) longRunningTimer, along with perTileTimerData, will time how long it takes to draw
124             // one tile fRepeats times, and take the average. As such, it will not respect the
125             // logPerIter or printMin options, since it does not know the time per iteration. It
126             // will also be unable to call flush() for each tile.
127             // The goal of this timer is to make up for a system timer that is not precise enough to
128             // measure the small amount of time it takes to draw one tile once.
129             //
130             // 2) perTileTimer, along with perTileTimerData, will record each run separately, and
131             // then take the average. As such, it supports logPerIter and printMin options.
132             //
133             // Although "legal", having two gpu timers running at the same time
134             // seems to cause problems (i.e., INVALID_OPERATIONs) on several
135             // platforms. To work around this, we disable the gpu timer on the
136             // long running timer.
137             SkAutoTDelete<Timer> longRunningTimer(this->setupTimer());
138             TimerData longRunningTimerData(numOuterLoops);
139 
140             for (int outer = 0; outer < numOuterLoops; ++outer) {
141                 SkAutoTDelete<Timer> perTileTimer(this->setupTimer(false));
142                 TimerData perTileTimerData(numInnerLoops);
143 
144                 longRunningTimer->start();
145                 for (int inner = 0; inner < numInnerLoops; ++inner) {
146                     perTileTimer->start();
147                     tiledRenderer->drawCurrentTile();
148                     perTileTimer->truncatedEnd();
149                     tiledRenderer->resetState(false);  // flush & swapBuffers, but don't Finish
150                     perTileTimer->end();
151                     SkAssertResult(perTileTimerData.appendTimes(perTileTimer.get()));
152 
153                     if (fPurgeDecodedTex) {
154                         fRenderer->purgeTextures();
155                     }
156                 }
157                 longRunningTimer->truncatedEnd();
158                 tiledRenderer->resetState(true);       // flush, swapBuffers and Finish
159                 longRunningTimer->end();
160                 SkAssertResult(longRunningTimerData.appendTimes(longRunningTimer.get()));
161             }
162 
163             fWriter->logRenderer(tiledRenderer);
164             fWriter->tileMeta(x, y, xTiles, yTiles);
165 
166             // TODO(borenet): Turn off per-iteration tile time reporting for now.
167             // Avoiding logging the time for every iteration for each tile cuts
168             // down on data file size by a significant amount. Re-enable this once
169             // we're loading the bench data directly into a data store and are no
170             // longer generating SVG graphs.
171 #if 0
172             fWriter->tileData(
173                     &perTileTimerData,
174                     timeFormat.c_str(),
175                     fTimerResult,
176                     timerTypes);
177 #endif
178 
179             if (fPurgeDecodedTex) {
180                 fWriter->addTileFlag(PictureResultsWriter::kPurging);
181             }
182             fWriter->addTileFlag(PictureResultsWriter::kAvg);
183             fWriter->tileData(
184                 &longRunningTimerData,
185                 tiledRenderer->getNormalTimeFormat().c_str(),
186                 TimerData::kAvg_Result,
187                 timerTypes,
188                 numInnerLoops);
189         }
190     } else {
191         SkAutoTDelete<Timer> longRunningTimer(this->setupTimer());
192         TimerData longRunningTimerData(numOuterLoops);
193 
194         for (int outer = 0; outer < numOuterLoops; ++outer) {
195             SkAutoTDelete<Timer> perRunTimer(this->setupTimer(false));
196             TimerData perRunTimerData(numInnerLoops);
197 
198             longRunningTimer->start();
199             for (int inner = 0; inner < numInnerLoops; ++inner) {
200                 fRenderer->setup();
201 
202                 perRunTimer->start();
203                 fRenderer->render(NULL);
204                 perRunTimer->truncatedEnd();
205                 fRenderer->resetState(false);   // flush & swapBuffers, but don't Finish
206                 perRunTimer->end();
207 
208                 SkAssertResult(perRunTimerData.appendTimes(perRunTimer.get()));
209 
210                 if (fPurgeDecodedTex) {
211                     fRenderer->purgeTextures();
212                 }
213             }
214             longRunningTimer->truncatedEnd();
215             fRenderer->resetState(true);        // flush, swapBuffers and Finish
216             longRunningTimer->end();
217             SkAssertResult(longRunningTimerData.appendTimes(longRunningTimer.get()));
218         }
219 
220         fWriter->logRenderer(fRenderer);
221         if (fPurgeDecodedTex) {
222             fWriter->addTileFlag(PictureResultsWriter::kPurging);
223         }
224 
225         // Beware - since the per-run-timer doesn't ever include a glFinish it can
226         // report a lower time then the long-running-timer
227 #if 0
228         fWriter->tileData(
229                 &perRunTimerData,
230                 timeFormat.c_str(),
231                 fTimerResult,
232                 timerTypes);
233 #else
234         fWriter->tileData(
235                 &longRunningTimerData,
236                 timeFormat.c_str(),
237                 fTimerResult,
238                 timerTypes,
239                 numInnerLoops);
240 #endif
241     }
242 
243     fRenderer->end();
244 }
245 
246 }
247