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 "GrContextFactory.h" | 8 #include "GrContextFactory.h" |
9 #include "SkCanvas.h" | 9 #include "SkCanvas.h" |
10 #include "SkOSFile.h" | 10 #include "SkOSFile.h" |
(...skipping 12 matching lines...) Expand all Loading... |
23 #include <vector> | 23 #include <vector> |
24 | 24 |
25 /** | 25 /** |
26 * This is a minimalist program whose sole purpose is to open an skp file, bench
mark it on a single | 26 * 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. | 27 * 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. | 28 * Limiting the entire process to a single config/skp pair helps to keep the res
ults repeatable. |
29 * | 29 * |
30 * No tiling, looping, or other fanciness is used; it just draws the skp whole i
nto a size-matched | 30 * 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. | 31 * render target and syncs the GPU after each draw. |
32 * | 32 * |
33 * The results consist of a fixed amount of samples (--samples). A sample is def
ined as the number | |
34 * of frames rendered within a set amount of time (--sampleMs). | |
35 * | |
36 * Currently, only GPU configs are supported. | 33 * Currently, only GPU configs are supported. |
37 */ | 34 */ |
38 | 35 |
39 DEFINE_int32(samples, 101, "number of samples to collect"); | 36 DEFINE_int32(duration, 5000, "number of milliseconds to run the benchmark"); |
40 DEFINE_int32(sampleMs, 50, "duration of each sample"); | 37 DEFINE_int32(sampleMs, 50, "minimum duration of a sample"); |
41 DEFINE_bool(fps, false, "use fps instead of ms"); | 38 DEFINE_bool(fps, false, "use fps instead of ms"); |
42 DEFINE_string(skp, "", "path to a single .skp file to benchmark"); | 39 DEFINE_string(skp, "", "path to a single .skp file to benchmark"); |
43 DEFINE_string(png, "", "if set, save a .png proof to disk at this file location"
); | 40 DEFINE_string(png, "", "if set, save a .png proof to disk at this file location"
); |
44 DEFINE_int32(verbosity, 4, "level of verbosity (0=none to 5=debug)"); | 41 DEFINE_int32(verbosity, 4, "level of verbosity (0=none to 5=debug)"); |
45 DEFINE_bool(suppressHeader, false, "don't print a header row before the results"
); | 42 DEFINE_bool(suppressHeader, false, "don't print a header row before the results"
); |
46 | 43 |
47 static const char* header = | 44 static const char* header = |
48 " median accum max min stddev metric samples sample_ms
config bench"; | 45 " median accum max min stddev metric samples sample_ms
config bench"; |
49 | 46 |
50 static const char* resultFormat = | 47 static const char* resultFormat = |
(...skipping 24 matching lines...) Expand all Loading... |
75 static void draw_skp_and_flush(SkCanvas*, const SkPicture*); | 72 static void draw_skp_and_flush(SkCanvas*, const SkPicture*); |
76 static SkPlatformGpuFence insert_verified_fence(const SkGpuFenceSync*); | 73 static SkPlatformGpuFence insert_verified_fence(const SkGpuFenceSync*); |
77 static void wait_fence_and_delete(const SkGpuFenceSync*, SkPlatformGpuFence); | 74 static void wait_fence_and_delete(const SkGpuFenceSync*, SkPlatformGpuFence); |
78 static bool mkdir_p(const SkString& name); | 75 static bool mkdir_p(const SkString& name); |
79 static SkString join(const SkCommandLineFlags::StringArray&); | 76 static SkString join(const SkCommandLineFlags::StringArray&); |
80 static void exitf(ExitErr, const char* format, ...); | 77 static void exitf(ExitErr, const char* format, ...); |
81 | 78 |
82 static void run_benchmark(const SkGpuFenceSync* sync, SkCanvas* canvas, const Sk
Picture* skp, | 79 static void run_benchmark(const SkGpuFenceSync* sync, SkCanvas* canvas, const Sk
Picture* skp, |
83 std::vector<Sample>* samples) { | 80 std::vector<Sample>* samples) { |
84 using clock = Sample::clock; | 81 using clock = Sample::clock; |
85 std::chrono::milliseconds sampleMs(FLAGS_sampleMs); | 82 const clock::duration sampleDuration = std::chrono::milliseconds(FLAGS_sampl
eMs); |
86 | 83 const clock::duration benchDuration = std::chrono::milliseconds(FLAGS_durati
on); |
87 samples->clear(); | |
88 samples->resize(FLAGS_samples); | |
89 | 84 |
90 // Prime the graphics pipe. | 85 // Prime the graphics pipe. |
91 SkPlatformGpuFence frameN_minus_2, frameN_minus_1; | 86 SkPlatformGpuFence frameN_minus_2, frameN_minus_1; |
92 { | 87 { |
93 draw_skp_and_flush(canvas, skp); | 88 draw_skp_and_flush(canvas, skp); |
94 SkPlatformGpuFence frame0 = insert_verified_fence(sync); | 89 SkPlatformGpuFence frame0 = insert_verified_fence(sync); |
95 | 90 |
96 draw_skp_and_flush(canvas, skp); | 91 draw_skp_and_flush(canvas, skp); |
97 frameN_minus_2 = insert_verified_fence(sync); | 92 frameN_minus_2 = insert_verified_fence(sync); |
98 | 93 |
99 draw_skp_and_flush(canvas, skp); | 94 draw_skp_and_flush(canvas, skp); |
100 frameN_minus_1 = insert_verified_fence(sync); | 95 frameN_minus_1 = insert_verified_fence(sync); |
101 | 96 |
102 wait_fence_and_delete(sync, frame0); | 97 wait_fence_and_delete(sync, frame0); |
103 } | 98 } |
104 | 99 |
105 clock::time_point start = clock::now(); | 100 clock::time_point now = clock::now(); |
| 101 const clock::time_point endTime = now + benchDuration; |
106 | 102 |
107 for (Sample& sample : *samples) { | 103 do { |
108 clock::time_point end; | 104 clock::time_point sampleStart = now; |
| 105 samples->emplace_back(); |
| 106 Sample& sample = samples->back(); |
| 107 |
109 do { | 108 do { |
110 draw_skp_and_flush(canvas, skp); | 109 draw_skp_and_flush(canvas, skp); |
111 | 110 |
112 // Sync the GPU. | 111 // Sync the GPU. |
113 wait_fence_and_delete(sync, frameN_minus_2); | 112 wait_fence_and_delete(sync, frameN_minus_2); |
114 frameN_minus_2 = frameN_minus_1; | 113 frameN_minus_2 = frameN_minus_1; |
115 frameN_minus_1 = insert_verified_fence(sync); | 114 frameN_minus_1 = insert_verified_fence(sync); |
116 | 115 |
117 end = clock::now(); | 116 now = clock::now(); |
118 sample.fDuration = end - start; | 117 sample.fDuration = now - sampleStart; |
119 ++sample.fFrames; | 118 ++sample.fFrames; |
120 } while (sample.fDuration < sampleMs); | 119 } while (sample.fDuration < sampleDuration); |
121 | 120 } while (now < endTime || 0 == samples->size() % 2); |
122 if (FLAGS_verbosity >= 5) { | |
123 fprintf(stderr, "%.4g%s [ms=%.4g frames=%i]\n", | |
124 sample.value(), Sample::metric(), sample.ms(), sampl
e.fFrames); | |
125 } | |
126 | |
127 start = end; | |
128 } | |
129 | 121 |
130 sync->deleteFence(frameN_minus_2); | 122 sync->deleteFence(frameN_minus_2); |
131 sync->deleteFence(frameN_minus_1); | 123 sync->deleteFence(frameN_minus_1); |
132 } | 124 } |
133 | 125 |
134 void print_result(const std::vector<Sample>& samples, const char* config, const
char* bench) { | 126 void print_result(const std::vector<Sample>& samples, const char* config, const
char* bench) { |
135 if (0 == (samples.size() % 2)) { | 127 if (0 == (samples.size() % 2)) { |
136 exitf(ExitErr::kSoftware, "attempted to gather stats on even number of s
amples"); | 128 exitf(ExitErr::kSoftware, "attempted to gather stats on even number of s
amples"); |
137 } | 129 } |
138 | 130 |
139 Sample accum = Sample(); | 131 Sample accum = Sample(); |
140 std::vector<double> values; | 132 std::vector<double> values; |
141 values.reserve(samples.size()); | 133 values.reserve(samples.size()); |
142 for (const Sample& sample : samples) { | 134 for (const Sample& sample : samples) { |
143 accum.fFrames += sample.fFrames; | 135 accum.fFrames += sample.fFrames; |
144 accum.fDuration += sample.fDuration; | 136 accum.fDuration += sample.fDuration; |
145 values.push_back(sample.value()); | 137 values.push_back(sample.value()); |
146 } | 138 } |
147 std::sort(values.begin(), values.end()); | 139 std::sort(values.begin(), values.end()); |
148 const double median = values[values.size() / 2]; | 140 const double medianValue = values[values.size() / 2]; |
| 141 const double accumValue = accum.value(); |
149 | 142 |
150 const double meanValue = accum.value(); | |
151 double variance = 0; | 143 double variance = 0; |
152 for (const Sample& sample : samples) { | 144 for (double value : values) { |
153 const double delta = sample.value() - meanValue; | 145 const double delta = value - accumValue; |
154 variance += delta * delta; | 146 variance += delta * delta; |
155 } | 147 } |
156 variance /= samples.size(); | 148 variance /= values.size(); |
157 // Technically, this is the relative standard deviation. | 149 // Technically, this is the relative standard deviation. |
158 const double stddev = 100/*%*/ * sqrt(variance) / meanValue; | 150 const double stddev = 100/*%*/ * sqrt(variance) / accumValue; |
159 | 151 |
160 printf(resultFormat, median, accum.value(), values.back(), values.front(), s
tddev, | 152 printf(resultFormat, medianValue, accumValue, values.back(), values.front(),
stddev, |
161 Sample::metric(), values.size(), FLAGS_sampleMs, config, bench); | 153 Sample::metric(), values.size(), FLAGS_sampleMs, config, bench); |
162 printf("\n"); | 154 printf("\n"); |
163 fflush(stdout); | 155 fflush(stdout); |
164 } | 156 } |
165 | 157 |
166 int main(int argc, char** argv) { | 158 int main(int argc, char** argv) { |
167 SkCommandLineFlags::SetUsage("Use skpbench.py instead. " | 159 SkCommandLineFlags::SetUsage("Use skpbench.py instead. " |
168 "You usually don't want to use this program dir
ectly."); | 160 "You usually don't want to use this program dir
ectly."); |
169 SkCommandLineFlags::Parse(argc, argv); | 161 SkCommandLineFlags::Parse(argc, argv); |
170 | 162 |
171 if (!FLAGS_suppressHeader) { | 163 if (!FLAGS_suppressHeader) { |
172 printf("%s\n", header); | 164 printf("%s\n", header); |
173 } | 165 } |
174 if (FLAGS_samples <= 0) { | 166 if (FLAGS_duration <= 0) { |
175 exit(0); // This can be used to print the header and quit. | 167 exit(0); // This can be used to print the header and quit. |
176 } | 168 } |
177 if (0 == FLAGS_samples % 2) { | |
178 fprintf(stderr, "WARNING: even number of samples requested (%i); " | |
179 "using %i so there can be a true median.\n", | |
180 FLAGS_samples, FLAGS_samples + 1); | |
181 ++FLAGS_samples; | |
182 } | |
183 | 169 |
184 // Parse the config. | 170 // Parse the config. |
185 const SkCommandLineConfigGpu* config = nullptr; // Initialize for spurious w
arning. | 171 const SkCommandLineConfigGpu* config = nullptr; // Initialize for spurious w
arning. |
186 SkCommandLineConfigArray configs; | 172 SkCommandLineConfigArray configs; |
187 ParseConfigs(FLAGS_config, &configs); | 173 ParseConfigs(FLAGS_config, &configs); |
188 if (configs.count() != 1 || !(config = configs[0]->asConfigGpu())) { | 174 if (configs.count() != 1 || !(config = configs[0]->asConfigGpu())) { |
189 exitf(ExitErr::kUsage, "invalid config %s, must specify one (and only on
e) GPU config", | 175 exitf(ExitErr::kUsage, "invalid config %s, must specify one (and only on
e) GPU config", |
190 join(FLAGS_config).c_str()); | 176 join(FLAGS_config).c_str()); |
191 } | 177 } |
192 | 178 |
(...skipping 51 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
244 kPremul_SkAlphaType, sk_ref_sp(config->
getColorSpace())); | 230 kPremul_SkAlphaType, sk_ref_sp(config->
getColorSpace())); |
245 uint32_t flags = config->getUseDIText() ? SkSurfaceProps::kUseDeviceIndepend
entFonts_Flag : 0; | 231 uint32_t flags = config->getUseDIText() ? SkSurfaceProps::kUseDeviceIndepend
entFonts_Flag : 0; |
246 SkSurfaceProps props(flags, SkSurfaceProps::kLegacyFontHost_InitType); | 232 SkSurfaceProps props(flags, SkSurfaceProps::kLegacyFontHost_InitType); |
247 sk_sp<SkSurface> surface = | 233 sk_sp<SkSurface> surface = |
248 SkSurface::MakeRenderTarget(ctx, SkBudgeted::kNo, info, config->getSampl
es(), &props); | 234 SkSurface::MakeRenderTarget(ctx, SkBudgeted::kNo, info, config->getSampl
es(), &props); |
249 if (!surface) { | 235 if (!surface) { |
250 exitf(ExitErr::kUnavailable, "failed to create %ix%i render target for c
onfig %s", | 236 exitf(ExitErr::kUnavailable, "failed to create %ix%i render target for c
onfig %s", |
251 width, height, config->getTag().c_str()); | 237 width, height, config->getTag().c_str()); |
252 } | 238 } |
253 | 239 |
| 240 std::vector<Sample> samples; |
| 241 if (FLAGS_sampleMs > 0) { |
| 242 // +1 because we might take one more sample in order to have an odd numb
er. |
| 243 samples.reserve(1 + (FLAGS_duration + FLAGS_sampleMs - 1) / FLAGS_sample
Ms); |
| 244 } else { |
| 245 samples.reserve(2 * FLAGS_duration); |
| 246 } |
| 247 |
254 // Run the benchmark. | 248 // Run the benchmark. |
255 std::vector<Sample> samples; | |
256 SkCanvas* canvas = surface->getCanvas(); | 249 SkCanvas* canvas = surface->getCanvas(); |
257 canvas->translate(-skp->cullRect().x(), -skp->cullRect().y()); | 250 canvas->translate(-skp->cullRect().x(), -skp->cullRect().y()); |
258 run_benchmark(testCtx->fenceSync(), canvas, skp.get(), &samples); | 251 run_benchmark(testCtx->fenceSync(), canvas, skp.get(), &samples); |
259 print_result(samples, config->getTag().c_str(), SkOSPath::Basename(skpfile).
c_str()); | 252 print_result(samples, config->getTag().c_str(), SkOSPath::Basename(skpfile).
c_str()); |
260 | 253 |
261 // Save a proof (if one was requested). | 254 // Save a proof (if one was requested). |
262 if (!FLAGS_png.isEmpty()) { | 255 if (!FLAGS_png.isEmpty()) { |
263 SkBitmap bmp; | 256 SkBitmap bmp; |
264 bmp.setInfo(info); | 257 bmp.setInfo(info); |
265 if (!surface->getCanvas()->readPixels(&bmp, 0, 0)) { | 258 if (!surface->getCanvas()->readPixels(&bmp, 0, 0)) { |
(...skipping 52 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
318 | 311 |
319 static void exitf(ExitErr err, const char* format, ...) { | 312 static void exitf(ExitErr err, const char* format, ...) { |
320 fprintf(stderr, ExitErr::kSoftware == err ? "INTERNAL ERROR: " : "ERROR: "); | 313 fprintf(stderr, ExitErr::kSoftware == err ? "INTERNAL ERROR: " : "ERROR: "); |
321 va_list args; | 314 va_list args; |
322 va_start(args, format); | 315 va_start(args, format); |
323 vfprintf(stderr, format, args); | 316 vfprintf(stderr, format, args); |
324 va_end(args); | 317 va_end(args); |
325 fprintf(stderr, ExitErr::kSoftware == err ? "; this should never happen.\n":
".\n"); | 318 fprintf(stderr, ExitErr::kSoftware == err ? "; this should never happen.\n":
".\n"); |
326 exit((int)err); | 319 exit((int)err); |
327 } | 320 } |
OLD | NEW |