OLD | NEW |
1 /* | 1 /* |
2 * Copyright 2015 Google Inc. | 2 * Copyright 2015 Google Inc. |
3 * | 3 * |
4 * Use of this source code is governed by a BSD-style license that can be | 4 * Use of this source code is governed by a BSD-style license that can be |
5 * found in the LICENSE file. | 5 * found in the LICENSE file. |
6 * | 6 * |
7 */ | 7 */ |
8 | 8 |
9 #include "VisualBench.h" | 9 #include "VisualLightweightBenchModule.h" |
10 | 10 |
11 #include "ProcStats.h" | 11 #include "ProcStats.h" |
12 #include "SkApplication.h" | 12 #include "SkApplication.h" |
13 #include "SkCanvas.h" | 13 #include "SkCanvas.h" |
14 #include "SkCommandLineFlags.h" | 14 #include "SkCommandLineFlags.h" |
15 #include "SkForceLinking.h" | 15 #include "SkForceLinking.h" |
16 #include "SkGraphics.h" | 16 #include "SkGraphics.h" |
17 #include "SkGr.h" | 17 #include "SkGr.h" |
18 #include "SkImageDecoder.h" | 18 #include "SkImageDecoder.h" |
19 #include "SkOSFile.h" | 19 #include "SkOSFile.h" |
20 #include "SkStream.h" | 20 #include "SkStream.h" |
21 #include "Stats.h" | 21 #include "Stats.h" |
22 #include "gl/GrGLInterface.h" | 22 #include "gl/GrGLInterface.h" |
23 | 23 |
24 __SK_FORCE_IMAGE_DECODER_LINKING; | 24 __SK_FORCE_IMAGE_DECODER_LINKING; |
25 | 25 |
26 // Between samples we reset context | 26 // Between samples we reset context |
27 // Between frames we swap buffers | 27 // Between frames we swap buffers |
28 // Between flushes we call flush on GrContext | |
29 | 28 |
30 DEFINE_int32(gpuFrameLag, 5, "Overestimate of maximum number of frames GPU allow
s to lag."); | 29 DEFINE_int32(gpuFrameLag, 5, "Overestimate of maximum number of frames GPU allow
s to lag."); |
31 DEFINE_int32(samples, 10, "Number of times to time each skp."); | 30 DEFINE_int32(samples, 10, "Number of times to time each skp."); |
32 DEFINE_int32(frames, 5, "Number of frames of each skp to render per sample."); | 31 DEFINE_int32(frames, 5, "Number of frames of each skp to render per sample."); |
33 DEFINE_double(loopMs, 5, "Target loop time in millseconds."); | 32 DEFINE_double(loopMs, 5, "Target loop time in millseconds."); |
34 DEFINE_int32(msaa, 0, "Number of msaa samples."); | |
35 DEFINE_bool2(fullscreen, f, true, "Run fullscreen."); | |
36 DEFINE_bool2(verbose, v, false, "enable verbose output from the test driver."); | 33 DEFINE_bool2(verbose, v, false, "enable verbose output from the test driver."); |
37 DEFINE_string(key, "", ""); // dummy to enable gm tests that have platform-spec
ific names | 34 DEFINE_string(key, "", ""); // dummy to enable gm tests that have platform-spec
ific names |
38 DEFINE_string(outResultsFile, "", "If given, write results here as JSON."); | 35 DEFINE_string(outResultsFile, "", "If given, write results here as JSON."); |
39 DEFINE_string(properties, "", | 36 DEFINE_string(properties, "", |
40 "Space-separated key/value pairs to add to JSON identifying this r
un."); | 37 "Space-separated key/value pairs to add to JSON identifying this r
un."); |
41 | 38 |
42 static SkString humanize(double ms) { | 39 static SkString humanize(double ms) { |
43 if (FLAGS_verbose) { | 40 if (FLAGS_verbose) { |
44 return SkStringPrintf("%llu", (uint64_t)(ms*1e6)); | 41 return SkStringPrintf("%llu", (uint64_t)(ms*1e6)); |
45 } | 42 } |
46 return HumanizeMs(ms); | 43 return HumanizeMs(ms); |
47 } | 44 } |
48 | 45 |
49 #define HUMANIZE(time) humanize(time).c_str() | 46 #define HUMANIZE(time) humanize(time).c_str() |
50 | 47 |
51 VisualBench::VisualBench(void* hwnd, int argc, char** argv) | 48 VisualLightweightBenchModule::VisualLightweightBenchModule(VisualBench* owner) |
52 : INHERITED(hwnd) | 49 : fCurrentSample(0) |
53 , fCurrentSample(0) | |
54 , fCurrentFrame(0) | 50 , fCurrentFrame(0) |
55 , fLoops(1) | 51 , fLoops(1) |
56 , fState(kPreWarmLoops_State) | 52 , fState(kPreWarmLoops_State) |
57 , fBenchmark(nullptr) | 53 , fBenchmark(nullptr) |
| 54 , fOwner(SkRef(owner)) |
58 , fResults(new ResultsWriter) { | 55 , fResults(new ResultsWriter) { |
59 SkCommandLineFlags::Parse(argc, argv); | |
60 | |
61 this->setTitle(); | |
62 this->setupBackend(); | |
63 | |
64 fBenchmarkStream.reset(new VisualBenchmarkStream); | 56 fBenchmarkStream.reset(new VisualBenchmarkStream); |
65 | 57 |
66 // Print header | 58 // Print header |
67 SkDebugf("curr/maxrss\tloops\tflushes\tmin\tmedian\tmean\tmax\tstddev\tbench
\n"); | 59 SkDebugf("curr/maxrss\tloops\tflushes\tmin\tmedian\tmean\tmax\tstddev\tbench
\n"); |
68 | 60 |
69 // setup json logging if required | 61 // setup json logging if required |
70 if (!FLAGS_outResultsFile.isEmpty()) { | 62 if (!FLAGS_outResultsFile.isEmpty()) { |
71 fResults.reset(new NanoJSONResultsWriter(FLAGS_outResultsFile[0])); | 63 fResults.reset(new NanoJSONResultsWriter(FLAGS_outResultsFile[0])); |
72 } | 64 } |
73 | 65 |
74 if (1 == FLAGS_properties.count() % 2) { | 66 if (1 == FLAGS_properties.count() % 2) { |
75 SkDebugf("ERROR: --properties must be passed with an even number of argu
ments.\n"); | 67 SkDebugf("ERROR: --properties must be passed with an even number of argu
ments.\n"); |
76 } else { | 68 } else { |
77 for (int i = 1; i < FLAGS_properties.count(); i += 2) { | 69 for (int i = 1; i < FLAGS_properties.count(); i += 2) { |
78 fResults->property(FLAGS_properties[i - 1], FLAGS_properties[i]); | 70 fResults->property(FLAGS_properties[i - 1], FLAGS_properties[i]); |
79 } | 71 } |
80 } | 72 } |
81 } | 73 } |
82 | 74 |
83 VisualBench::~VisualBench() { | 75 inline void VisualLightweightBenchModule::renderFrame(SkCanvas* canvas) { |
84 INHERITED::detach(); | 76 fBenchmark->draw(fLoops, canvas); |
| 77 canvas->flush(); |
| 78 fOwner->present(); |
85 } | 79 } |
86 | 80 |
87 void VisualBench::setTitle() { | 81 void VisualLightweightBenchModule::printStats() { |
88 SkString title("VisualBench"); | |
89 INHERITED::setTitle(title.c_str()); | |
90 } | |
91 | |
92 SkSurface* VisualBench::createSurface() { | |
93 if (!fSurface) { | |
94 SkSurfaceProps props(INHERITED::getSurfaceProps()); | |
95 fSurface.reset(SkSurface::NewRenderTargetDirect(fRenderTarget, &props)); | |
96 } | |
97 | |
98 // The caller will wrap the SkSurface in an SkAutoTUnref | |
99 return SkRef(fSurface.get()); | |
100 } | |
101 | |
102 bool VisualBench::setupBackend() { | |
103 this->setColorType(kRGBA_8888_SkColorType); | |
104 this->setVisibleP(true); | |
105 this->setClipToBounds(false); | |
106 | |
107 if (FLAGS_fullscreen) { | |
108 if (!this->makeFullscreen()) { | |
109 SkDebugf("Could not go fullscreen!"); | |
110 } | |
111 } | |
112 if (!this->attach(kNativeGL_BackEndType, FLAGS_msaa, &fAttachmentInfo)) { | |
113 SkDebugf("Not possible to create backend.\n"); | |
114 INHERITED::detach(); | |
115 return false; | |
116 } | |
117 | |
118 this->setVsync(false); | |
119 this->resetContext(); | |
120 return true; | |
121 } | |
122 | |
123 void VisualBench::resetContext() { | |
124 fSurface.reset(nullptr); | |
125 | |
126 fInterface.reset(GrGLCreateNativeInterface()); | |
127 SkASSERT(fInterface); | |
128 | |
129 // setup contexts | |
130 fContext.reset(GrContext::Create(kOpenGL_GrBackend, (GrBackendContext)fInter
face.get())); | |
131 SkASSERT(fContext); | |
132 | |
133 // setup rendertargets | |
134 this->setupRenderTarget(); | |
135 } | |
136 | |
137 void VisualBench::setupRenderTarget() { | |
138 if (fContext) { | |
139 fRenderTarget.reset(this->renderTarget(fAttachmentInfo, fInterface, fCon
text)); | |
140 } | |
141 } | |
142 | |
143 inline void VisualBench::renderFrame(SkCanvas* canvas) { | |
144 fBenchmark->draw(fLoops, canvas); | |
145 canvas->flush(); | |
146 INHERITED::present(); | |
147 } | |
148 | |
149 void VisualBench::printStats() { | |
150 const SkTArray<double>& measurements = fRecords.back().fMeasurements; | 82 const SkTArray<double>& measurements = fRecords.back().fMeasurements; |
151 const char* shortName = fBenchmark->getUniqueName(); | 83 const char* shortName = fBenchmark->getUniqueName(); |
152 | 84 |
153 // update log | 85 // update log |
154 // Note: We currently log only the minimum. It would be interesting to log
more information | 86 // Note: We currently log only the minimum. It would be interesting to log
more information |
155 SkString configName; | 87 SkString configName; |
156 if (FLAGS_msaa > 0) { | 88 if (FLAGS_msaa > 0) { |
157 configName.appendf("msaa_%d", FLAGS_msaa); | 89 configName.appendf("msaa_%d", FLAGS_msaa); |
158 } else { | 90 } else { |
159 configName.appendf("gpu"); | 91 configName.appendf("gpu"); |
(...skipping 18 matching lines...) Expand all Loading... |
178 fLoops, | 110 fLoops, |
179 HUMANIZE(stats.min), | 111 HUMANIZE(stats.min), |
180 HUMANIZE(stats.median), | 112 HUMANIZE(stats.median), |
181 HUMANIZE(stats.mean), | 113 HUMANIZE(stats.mean), |
182 HUMANIZE(stats.max), | 114 HUMANIZE(stats.max), |
183 stdDevPercent, | 115 stdDevPercent, |
184 shortName); | 116 shortName); |
185 } | 117 } |
186 } | 118 } |
187 | 119 |
188 bool VisualBench::advanceRecordIfNecessary(SkCanvas* canvas) { | 120 bool VisualLightweightBenchModule::advanceRecordIfNecessary(SkCanvas* canvas) { |
189 if (fBenchmark) { | 121 if (fBenchmark) { |
190 return true; | 122 return true; |
191 } | 123 } |
192 | 124 |
193 fBenchmark.reset(fBenchmarkStream->next()); | 125 fBenchmark.reset(fBenchmarkStream->next()); |
194 if (!fBenchmark) { | 126 if (!fBenchmark) { |
195 return false; | 127 return false; |
196 } | 128 } |
197 | 129 |
198 canvas->clear(0xffffffff); | 130 canvas->clear(0xffffffff); |
199 fBenchmark->preDraw(); | 131 fBenchmark->preDraw(); |
200 fRecords.push_back(); | 132 fRecords.push_back(); |
201 | 133 |
202 // Log bench name | 134 // Log bench name |
203 fResults->bench(fBenchmark->getUniqueName(), fBenchmark->getSize().fX, | 135 fResults->bench(fBenchmark->getUniqueName(), fBenchmark->getSize().fX, |
204 fBenchmark->getSize().fY); | 136 fBenchmark->getSize().fY); |
205 return true; | 137 return true; |
206 } | 138 } |
207 | 139 |
208 inline void VisualBench::nextState(State nextState) { | 140 inline void VisualLightweightBenchModule::nextState(State nextState) { |
209 fState = nextState; | 141 fState = nextState; |
210 } | 142 } |
211 | 143 |
212 void VisualBench::perCanvasPreDraw(SkCanvas* canvas, State nextState) { | 144 void VisualLightweightBenchModule::perCanvasPreDraw(SkCanvas* canvas, State next
State) { |
213 fBenchmark->perCanvasPreDraw(canvas); | 145 fBenchmark->perCanvasPreDraw(canvas); |
214 fCurrentFrame = 0; | 146 fCurrentFrame = 0; |
215 this->nextState(nextState); | 147 this->nextState(nextState); |
216 } | 148 } |
217 | 149 |
218 void VisualBench::preWarm(State nextState) { | 150 void VisualLightweightBenchModule::preWarm(State nextState) { |
219 if (fCurrentFrame >= FLAGS_gpuFrameLag) { | 151 if (fCurrentFrame >= FLAGS_gpuFrameLag) { |
220 // we currently time across all frames to make sure we capture all GPU w
ork | 152 // we currently time across all frames to make sure we capture all GPU w
ork |
221 this->nextState(nextState); | 153 this->nextState(nextState); |
222 fCurrentFrame = 0; | 154 fCurrentFrame = 0; |
223 fTimer.start(); | 155 fTimer.start(); |
224 } else { | 156 } else { |
225 fCurrentFrame++; | 157 fCurrentFrame++; |
226 } | 158 } |
227 } | 159 } |
228 | 160 |
229 void VisualBench::draw(SkCanvas* canvas) { | 161 void VisualLightweightBenchModule::draw(SkCanvas* canvas) { |
230 if (!this->advanceRecordIfNecessary(canvas)) { | 162 if (!this->advanceRecordIfNecessary(canvas)) { |
231 SkDebugf("Exiting VisualBench successfully\n"); | 163 SkDebugf("Exiting VisualBench successfully\n"); |
232 this->closeWindow(); | 164 fOwner->closeWindow(); |
233 return; | 165 return; |
234 } | 166 } |
235 this->renderFrame(canvas); | 167 this->renderFrame(canvas); |
236 switch (fState) { | 168 switch (fState) { |
237 case kPreWarmLoopsPerCanvasPreDraw_State: { | 169 case kPreWarmLoopsPerCanvasPreDraw_State: { |
238 this->perCanvasPreDraw(canvas, kPreWarmLoops_State); | 170 this->perCanvasPreDraw(canvas, kPreWarmLoops_State); |
239 break; | 171 break; |
240 } | 172 } |
241 case kPreWarmLoops_State: { | 173 case kPreWarmLoops_State: { |
242 this->preWarm(kTuneLoops_State); | 174 this->preWarm(kTuneLoops_State); |
243 break; | 175 break; |
244 } | 176 } |
245 case kTuneLoops_State: { | 177 case kTuneLoops_State: { |
246 this->tuneLoops(); | 178 this->tuneLoops(); |
247 break; | 179 break; |
248 } | 180 } |
249 case kPreWarmTimingPerCanvasPreDraw_State: { | 181 case kPreWarmTimingPerCanvasPreDraw_State: { |
250 this->perCanvasPreDraw(canvas, kPreWarmTiming_State); | 182 this->perCanvasPreDraw(canvas, kPreWarmTiming_State); |
251 break; | 183 break; |
252 } | 184 } |
253 case kPreWarmTiming_State: { | 185 case kPreWarmTiming_State: { |
254 this->preWarm(kTiming_State); | 186 this->preWarm(kTiming_State); |
255 break; | 187 break; |
256 } | 188 } |
257 case kTiming_State: { | 189 case kTiming_State: { |
258 this->timing(canvas); | 190 this->timing(canvas); |
259 break; | 191 break; |
260 } | 192 } |
261 } | 193 } |
262 | |
263 // Invalidate the window to force a redraw. Poor man's animation mechanism. | |
264 this->inval(nullptr); | |
265 } | 194 } |
266 | 195 |
267 inline double VisualBench::elapsed() { | 196 inline double VisualLightweightBenchModule::elapsed() { |
268 fTimer.end(); | 197 fTimer.end(); |
269 return fTimer.fWall; | 198 return fTimer.fWall; |
270 } | 199 } |
271 | 200 |
272 void VisualBench::resetTimingState() { | 201 void VisualLightweightBenchModule::resetTimingState() { |
273 fCurrentFrame = 0; | 202 fCurrentFrame = 0; |
274 fTimer = WallTimer(); | 203 fTimer = WallTimer(); |
275 this->resetContext(); | 204 fOwner->reset(); |
276 } | 205 } |
277 | 206 |
278 void VisualBench::scaleLoops(double elapsedMs) { | 207 void VisualLightweightBenchModule::scaleLoops(double elapsedMs) { |
279 // Scale back the number of loops | 208 // Scale back the number of loops |
280 fLoops = (int)ceil(fLoops * FLAGS_loopMs / elapsedMs); | 209 fLoops = (int)ceil(fLoops * FLAGS_loopMs / elapsedMs); |
281 } | 210 } |
282 | 211 |
283 inline void VisualBench::tuneLoops() { | 212 inline void VisualLightweightBenchModule::tuneLoops() { |
284 if (1 << 30 == fLoops) { | 213 if (1 << 30 == fLoops) { |
285 // We're about to wrap. Something's wrong with the bench. | 214 // We're about to wrap. Something's wrong with the bench. |
286 SkDebugf("InnerLoops wrapped\n"); | 215 SkDebugf("InnerLoops wrapped\n"); |
287 fLoops = 0; | 216 fLoops = 0; |
288 } else { | 217 } else { |
289 double elapsedMs = this->elapsed(); | 218 double elapsedMs = this->elapsed(); |
290 if (elapsedMs > FLAGS_loopMs) { | 219 if (elapsedMs > FLAGS_loopMs) { |
291 this->scaleLoops(elapsedMs); | 220 this->scaleLoops(elapsedMs); |
292 this->nextState(kPreWarmTimingPerCanvasPreDraw_State); | 221 this->nextState(kPreWarmTimingPerCanvasPreDraw_State); |
293 } else { | 222 } else { |
294 fLoops *= 2; | 223 fLoops *= 2; |
295 this->nextState(kPreWarmLoops_State); | 224 this->nextState(kPreWarmLoops_State); |
296 } | 225 } |
297 this->resetTimingState(); | 226 this->resetTimingState(); |
298 } | 227 } |
299 } | 228 } |
300 | 229 |
301 void VisualBench::recordMeasurement() { | 230 void VisualLightweightBenchModule::recordMeasurement() { |
302 double measurement = this->elapsed() / (FLAGS_frames * fLoops); | 231 double measurement = this->elapsed() / (FLAGS_frames * fLoops); |
303 fRecords.back().fMeasurements.push_back(measurement); | 232 fRecords.back().fMeasurements.push_back(measurement); |
304 } | 233 } |
305 | 234 |
306 void VisualBench::postDraw(SkCanvas* canvas) { | 235 void VisualLightweightBenchModule::postDraw(SkCanvas* canvas) { |
307 fBenchmark->perCanvasPostDraw(canvas); | 236 fBenchmark->perCanvasPostDraw(canvas); |
308 fBenchmark.reset(nullptr); | 237 fBenchmark.reset(nullptr); |
309 fCurrentSample = 0; | 238 fCurrentSample = 0; |
310 fLoops = 1; | 239 fLoops = 1; |
311 } | 240 } |
312 | 241 |
313 inline void VisualBench::timing(SkCanvas* canvas) { | 242 inline void VisualLightweightBenchModule::timing(SkCanvas* canvas) { |
314 if (fCurrentFrame >= FLAGS_frames) { | 243 if (fCurrentFrame >= FLAGS_frames) { |
315 this->recordMeasurement(); | 244 this->recordMeasurement(); |
316 if (fCurrentSample++ >= FLAGS_samples) { | 245 if (fCurrentSample++ >= FLAGS_samples) { |
317 this->printStats(); | 246 this->printStats(); |
318 this->postDraw(canvas); | 247 this->postDraw(canvas); |
319 this->nextState(kPreWarmLoopsPerCanvasPreDraw_State); | 248 this->nextState(kPreWarmLoopsPerCanvasPreDraw_State); |
320 } else { | 249 } else { |
321 this->nextState(kPreWarmTimingPerCanvasPreDraw_State); | 250 this->nextState(kPreWarmTimingPerCanvasPreDraw_State); |
322 } | 251 } |
323 this->resetTimingState(); | 252 this->resetTimingState(); |
324 } else { | 253 } else { |
325 fCurrentFrame++; | 254 fCurrentFrame++; |
326 } | 255 } |
327 } | 256 } |
328 | |
329 void VisualBench::onSizeChange() { | |
330 this->setupRenderTarget(); | |
331 } | |
332 | |
333 bool VisualBench::onHandleChar(SkUnichar unichar) { | |
334 return true; | |
335 } | |
336 | |
337 // Externally declared entry points | |
338 void application_init() { | |
339 SkGraphics::Init(); | |
340 SkEvent::Init(); | |
341 } | |
342 | |
343 void application_term() { | |
344 SkEvent::Term(); | |
345 SkGraphics::Term(); | |
346 } | |
347 | |
348 SkOSWindow* create_sk_window(void* hwnd, int argc, char** argv) { | |
349 return new VisualBench(hwnd, argc, argv); | |
350 } | |
351 | |
OLD | NEW |