Chromium Code Reviews| 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 "GrAllocator.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 * 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. | 34 * Currently, only GPU configs are supported. |
| 37 */ | 35 */ |
| 38 | 36 |
| 39 DEFINE_int32(samples, 101, "number of samples to collect"); | 37 DEFINE_int32(duration, 5000, "number of milliseconds to run the benchmark"); |
| 40 DEFINE_int32(sampleMs, 50, "duration of each sample"); | 38 DEFINE_int32(sampleMs, 50, "minimum duration of a sample"); |
| 41 DEFINE_bool(fps, false, "use fps instead of ms"); | 39 DEFINE_bool(fps, false, "use fps instead of ms"); |
| 42 DEFINE_string(skp, "", "path to a single .skp file to benchmark"); | 40 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" ); | 41 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)"); | 42 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" ); | 43 DEFINE_bool(suppressHeader, false, "don't print a header row before the results" ); |
| 46 | 44 |
| 47 static const char* header = | 45 static const char* header = |
| 48 " median accum max min stddev metric samples sample_ms config bench"; | 46 " median accum max min stddev metric samples sample_ms config bench"; |
| 49 | 47 |
| 50 static const char* resultFormat = | 48 static const char* resultFormat = |
| (...skipping 22 matching lines...) Expand all Loading... | |
| 73 }; | 71 }; |
| 74 | 72 |
| 75 static void draw_skp_and_flush(SkCanvas*, const SkPicture*); | 73 static void draw_skp_and_flush(SkCanvas*, const SkPicture*); |
| 76 static SkPlatformGpuFence insert_verified_fence(const SkGpuFenceSync*); | 74 static SkPlatformGpuFence insert_verified_fence(const SkGpuFenceSync*); |
| 77 static void wait_fence_and_delete(const SkGpuFenceSync*, SkPlatformGpuFence); | 75 static void wait_fence_and_delete(const SkGpuFenceSync*, SkPlatformGpuFence); |
| 78 static bool mkdir_p(const SkString& name); | 76 static bool mkdir_p(const SkString& name); |
| 79 static SkString join(const SkCommandLineFlags::StringArray&); | 77 static SkString join(const SkCommandLineFlags::StringArray&); |
| 80 static void exitf(ExitErr, const char* format, ...); | 78 static void exitf(ExitErr, const char* format, ...); |
| 81 | 79 |
| 82 static void run_benchmark(const SkGpuFenceSync* sync, SkCanvas* canvas, const Sk Picture* skp, | 80 static void run_benchmark(const SkGpuFenceSync* sync, SkCanvas* canvas, const Sk Picture* skp, |
| 83 std::vector<Sample>* samples) { | 81 GrTAllocator<Sample>* samples) { |
|
bsalomon
2016/09/28 18:51:16
Why the change to GrTAllocator?
csmartdalton
2016/09/28 19:17:54
Because with "--sampleMs 0" we don't know an upper
| |
| 84 using clock = Sample::clock; | 82 using clock = Sample::clock; |
| 85 std::chrono::milliseconds sampleMs(FLAGS_sampleMs); | 83 const clock::duration sampleDuration = std::chrono::milliseconds(FLAGS_sampl eMs); |
| 86 | 84 const clock::duration benchDuration = std::chrono::milliseconds(FLAGS_durati on); |
| 87 samples->clear(); | |
| 88 samples->resize(FLAGS_samples); | |
| 89 | 85 |
| 90 // Prime the graphics pipe. | 86 // Prime the graphics pipe. |
| 91 SkPlatformGpuFence frameN_minus_2, frameN_minus_1; | 87 SkPlatformGpuFence frameN_minus_2, frameN_minus_1; |
| 92 { | 88 { |
| 93 draw_skp_and_flush(canvas, skp); | 89 draw_skp_and_flush(canvas, skp); |
| 94 SkPlatformGpuFence frame0 = insert_verified_fence(sync); | 90 SkPlatformGpuFence frame0 = insert_verified_fence(sync); |
| 95 | 91 |
| 96 draw_skp_and_flush(canvas, skp); | 92 draw_skp_and_flush(canvas, skp); |
| 97 frameN_minus_2 = insert_verified_fence(sync); | 93 frameN_minus_2 = insert_verified_fence(sync); |
| 98 | 94 |
| 99 draw_skp_and_flush(canvas, skp); | 95 draw_skp_and_flush(canvas, skp); |
| 100 frameN_minus_1 = insert_verified_fence(sync); | 96 frameN_minus_1 = insert_verified_fence(sync); |
| 101 | 97 |
| 102 wait_fence_and_delete(sync, frame0); | 98 wait_fence_and_delete(sync, frame0); |
| 103 } | 99 } |
| 104 | 100 |
| 105 clock::time_point start = clock::now(); | 101 clock::time_point now = clock::now(); |
| 102 const clock::time_point endTime = now + benchDuration; | |
| 106 | 103 |
| 107 for (Sample& sample : *samples) { | 104 do { |
| 108 clock::time_point end; | 105 clock::time_point sampleStart = now; |
| 106 Sample& sample = samples->push_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->count() % 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 GrTAllocator<Sample>& samples, const char* config, const char* bench) { |
| 135 if (0 == (samples.size() % 2)) { | 127 if (0 == (samples.count() % 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.count()); |
| 142 for (const Sample& sample : samples) { | 134 for (GrTAllocator<Sample>::Iter it(&samples); it.next();) { |
| 143 accum.fFrames += sample.fFrames; | 135 accum.fFrames += it->fFrames; |
| 144 accum.fDuration += sample.fDuration; | 136 accum.fDuration += it->fDuration; |
| 145 values.push_back(sample.value()); | 137 values.push_back(it->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 int reserve; | |
| 241 if (FLAGS_sampleMs > 0) { | |
| 242 // +1 because we might take one more sample in order to have an odd numb er. | |
| 243 reserve = 1 + (FLAGS_duration + FLAGS_sampleMs - 1) / FLAGS_sampleMs; | |
| 244 } else { | |
| 245 reserve = 2 * FLAGS_duration; | |
| 246 } | |
| 247 GrTAllocator<Sample> samples(reserve); | |
| 248 | |
| 254 // Run the benchmark. | 249 // Run the benchmark. |
| 255 std::vector<Sample> samples; | |
| 256 SkCanvas* canvas = surface->getCanvas(); | 250 SkCanvas* canvas = surface->getCanvas(); |
| 257 canvas->translate(-skp->cullRect().x(), -skp->cullRect().y()); | 251 canvas->translate(-skp->cullRect().x(), -skp->cullRect().y()); |
| 258 run_benchmark(testCtx->fenceSync(), canvas, skp.get(), &samples); | 252 run_benchmark(testCtx->fenceSync(), canvas, skp.get(), &samples); |
| 259 print_result(samples, config->getTag().c_str(), SkOSPath::Basename(skpfile). c_str()); | 253 print_result(samples, config->getTag().c_str(), SkOSPath::Basename(skpfile). c_str()); |
| 260 | 254 |
| 261 // Save a proof (if one was requested). | 255 // Save a proof (if one was requested). |
| 262 if (!FLAGS_png.isEmpty()) { | 256 if (!FLAGS_png.isEmpty()) { |
| 263 SkBitmap bmp; | 257 SkBitmap bmp; |
| 264 bmp.setInfo(info); | 258 bmp.setInfo(info); |
| 265 if (!surface->getCanvas()->readPixels(&bmp, 0, 0)) { | 259 if (!surface->getCanvas()->readPixels(&bmp, 0, 0)) { |
| (...skipping 52 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 318 | 312 |
| 319 static void exitf(ExitErr err, const char* format, ...) { | 313 static void exitf(ExitErr err, const char* format, ...) { |
| 320 fprintf(stderr, ExitErr::kSoftware == err ? "INTERNAL ERROR: " : "ERROR: "); | 314 fprintf(stderr, ExitErr::kSoftware == err ? "INTERNAL ERROR: " : "ERROR: "); |
| 321 va_list args; | 315 va_list args; |
| 322 va_start(args, format); | 316 va_start(args, format); |
| 323 vfprintf(stderr, format, args); | 317 vfprintf(stderr, format, args); |
| 324 va_end(args); | 318 va_end(args); |
| 325 fprintf(stderr, ExitErr::kSoftware == err ? "; this should never happen.\n": ".\n"); | 319 fprintf(stderr, ExitErr::kSoftware == err ? "; this should never happen.\n": ".\n"); |
| 326 exit((int)err); | 320 exit((int)err); |
| 327 } | 321 } |
| OLD | NEW |