skpbench.cpp 21 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589
  1. /*
  2. * Copyright 2016 Google Inc.
  3. *
  4. * Use of this source code is governed by a BSD-style license that can be
  5. * found in the LICENSE file.
  6. */
  7. #include "include/core/SkCanvas.h"
  8. #include "include/core/SkGraphics.h"
  9. #include "include/core/SkPicture.h"
  10. #include "include/core/SkPictureRecorder.h"
  11. #include "include/core/SkStream.h"
  12. #include "include/core/SkSurface.h"
  13. #include "include/core/SkSurfaceProps.h"
  14. #include "include/effects/SkPerlinNoiseShader.h"
  15. #include "include/private/SkDeferredDisplayList.h"
  16. #include "src/core/SkOSFile.h"
  17. #include "src/core/SkTaskGroup.h"
  18. #include "src/gpu/GrCaps.h"
  19. #include "src/gpu/GrContextPriv.h"
  20. #include "src/gpu/SkGr.h"
  21. #include "src/utils/SkOSPath.h"
  22. #include "tools/DDLPromiseImageHelper.h"
  23. #include "tools/DDLTileHelper.h"
  24. #include "tools/ToolUtils.h"
  25. #include "tools/flags/CommandLineFlags.h"
  26. #include "tools/flags/CommonFlags.h"
  27. #include "tools/flags/CommonFlagsConfig.h"
  28. #include "tools/gpu/GpuTimer.h"
  29. #include "tools/gpu/GrContextFactory.h"
  30. #ifdef SK_XML
  31. #include "experimental/svg/model/SkSVGDOM.h"
  32. #include "src/xml/SkDOM.h"
  33. #endif
  34. #include <stdlib.h>
  35. #include <algorithm>
  36. #include <array>
  37. #include <chrono>
  38. #include <cmath>
  39. #include <vector>
  40. /**
  41. * This is a minimalist program whose sole purpose is to open a .skp or .svg file, benchmark it on a
  42. * single config, and exit. It is intended to be used through skpbench.py rather than invoked
  43. * directly. Limiting the entire process to a single config/skp pair helps to keep the results
  44. * repeatable.
  45. *
  46. * No tiling, looping, or other fanciness is used; it just draws the skp whole into a size-matched
  47. * render target and syncs the GPU after each draw.
  48. *
  49. * Currently, only GPU configs are supported.
  50. */
  51. static DEFINE_bool(ddl, false, "record the skp into DDLs before rendering");
  52. static DEFINE_int(ddlNumAdditionalThreads, 0,
  53. "number of DDL recording threads in addition to main one");
  54. static DEFINE_int(ddlTilingWidthHeight, 0, "number of tiles along one edge when in DDL mode");
  55. static DEFINE_bool(ddlRecordTime, false, "report just the cpu time spent recording DDLs");
  56. static DEFINE_int(duration, 5000, "number of milliseconds to run the benchmark");
  57. static DEFINE_int(sampleMs, 50, "minimum duration of a sample");
  58. static DEFINE_bool(gpuClock, false, "time on the gpu clock (gpu work only)");
  59. static DEFINE_bool(fps, false, "use fps instead of ms");
  60. static DEFINE_string(src, "",
  61. "path to a single .skp or .svg file, or 'warmup' for a builtin warmup run");
  62. static DEFINE_string(png, "", "if set, save a .png proof to disk at this file location");
  63. static DEFINE_int(verbosity, 4, "level of verbosity (0=none to 5=debug)");
  64. static DEFINE_bool(suppressHeader, false, "don't print a header row before the results");
  65. static const char* header =
  66. " accum median max min stddev samples sample_ms clock metric config bench";
  67. static const char* resultFormat =
  68. "%8.4g %8.4g %8.4g %8.4g %6.3g%% %7li %9i %-5s %-6s %-9s %s";
  69. static constexpr int kNumFlushesToPrimeCache = 3;
  70. struct Sample {
  71. using duration = std::chrono::nanoseconds;
  72. Sample() : fFrames(0), fDuration(0) {}
  73. double seconds() const { return std::chrono::duration<double>(fDuration).count(); }
  74. double ms() const { return std::chrono::duration<double, std::milli>(fDuration).count(); }
  75. double value() const { return FLAGS_fps ? fFrames / this->seconds() : this->ms() / fFrames; }
  76. static const char* metric() { return FLAGS_fps ? "fps" : "ms"; }
  77. int fFrames;
  78. duration fDuration;
  79. };
  80. class GpuSync {
  81. public:
  82. GpuSync(const sk_gpu_test::FenceSync* fenceSync);
  83. ~GpuSync();
  84. void syncToPreviousFrame();
  85. private:
  86. void updateFence();
  87. const sk_gpu_test::FenceSync* const fFenceSync;
  88. sk_gpu_test::PlatformFence fFence;
  89. };
  90. enum class ExitErr {
  91. kOk = 0,
  92. kUsage = 64,
  93. kData = 65,
  94. kUnavailable = 69,
  95. kIO = 74,
  96. kSoftware = 70
  97. };
  98. static void draw_skp_and_flush(SkSurface*, const SkPicture*);
  99. static sk_sp<SkPicture> create_warmup_skp();
  100. static sk_sp<SkPicture> create_skp_from_svg(SkStream*, const char* filename);
  101. static bool mkdir_p(const SkString& name);
  102. static SkString join(const CommandLineFlags::StringArray&);
  103. static void exitf(ExitErr, const char* format, ...);
  104. static void ddl_sample(GrContext* context, DDLTileHelper* tiles, GpuSync* gpuSync, Sample* sample,
  105. std::chrono::high_resolution_clock::time_point* startStopTime) {
  106. using clock = std::chrono::high_resolution_clock;
  107. clock::time_point start = *startStopTime;
  108. tiles->createDDLsInParallel();
  109. if (!FLAGS_ddlRecordTime) {
  110. tiles->drawAllTilesAndFlush(context, true);
  111. if (gpuSync) {
  112. gpuSync->syncToPreviousFrame();
  113. }
  114. }
  115. *startStopTime = clock::now();
  116. tiles->resetAllTiles();
  117. if (sample) {
  118. SkASSERT(gpuSync);
  119. sample->fDuration += *startStopTime - start;
  120. sample->fFrames++;
  121. }
  122. }
  123. static void run_ddl_benchmark(const sk_gpu_test::FenceSync* fenceSync,
  124. GrContext* context, SkCanvas* finalCanvas,
  125. SkPicture* inputPicture, std::vector<Sample>* samples) {
  126. using clock = std::chrono::high_resolution_clock;
  127. const Sample::duration sampleDuration = std::chrono::milliseconds(FLAGS_sampleMs);
  128. const clock::duration benchDuration = std::chrono::milliseconds(FLAGS_duration);
  129. SkIRect viewport = finalCanvas->imageInfo().bounds();
  130. DDLPromiseImageHelper promiseImageHelper;
  131. sk_sp<SkData> compressedPictureData = promiseImageHelper.deflateSKP(inputPicture);
  132. if (!compressedPictureData) {
  133. exitf(ExitErr::kUnavailable, "DDL: conversion of skp failed");
  134. }
  135. promiseImageHelper.uploadAllToGPU(context);
  136. DDLTileHelper tiles(finalCanvas, viewport, FLAGS_ddlTilingWidthHeight);
  137. tiles.createSKPPerTile(compressedPictureData.get(), promiseImageHelper);
  138. SkTaskGroup::Enabler enabled(FLAGS_ddlNumAdditionalThreads);
  139. clock::time_point startStopTime = clock::now();
  140. ddl_sample(context, &tiles, nullptr, nullptr, &startStopTime);
  141. GpuSync gpuSync(fenceSync);
  142. ddl_sample(context, &tiles, &gpuSync, nullptr, &startStopTime);
  143. clock::duration cumulativeDuration = std::chrono::milliseconds(0);
  144. do {
  145. samples->emplace_back();
  146. Sample& sample = samples->back();
  147. do {
  148. ddl_sample(context, &tiles, &gpuSync, &sample, &startStopTime);
  149. } while (sample.fDuration < sampleDuration);
  150. cumulativeDuration += sample.fDuration;
  151. } while (cumulativeDuration < benchDuration || 0 == samples->size() % 2);
  152. if (!FLAGS_png.isEmpty()) {
  153. // The user wants to see the final result
  154. tiles.composeAllTiles(finalCanvas);
  155. }
  156. }
  157. static void run_benchmark(const sk_gpu_test::FenceSync* fenceSync, SkSurface* surface,
  158. const SkPicture* skp, std::vector<Sample>* samples) {
  159. using clock = std::chrono::high_resolution_clock;
  160. const Sample::duration sampleDuration = std::chrono::milliseconds(FLAGS_sampleMs);
  161. const clock::duration benchDuration = std::chrono::milliseconds(FLAGS_duration);
  162. draw_skp_and_flush(surface, skp); // draw 1
  163. GpuSync gpuSync(fenceSync);
  164. for (int i = 1; i < kNumFlushesToPrimeCache; ++i) {
  165. draw_skp_and_flush(surface, skp); // draw N
  166. // Waits for draw N-1 to finish (after draw N's cpu work is done).
  167. gpuSync.syncToPreviousFrame();
  168. }
  169. clock::time_point now = clock::now();
  170. const clock::time_point endTime = now + benchDuration;
  171. do {
  172. clock::time_point sampleStart = now;
  173. samples->emplace_back();
  174. Sample& sample = samples->back();
  175. do {
  176. draw_skp_and_flush(surface, skp);
  177. gpuSync.syncToPreviousFrame();
  178. now = clock::now();
  179. sample.fDuration = now - sampleStart;
  180. ++sample.fFrames;
  181. } while (sample.fDuration < sampleDuration);
  182. } while (now < endTime || 0 == samples->size() % 2);
  183. }
  184. static void run_gpu_time_benchmark(sk_gpu_test::GpuTimer* gpuTimer,
  185. const sk_gpu_test::FenceSync* fenceSync, SkSurface* surface,
  186. const SkPicture* skp, std::vector<Sample>* samples) {
  187. using sk_gpu_test::PlatformTimerQuery;
  188. using clock = std::chrono::steady_clock;
  189. const clock::duration sampleDuration = std::chrono::milliseconds(FLAGS_sampleMs);
  190. const clock::duration benchDuration = std::chrono::milliseconds(FLAGS_duration);
  191. if (!gpuTimer->disjointSupport()) {
  192. fprintf(stderr, "WARNING: GPU timer cannot detect disjoint operations; "
  193. "results may be unreliable\n");
  194. }
  195. draw_skp_and_flush(surface, skp);
  196. GpuSync gpuSync(fenceSync);
  197. PlatformTimerQuery previousTime = 0;
  198. for (int i = 1; i < kNumFlushesToPrimeCache; ++i) {
  199. gpuTimer->queueStart();
  200. draw_skp_and_flush(surface, skp);
  201. previousTime = gpuTimer->queueStop();
  202. gpuSync.syncToPreviousFrame();
  203. }
  204. clock::time_point now = clock::now();
  205. const clock::time_point endTime = now + benchDuration;
  206. do {
  207. const clock::time_point sampleEndTime = now + sampleDuration;
  208. samples->emplace_back();
  209. Sample& sample = samples->back();
  210. do {
  211. gpuTimer->queueStart();
  212. draw_skp_and_flush(surface, skp);
  213. PlatformTimerQuery time = gpuTimer->queueStop();
  214. gpuSync.syncToPreviousFrame();
  215. switch (gpuTimer->checkQueryStatus(previousTime)) {
  216. using QueryStatus = sk_gpu_test::GpuTimer::QueryStatus;
  217. case QueryStatus::kInvalid:
  218. exitf(ExitErr::kUnavailable, "GPU timer failed");
  219. case QueryStatus::kPending:
  220. exitf(ExitErr::kUnavailable, "timer query still not ready after fence sync");
  221. case QueryStatus::kDisjoint:
  222. if (FLAGS_verbosity >= 4) {
  223. fprintf(stderr, "discarding timer query due to disjoint operations.\n");
  224. }
  225. break;
  226. case QueryStatus::kAccurate:
  227. sample.fDuration += gpuTimer->getTimeElapsed(previousTime);
  228. ++sample.fFrames;
  229. break;
  230. }
  231. gpuTimer->deleteQuery(previousTime);
  232. previousTime = time;
  233. now = clock::now();
  234. } while (now < sampleEndTime || 0 == sample.fFrames);
  235. } while (now < endTime || 0 == samples->size() % 2);
  236. gpuTimer->deleteQuery(previousTime);
  237. }
  238. void print_result(const std::vector<Sample>& samples, const char* config, const char* bench) {
  239. if (0 == (samples.size() % 2)) {
  240. exitf(ExitErr::kSoftware, "attempted to gather stats on even number of samples");
  241. }
  242. Sample accum = Sample();
  243. std::vector<double> values;
  244. values.reserve(samples.size());
  245. for (const Sample& sample : samples) {
  246. accum.fFrames += sample.fFrames;
  247. accum.fDuration += sample.fDuration;
  248. values.push_back(sample.value());
  249. }
  250. std::sort(values.begin(), values.end());
  251. const double accumValue = accum.value();
  252. double variance = 0;
  253. for (double value : values) {
  254. const double delta = value - accumValue;
  255. variance += delta * delta;
  256. }
  257. variance /= values.size();
  258. // Technically, this is the relative standard deviation.
  259. const double stddev = 100/*%*/ * sqrt(variance) / accumValue;
  260. printf(resultFormat, accumValue, values[values.size() / 2], values.back(), values.front(),
  261. stddev, values.size(), FLAGS_sampleMs, FLAGS_gpuClock ? "gpu" : "cpu", Sample::metric(),
  262. config, bench);
  263. printf("\n");
  264. fflush(stdout);
  265. }
  266. int main(int argc, char** argv) {
  267. CommandLineFlags::SetUsage(
  268. "Use skpbench.py instead. "
  269. "You usually don't want to use this program directly.");
  270. CommandLineFlags::Parse(argc, argv);
  271. if (!FLAGS_suppressHeader) {
  272. printf("%s\n", header);
  273. }
  274. if (FLAGS_duration <= 0) {
  275. exit(0); // This can be used to print the header and quit.
  276. }
  277. // Parse the config.
  278. const SkCommandLineConfigGpu* config = nullptr; // Initialize for spurious warning.
  279. SkCommandLineConfigArray configs;
  280. ParseConfigs(FLAGS_config, &configs);
  281. if (configs.count() != 1 || !(config = configs[0]->asConfigGpu())) {
  282. exitf(ExitErr::kUsage, "invalid config '%s': must specify one (and only one) GPU config",
  283. join(FLAGS_config).c_str());
  284. }
  285. // Parse the skp.
  286. if (FLAGS_src.count() != 1) {
  287. exitf(ExitErr::kUsage,
  288. "invalid input '%s': must specify a single .skp or .svg file, or 'warmup'",
  289. join(FLAGS_src).c_str());
  290. }
  291. SkGraphics::Init();
  292. sk_sp<SkPicture> skp;
  293. SkString srcname;
  294. if (0 == strcmp(FLAGS_src[0], "warmup")) {
  295. skp = create_warmup_skp();
  296. srcname = "warmup";
  297. } else {
  298. SkString srcfile(FLAGS_src[0]);
  299. std::unique_ptr<SkStream> srcstream(SkStream::MakeFromFile(srcfile.c_str()));
  300. if (!srcstream) {
  301. exitf(ExitErr::kIO, "failed to open file %s", srcfile.c_str());
  302. }
  303. if (srcfile.endsWith(".svg")) {
  304. skp = create_skp_from_svg(srcstream.get(), srcfile.c_str());
  305. } else {
  306. skp = SkPicture::MakeFromStream(srcstream.get());
  307. }
  308. if (!skp) {
  309. exitf(ExitErr::kData, "failed to parse file %s", srcfile.c_str());
  310. }
  311. srcname = SkOSPath::Basename(srcfile.c_str());
  312. }
  313. int width = SkTMin(SkScalarCeilToInt(skp->cullRect().width()), 2048),
  314. height = SkTMin(SkScalarCeilToInt(skp->cullRect().height()), 2048);
  315. if (FLAGS_verbosity >= 3 &&
  316. (width != skp->cullRect().width() || height != skp->cullRect().height())) {
  317. fprintf(stderr, "%s is too large (%ix%i), cropping to %ix%i.\n",
  318. srcname.c_str(), SkScalarCeilToInt(skp->cullRect().width()),
  319. SkScalarCeilToInt(skp->cullRect().height()), width, height);
  320. }
  321. if (config->getSurfType() != SkCommandLineConfigGpu::SurfType::kDefault) {
  322. exitf(ExitErr::kUnavailable, "This tool only supports the default surface type. (%s)",
  323. config->getTag().c_str());
  324. }
  325. // Create a context.
  326. GrContextOptions ctxOptions;
  327. SetCtxOptionsFromCommonFlags(&ctxOptions);
  328. sk_gpu_test::GrContextFactory factory(ctxOptions);
  329. sk_gpu_test::ContextInfo ctxInfo =
  330. factory.getContextInfo(config->getContextType(), config->getContextOverrides());
  331. GrContext* ctx = ctxInfo.grContext();
  332. if (!ctx) {
  333. exitf(ExitErr::kUnavailable, "failed to create context for config %s",
  334. config->getTag().c_str());
  335. }
  336. if (ctx->maxRenderTargetSize() < SkTMax(width, height)) {
  337. exitf(ExitErr::kUnavailable, "render target size %ix%i not supported by platform (max: %i)",
  338. width, height, ctx->maxRenderTargetSize());
  339. }
  340. GrPixelConfig grPixConfig = SkColorType2GrPixelConfig(config->getColorType());
  341. if (kUnknown_GrPixelConfig == grPixConfig) {
  342. exitf(ExitErr::kUnavailable, "failed to get GrPixelConfig from SkColorType: %d",
  343. config->getColorType());
  344. }
  345. int supportedSampleCount = ctx->priv().caps()->getRenderTargetSampleCount(
  346. config->getSamples(), grPixConfig);
  347. if (supportedSampleCount != config->getSamples()) {
  348. exitf(ExitErr::kUnavailable, "sample count %i not supported by platform",
  349. config->getSamples());
  350. }
  351. sk_gpu_test::TestContext* testCtx = ctxInfo.testContext();
  352. if (!testCtx) {
  353. exitf(ExitErr::kSoftware, "testContext is null");
  354. }
  355. if (!testCtx->fenceSyncSupport()) {
  356. exitf(ExitErr::kUnavailable, "GPU does not support fence sync");
  357. }
  358. // Create a render target.
  359. SkImageInfo info =
  360. SkImageInfo::Make(width, height, config->getColorType(), config->getAlphaType(),
  361. sk_ref_sp(config->getColorSpace()));
  362. uint32_t flags = config->getUseDIText() ? SkSurfaceProps::kUseDeviceIndependentFonts_Flag : 0;
  363. SkSurfaceProps props(flags, SkSurfaceProps::kLegacyFontHost_InitType);
  364. sk_sp<SkSurface> surface =
  365. SkSurface::MakeRenderTarget(ctx, SkBudgeted::kNo, info, config->getSamples(), &props);
  366. if (!surface) {
  367. exitf(ExitErr::kUnavailable, "failed to create %ix%i render target for config %s",
  368. width, height, config->getTag().c_str());
  369. }
  370. // Run the benchmark.
  371. std::vector<Sample> samples;
  372. if (FLAGS_sampleMs > 0) {
  373. // +1 because we might take one more sample in order to have an odd number.
  374. samples.reserve(1 + (FLAGS_duration + FLAGS_sampleMs - 1) / FLAGS_sampleMs);
  375. } else {
  376. samples.reserve(2 * FLAGS_duration);
  377. }
  378. SkCanvas* canvas = surface->getCanvas();
  379. canvas->translate(-skp->cullRect().x(), -skp->cullRect().y());
  380. if (!FLAGS_gpuClock) {
  381. if (FLAGS_ddl) {
  382. run_ddl_benchmark(testCtx->fenceSync(), ctx, canvas, skp.get(), &samples);
  383. } else {
  384. run_benchmark(testCtx->fenceSync(), surface.get(), skp.get(), &samples);
  385. }
  386. } else {
  387. if (FLAGS_ddl) {
  388. exitf(ExitErr::kUnavailable, "DDL: GPU-only timing not supported");
  389. }
  390. if (!testCtx->gpuTimingSupport()) {
  391. exitf(ExitErr::kUnavailable, "GPU does not support timing");
  392. }
  393. run_gpu_time_benchmark(testCtx->gpuTimer(), testCtx->fenceSync(), surface.get(), skp.get(),
  394. &samples);
  395. }
  396. print_result(samples, config->getTag().c_str(), srcname.c_str());
  397. // Save a proof (if one was requested).
  398. if (!FLAGS_png.isEmpty()) {
  399. SkBitmap bmp;
  400. bmp.allocPixels(info);
  401. if (!surface->getCanvas()->readPixels(bmp, 0, 0)) {
  402. exitf(ExitErr::kUnavailable, "failed to read canvas pixels for png");
  403. }
  404. if (!mkdir_p(SkOSPath::Dirname(FLAGS_png[0]))) {
  405. exitf(ExitErr::kIO, "failed to create directory for png \"%s\"", FLAGS_png[0]);
  406. }
  407. if (!ToolUtils::EncodeImageToFile(FLAGS_png[0], bmp, SkEncodedImageFormat::kPNG, 100)) {
  408. exitf(ExitErr::kIO, "failed to save png to \"%s\"", FLAGS_png[0]);
  409. }
  410. }
  411. exit(0);
  412. }
  413. static void draw_skp_and_flush(SkSurface* surface, const SkPicture* skp) {
  414. auto canvas = surface->getCanvas();
  415. canvas->drawPicture(skp);
  416. surface->flush();
  417. }
  418. static sk_sp<SkPicture> create_warmup_skp() {
  419. static constexpr SkRect bounds{0, 0, 500, 500};
  420. SkPictureRecorder recorder;
  421. SkCanvas* recording = recorder.beginRecording(bounds);
  422. recording->clear(SK_ColorWHITE);
  423. SkPaint stroke;
  424. stroke.setStyle(SkPaint::kStroke_Style);
  425. stroke.setStrokeWidth(2);
  426. // Use a big path to (theoretically) warmup the CPU.
  427. SkPath bigPath;
  428. ToolUtils::make_big_path(bigPath);
  429. recording->drawPath(bigPath, stroke);
  430. // Use a perlin shader to warmup the GPU.
  431. SkPaint perlin;
  432. perlin.setShader(SkPerlinNoiseShader::MakeTurbulence(0.1f, 0.1f, 1, 0, nullptr));
  433. recording->drawRect(bounds, perlin);
  434. return recorder.finishRecordingAsPicture();
  435. }
  436. static sk_sp<SkPicture> create_skp_from_svg(SkStream* stream, const char* filename) {
  437. #ifdef SK_XML
  438. SkDOM xml;
  439. if (!xml.build(*stream)) {
  440. exitf(ExitErr::kData, "failed to parse xml in file %s", filename);
  441. }
  442. sk_sp<SkSVGDOM> svg = SkSVGDOM::MakeFromDOM(xml);
  443. if (!svg) {
  444. exitf(ExitErr::kData, "failed to build svg dom from file %s", filename);
  445. }
  446. static constexpr SkRect bounds{0, 0, 1200, 1200};
  447. SkPictureRecorder recorder;
  448. SkCanvas* recording = recorder.beginRecording(bounds);
  449. svg->setContainerSize(SkSize::Make(recording->getBaseLayerSize()));
  450. svg->render(recording);
  451. return recorder.finishRecordingAsPicture();
  452. #endif
  453. exitf(ExitErr::kData, "SK_XML is disabled; cannot open svg file %s", filename);
  454. return nullptr;
  455. }
  456. bool mkdir_p(const SkString& dirname) {
  457. if (dirname.isEmpty() || dirname == SkString("/")) {
  458. return true;
  459. }
  460. return mkdir_p(SkOSPath::Dirname(dirname.c_str())) && sk_mkdir(dirname.c_str());
  461. }
  462. static SkString join(const CommandLineFlags::StringArray& stringArray) {
  463. SkString joined;
  464. for (int i = 0; i < stringArray.count(); ++i) {
  465. joined.appendf(i ? " %s" : "%s", stringArray[i]);
  466. }
  467. return joined;
  468. }
  469. static void exitf(ExitErr err, const char* format, ...) {
  470. fprintf(stderr, ExitErr::kSoftware == err ? "INTERNAL ERROR: " : "ERROR: ");
  471. va_list args;
  472. va_start(args, format);
  473. vfprintf(stderr, format, args);
  474. va_end(args);
  475. fprintf(stderr, ExitErr::kSoftware == err ? "; this should never happen.\n": ".\n");
  476. exit((int)err);
  477. }
  478. GpuSync::GpuSync(const sk_gpu_test::FenceSync* fenceSync)
  479. : fFenceSync(fenceSync) {
  480. this->updateFence();
  481. }
  482. GpuSync::~GpuSync() {
  483. fFenceSync->deleteFence(fFence);
  484. }
  485. void GpuSync::syncToPreviousFrame() {
  486. if (sk_gpu_test::kInvalidFence == fFence) {
  487. exitf(ExitErr::kSoftware, "attempted to sync with invalid fence");
  488. }
  489. if (!fFenceSync->waitFence(fFence)) {
  490. exitf(ExitErr::kUnavailable, "failed to wait for fence");
  491. }
  492. fFenceSync->deleteFence(fFence);
  493. this->updateFence();
  494. }
  495. void GpuSync::updateFence() {
  496. fFence = fFenceSync->insertFence();
  497. if (sk_gpu_test::kInvalidFence == fFence) {
  498. exitf(ExitErr::kUnavailable, "failed to insert fence");
  499. }
  500. }