OLD | NEW |
| (Empty) |
1 /* | |
2 * Copyright 2015 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 | |
9 #include "VisualBench.h" | |
10 | |
11 #include "ProcStats.h" | |
12 #include "SkApplication.h" | |
13 #include "SkCanvas.h" | |
14 #include "SkCommandLineFlags.h" | |
15 #include "SkCommonFlags.h" | |
16 #include "SkForceLinking.h" | |
17 #include "SkGraphics.h" | |
18 #include "SkGr.h" | |
19 #include "SkImageDecoder.h" | |
20 #include "SkOSFile.h" | |
21 #include "SkStream.h" | |
22 #include "Stats.h" | |
23 #include "gl/GrGLInterface.h" | |
24 | |
25 __SK_FORCE_IMAGE_DECODER_LINKING; | |
26 | |
27 // Between samples we reset context | |
28 // Between frames we swap buffers | |
29 // Between flushes we call flush on GrContext | |
30 | |
31 DEFINE_int32(gpuFrameLag, 5, "Overestimate of maximum number of frames GPU allow
s to lag."); | |
32 DEFINE_int32(samples, 10, "Number of times to time each skp."); | |
33 DEFINE_int32(frames, 5, "Number of frames of each skp to render per sample."); | |
34 DEFINE_double(flushMs, 20, "Target flush time in millseconds."); | |
35 DEFINE_double(loopMs, 5, "Target loop time in millseconds."); | |
36 DEFINE_int32(msaa, 0, "Number of msaa samples."); | |
37 DEFINE_bool2(fullscreen, f, true, "Run fullscreen."); | |
38 | |
39 static SkString humanize(double ms) { | |
40 if (FLAGS_verbose) { | |
41 return SkStringPrintf("%llu", (uint64_t)(ms*1e6)); | |
42 } | |
43 return HumanizeMs(ms); | |
44 } | |
45 | |
46 #define HUMANIZE(time) humanize(time).c_str() | |
47 | |
48 VisualBench::VisualBench(void* hwnd, int argc, char** argv) | |
49 : INHERITED(hwnd) | |
50 , fCurrentPictureIdx(-1) | |
51 , fCurrentSample(0) | |
52 , fCurrentFrame(0) | |
53 , fFlushes(1) | |
54 , fLoops(1) | |
55 , fState(kPreWarmLoops_State) { | |
56 SkCommandLineFlags::Parse(argc, argv); | |
57 | |
58 // read all the skp file names. | |
59 for (int i = 0; i < FLAGS_skps.count(); i++) { | |
60 if (SkStrEndsWith(FLAGS_skps[i], ".skp")) { | |
61 fRecords.push_back().fFilename = FLAGS_skps[i]; | |
62 } else { | |
63 SkOSFile::Iter it(FLAGS_skps[i], ".skp"); | |
64 SkString path; | |
65 while (it.next(&path)) { | |
66 fRecords.push_back().fFilename = SkOSPath::Join(FLAGS_skps[i], p
ath.c_str());; | |
67 } | |
68 } | |
69 } | |
70 | |
71 if (fRecords.empty()) { | |
72 SkDebugf("no valid skps found\n"); | |
73 } | |
74 | |
75 this->setTitle(); | |
76 this->setupBackend(); | |
77 | |
78 // Print header | |
79 SkDebugf("curr/maxrss\tloops\tflushes\tmin\tmedian\tmean\tmax\tstddev\tbench
\n"); | |
80 } | |
81 | |
82 VisualBench::~VisualBench() { | |
83 INHERITED::detach(); | |
84 } | |
85 | |
86 void VisualBench::setTitle() { | |
87 SkString title("VisualBench"); | |
88 INHERITED::setTitle(title.c_str()); | |
89 } | |
90 | |
91 SkSurface* VisualBench::createSurface() { | |
92 SkSurfaceProps props(INHERITED::getSurfaceProps()); | |
93 return SkSurface::NewRenderTargetDirect(fRenderTarget, &props); | |
94 } | |
95 | |
96 bool VisualBench::setupBackend() { | |
97 this->setColorType(kRGBA_8888_SkColorType); | |
98 this->setVisibleP(true); | |
99 this->setClipToBounds(false); | |
100 | |
101 if (FLAGS_fullscreen) { | |
102 if (!this->makeFullscreen()) { | |
103 SkDebugf("Could not go fullscreen!"); | |
104 } | |
105 } | |
106 if (!this->attach(kNativeGL_BackEndType, FLAGS_msaa, &fAttachmentInfo)) { | |
107 SkDebugf("Not possible to create backend.\n"); | |
108 INHERITED::detach(); | |
109 return false; | |
110 } | |
111 | |
112 this->setVsync(false); | |
113 this->resetContext(); | |
114 return true; | |
115 } | |
116 | |
117 void VisualBench::resetContext() { | |
118 fInterface.reset(GrGLCreateNativeInterface()); | |
119 SkASSERT(fInterface); | |
120 | |
121 // setup contexts | |
122 fContext.reset(GrContext::Create(kOpenGL_GrBackend, (GrBackendContext)fInter
face.get())); | |
123 SkASSERT(fContext); | |
124 | |
125 // setup rendertargets | |
126 this->setupRenderTarget(); | |
127 } | |
128 | |
129 void VisualBench::setupRenderTarget() { | |
130 if (fContext) { | |
131 fRenderTarget.reset(this->renderTarget(fAttachmentInfo, fInterface, fCon
text)); | |
132 } | |
133 } | |
134 | |
135 inline void VisualBench::renderFrame(SkCanvas* canvas) { | |
136 for (int flush = 0; flush < fFlushes; flush++) { | |
137 for (int loop = 0; loop < fLoops; loop++) { | |
138 canvas->drawPicture(fPicture); | |
139 } | |
140 canvas->flush(); | |
141 } | |
142 INHERITED::present(); | |
143 } | |
144 | |
145 void VisualBench::printStats() { | |
146 const SkTArray<double>& measurements = fRecords[fCurrentPictureIdx].fMeasure
ments; | |
147 SkString shortName = SkOSPath::Basename(fRecords[fCurrentPictureIdx].fFilena
me.c_str()); | |
148 if (FLAGS_verbose) { | |
149 for (int i = 0; i < measurements.count(); i++) { | |
150 SkDebugf("%s ", HUMANIZE(measurements[i])); | |
151 } | |
152 SkDebugf("%s\n", shortName.c_str()); | |
153 } else { | |
154 SkASSERT(measurements.count()); | |
155 Stats stats(measurements); | |
156 const double stdDevPercent = 100 * sqrt(stats.var) / stats.mean; | |
157 SkDebugf("%4d/%-4dMB\t%d\t%d\t%s\t%s\t%s\t%s\t%.0f%%\t%s\n", | |
158 sk_tools::getCurrResidentSetSizeMB(), | |
159 sk_tools::getMaxResidentSetSizeMB(), | |
160 fLoops, | |
161 fFlushes, | |
162 HUMANIZE(stats.min), | |
163 HUMANIZE(stats.median), | |
164 HUMANIZE(stats.mean), | |
165 HUMANIZE(stats.max), | |
166 stdDevPercent, | |
167 shortName.c_str()); | |
168 } | |
169 } | |
170 | |
171 bool VisualBench::advanceRecordIfNecessary() { | |
172 if (fPicture) { | |
173 return true; | |
174 } | |
175 ++fCurrentPictureIdx; | |
176 while (true) { | |
177 if (fCurrentPictureIdx >= fRecords.count()) { | |
178 return false; | |
179 } | |
180 if (this->loadPicture()) { | |
181 return true; | |
182 } | |
183 fRecords.removeShuffle(fCurrentPictureIdx); | |
184 } | |
185 } | |
186 | |
187 bool VisualBench::loadPicture() { | |
188 const char* fileName = fRecords[fCurrentPictureIdx].fFilename.c_str(); | |
189 SkFILEStream stream(fileName); | |
190 if (stream.isValid()) { | |
191 fPicture.reset(SkPicture::CreateFromStream(&stream)); | |
192 if (SkToBool(fPicture)) { | |
193 return true; | |
194 } | |
195 } | |
196 SkDebugf("couldn't load picture at \"%s\"\n", fileName); | |
197 return false; | |
198 } | |
199 | |
200 void VisualBench::preWarm(State nextState) { | |
201 if (fCurrentFrame >= FLAGS_gpuFrameLag) { | |
202 // we currently time across all frames to make sure we capture all GPU w
ork | |
203 fState = nextState; | |
204 fCurrentFrame = 0; | |
205 fTimer.start(); | |
206 } else { | |
207 fCurrentFrame++; | |
208 } | |
209 } | |
210 | |
211 void VisualBench::draw(SkCanvas* canvas) { | |
212 if (!this->advanceRecordIfNecessary()) { | |
213 this->closeWindow(); | |
214 return; | |
215 } | |
216 this->renderFrame(canvas); | |
217 switch (fState) { | |
218 case kPreWarmLoops_State: { | |
219 this->preWarm(kTuneLoops_State); | |
220 break; | |
221 } | |
222 case kTuneLoops_State: { | |
223 if (1 << 30 == fLoops) { | |
224 // We're about to wrap. Something's wrong with the bench. | |
225 SkDebugf("InnerLoops wrapped\n"); | |
226 fLoops = 0; | |
227 } else { | |
228 fTimer.end(); | |
229 double elapsed = fTimer.fWall; | |
230 if (elapsed > FLAGS_loopMs) { | |
231 fState = kPreWarmTiming_State; | |
232 | |
233 // Scale back the number of loops | |
234 fLoops = (int)ceil(fLoops * FLAGS_loopMs / elapsed); | |
235 fFlushes = (int)ceil(FLAGS_flushMs / elapsed); | |
236 } else { | |
237 fState = kPreWarmLoops_State; | |
238 fLoops *= 2; | |
239 } | |
240 | |
241 fCurrentFrame = 0; | |
242 fTimer = WallTimer(); | |
243 this->resetContext(); | |
244 } | |
245 break; | |
246 } | |
247 case kPreWarmTiming_State: { | |
248 this->preWarm(kTiming_State); | |
249 break; | |
250 } | |
251 case kTiming_State: { | |
252 if (fCurrentFrame >= FLAGS_frames) { | |
253 fTimer.end(); | |
254 fRecords[fCurrentPictureIdx].fMeasurements.push_back( | |
255 fTimer.fWall / (FLAGS_frames * fLoops * fFlushes)); | |
256 if (fCurrentSample++ >= FLAGS_samples) { | |
257 fState = kPreWarmLoops_State; | |
258 this->printStats(); | |
259 fPicture.reset(NULL); | |
260 fCurrentSample = 0; | |
261 fFlushes = 1; | |
262 fLoops = 1; | |
263 } else { | |
264 fState = kPreWarmTiming_State; | |
265 } | |
266 fTimer = WallTimer(); | |
267 this->resetContext(); | |
268 fCurrentFrame = 0; | |
269 } else { | |
270 fCurrentFrame++; | |
271 } | |
272 break; | |
273 } | |
274 } | |
275 | |
276 // Invalidate the window to force a redraw. Poor man's animation mechanism. | |
277 this->inval(NULL); | |
278 } | |
279 | |
280 void VisualBench::onSizeChange() { | |
281 this->setupRenderTarget(); | |
282 } | |
283 | |
284 bool VisualBench::onHandleChar(SkUnichar unichar) { | |
285 return true; | |
286 } | |
287 | |
288 // Externally declared entry points | |
289 void application_init() { | |
290 SkGraphics::Init(); | |
291 SkEvent::Init(); | |
292 } | |
293 | |
294 void application_term() { | |
295 SkEvent::Term(); | |
296 SkGraphics::Term(); | |
297 } | |
298 | |
299 SkOSWindow* create_sk_window(void* hwnd, int argc, char** argv) { | |
300 return new VisualBench(hwnd, argc, argv); | |
301 } | |
302 | |
OLD | NEW |