Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(73)

Side by Side Diff: tools/skpbench/skpbench.cpp

Issue 2410373002: skpbench: add warmup run (Closed)
Patch Set: skpbench: add warmup run Created 4 years, 2 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
« no previous file with comments | « tools/skpbench/_hardware_android.py ('k') | tools/skpbench/skpbench.py » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
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 "GpuTimer.h" 8 #include "GpuTimer.h"
9 #include "GrContextFactory.h" 9 #include "GrContextFactory.h"
10 #include "SkCanvas.h" 10 #include "SkCanvas.h"
11 #include "SkOSFile.h" 11 #include "SkOSFile.h"
12 #include "SkPerlinNoiseShader.h"
12 #include "SkPicture.h" 13 #include "SkPicture.h"
14 #include "SkPictureRecorder.h"
13 #include "SkStream.h" 15 #include "SkStream.h"
14 #include "SkSurface.h" 16 #include "SkSurface.h"
15 #include "SkSurfaceProps.h" 17 #include "SkSurfaceProps.h"
16 #include "picture_utils.h" 18 #include "picture_utils.h"
19 #include "sk_tool_utils.h"
17 #include "flags/SkCommandLineFlags.h" 20 #include "flags/SkCommandLineFlags.h"
18 #include "flags/SkCommonFlagsConfig.h" 21 #include "flags/SkCommonFlagsConfig.h"
19 #include <stdlib.h> 22 #include <stdlib.h>
20 #include <algorithm> 23 #include <algorithm>
21 #include <array> 24 #include <array>
22 #include <chrono> 25 #include <chrono>
23 #include <cmath> 26 #include <cmath>
24 #include <vector> 27 #include <vector>
25 28
26 /** 29 /**
27 * This is a minimalist program whose sole purpose is to open an skp file, bench mark it on a single 30 * This is a minimalist program whose sole purpose is to open an skp file, bench mark it on a single
28 * config, and exit. It is intended to be used through skpbench.py rather than i nvoked directly. 31 * config, and exit. It is intended to be used through skpbench.py rather than i nvoked directly.
29 * Limiting the entire process to a single config/skp pair helps to keep the res ults repeatable. 32 * Limiting the entire process to a single config/skp pair helps to keep the res ults repeatable.
30 * 33 *
31 * No tiling, looping, or other fanciness is used; it just draws the skp whole i nto a size-matched 34 * No tiling, looping, or other fanciness is used; it just draws the skp whole i nto a size-matched
32 * render target and syncs the GPU after each draw. 35 * render target and syncs the GPU after each draw.
33 * 36 *
34 * Currently, only GPU configs are supported. 37 * Currently, only GPU configs are supported.
35 */ 38 */
36 39
37 DEFINE_int32(duration, 5000, "number of milliseconds to run the benchmark"); 40 DEFINE_int32(duration, 5000, "number of milliseconds to run the benchmark");
38 DEFINE_int32(sampleMs, 50, "minimum duration of a sample"); 41 DEFINE_int32(sampleMs, 50, "minimum duration of a sample");
39 DEFINE_bool(gpuClock, false, "time on the gpu clock (gpu work only)"); 42 DEFINE_bool(gpuClock, false, "time on the gpu clock (gpu work only)");
40 DEFINE_bool(fps, false, "use fps instead of ms"); 43 DEFINE_bool(fps, false, "use fps instead of ms");
41 DEFINE_string(skp, "", "path to a single .skp file to benchmark"); 44 DEFINE_string(skp, "", "path to a single .skp file, or 'warmup' for a builtin wa rmup run");
42 DEFINE_string(png, "", "if set, save a .png proof to disk at this file location" ); 45 DEFINE_string(png, "", "if set, save a .png proof to disk at this file location" );
43 DEFINE_int32(verbosity, 4, "level of verbosity (0=none to 5=debug)"); 46 DEFINE_int32(verbosity, 4, "level of verbosity (0=none to 5=debug)");
44 DEFINE_bool(suppressHeader, false, "don't print a header row before the results" ); 47 DEFINE_bool(suppressHeader, false, "don't print a header row before the results" );
45 48
46 static const char* header = 49 static const char* header =
47 " accum median max min stddev samples sample_ms clock met ric config bench"; 50 " accum median max min stddev samples sample_ms clock met ric config bench";
48 51
49 static const char* resultFormat = 52 static const char* resultFormat =
50 "%8.4g %8.4g %8.4g %8.4g %6.3g%% %7li %9i %-5s %-6s %-9s %s"; 53 "%8.4g %8.4g %8.4g %8.4g %6.3g%% %7li %9i %-5s %-6s %-9s %s";
51 54
(...skipping 27 matching lines...) Expand all
79 enum class ExitErr { 82 enum class ExitErr {
80 kOk = 0, 83 kOk = 0,
81 kUsage = 64, 84 kUsage = 64,
82 kData = 65, 85 kData = 65,
83 kUnavailable = 69, 86 kUnavailable = 69,
84 kIO = 74, 87 kIO = 74,
85 kSoftware = 70 88 kSoftware = 70
86 }; 89 };
87 90
88 static void draw_skp_and_flush(SkCanvas*, const SkPicture*); 91 static void draw_skp_and_flush(SkCanvas*, const SkPicture*);
92 static sk_sp<SkPicture> create_warmup_skp();
89 static bool mkdir_p(const SkString& name); 93 static bool mkdir_p(const SkString& name);
90 static SkString join(const SkCommandLineFlags::StringArray&); 94 static SkString join(const SkCommandLineFlags::StringArray&);
91 static void exitf(ExitErr, const char* format, ...); 95 static void exitf(ExitErr, const char* format, ...);
92 96
93 static void run_benchmark(const sk_gpu_test::FenceSync* fenceSync, SkCanvas* can vas, 97 static void run_benchmark(const sk_gpu_test::FenceSync* fenceSync, SkCanvas* can vas,
94 const SkPicture* skp, std::vector<Sample>* samples) { 98 const SkPicture* skp, std::vector<Sample>* samples) {
95 using clock = std::chrono::high_resolution_clock; 99 using clock = std::chrono::high_resolution_clock;
96 const Sample::duration sampleDuration = std::chrono::milliseconds(FLAGS_samp leMs); 100 const Sample::duration sampleDuration = std::chrono::milliseconds(FLAGS_samp leMs);
97 const clock::duration benchDuration = std::chrono::milliseconds(FLAGS_durati on); 101 const clock::duration benchDuration = std::chrono::milliseconds(FLAGS_durati on);
98 102
(...skipping 124 matching lines...) Expand 10 before | Expand all | Expand 10 after
223 } 227 }
224 if (FLAGS_duration <= 0) { 228 if (FLAGS_duration <= 0) {
225 exit(0); // This can be used to print the header and quit. 229 exit(0); // This can be used to print the header and quit.
226 } 230 }
227 231
228 // Parse the config. 232 // Parse the config.
229 const SkCommandLineConfigGpu* config = nullptr; // Initialize for spurious w arning. 233 const SkCommandLineConfigGpu* config = nullptr; // Initialize for spurious w arning.
230 SkCommandLineConfigArray configs; 234 SkCommandLineConfigArray configs;
231 ParseConfigs(FLAGS_config, &configs); 235 ParseConfigs(FLAGS_config, &configs);
232 if (configs.count() != 1 || !(config = configs[0]->asConfigGpu())) { 236 if (configs.count() != 1 || !(config = configs[0]->asConfigGpu())) {
233 exitf(ExitErr::kUsage, "invalid config %s, must specify one (and only on e) GPU config", 237 exitf(ExitErr::kUsage, "invalid config '%s': must specify one (and only one) GPU config",
234 join(FLAGS_config).c_str()); 238 join(FLAGS_config).c_str());
235 } 239 }
236 240
237 // Parse the skp. 241 // Parse the skp.
238 if (FLAGS_skp.count() != 1) { 242 if (FLAGS_skp.count() != 1) {
239 exitf(ExitErr::kUsage, "invalid skp %s, must specify (and only one) skp path name.", 243 exitf(ExitErr::kUsage, "invalid skp '%s': must specify a single skp file , or 'warmup'",
240 join(FLAGS_skp).c_str()); 244 join(FLAGS_skp).c_str());
241 } 245 }
242 const char* skpfile = FLAGS_skp[0]; 246 sk_sp<SkPicture> skp;
243 std::unique_ptr<SkStream> skpstream(SkStream::MakeFromFile(skpfile)); 247 SkString skpname;
244 if (!skpstream) { 248 if (0 == strcmp(FLAGS_skp[0], "warmup")) {
245 exitf(ExitErr::kIO, "failed to open skp file %s", skpfile); 249 skp = create_warmup_skp();
246 } 250 skpname = "warmup";
247 sk_sp<SkPicture> skp = SkPicture::MakeFromStream(skpstream.get()); 251 } else {
248 if (!skp) { 252 const char* skpfile = FLAGS_skp[0];
249 exitf(ExitErr::kData, "failed to parse skp file %s", skpfile); 253 std::unique_ptr<SkStream> skpstream(SkStream::MakeFromFile(skpfile));
254 if (!skpstream) {
255 exitf(ExitErr::kIO, "failed to open skp file %s", skpfile);
256 }
257 skp = SkPicture::MakeFromStream(skpstream.get());
258 if (!skp) {
259 exitf(ExitErr::kData, "failed to parse skp file %s", skpfile);
260 }
261 skpname = SkOSPath::Basename(skpfile);
250 } 262 }
251 int width = SkTMin(SkScalarCeilToInt(skp->cullRect().width()), 2048), 263 int width = SkTMin(SkScalarCeilToInt(skp->cullRect().width()), 2048),
252 height = SkTMin(SkScalarCeilToInt(skp->cullRect().height()), 2048); 264 height = SkTMin(SkScalarCeilToInt(skp->cullRect().height()), 2048);
253 if (FLAGS_verbosity >= 3 && 265 if (FLAGS_verbosity >= 3 &&
254 (width != skp->cullRect().width() || height != skp->cullRect().height()) ) { 266 (width != skp->cullRect().width() || height != skp->cullRect().height()) ) {
255 fprintf(stderr, "%s is too large (%ix%i), cropping to %ix%i.\n", 267 fprintf(stderr, "%s is too large (%ix%i), cropping to %ix%i.\n",
256 SkOSPath::Basename(skpfile).c_str(), 268 skpname.c_str(), SkScalarCeilToInt(skp->cullRect().width ()),
257 SkScalarCeilToInt(skp->cullRect().width()),
258 SkScalarCeilToInt(skp->cullRect().height()), width, heig ht); 269 SkScalarCeilToInt(skp->cullRect().height()), width, heig ht);
259 } 270 }
260 271
261 // Create a context. 272 // Create a context.
262 sk_gpu_test::GrContextFactory factory; 273 sk_gpu_test::GrContextFactory factory;
263 sk_gpu_test::ContextInfo ctxInfo = 274 sk_gpu_test::ContextInfo ctxInfo =
264 factory.getContextInfo(config->getContextType(), config->getContextOptio ns()); 275 factory.getContextInfo(config->getContextType(), config->getContextOptio ns());
265 GrContext* ctx = ctxInfo.grContext(); 276 GrContext* ctx = ctxInfo.grContext();
266 if (!ctx) { 277 if (!ctx) {
267 exitf(ExitErr::kUnavailable, "failed to create context for config %s", 278 exitf(ExitErr::kUnavailable, "failed to create context for config %s",
(...skipping 20 matching lines...) Expand all
288 kPremul_SkAlphaType, sk_ref_sp(config-> getColorSpace())); 299 kPremul_SkAlphaType, sk_ref_sp(config-> getColorSpace()));
289 uint32_t flags = config->getUseDIText() ? SkSurfaceProps::kUseDeviceIndepend entFonts_Flag : 0; 300 uint32_t flags = config->getUseDIText() ? SkSurfaceProps::kUseDeviceIndepend entFonts_Flag : 0;
290 SkSurfaceProps props(flags, SkSurfaceProps::kLegacyFontHost_InitType); 301 SkSurfaceProps props(flags, SkSurfaceProps::kLegacyFontHost_InitType);
291 sk_sp<SkSurface> surface = 302 sk_sp<SkSurface> surface =
292 SkSurface::MakeRenderTarget(ctx, SkBudgeted::kNo, info, config->getSampl es(), &props); 303 SkSurface::MakeRenderTarget(ctx, SkBudgeted::kNo, info, config->getSampl es(), &props);
293 if (!surface) { 304 if (!surface) {
294 exitf(ExitErr::kUnavailable, "failed to create %ix%i render target for c onfig %s", 305 exitf(ExitErr::kUnavailable, "failed to create %ix%i render target for c onfig %s",
295 width, height, config->getTag().c_str()); 306 width, height, config->getTag().c_str());
296 } 307 }
297 308
309 // Run the benchmark.
298 std::vector<Sample> samples; 310 std::vector<Sample> samples;
299 if (FLAGS_sampleMs > 0) { 311 if (FLAGS_sampleMs > 0) {
300 // +1 because we might take one more sample in order to have an odd numb er. 312 // +1 because we might take one more sample in order to have an odd numb er.
301 samples.reserve(1 + (FLAGS_duration + FLAGS_sampleMs - 1) / FLAGS_sample Ms); 313 samples.reserve(1 + (FLAGS_duration + FLAGS_sampleMs - 1) / FLAGS_sample Ms);
302 } else { 314 } else {
303 samples.reserve(2 * FLAGS_duration); 315 samples.reserve(2 * FLAGS_duration);
304 } 316 }
305
306 // Run the benchmark.
307 SkCanvas* canvas = surface->getCanvas(); 317 SkCanvas* canvas = surface->getCanvas();
308 canvas->translate(-skp->cullRect().x(), -skp->cullRect().y()); 318 canvas->translate(-skp->cullRect().x(), -skp->cullRect().y());
309 if (!FLAGS_gpuClock) { 319 if (!FLAGS_gpuClock) {
310 run_benchmark(testCtx->fenceSync(), canvas, skp.get(), &samples); 320 run_benchmark(testCtx->fenceSync(), canvas, skp.get(), &samples);
311 } else { 321 } else {
312 if (!testCtx->gpuTimingSupport()) { 322 if (!testCtx->gpuTimingSupport()) {
313 exitf(ExitErr::kUnavailable, "GPU does not support timing"); 323 exitf(ExitErr::kUnavailable, "GPU does not support timing");
314 } 324 }
315 run_gpu_time_benchmark(testCtx->gpuTimer(), testCtx->fenceSync(), canvas , skp.get(), 325 run_gpu_time_benchmark(testCtx->gpuTimer(), testCtx->fenceSync(), canvas , skp.get(),
316 &samples); 326 &samples);
317 } 327 }
318 print_result(samples, config->getTag().c_str(), SkOSPath::Basename(skpfile). c_str()); 328 print_result(samples, config->getTag().c_str(), skpname.c_str());
319 329
320 // Save a proof (if one was requested). 330 // Save a proof (if one was requested).
321 if (!FLAGS_png.isEmpty()) { 331 if (!FLAGS_png.isEmpty()) {
322 SkBitmap bmp; 332 SkBitmap bmp;
323 bmp.setInfo(info); 333 bmp.setInfo(info);
324 if (!surface->getCanvas()->readPixels(&bmp, 0, 0)) { 334 if (!surface->getCanvas()->readPixels(&bmp, 0, 0)) {
325 exitf(ExitErr::kUnavailable, "failed to read canvas pixels for png") ; 335 exitf(ExitErr::kUnavailable, "failed to read canvas pixels for png") ;
326 } 336 }
327 const SkString &dirname = SkOSPath::Dirname(FLAGS_png[0]), 337 const SkString &dirname = SkOSPath::Dirname(FLAGS_png[0]),
328 &basename = SkOSPath::Basename(FLAGS_png[0]); 338 &basename = SkOSPath::Basename(FLAGS_png[0]);
329 if (!mkdir_p(dirname)) { 339 if (!mkdir_p(dirname)) {
330 exitf(ExitErr::kIO, "failed to create directory \"%s\" for png", dir name.c_str()); 340 exitf(ExitErr::kIO, "failed to create directory \"%s\" for png", dir name.c_str());
331 } 341 }
332 if (!sk_tools::write_bitmap_to_disk(bmp, dirname, nullptr, basename)) { 342 if (!sk_tools::write_bitmap_to_disk(bmp, dirname, nullptr, basename)) {
333 exitf(ExitErr::kIO, "failed to save png to \"%s\"", FLAGS_png[0]); 343 exitf(ExitErr::kIO, "failed to save png to \"%s\"", FLAGS_png[0]);
334 } 344 }
335 } 345 }
336 346
337 exit(0); 347 exit(0);
338 } 348 }
339 349
340 static void draw_skp_and_flush(SkCanvas* canvas, const SkPicture* skp) { 350 static void draw_skp_and_flush(SkCanvas* canvas, const SkPicture* skp) {
341 canvas->drawPicture(skp); 351 canvas->drawPicture(skp);
342 canvas->flush(); 352 canvas->flush();
343 } 353 }
344 354
355 static sk_sp<SkPicture> create_warmup_skp() {
356 static constexpr SkRect bounds{0, 0, 500, 500};
357 SkPictureRecorder recorder;
358 SkCanvas* recording = recorder.beginRecording(bounds);
359
360 recording->clear(SK_ColorWHITE);
361
362 SkPaint stroke;
363 stroke.setStyle(SkPaint::kStroke_Style);
364 stroke.setStrokeWidth(2);
365
366 // Use a big path to (theoretically) warmup the CPU.
367 SkPath bigPath;
368 sk_tool_utils::make_big_path(bigPath);
369 recording->drawPath(bigPath, stroke);
370
371 // Use a perlin shader to warmup the GPU.
372 SkPaint perlin;
373 perlin.setShader(SkPerlinNoiseShader::MakeTurbulence(0.1f, 0.1f, 1, 0, nullp tr));
374 recording->drawRect(bounds, perlin);
375
376 return recorder.finishRecordingAsPicture();
377 }
378
345 bool mkdir_p(const SkString& dirname) { 379 bool mkdir_p(const SkString& dirname) {
346 if (dirname.isEmpty()) { 380 if (dirname.isEmpty()) {
347 return true; 381 return true;
348 } 382 }
349 return mkdir_p(SkOSPath::Dirname(dirname.c_str())) && sk_mkdir(dirname.c_str ()); 383 return mkdir_p(SkOSPath::Dirname(dirname.c_str())) && sk_mkdir(dirname.c_str ());
350 } 384 }
351 385
352 static SkString join(const SkCommandLineFlags::StringArray& stringArray) { 386 static SkString join(const SkCommandLineFlags::StringArray& stringArray) {
353 SkString joined; 387 SkString joined;
354 for (int i = 0; i < FLAGS_config.count(); ++i) { 388 for (int i = 0; i < stringArray.count(); ++i) {
355 joined.appendf(i ? " %s" : "%s", FLAGS_config[i]); 389 joined.appendf(i ? " %s" : "%s", stringArray[i]);
356 } 390 }
357 return joined; 391 return joined;
358 } 392 }
359 393
360 static void exitf(ExitErr err, const char* format, ...) { 394 static void exitf(ExitErr err, const char* format, ...) {
361 fprintf(stderr, ExitErr::kSoftware == err ? "INTERNAL ERROR: " : "ERROR: "); 395 fprintf(stderr, ExitErr::kSoftware == err ? "INTERNAL ERROR: " : "ERROR: ");
362 va_list args; 396 va_list args;
363 va_start(args, format); 397 va_start(args, format);
364 vfprintf(stderr, format, args); 398 vfprintf(stderr, format, args);
365 va_end(args); 399 va_end(args);
(...skipping 20 matching lines...) Expand all
386 fFenceSync->deleteFence(fFence); 420 fFenceSync->deleteFence(fFence);
387 this->updateFence(); 421 this->updateFence();
388 } 422 }
389 423
390 void GpuSync::updateFence() { 424 void GpuSync::updateFence() {
391 fFence = fFenceSync->insertFence(); 425 fFence = fFenceSync->insertFence();
392 if (sk_gpu_test::kInvalidFence == fFence) { 426 if (sk_gpu_test::kInvalidFence == fFence) {
393 exitf(ExitErr::kUnavailable, "failed to insert fence"); 427 exitf(ExitErr::kUnavailable, "failed to insert fence");
394 } 428 }
395 } 429 }
OLDNEW
« no previous file with comments | « tools/skpbench/_hardware_android.py ('k') | tools/skpbench/skpbench.py » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698