OLD | NEW |
1 /* | 1 /* |
2 * Copyright 2016 Google Inc. | 2 * Copyright 2016 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 #include "GrCaps.h" | 8 #include "GrCaps.h" |
9 #include "GrContextFactory.h" | 9 #include "GrContextFactory.h" |
10 #include "Benchmark.h" | 10 #include "Benchmark.h" |
| 11 #include "ResultsWriter.h" |
11 #include "SkCommandLineFlags.h" | 12 #include "SkCommandLineFlags.h" |
12 #include "SkOSFile.h" | 13 #include "SkOSFile.h" |
13 #include "SkStream.h" | 14 #include "SkStream.h" |
14 #include "SkSurface.h" | 15 #include "SkSurface.h" |
15 #include "SkTime.h" | 16 #include "SkTime.h" |
16 #include "Stats.h" | 17 #include "Stats.h" |
17 #include "Timer.h" | 18 #include "Timer.h" |
18 #include "VisualSKPBench.h" | 19 #include "VisualSKPBench.h" |
19 #include "gl/GrGLDefines.h" | 20 #include "gl/GrGLDefines.h" |
20 | 21 |
| 22 // posix only for now |
| 23 #include <unistd.h> |
| 24 #include <sys/types.h> |
| 25 #include <sys/wait.h> |
| 26 |
21 /* | 27 /* |
22 * This is an experimental GPU only benchmarking program. The initial implement
ation will only | 28 * This is an experimental GPU only benchmarking program. The initial implement
ation will only |
23 * support SKPs. | 29 * support SKPs. |
24 */ | 30 */ |
25 | 31 |
26 // To get image decoders linked in we have to do the below magic | 32 // To get image decoders linked in we have to do the below magic |
27 #include "SkForceLinking.h" | 33 #include "SkForceLinking.h" |
28 #include "SkImageDecoder.h" | 34 #include "SkImageDecoder.h" |
29 __SK_FORCE_IMAGE_DECODER_LINKING; | 35 __SK_FORCE_IMAGE_DECODER_LINKING; |
30 | 36 |
(...skipping 25 matching lines...) Expand all Loading... |
56 "^ and $ requires an exact match\n" | 62 "^ and $ requires an exact match\n" |
57 "If a bench does not match any list entry,\n" | 63 "If a bench does not match any list entry,\n" |
58 "it is skipped unless some list entry starts with ~"); | 64 "it is skipped unless some list entry starts with ~"); |
59 DEFINE_int32(gpuFrameLag, 5, "If unknown, estimated maximum number of frames GPU
allows to lag."); | 65 DEFINE_int32(gpuFrameLag, 5, "If unknown, estimated maximum number of frames GPU
allows to lag."); |
60 DEFINE_int32(samples, 10, "Number of samples to measure for each bench."); | 66 DEFINE_int32(samples, 10, "Number of samples to measure for each bench."); |
61 DEFINE_int32(maxLoops, 1000000, "Never run a bench more times than this."); | 67 DEFINE_int32(maxLoops, 1000000, "Never run a bench more times than this."); |
62 DEFINE_int32(loops, kDefaultLoops, loops_help_txt().c_str()); | 68 DEFINE_int32(loops, kDefaultLoops, loops_help_txt().c_str()); |
63 DEFINE_double(gpuMs, 5, "Target bench time in millseconds for GPU."); | 69 DEFINE_double(gpuMs, 5, "Target bench time in millseconds for GPU."); |
64 DEFINE_string2(writePath, w, "", "If set, write bitmaps here as .pngs."); | 70 DEFINE_string2(writePath, w, "", "If set, write bitmaps here as .pngs."); |
65 | 71 |
| 72 static SkString humanize(double ms) { |
| 73 return HumanizeMs(ms); |
| 74 } |
| 75 #define HUMANIZE(ms) humanize(ms).c_str() |
| 76 |
66 namespace kilobench { | 77 namespace kilobench { |
67 class BenchmarkStream { | 78 class BenchmarkStream { |
68 public: | 79 public: |
69 BenchmarkStream() : fCurrentSKP(0) { | 80 BenchmarkStream() : fCurrentSKP(0) { |
70 for (int i = 0; i < FLAGS_skps.count(); i++) { | 81 for (int i = 0; i < FLAGS_skps.count(); i++) { |
71 if (SkStrEndsWith(FLAGS_skps[i], ".skp")) { | 82 if (SkStrEndsWith(FLAGS_skps[i], ".skp")) { |
72 fSKPs.push_back() = FLAGS_skps[i]; | 83 fSKPs.push_back() = FLAGS_skps[i]; |
73 } else { | 84 } else { |
74 SkOSFile::Iter it(FLAGS_skps[i], ".skp"); | 85 SkOSFile::Iter it(FLAGS_skps[i], ".skp"); |
75 SkString path; | 86 SkString path; |
(...skipping 239 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
315 | 326 |
316 // Pretty much the same deal as the calibration: do some warmup to make | 327 // Pretty much the same deal as the calibration: do some warmup to make |
317 // sure we're timing steady-state pipelined frames. | 328 // sure we're timing steady-state pipelined frames. |
318 for (int i = 0; i < maxGpuFrameLag - 1; i++) { | 329 for (int i = 0; i < maxGpuFrameLag - 1; i++) { |
319 time(loops, bench, target); | 330 time(loops, bench, target); |
320 } | 331 } |
321 | 332 |
322 return loops; | 333 return loops; |
323 } | 334 } |
324 | 335 |
325 static SkString humanize(double ms) { | 336 struct AutoSetupContextBenchAndTarget { |
326 return HumanizeMs(ms); | 337 AutoSetupContextBenchAndTarget(Benchmark* bench) : fBenchmark(bench) { |
327 } | 338 GrContextOptions grContextOpts; |
328 #define HUMANIZE(ms) humanize(ms).c_str() | 339 fCtxFactory.reset(new GrContextFactory(grContextOpts)); |
329 | 340 |
330 void benchmark_inner_loop(Benchmark* bench, GrContextFactory* ctxFactory) { | 341 SkAssertResult(fTarget.init(bench, fCtxFactory, false, |
331 SkTArray<double> samples; | 342 GrContextFactory::kNative_GLContextType, |
332 GPUTarget target; | 343 GrContextFactory::kNone_GLContextOptions, 0)
); |
333 SkAssertResult(target.init(bench, ctxFactory, false, | |
334 GrContextFactory::kNative_GLContextType, | |
335 GrContextFactory::kNone_GLContextOptions, 0)); | |
336 | 344 |
337 SkCanvas* canvas = target.getCanvas(); | 345 fCanvas = fTarget.getCanvas(); |
338 target.setup(); | 346 fTarget.setup(); |
339 | 347 |
340 bench->perCanvasPreDraw(canvas); | 348 bench->perCanvasPreDraw(fCanvas); |
341 int maxFrameLag; | 349 fTarget.needsFrameTiming(&fMaxFrameLag); |
342 target.needsFrameTiming(&maxFrameLag); | |
343 int loops = setup_gpu_bench(&target, bench, maxFrameLag); | |
344 | |
345 samples.reset(FLAGS_samples); | |
346 for (int s = 0; s < FLAGS_samples; s++) { | |
347 samples[s] = time(loops, bench, &target) / loops; | |
348 } | 350 } |
349 | 351 |
350 bench->perCanvasPostDraw(canvas); | 352 int getLoops() { return setup_gpu_bench(&fTarget, fBenchmark, fMaxFrameLag);
} |
351 | 353 |
352 Stats stats(samples); | 354 double timeSample(int loops) { |
353 const double stddev_percent = 100 * sqrt(stats.var) / stats.mean; | 355 for (int i = 0; i < fMaxFrameLag; i++) { |
354 SkDebugf("%d\t%s\t%s\t%s\t%s\t%.0f%%\t%s\t%s\t%s\n" | 356 time(loops, fBenchmark, &fTarget); |
355 , loops | 357 } |
356 , HUMANIZE(stats.min) | 358 |
357 , HUMANIZE(stats.median) | 359 return time(loops, fBenchmark, &fTarget) / loops; |
358 , HUMANIZE(stats.mean) | 360 } |
359 , HUMANIZE(stats.max) | 361 void teardownBench() { fBenchmark->perCanvasPostDraw(fCanvas); } |
360 , stddev_percent | 362 |
361 , stats.plot.c_str() | 363 SkAutoTDelete<GrContextFactory> fCtxFactory; |
362 , "gpu" | 364 GPUTarget fTarget; |
363 , bench->getUniqueName() | 365 SkCanvas* fCanvas; |
364 ); | 366 Benchmark* fBenchmark; |
| 367 int fMaxFrameLag; |
| 368 }; |
| 369 |
| 370 int setup_loops(Benchmark* bench) { |
| 371 AutoSetupContextBenchAndTarget ascbt(bench); |
| 372 int loops = ascbt.getLoops(); |
| 373 ascbt.teardownBench(); |
365 | 374 |
366 if (!FLAGS_writePath.isEmpty() && FLAGS_writePath[0]) { | 375 if (!FLAGS_writePath.isEmpty() && FLAGS_writePath[0]) { |
367 SkString pngFilename = SkOSPath::Join(FLAGS_writePath[0], "gpu"); | 376 SkString pngFilename = SkOSPath::Join(FLAGS_writePath[0], "gpu"); |
368 pngFilename = SkOSPath::Join(pngFilename.c_str(), bench->getUniqueName()
); | 377 pngFilename = SkOSPath::Join(pngFilename.c_str(), bench->getUniqueName()
); |
369 pngFilename.append(".png"); | 378 pngFilename.append(".png"); |
370 write_canvas_png(&target, pngFilename); | 379 write_canvas_png(&ascbt.fTarget, pngFilename); |
371 } | 380 } |
| 381 return loops; |
| 382 } |
| 383 |
| 384 double time_sample(Benchmark* bench, int loops) { |
| 385 AutoSetupContextBenchAndTarget ascbt(bench); |
| 386 double sample = ascbt.timeSample(loops); |
| 387 ascbt.teardownBench(); |
| 388 |
| 389 return sample; |
372 } | 390 } |
373 | 391 |
374 } // namespace kilobench | 392 } // namespace kilobench |
375 | 393 |
| 394 static const int kOutResultSize = 1024; |
| 395 |
376 int kilobench_main() { | 396 int kilobench_main() { |
377 SkAutoTDelete<GrContextFactory> ctxFactory; | |
378 | |
379 GrContextOptions grContextOpts; | |
380 ctxFactory.reset(new GrContextFactory(grContextOpts)); | |
381 | |
382 kilobench::BenchmarkStream benchStream; | 397 kilobench::BenchmarkStream benchStream; |
383 | 398 |
384 SkDebugf("loops\tmin\tmedian\tmean\tmax\tstddev\t%-*s\tconfig\tbench\n", | 399 SkDebugf("loops\tmin\tmedian\tmean\tmax\tstddev\t%-*s\tconfig\tbench\n", |
385 FLAGS_samples, "samples"); | 400 FLAGS_samples, "samples"); |
386 | 401 |
| 402 int descriptors[2]; |
| 403 if (pipe(descriptors) != 0) { |
| 404 SkFAIL("Failed to open a pipe\n"); |
| 405 } |
| 406 |
387 while (Benchmark* b = benchStream.next()) { | 407 while (Benchmark* b = benchStream.next()) { |
388 SkAutoTDelete<Benchmark> bench(b); | 408 SkAutoTDelete<Benchmark> bench(b); |
389 kilobench::benchmark_inner_loop(bench.get(), ctxFactory.get()); | 409 |
| 410 int loops; |
| 411 SkTArray<double> samples; |
| 412 for (int i = 0; i < FLAGS_samples + 1; i++) { |
| 413 // We fork off a new process to setup the grcontext and run the test
while we wait |
| 414 int childPid = fork(); |
| 415 if (childPid > 0) { |
| 416 char result[kOutResultSize]; |
| 417 if (read(descriptors[0], result, kOutResultSize) < 0) { |
| 418 SkFAIL("Failed to read from pipe\n"); |
| 419 } |
| 420 |
| 421 // if samples == 0 then parse # of loops |
| 422 // else parse float |
| 423 if (i == 0) { |
| 424 sscanf(result, "%d", &loops); |
| 425 } else { |
| 426 sscanf(result, "%lf", &samples.push_back()); |
| 427 } |
| 428 |
| 429 // wait until exit |
| 430 int status; |
| 431 waitpid(childPid, &status, 0); |
| 432 } else if (0 == childPid) { |
| 433 char result[kOutResultSize]; |
| 434 if (i == 0) { |
| 435 sprintf(result, "%d", kilobench::setup_loops(bench)); |
| 436 } else { |
| 437 sprintf(result, "%lf", kilobench::time_sample(bench, loops))
; |
| 438 } |
| 439 |
| 440 // Make sure to write the null terminator |
| 441 if (write(descriptors[1], result, strlen(result) + 1) < 0) { |
| 442 SkFAIL("Failed to write to pipe\n"); |
| 443 } |
| 444 return 0; |
| 445 } else { |
| 446 SkFAIL("Fork failed\n"); |
| 447 } |
| 448 } |
| 449 |
| 450 Stats stats(samples); |
| 451 const double stddev_percent = 100 * sqrt(stats.var) / stats.mean; |
| 452 SkDebugf("%d\t%s\t%s\t%s\t%s\t%.0f%%\t%s\t%s\t%s\n" |
| 453 , loops |
| 454 , HUMANIZE(stats.min) |
| 455 , HUMANIZE(stats.median) |
| 456 , HUMANIZE(stats.mean) |
| 457 , HUMANIZE(stats.max) |
| 458 , stddev_percent |
| 459 , stats.plot.c_str() |
| 460 , "gpu" |
| 461 , bench->getUniqueName() |
| 462 ); |
| 463 |
390 } | 464 } |
391 | |
392 // Make sure we clean up the global GrContextFactory here, otherwise we migh
t race with the | |
393 // SkEventTracer destructor | |
394 ctxFactory.reset(nullptr); | |
395 return 0; | 465 return 0; |
396 } | 466 } |
397 | 467 |
398 #if !defined SK_BUILD_FOR_IOS | 468 #if !defined SK_BUILD_FOR_IOS |
399 int main(int argc, char** argv) { | 469 int main(int argc, char** argv) { |
400 SkCommandLineFlags::Parse(argc, argv); | 470 SkCommandLineFlags::Parse(argc, argv); |
401 return kilobench_main(); | 471 return kilobench_main(); |
402 } | 472 } |
403 #endif | 473 #endif |
OLD | NEW |