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

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

Issue 2388433003: skpbench: add option for gpu timing (Closed)
Patch Set: SkAutoTDelete 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/parseskpbench.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 "GrContextFactory.h" 9 #include "GrContextFactory.h"
9 #include "SkCanvas.h" 10 #include "SkCanvas.h"
10 #include "SkOSFile.h" 11 #include "SkOSFile.h"
11 #include "SkPicture.h" 12 #include "SkPicture.h"
12 #include "SkStream.h" 13 #include "SkStream.h"
13 #include "SkSurface.h" 14 #include "SkSurface.h"
14 #include "SkSurfaceProps.h" 15 #include "SkSurfaceProps.h"
15 #include "picture_utils.h" 16 #include "picture_utils.h"
16 #include "flags/SkCommandLineFlags.h" 17 #include "flags/SkCommandLineFlags.h"
17 #include "flags/SkCommonFlagsConfig.h" 18 #include "flags/SkCommonFlagsConfig.h"
18 #include <stdlib.h> 19 #include <stdlib.h>
19 #include <algorithm> 20 #include <algorithm>
20 #include <array> 21 #include <array>
21 #include <chrono> 22 #include <chrono>
22 #include <cmath> 23 #include <cmath>
23 #include <vector> 24 #include <vector>
24 25
25 /** 26 /**
26 * This is a minimalist program whose sole purpose is to open an skp file, bench mark it on a single 27 * This is a minimalist program whose sole purpose is to open an skp file, bench mark it on a single
27 * config, and exit. It is intended to be used through skpbench.py rather than i nvoked directly. 28 * config, and exit. It is intended to be used through skpbench.py rather than i nvoked directly.
28 * Limiting the entire process to a single config/skp pair helps to keep the res ults repeatable. 29 * Limiting the entire process to a single config/skp pair helps to keep the res ults repeatable.
29 * 30 *
30 * No tiling, looping, or other fanciness is used; it just draws the skp whole i nto a size-matched 31 * No tiling, looping, or other fanciness is used; it just draws the skp whole i nto a size-matched
31 * render target and syncs the GPU after each draw. 32 * render target and syncs the GPU after each draw.
32 * 33 *
33 * Currently, only GPU configs are supported. 34 * Currently, only GPU configs are supported.
34 */ 35 */
35 36
36 using sk_gpu_test::PlatformFence;
37 using sk_gpu_test::kInvalidPlatformFence;
38 using sk_gpu_test::FenceSync;
39
40 DEFINE_int32(duration, 5000, "number of milliseconds to run the benchmark"); 37 DEFINE_int32(duration, 5000, "number of milliseconds to run the benchmark");
41 DEFINE_int32(sampleMs, 50, "minimum duration of a sample"); 38 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(fps, false, "use fps instead of ms"); 40 DEFINE_bool(fps, false, "use fps instead of ms");
43 DEFINE_string(skp, "", "path to a single .skp file to benchmark"); 41 DEFINE_string(skp, "", "path to a single .skp file to benchmark");
44 DEFINE_string(png, "", "if set, save a .png proof to disk at this file location" ); 42 DEFINE_string(png, "", "if set, save a .png proof to disk at this file location" );
45 DEFINE_int32(verbosity, 4, "level of verbosity (0=none to 5=debug)"); 43 DEFINE_int32(verbosity, 4, "level of verbosity (0=none to 5=debug)");
46 DEFINE_bool(suppressHeader, false, "don't print a header row before the results" ); 44 DEFINE_bool(suppressHeader, false, "don't print a header row before the results" );
47 45
48 static const char* header = 46 static const char* header =
49 " accum median max min stddev samples sample_ms metric config bench"; 47 " accum median max min stddev samples sample_ms clock met ric config bench";
50 48
51 static const char* resultFormat = 49 static const char* resultFormat =
52 "%8.4g %8.4g %8.4g %8.4g %6.3g%% %7li %9i %-6s %-9s %s"; 50 "%8.4g %8.4g %8.4g %8.4g %6.3g%% %7li %9i %-5s %-6s %-9s %s";
53 51
54 struct Sample { 52 struct Sample {
55 using clock = std::chrono::high_resolution_clock; 53 using duration = std::chrono::nanoseconds;
56 54
57 Sample() : fFrames(0), fDuration(0) {} 55 Sample() : fFrames(0), fDuration(0) {}
58 double seconds() const { return std::chrono::duration<double>(fDuration).cou nt(); } 56 double seconds() const { return std::chrono::duration<double>(fDuration).cou nt(); }
59 double ms() const { return std::chrono::duration<double, std::milli>(fDurati on).count(); } 57 double ms() const { return std::chrono::duration<double, std::milli>(fDurati on).count(); }
60 double value() const { return FLAGS_fps ? fFrames / this->seconds() : this-> ms() / fFrames; } 58 double value() const { return FLAGS_fps ? fFrames / this->seconds() : this-> ms() / fFrames; }
61 static const char* metric() { return FLAGS_fps ? "fps" : "ms"; } 59 static const char* metric() { return FLAGS_fps ? "fps" : "ms"; }
62 60
63 int fFrames; 61 int fFrames;
64 clock::duration fDuration; 62 duration fDuration;
65 }; 63 };
66 64
67 class GpuSync { 65 class GpuSync {
68 public: 66 public:
69 GpuSync(const FenceSync* fenceSync); 67 GpuSync(const sk_gpu_test::FenceSync* fenceSync);
70 ~GpuSync(); 68 ~GpuSync();
71 69
72 void syncToPreviousFrame(); 70 void syncToPreviousFrame();
73 71
74 private: 72 private:
75 void updateFence(); 73 void updateFence();
76 74
77 const FenceSync* const fFenceSync; 75 const sk_gpu_test::FenceSync* const fFenceSync;
78 PlatformFence fFence; 76 sk_gpu_test::PlatformFence fFence;
79 }; 77 };
80 78
81 enum class ExitErr { 79 enum class ExitErr {
82 kOk = 0, 80 kOk = 0,
83 kUsage = 64, 81 kUsage = 64,
84 kData = 65, 82 kData = 65,
85 kUnavailable = 69, 83 kUnavailable = 69,
86 kIO = 74, 84 kIO = 74,
87 kSoftware = 70 85 kSoftware = 70
88 }; 86 };
89 87
90 static void draw_skp_and_flush(SkCanvas*, const SkPicture*); 88 static void draw_skp_and_flush(SkCanvas*, const SkPicture*);
91 static bool mkdir_p(const SkString& name); 89 static bool mkdir_p(const SkString& name);
92 static SkString join(const SkCommandLineFlags::StringArray&); 90 static SkString join(const SkCommandLineFlags::StringArray&);
93 static void exitf(ExitErr, const char* format, ...); 91 static void exitf(ExitErr, const char* format, ...);
94 92
95 static void run_benchmark(const FenceSync* fenceSync, SkCanvas* canvas, const Sk Picture* skp, 93 static void run_benchmark(const sk_gpu_test::FenceSync* fenceSync, SkCanvas* can vas,
96 std::vector<Sample>* samples) { 94 const SkPicture* skp, std::vector<Sample>* samples) {
97 using clock = Sample::clock; 95 using clock = std::chrono::high_resolution_clock;
98 const clock::duration sampleDuration = std::chrono::milliseconds(FLAGS_sampl eMs); 96 const Sample::duration sampleDuration = std::chrono::milliseconds(FLAGS_samp leMs);
99 const clock::duration benchDuration = std::chrono::milliseconds(FLAGS_durati on); 97 const clock::duration benchDuration = std::chrono::milliseconds(FLAGS_durati on);
100 98
101 draw_skp_and_flush(canvas, skp); 99 draw_skp_and_flush(canvas, skp);
102 GpuSync gpuSync(fenceSync); 100 GpuSync gpuSync(fenceSync);
103 101
104 draw_skp_and_flush(canvas, skp); 102 draw_skp_and_flush(canvas, skp);
105 gpuSync.syncToPreviousFrame(); 103 gpuSync.syncToPreviousFrame();
106 104
107 clock::time_point now = clock::now(); 105 clock::time_point now = clock::now();
108 const clock::time_point endTime = now + benchDuration; 106 const clock::time_point endTime = now + benchDuration;
109 107
110 do { 108 do {
111 clock::time_point sampleStart = now; 109 clock::time_point sampleStart = now;
112 samples->emplace_back(); 110 samples->emplace_back();
113 Sample& sample = samples->back(); 111 Sample& sample = samples->back();
114 112
115 do { 113 do {
116 draw_skp_and_flush(canvas, skp); 114 draw_skp_and_flush(canvas, skp);
117 gpuSync.syncToPreviousFrame(); 115 gpuSync.syncToPreviousFrame();
118 116
119 now = clock::now(); 117 now = clock::now();
120 sample.fDuration = now - sampleStart; 118 sample.fDuration = now - sampleStart;
121 ++sample.fFrames; 119 ++sample.fFrames;
122 } while (sample.fDuration < sampleDuration); 120 } while (sample.fDuration < sampleDuration);
123 } while (now < endTime || 0 == samples->size() % 2); 121 } while (now < endTime || 0 == samples->size() % 2);
124 } 122 }
125 123
124 static void run_gpu_time_benchmark(sk_gpu_test::GpuTimer* gpuTimer,
125 const sk_gpu_test::FenceSync* fenceSync, SkCa nvas* canvas,
126 const SkPicture* skp, std::vector<Sample>* sa mples) {
127 using sk_gpu_test::PlatformTimerQuery;
128 using clock = std::chrono::steady_clock;
129 const clock::duration sampleDuration = std::chrono::milliseconds(FLAGS_sampl eMs);
130 const clock::duration benchDuration = std::chrono::milliseconds(FLAGS_durati on);
131
132 if (!gpuTimer->disjointSupport()) {
133 fprintf(stderr, "WARNING: GPU timer cannot detect disjoint operations; "
134 "results may be unreliable\n");
135 }
136
137 draw_skp_and_flush(canvas, skp);
138 GpuSync gpuSync(fenceSync);
139
140 gpuTimer->queueStart();
141 draw_skp_and_flush(canvas, skp);
142 PlatformTimerQuery previousTime = gpuTimer->queueStop();
143 gpuSync.syncToPreviousFrame();
144
145 clock::time_point now = clock::now();
146 const clock::time_point endTime = now + benchDuration;
147
148 do {
149 const clock::time_point sampleEndTime = now + sampleDuration;
150 samples->emplace_back();
151 Sample& sample = samples->back();
152
153 do {
154 gpuTimer->queueStart();
155 draw_skp_and_flush(canvas, skp);
156 PlatformTimerQuery time = gpuTimer->queueStop();
157 gpuSync.syncToPreviousFrame();
158
159 switch (gpuTimer->checkQueryStatus(previousTime)) {
160 using QueryStatus = sk_gpu_test::GpuTimer::QueryStatus;
161 case QueryStatus::kInvalid:
162 exitf(ExitErr::kUnavailable, "GPU timer failed");
163 case QueryStatus::kPending:
164 exitf(ExitErr::kUnavailable, "timer query still not ready af ter fence sync");
165 case QueryStatus::kDisjoint:
166 if (FLAGS_verbosity >= 4) {
167 fprintf(stderr, "discarding timer query due to disjoint operations.\n");
168 }
169 break;
170 case QueryStatus::kAccurate:
171 sample.fDuration += gpuTimer->getTimeElapsed(previousTime);
172 ++sample.fFrames;
173 break;
174 }
175 gpuTimer->deleteQuery(previousTime);
176 previousTime = time;
177 now = clock::now();
178 } while (now < sampleEndTime || 0 == sample.fFrames);
179 } while (now < endTime || 0 == samples->size() % 2);
180
181 gpuTimer->deleteQuery(previousTime);
182 }
183
126 void print_result(const std::vector<Sample>& samples, const char* config, const char* bench) { 184 void print_result(const std::vector<Sample>& samples, const char* config, const char* bench) {
127 if (0 == (samples.size() % 2)) { 185 if (0 == (samples.size() % 2)) {
128 exitf(ExitErr::kSoftware, "attempted to gather stats on even number of s amples"); 186 exitf(ExitErr::kSoftware, "attempted to gather stats on even number of s amples");
129 } 187 }
130 188
131 Sample accum = Sample(); 189 Sample accum = Sample();
132 std::vector<double> values; 190 std::vector<double> values;
133 values.reserve(samples.size()); 191 values.reserve(samples.size());
134 for (const Sample& sample : samples) { 192 for (const Sample& sample : samples) {
135 accum.fFrames += sample.fFrames; 193 accum.fFrames += sample.fFrames;
136 accum.fDuration += sample.fDuration; 194 accum.fDuration += sample.fDuration;
137 values.push_back(sample.value()); 195 values.push_back(sample.value());
138 } 196 }
139 std::sort(values.begin(), values.end()); 197 std::sort(values.begin(), values.end());
140 198
141 const double accumValue = accum.value(); 199 const double accumValue = accum.value();
142 double variance = 0; 200 double variance = 0;
143 for (double value : values) { 201 for (double value : values) {
144 const double delta = value - accumValue; 202 const double delta = value - accumValue;
145 variance += delta * delta; 203 variance += delta * delta;
146 } 204 }
147 variance /= values.size(); 205 variance /= values.size();
148 // Technically, this is the relative standard deviation. 206 // Technically, this is the relative standard deviation.
149 const double stddev = 100/*%*/ * sqrt(variance) / accumValue; 207 const double stddev = 100/*%*/ * sqrt(variance) / accumValue;
150 208
151 printf(resultFormat, accumValue, values[values.size() / 2], values.back(), v alues.front(), 209 printf(resultFormat, accumValue, values[values.size() / 2], values.back(), v alues.front(),
152 stddev, values.size(), FLAGS_sampleMs, Sample::metric(), config, benc h); 210 stddev, values.size(), FLAGS_sampleMs, FLAGS_gpuClock ? "gpu" : "cpu" , Sample::metric(),
211 config, bench);
153 printf("\n"); 212 printf("\n");
154 fflush(stdout); 213 fflush(stdout);
155 } 214 }
156 215
157 int main(int argc, char** argv) { 216 int main(int argc, char** argv) {
158 SkCommandLineFlags::SetUsage("Use skpbench.py instead. " 217 SkCommandLineFlags::SetUsage("Use skpbench.py instead. "
159 "You usually don't want to use this program dir ectly."); 218 "You usually don't want to use this program dir ectly.");
160 SkCommandLineFlags::Parse(argc, argv); 219 SkCommandLineFlags::Parse(argc, argv);
161 220
162 if (!FLAGS_suppressHeader) { 221 if (!FLAGS_suppressHeader) {
(...skipping 77 matching lines...) Expand 10 before | Expand all | Expand 10 after
240 if (FLAGS_sampleMs > 0) { 299 if (FLAGS_sampleMs > 0) {
241 // +1 because we might take one more sample in order to have an odd numb er. 300 // +1 because we might take one more sample in order to have an odd numb er.
242 samples.reserve(1 + (FLAGS_duration + FLAGS_sampleMs - 1) / FLAGS_sample Ms); 301 samples.reserve(1 + (FLAGS_duration + FLAGS_sampleMs - 1) / FLAGS_sample Ms);
243 } else { 302 } else {
244 samples.reserve(2 * FLAGS_duration); 303 samples.reserve(2 * FLAGS_duration);
245 } 304 }
246 305
247 // Run the benchmark. 306 // Run the benchmark.
248 SkCanvas* canvas = surface->getCanvas(); 307 SkCanvas* canvas = surface->getCanvas();
249 canvas->translate(-skp->cullRect().x(), -skp->cullRect().y()); 308 canvas->translate(-skp->cullRect().x(), -skp->cullRect().y());
250 run_benchmark(testCtx->fenceSync(), canvas, skp.get(), &samples); 309 if (!FLAGS_gpuClock) {
310 run_benchmark(testCtx->fenceSync(), canvas, skp.get(), &samples);
311 } else {
312 if (!testCtx->gpuTimingSupport()) {
313 exitf(ExitErr::kUnavailable, "GPU does not support timing");
314 }
315 run_gpu_time_benchmark(testCtx->gpuTimer(), testCtx->fenceSync(), canvas , skp.get(),
316 &samples);
317 }
251 print_result(samples, config->getTag().c_str(), SkOSPath::Basename(skpfile). c_str()); 318 print_result(samples, config->getTag().c_str(), SkOSPath::Basename(skpfile). c_str());
252 319
253 // Save a proof (if one was requested). 320 // Save a proof (if one was requested).
254 if (!FLAGS_png.isEmpty()) { 321 if (!FLAGS_png.isEmpty()) {
255 SkBitmap bmp; 322 SkBitmap bmp;
256 bmp.setInfo(info); 323 bmp.setInfo(info);
257 if (!surface->getCanvas()->readPixels(&bmp, 0, 0)) { 324 if (!surface->getCanvas()->readPixels(&bmp, 0, 0)) {
258 exitf(ExitErr::kUnavailable, "failed to read canvas pixels for png") ; 325 exitf(ExitErr::kUnavailable, "failed to read canvas pixels for png") ;
259 } 326 }
260 const SkString &dirname = SkOSPath::Dirname(FLAGS_png[0]), 327 const SkString &dirname = SkOSPath::Dirname(FLAGS_png[0]),
(...skipping 32 matching lines...) Expand 10 before | Expand all | Expand 10 after
293 static void exitf(ExitErr err, const char* format, ...) { 360 static void exitf(ExitErr err, const char* format, ...) {
294 fprintf(stderr, ExitErr::kSoftware == err ? "INTERNAL ERROR: " : "ERROR: "); 361 fprintf(stderr, ExitErr::kSoftware == err ? "INTERNAL ERROR: " : "ERROR: ");
295 va_list args; 362 va_list args;
296 va_start(args, format); 363 va_start(args, format);
297 vfprintf(stderr, format, args); 364 vfprintf(stderr, format, args);
298 va_end(args); 365 va_end(args);
299 fprintf(stderr, ExitErr::kSoftware == err ? "; this should never happen.\n": ".\n"); 366 fprintf(stderr, ExitErr::kSoftware == err ? "; this should never happen.\n": ".\n");
300 exit((int)err); 367 exit((int)err);
301 } 368 }
302 369
303 GpuSync::GpuSync(const FenceSync* fenceSync) 370 GpuSync::GpuSync(const sk_gpu_test::FenceSync* fenceSync)
304 : fFenceSync(fenceSync) { 371 : fFenceSync(fenceSync) {
305 this->updateFence(); 372 this->updateFence();
306 } 373 }
307 374
308 GpuSync::~GpuSync() { 375 GpuSync::~GpuSync() {
309 fFenceSync->deleteFence(fFence); 376 fFenceSync->deleteFence(fFence);
310 } 377 }
311 378
312 void GpuSync::syncToPreviousFrame() { 379 void GpuSync::syncToPreviousFrame() {
313 if (kInvalidPlatformFence == fFence) { 380 if (sk_gpu_test::kInvalidFence == fFence) {
314 exitf(ExitErr::kSoftware, "attempted to sync with invalid fence"); 381 exitf(ExitErr::kSoftware, "attempted to sync with invalid fence");
315 } 382 }
316 if (!fFenceSync->waitFence(fFence)) { 383 if (!fFenceSync->waitFence(fFence)) {
317 exitf(ExitErr::kUnavailable, "failed to wait for fence"); 384 exitf(ExitErr::kUnavailable, "failed to wait for fence");
318 } 385 }
319 fFenceSync->deleteFence(fFence); 386 fFenceSync->deleteFence(fFence);
320 this->updateFence(); 387 this->updateFence();
321 } 388 }
322 389
323 void GpuSync::updateFence() { 390 void GpuSync::updateFence() {
324 fFence = fFenceSync->insertFence(); 391 fFence = fFenceSync->insertFence();
325 if (kInvalidPlatformFence == fFence) { 392 if (sk_gpu_test::kInvalidFence == fFence) {
326 exitf(ExitErr::kUnavailable, "failed to insert fence"); 393 exitf(ExitErr::kUnavailable, "failed to insert fence");
327 } 394 }
328 } 395 }
OLDNEW
« no previous file with comments | « tools/skpbench/parseskpbench.py ('k') | tools/skpbench/skpbench.py » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698