blob: 71f236a9eab000db34c4205807b1ab8f826238c2 [file] [log] [blame]
csmartdalton4b5179b2016-09-19 11:03:58 -07001/*
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
Kevin Lubickf541ddf2021-10-13 16:02:56 -04008#include "bench/BigPath.h"
Mike Kleinc0bd9f92019-04-23 12:05:21 -05009#include "include/core/SkCanvas.h"
10#include "include/core/SkGraphics.h"
11#include "include/core/SkPicture.h"
12#include "include/core/SkPictureRecorder.h"
13#include "include/core/SkStream.h"
14#include "include/core/SkSurface.h"
15#include "include/core/SkSurfaceProps.h"
Kevin Lubickee6dbb42023-11-03 09:19:25 -040016#include "include/docs/SkMultiPictureDocument.h"
Mike Kleinc0bd9f92019-04-23 12:05:21 -050017#include "include/effects/SkPerlinNoiseShader.h"
Robert Phillips00f78de2020-07-01 16:09:43 -040018#include "include/gpu/GrDirectContext.h"
Kevin Lubick5c93acf2023-05-09 12:11:43 -040019#include "include/gpu/ganesh/SkSurfaceGanesh.h"
Kevin Lubick0bff57e2023-06-09 14:29:17 -040020#include "include/private/chromium/GrDeferredDisplayList.h"
Mike Kleinc0bd9f92019-04-23 12:05:21 -050021#include "src/core/SkOSFile.h"
22#include "src/core/SkTaskGroup.h"
Greg Daniel719239c2022-04-07 11:20:24 -040023#include "src/gpu/ganesh/GrCaps.h"
24#include "src/gpu/ganesh/GrDirectContextPriv.h"
25#include "src/gpu/ganesh/SkGr.h"
Kevin Lubicka2019432023-06-12 15:42:51 -040026#include "src/gpu/ganesh/image/GrImageUtils.h"
Mike Kleinc0bd9f92019-04-23 12:05:21 -050027#include "src/utils/SkOSPath.h"
28#include "tools/DDLPromiseImageHelper.h"
29#include "tools/DDLTileHelper.h"
Kevin Lubick9b028372023-10-05 15:04:54 -040030#include "tools/EncodeUtils.h"
Nathaniel Nifong1b458e82019-07-26 11:32:46 -040031#include "tools/SkSharingProc.h"
Mike Kleinc0bd9f92019-04-23 12:05:21 -050032#include "tools/flags/CommandLineFlags.h"
33#include "tools/flags/CommonFlags.h"
34#include "tools/flags/CommonFlagsConfig.h"
Kevin Lubickee6dbb42023-11-03 09:19:25 -040035#include "tools/fonts/FontToolUtils.h"
Greg Daniel02497d42020-02-21 15:46:27 -050036#include "tools/gpu/FlushFinishTracker.h"
Mike Kleinc0bd9f92019-04-23 12:05:21 -050037#include "tools/gpu/GpuTimer.h"
38#include "tools/gpu/GrContextFactory.h"
csmartdalton4b5179b2016-09-19 11:03:58 -070039
Robert Phillips2af13c12021-09-01 16:47:01 +000040#if defined(SK_ENABLE_SVG)
Florin Malitab3418102020-10-15 18:10:29 -040041#include "modules/svg/include/SkSVGDOM.h"
Mike Kleinc0bd9f92019-04-23 12:05:21 -050042#include "src/xml/SkDOM.h"
Chris Daltona4f5ce02018-06-26 10:13:06 -060043#endif
44
Hal Canary8a001442018-09-19 11:31:27 -040045#include <stdlib.h>
46#include <algorithm>
47#include <array>
48#include <chrono>
Herb Derby1db274d2021-02-02 12:34:33 -050049#include <cinttypes>
Hal Canary8a001442018-09-19 11:31:27 -040050#include <cmath>
51#include <vector>
Chris Daltona4f5ce02018-06-26 10:13:06 -060052
csmartdalton4b5179b2016-09-19 11:03:58 -070053/**
Chris Daltona4f5ce02018-06-26 10:13:06 -060054 * This is a minimalist program whose sole purpose is to open a .skp or .svg file, benchmark it on a
55 * single config, and exit. It is intended to be used through skpbench.py rather than invoked
56 * directly. Limiting the entire process to a single config/skp pair helps to keep the results
57 * repeatable.
csmartdalton4b5179b2016-09-19 11:03:58 -070058 *
59 * No tiling, looping, or other fanciness is used; it just draws the skp whole into a size-matched
60 * render target and syncs the GPU after each draw.
61 *
Nathaniel Nifongfa408512019-07-26 09:44:46 -040062 * Well, maybe a little fanciness, MSKP's can be loaded and played. The animation is played as many
63 * times as necessary to reach the target sample duration and FPS is reported.
64 *
csmartdalton4b5179b2016-09-19 11:03:58 -070065 * Currently, only GPU configs are supported.
66 */
67
Mike Klein84836b72019-03-21 11:31:36 -050068static DEFINE_bool(ddl, false, "record the skp into DDLs before rendering");
Adlai Hollere5c379d2020-05-18 10:21:53 -040069static DEFINE_int(ddlNumRecordingThreads, 0, "number of DDL recording threads (0=num_cores)");
Mike Klein5b3f3432019-03-21 11:42:21 -050070static DEFINE_int(ddlTilingWidthHeight, 0, "number of tiles along one edge when in DDL mode");
Robert Phillips24a8e9e2020-03-06 20:26:28 +000071
72static DEFINE_bool(comparableDDL, false, "render in a way that is comparable to 'comparableSKP'");
73static DEFINE_bool(comparableSKP, false, "report in a way that is comparable to 'comparableDDL'");
Robert Phillips96601082018-05-29 16:13:26 -040074
Mike Klein5b3f3432019-03-21 11:42:21 -050075static DEFINE_int(duration, 5000, "number of milliseconds to run the benchmark");
76static DEFINE_int(sampleMs, 50, "minimum duration of a sample");
Mike Klein84836b72019-03-21 11:31:36 -050077static DEFINE_bool(gpuClock, false, "time on the gpu clock (gpu work only)");
78static DEFINE_bool(fps, false, "use fps instead of ms");
79static DEFINE_string(src, "",
80 "path to a single .skp or .svg file, or 'warmup' for a builtin warmup run");
81static DEFINE_string(png, "", "if set, save a .png proof to disk at this file location");
Mike Klein5b3f3432019-03-21 11:42:21 -050082static DEFINE_int(verbosity, 4, "level of verbosity (0=none to 5=debug)");
Mike Klein84836b72019-03-21 11:31:36 -050083static DEFINE_bool(suppressHeader, false, "don't print a header row before the results");
Chris Dalton84c87872020-01-16 14:45:57 -070084static DEFINE_double(scale, 1, "Scale the size of the canvas and the zoom level by this factor.");
Herb Derby1db274d2021-02-02 12:34:33 -050085static DEFINE_bool(dumpSamples, false, "print the individual samples to stdout");
csmartdalton4b5179b2016-09-19 11:03:58 -070086
John Stiles00ee8f52020-05-05 15:57:46 -040087static const char header[] =
csmartdaltonc6618dd2016-10-05 08:42:03 -070088" accum median max min stddev samples sample_ms clock metric config bench";
csmartdalton4b5179b2016-09-19 11:03:58 -070089
John Stiles00ee8f52020-05-05 15:57:46 -040090static const char resultFormat[] =
91"%8.4g %8.4g %8.4g %8.4g %6.3g%% %7zu %9i %-5s %-6s %-9s %s";
csmartdalton4b5179b2016-09-19 11:03:58 -070092
Chris Daltona2b5b642018-06-24 13:08:57 -060093static constexpr int kNumFlushesToPrimeCache = 3;
94
csmartdalton4b5179b2016-09-19 11:03:58 -070095struct Sample {
csmartdaltonc6618dd2016-10-05 08:42:03 -070096 using duration = std::chrono::nanoseconds;
csmartdalton4b5179b2016-09-19 11:03:58 -070097
98 Sample() : fFrames(0), fDuration(0) {}
99 double seconds() const { return std::chrono::duration<double>(fDuration).count(); }
100 double ms() const { return std::chrono::duration<double, std::milli>(fDuration).count(); }
101 double value() const { return FLAGS_fps ? fFrames / this->seconds() : this->ms() / fFrames; }
102 static const char* metric() { return FLAGS_fps ? "fps" : "ms"; }
103
csmartdaltonc6618dd2016-10-05 08:42:03 -0700104 int fFrames;
105 duration fDuration;
csmartdalton4b5179b2016-09-19 11:03:58 -0700106};
107
csmartdaltone0384892016-09-28 14:53:07 -0700108class GpuSync {
109public:
Greg Daniel02497d42020-02-21 15:46:27 -0500110 GpuSync() {}
111 ~GpuSync() {}
csmartdaltone0384892016-09-28 14:53:07 -0700112
Greg Daniel02497d42020-02-21 15:46:27 -0500113 void waitIfNeeded();
114
Robert Phillips00f78de2020-07-01 16:09:43 -0400115 sk_gpu_test::FlushFinishTracker* newFlushTracker(GrDirectContext* context);
csmartdaltone0384892016-09-28 14:53:07 -0700116
117private:
Greg Daniel02497d42020-02-21 15:46:27 -0500118 enum { kMaxFrameLag = 3 };
119 sk_sp<sk_gpu_test::FlushFinishTracker> fFinishTrackers[kMaxFrameLag - 1];
120 int fCurrentFlushIdx = 0;
csmartdaltone0384892016-09-28 14:53:07 -0700121};
122
csmartdalton4b5179b2016-09-19 11:03:58 -0700123enum class ExitErr {
124 kOk = 0,
125 kUsage = 64,
126 kData = 65,
127 kUnavailable = 69,
128 kIO = 74,
129 kSoftware = 70
130};
131
Robert Phillips00f78de2020-07-01 16:09:43 -0400132static void flush_with_sync(GrDirectContext*, GpuSync&);
133static void draw_skp_and_flush_with_sync(GrDirectContext*, SkSurface*, const SkPicture*, GpuSync&);
csmartdalton5772eaa2016-10-11 18:28:54 -0700134static sk_sp<SkPicture> create_warmup_skp();
Chris Daltona4f5ce02018-06-26 10:13:06 -0600135static sk_sp<SkPicture> create_skp_from_svg(SkStream*, const char* filename);
csmartdalton4b5179b2016-09-19 11:03:58 -0700136static bool mkdir_p(const SkString& name);
Mike Klein88544fb2019-03-20 10:50:33 -0500137static SkString join(const CommandLineFlags::StringArray&);
csmartdalton4b5179b2016-09-19 11:03:58 -0700138static void exitf(ExitErr, const char* format, ...);
139
Nathaniel Nifongfa408512019-07-26 09:44:46 -0400140// An interface used by both static SKPs and animated SKPs
141class SkpProducer {
Greg Daniel02497d42020-02-21 15:46:27 -0500142public:
143 virtual ~SkpProducer() {}
144 // Draw an SkPicture to the provided surface, flush the surface, and sync the GPU.
145 // You may use the static draw_skp_and_flush_with_sync declared above.
146 // returned int tells how many draw/flush/sync were done.
Robert Phillips00f78de2020-07-01 16:09:43 -0400147 virtual int drawAndFlushAndSync(GrDirectContext*, SkSurface* surface, GpuSync& gpuSync) = 0;
Nathaniel Nifongfa408512019-07-26 09:44:46 -0400148};
149
150class StaticSkp : public SkpProducer {
Greg Daniel02497d42020-02-21 15:46:27 -0500151public:
152 StaticSkp(sk_sp<SkPicture> skp) : fSkp(skp) {}
Nathaniel Nifongfa408512019-07-26 09:44:46 -0400153
Robert Phillips00f78de2020-07-01 16:09:43 -0400154 int drawAndFlushAndSync(GrDirectContext* context,
155 SkSurface* surface,
156 GpuSync& gpuSync) override {
Greg Daniel02497d42020-02-21 15:46:27 -0500157 draw_skp_and_flush_with_sync(context, surface, fSkp.get(), gpuSync);
Nathaniel Nifongfa408512019-07-26 09:44:46 -0400158 return 1;
159 }
Greg Daniel02497d42020-02-21 15:46:27 -0500160
161private:
162 sk_sp<SkPicture> fSkp;
Nathaniel Nifongfa408512019-07-26 09:44:46 -0400163};
164
165// A class for playing/benchmarking a multi frame SKP file.
166// the recorded frames are looped over repeatedly.
167// This type of benchmark may have a much higher std dev in frame times.
168class MultiFrameSkp : public SkpProducer {
169public:
170 MultiFrameSkp(const std::vector<SkDocumentPage>& frames) : fFrames(frames){}
171
172 static std::unique_ptr<MultiFrameSkp> MakeFromFile(const SkString& path) {
173 // Load the multi frame skp at the given filename.
174 std::unique_ptr<SkStreamAsset> stream = SkStream::MakeFromFile(path.c_str());
175 if (!stream) { return nullptr; }
176
177 // Attempt to deserialize with an image sharing serial proc.
178 auto deserialContext = std::make_unique<SkSharingDeserialContext>();
179 SkDeserialProcs procs;
180 procs.fImageProc = SkSharingDeserialContext::deserializeImage;
181 procs.fImageCtx = deserialContext.get();
182
183 // The outer format of multi-frame skps is the multi-picture document, which is a
184 // skp file containing subpictures separated by annotations.
Kevin Lubick68de6e32023-10-13 11:19:03 -0400185 int page_count = SkMultiPictureDocument::ReadPageCount(stream.get());
Nathaniel Nifongfa408512019-07-26 09:44:46 -0400186 if (!page_count) {
187 return nullptr;
188 }
189 std::vector<SkDocumentPage> frames(page_count); // can't call reserve, why?
Kevin Lubick68de6e32023-10-13 11:19:03 -0400190 if (!SkMultiPictureDocument::Read(stream.get(), frames.data(), page_count, &procs)) {
Nathaniel Nifongfa408512019-07-26 09:44:46 -0400191 return nullptr;
192 }
193
194 return std::make_unique<MultiFrameSkp>(frames);
195 }
196
197 // Draw the whole animation once.
Robert Phillips00f78de2020-07-01 16:09:43 -0400198 int drawAndFlushAndSync(GrDirectContext* context,
199 SkSurface* surface,
200 GpuSync& gpuSync) override {
Nathaniel Nifongfa408512019-07-26 09:44:46 -0400201 for (int i=0; i<this->count(); i++){
Greg Daniel02497d42020-02-21 15:46:27 -0500202 draw_skp_and_flush_with_sync(context, surface, this->frame(i).get(), gpuSync);
Nathaniel Nifongfa408512019-07-26 09:44:46 -0400203 }
204 return this->count();
205 }
206 // Return the requested frame.
207 sk_sp<SkPicture> frame(int n) const { return fFrames[n].fPicture; }
208 // Return the number of frames in the recording.
209 int count() const { return fFrames.size(); }
210private:
211 std::vector<SkDocumentPage> fFrames;
212};
213
Robert Phillips0d8722c2021-03-29 13:29:40 -0400214static void ddl_sample(GrDirectContext* dContext, DDLTileHelper* tiles, GpuSync& gpuSync,
Adlai Hollere5c379d2020-05-18 10:21:53 -0400215 Sample* sample, SkTaskGroup* recordingTaskGroup, SkTaskGroup* gpuTaskGroup,
Robert Phillips0d8722c2021-03-29 13:29:40 -0400216 std::chrono::high_resolution_clock::time_point* startStopTime,
217 SkPicture* picture) {
Robert Phillips96601082018-05-29 16:13:26 -0400218 using clock = std::chrono::high_resolution_clock;
219
220 clock::time_point start = *startStopTime;
221
Robert Phillips24a8e9e2020-03-06 20:26:28 +0000222 if (FLAGS_comparableDDL) {
223 SkASSERT(!FLAGS_comparableSKP);
Robert Phillips96601082018-05-29 16:13:26 -0400224
Robert Phillips24a8e9e2020-03-06 20:26:28 +0000225 // In this mode we simply alternate between creating a DDL and drawing it - all on one
226 // thread. The interleaving is so that we don't starve the GPU.
227 // One unfortunate side effect of this is that we can't delete the DDLs until after
228 // the GPU work is flushed.
Robert Phillips0d8722c2021-03-29 13:29:40 -0400229 tiles->interleaveDDLCreationAndDraw(dContext, picture);
Robert Phillips24a8e9e2020-03-06 20:26:28 +0000230 } else if (FLAGS_comparableSKP) {
231 // In this mode simply draw the re-inflated per-tile SKPs directly to the GPU w/o going
232 // through a DDL.
Robert Phillips0d8722c2021-03-29 13:29:40 -0400233 tiles->drawAllTilesDirectly(dContext, picture);
Robert Phillips24a8e9e2020-03-06 20:26:28 +0000234 } else {
Robert Phillips0d8722c2021-03-29 13:29:40 -0400235 tiles->kickOffThreadedWork(recordingTaskGroup, gpuTaskGroup, dContext, picture);
Adlai Hollere5c379d2020-05-18 10:21:53 -0400236 recordingTaskGroup->wait();
Robert Phillips96601082018-05-29 16:13:26 -0400237 }
Adlai Hollere5c379d2020-05-18 10:21:53 -0400238
239 if (gpuTaskGroup) {
240 gpuTaskGroup->add([&]{
Robert Phillips0d8722c2021-03-29 13:29:40 -0400241 flush_with_sync(dContext, gpuSync);
Adlai Hollere5c379d2020-05-18 10:21:53 -0400242 });
243 gpuTaskGroup->wait();
244 } else {
Robert Phillips0d8722c2021-03-29 13:29:40 -0400245 flush_with_sync(dContext, gpuSync);
Adlai Hollere5c379d2020-05-18 10:21:53 -0400246 }
Robert Phillips96601082018-05-29 16:13:26 -0400247
248 *startStopTime = clock::now();
249
Robert Phillips96601082018-05-29 16:13:26 -0400250 if (sample) {
Robert Phillips96601082018-05-29 16:13:26 -0400251 sample->fDuration += *startStopTime - start;
252 sample->fFrames++;
253 }
254}
255
Kevin Lubick03771992023-05-24 11:09:36 -0400256static void run_ddl_benchmark(sk_gpu_test::TestContext* testContext,
257 GrDirectContext* dContext,
258 sk_sp<SkSurface> dstSurface,
259 SkPicture* inputPicture,
Adlai Hollere5c379d2020-05-18 10:21:53 -0400260 std::vector<Sample>* samples) {
Robert Phillips96601082018-05-29 16:13:26 -0400261 using clock = std::chrono::high_resolution_clock;
262 const Sample::duration sampleDuration = std::chrono::milliseconds(FLAGS_sampleMs);
263 const clock::duration benchDuration = std::chrono::milliseconds(FLAGS_duration);
264
Kevin Lubick0bff57e2023-06-09 14:29:17 -0400265 GrSurfaceCharacterization dstCharacterization;
Robert Phillips11c67672020-04-23 15:10:03 -0400266 SkAssertResult(dstSurface->characterize(&dstCharacterization));
Robert Phillips19f466d2020-02-26 10:27:07 -0500267
Robert Phillips11c67672020-04-23 15:10:03 -0400268 SkIRect viewport = dstSurface->imageInfo().bounds();
Robert Phillips96601082018-05-29 16:13:26 -0400269
Kevin Lubicka2019432023-06-12 15:42:51 -0400270 auto supportedYUVADataTypes = skgpu::ganesh::SupportedTextureFormats(*dContext);
Brian Salomon59c60b02020-09-01 15:01:15 -0400271 DDLPromiseImageHelper promiseImageHelper(supportedYUVADataTypes);
Robert Phillips0d8722c2021-03-29 13:29:40 -0400272 sk_sp<SkPicture> newSKP = promiseImageHelper.recreateSKP(dContext, inputPicture);
273 if (!newSKP) {
Robert Phillips96601082018-05-29 16:13:26 -0400274 exitf(ExitErr::kUnavailable, "DDL: conversion of skp failed");
275 }
276
Robert Phillips0d8722c2021-03-29 13:29:40 -0400277 promiseImageHelper.uploadAllToGPU(nullptr, dContext);
Robert Phillips923181b2020-02-14 12:36:37 -0500278
Robert Phillips0d8722c2021-03-29 13:29:40 -0400279 DDLTileHelper tiles(dContext, dstCharacterization, viewport,
Robert Phillips96f6d9a2021-02-26 10:41:06 -0500280 FLAGS_ddlTilingWidthHeight, FLAGS_ddlTilingWidthHeight,
Robert Phillips0c088492020-11-10 08:30:50 -0500281 /* addRandomPaddingToDst */ false);
Robert Phillips96601082018-05-29 16:13:26 -0400282
Robert Phillips0d8722c2021-03-29 13:29:40 -0400283 tiles.createBackendTextures(nullptr, dContext);
Robert Phillips96601082018-05-29 16:13:26 -0400284
Adlai Hollere5c379d2020-05-18 10:21:53 -0400285 // In comparable modes, there is no GPU thread. The following pointers are all null.
286 // Otherwise, we transfer testContext onto the GPU thread until after the bench.
287 std::unique_ptr<SkExecutor> gpuThread;
288 std::unique_ptr<SkTaskGroup> gpuTaskGroup;
289 std::unique_ptr<SkExecutor> recordingThreadPool;
290 std::unique_ptr<SkTaskGroup> recordingTaskGroup;
291 if (!FLAGS_comparableDDL && !FLAGS_comparableSKP) {
292 gpuThread = SkExecutor::MakeFIFOThreadPool(1, false);
293 gpuTaskGroup = std::make_unique<SkTaskGroup>(*gpuThread);
294 recordingThreadPool = SkExecutor::MakeFIFOThreadPool(FLAGS_ddlNumRecordingThreads, false);
295 recordingTaskGroup = std::make_unique<SkTaskGroup>(*recordingThreadPool);
296 testContext->makeNotCurrent();
297 gpuTaskGroup->add([=]{ testContext->makeCurrent(); });
298 }
Robert Phillipsf7dcdb02018-06-21 11:18:25 -0400299
Robert Phillips96601082018-05-29 16:13:26 -0400300 clock::time_point startStopTime = clock::now();
301
Greg Daniel02497d42020-02-21 15:46:27 -0500302 GpuSync gpuSync;
Robert Phillips0d8722c2021-03-29 13:29:40 -0400303 ddl_sample(dContext, &tiles, gpuSync, nullptr, recordingTaskGroup.get(),
304 gpuTaskGroup.get(), &startStopTime, newSKP.get());
Robert Phillips96601082018-05-29 16:13:26 -0400305
306 clock::duration cumulativeDuration = std::chrono::milliseconds(0);
307
308 do {
309 samples->emplace_back();
310 Sample& sample = samples->back();
311
312 do {
Robert Phillips24a8e9e2020-03-06 20:26:28 +0000313 tiles.resetAllTiles();
Robert Phillips0d8722c2021-03-29 13:29:40 -0400314 ddl_sample(dContext, &tiles, gpuSync, &sample, recordingTaskGroup.get(),
315 gpuTaskGroup.get(), &startStopTime, newSKP.get());
Robert Phillips96601082018-05-29 16:13:26 -0400316 } while (sample.fDuration < sampleDuration);
317
318 cumulativeDuration += sample.fDuration;
319 } while (cumulativeDuration < benchDuration || 0 == samples->size() % 2);
320
Adlai Hollere5c379d2020-05-18 10:21:53 -0400321 // Move the context back to this thread now that we're done benching.
322 if (gpuTaskGroup) {
323 gpuTaskGroup->add([=]{
324 testContext->makeNotCurrent();
325 });
326 gpuTaskGroup->wait();
327 testContext->makeCurrent();
328 }
329
Robert Phillips96601082018-05-29 16:13:26 -0400330 if (!FLAGS_png.isEmpty()) {
331 // The user wants to see the final result
Kevin Lubick0bff57e2023-06-09 14:29:17 -0400332 skgpu::ganesh::DrawDDL(dstSurface, tiles.composeDDL());
Kevin Lubick99bcee22023-09-06 10:09:08 -0400333 dContext->flushAndSubmit(dstSurface.get(), GrSyncCpu::kNo);
Robert Phillips96601082018-05-29 16:13:26 -0400334 }
Greg Danield78a9b42019-12-16 15:37:19 -0500335
Robert Phillips24a8e9e2020-03-06 20:26:28 +0000336 tiles.resetAllTiles();
337
Greg Danield78a9b42019-12-16 15:37:19 -0500338 // Make sure the gpu has finished all its work before we exit this function and delete the
339 // fence.
Robert Phillips0d8722c2021-03-29 13:29:40 -0400340 dContext->flush();
Kevin Lubick99bcee22023-09-06 10:09:08 -0400341 dContext->submit(GrSyncCpu::kYes);
Robert Phillips8472a3d2020-04-16 16:27:45 -0400342
Robert Phillips0d8722c2021-03-29 13:29:40 -0400343 promiseImageHelper.deleteAllFromGPU(nullptr, dContext);
Robert Phillips8472a3d2020-04-16 16:27:45 -0400344
Robert Phillips0d8722c2021-03-29 13:29:40 -0400345 tiles.deleteBackendTextures(nullptr, dContext);
Robert Phillips96601082018-05-29 16:13:26 -0400346}
347
Kevin Lubick03771992023-05-24 11:09:36 -0400348static void run_benchmark(GrDirectContext* context,
349 sk_sp<SkSurface> surface,
350 SkpProducer* skpp,
Greg Daniel02497d42020-02-21 15:46:27 -0500351 std::vector<Sample>* samples) {
csmartdaltonc6618dd2016-10-05 08:42:03 -0700352 using clock = std::chrono::high_resolution_clock;
353 const Sample::duration sampleDuration = std::chrono::milliseconds(FLAGS_sampleMs);
csmartdalton037adf32016-09-28 13:56:01 -0700354 const clock::duration benchDuration = std::chrono::milliseconds(FLAGS_duration);
csmartdalton4b5179b2016-09-19 11:03:58 -0700355
Greg Daniel02497d42020-02-21 15:46:27 -0500356 GpuSync gpuSync;
Nathaniel Nifongfa408512019-07-26 09:44:46 -0400357 int i = 0;
358 do {
Kevin Lubick03771992023-05-24 11:09:36 -0400359 i += skpp->drawAndFlushAndSync(context, surface.get(), gpuSync);
Nathaniel Nifongfa408512019-07-26 09:44:46 -0400360 } while(i < kNumFlushesToPrimeCache);
csmartdalton4b5179b2016-09-19 11:03:58 -0700361
csmartdalton037adf32016-09-28 13:56:01 -0700362 clock::time_point now = clock::now();
363 const clock::time_point endTime = now + benchDuration;
csmartdalton4b5179b2016-09-19 11:03:58 -0700364
csmartdalton037adf32016-09-28 13:56:01 -0700365 do {
366 clock::time_point sampleStart = now;
367 samples->emplace_back();
368 Sample& sample = samples->back();
369
csmartdalton4b5179b2016-09-19 11:03:58 -0700370 do {
Kevin Lubick03771992023-05-24 11:09:36 -0400371 sample.fFrames += skpp->drawAndFlushAndSync(context, surface.get(), gpuSync);
Greg Daniel02497d42020-02-21 15:46:27 -0500372 now = clock::now();
373 sample.fDuration = now - sampleStart;
csmartdalton037adf32016-09-28 13:56:01 -0700374 } while (sample.fDuration < sampleDuration);
375 } while (now < endTime || 0 == samples->size() % 2);
Greg Danield78a9b42019-12-16 15:37:19 -0500376
377 // Make sure the gpu has finished all its work before we exit this function and delete the
378 // fence.
Kevin Lubick99bcee22023-09-06 10:09:08 -0400379 context->flush(surface.get());
380 context->submit(GrSyncCpu::kYes);
csmartdalton4b5179b2016-09-19 11:03:58 -0700381}
382
Kevin Lubick03771992023-05-24 11:09:36 -0400383static void run_gpu_time_benchmark(sk_gpu_test::GpuTimer* gpuTimer,
384 GrDirectContext* context,
385 sk_sp<SkSurface> surface,
386 const SkPicture* skp,
Greg Daniel02497d42020-02-21 15:46:27 -0500387 std::vector<Sample>* samples) {
csmartdaltonc6618dd2016-10-05 08:42:03 -0700388 using sk_gpu_test::PlatformTimerQuery;
389 using clock = std::chrono::steady_clock;
390 const clock::duration sampleDuration = std::chrono::milliseconds(FLAGS_sampleMs);
391 const clock::duration benchDuration = std::chrono::milliseconds(FLAGS_duration);
392
393 if (!gpuTimer->disjointSupport()) {
394 fprintf(stderr, "WARNING: GPU timer cannot detect disjoint operations; "
395 "results may be unreliable\n");
396 }
397
Greg Daniel02497d42020-02-21 15:46:27 -0500398 GpuSync gpuSync;
Kevin Lubick03771992023-05-24 11:09:36 -0400399 draw_skp_and_flush_with_sync(context, surface.get(), skp, gpuSync);
csmartdaltonc6618dd2016-10-05 08:42:03 -0700400
Chris Daltona2b5b642018-06-24 13:08:57 -0600401 PlatformTimerQuery previousTime = 0;
402 for (int i = 1; i < kNumFlushesToPrimeCache; ++i) {
403 gpuTimer->queueStart();
Kevin Lubick03771992023-05-24 11:09:36 -0400404 draw_skp_and_flush_with_sync(context, surface.get(), skp, gpuSync);
Chris Daltona2b5b642018-06-24 13:08:57 -0600405 previousTime = gpuTimer->queueStop();
Chris Daltona2b5b642018-06-24 13:08:57 -0600406 }
csmartdaltonc6618dd2016-10-05 08:42:03 -0700407
408 clock::time_point now = clock::now();
409 const clock::time_point endTime = now + benchDuration;
410
411 do {
412 const clock::time_point sampleEndTime = now + sampleDuration;
413 samples->emplace_back();
414 Sample& sample = samples->back();
415
416 do {
417 gpuTimer->queueStart();
Kevin Lubick03771992023-05-24 11:09:36 -0400418 draw_skp_and_flush_with_sync(context, surface.get(), skp, gpuSync);
csmartdaltonc6618dd2016-10-05 08:42:03 -0700419 PlatformTimerQuery time = gpuTimer->queueStop();
csmartdaltonc6618dd2016-10-05 08:42:03 -0700420
421 switch (gpuTimer->checkQueryStatus(previousTime)) {
422 using QueryStatus = sk_gpu_test::GpuTimer::QueryStatus;
423 case QueryStatus::kInvalid:
424 exitf(ExitErr::kUnavailable, "GPU timer failed");
John Stiles30212b72020-06-11 17:55:07 -0400425 break;
csmartdaltonc6618dd2016-10-05 08:42:03 -0700426 case QueryStatus::kPending:
427 exitf(ExitErr::kUnavailable, "timer query still not ready after fence sync");
John Stiles30212b72020-06-11 17:55:07 -0400428 break;
csmartdaltonc6618dd2016-10-05 08:42:03 -0700429 case QueryStatus::kDisjoint:
430 if (FLAGS_verbosity >= 4) {
431 fprintf(stderr, "discarding timer query due to disjoint operations.\n");
432 }
433 break;
434 case QueryStatus::kAccurate:
435 sample.fDuration += gpuTimer->getTimeElapsed(previousTime);
436 ++sample.fFrames;
437 break;
438 }
439 gpuTimer->deleteQuery(previousTime);
440 previousTime = time;
441 now = clock::now();
442 } while (now < sampleEndTime || 0 == sample.fFrames);
443 } while (now < endTime || 0 == samples->size() % 2);
444
445 gpuTimer->deleteQuery(previousTime);
Greg Danield78a9b42019-12-16 15:37:19 -0500446
447 // Make sure the gpu has finished all its work before we exit this function and delete the
448 // fence.
Kevin Lubick99bcee22023-09-06 10:09:08 -0400449 context->flush(surface.get());
450 context->submit(GrSyncCpu::kYes);
csmartdaltonc6618dd2016-10-05 08:42:03 -0700451}
452
csmartdalton4b5179b2016-09-19 11:03:58 -0700453void print_result(const std::vector<Sample>& samples, const char* config, const char* bench) {
454 if (0 == (samples.size() % 2)) {
455 exitf(ExitErr::kSoftware, "attempted to gather stats on even number of samples");
456 }
457
Herb Derby1db274d2021-02-02 12:34:33 -0500458 if (FLAGS_dumpSamples) {
459 printf("Samples: ");
460 for (const Sample& sample : samples) {
461 printf("%" PRId64 " ", static_cast<int64_t>(sample.fDuration.count()));
462 }
463 printf("%s\n", bench);
464 }
465
csmartdalton4b5179b2016-09-19 11:03:58 -0700466 Sample accum = Sample();
467 std::vector<double> values;
468 values.reserve(samples.size());
469 for (const Sample& sample : samples) {
470 accum.fFrames += sample.fFrames;
471 accum.fDuration += sample.fDuration;
472 values.push_back(sample.value());
473 }
474 std::sort(values.begin(), values.end());
csmartdalton4b5179b2016-09-19 11:03:58 -0700475
csmartdalton6904b192016-09-29 06:23:23 -0700476 const double accumValue = accum.value();
csmartdalton4b5179b2016-09-19 11:03:58 -0700477 double variance = 0;
csmartdalton037adf32016-09-28 13:56:01 -0700478 for (double value : values) {
479 const double delta = value - accumValue;
csmartdalton4b5179b2016-09-19 11:03:58 -0700480 variance += delta * delta;
481 }
csmartdalton037adf32016-09-28 13:56:01 -0700482 variance /= values.size();
csmartdalton4b5179b2016-09-19 11:03:58 -0700483 // Technically, this is the relative standard deviation.
csmartdalton037adf32016-09-28 13:56:01 -0700484 const double stddev = 100/*%*/ * sqrt(variance) / accumValue;
csmartdalton4b5179b2016-09-19 11:03:58 -0700485
csmartdalton6904b192016-09-29 06:23:23 -0700486 printf(resultFormat, accumValue, values[values.size() / 2], values.back(), values.front(),
csmartdaltonc6618dd2016-10-05 08:42:03 -0700487 stddev, values.size(), FLAGS_sampleMs, FLAGS_gpuClock ? "gpu" : "cpu", Sample::metric(),
488 config, bench);
csmartdalton4b5179b2016-09-19 11:03:58 -0700489 printf("\n");
490 fflush(stdout);
491}
492
493int main(int argc, char** argv) {
Mike Klein88544fb2019-03-20 10:50:33 -0500494 CommandLineFlags::SetUsage(
495 "Use skpbench.py instead. "
496 "You usually don't want to use this program directly.");
497 CommandLineFlags::Parse(argc, argv);
csmartdalton4b5179b2016-09-19 11:03:58 -0700498
499 if (!FLAGS_suppressHeader) {
500 printf("%s\n", header);
501 }
csmartdalton037adf32016-09-28 13:56:01 -0700502 if (FLAGS_duration <= 0) {
csmartdalton4b5179b2016-09-19 11:03:58 -0700503 exit(0); // This can be used to print the header and quit.
504 }
csmartdalton4b5179b2016-09-19 11:03:58 -0700505
506 // Parse the config.
507 const SkCommandLineConfigGpu* config = nullptr; // Initialize for spurious warning.
508 SkCommandLineConfigArray configs;
509 ParseConfigs(FLAGS_config, &configs);
Herb Derbyffacce52022-11-09 10:51:34 -0500510 if (configs.size() != 1 || !(config = configs[0]->asConfigGpu())) {
csmartdalton5772eaa2016-10-11 18:28:54 -0700511 exitf(ExitErr::kUsage, "invalid config '%s': must specify one (and only one) GPU config",
csmartdalton4b5179b2016-09-19 11:03:58 -0700512 join(FLAGS_config).c_str());
513 }
514
515 // Parse the skp.
Herb Derby6a0a4c42022-09-30 16:43:14 -0400516 if (FLAGS_src.size() != 1) {
Chris Daltona4f5ce02018-06-26 10:13:06 -0600517 exitf(ExitErr::kUsage,
518 "invalid input '%s': must specify a single .skp or .svg file, or 'warmup'",
519 join(FLAGS_src).c_str());
csmartdalton4b5179b2016-09-19 11:03:58 -0700520 }
Robert Phillips96601082018-05-29 16:13:26 -0400521
522 SkGraphics::Init();
Robert Phillips96601082018-05-29 16:13:26 -0400523
csmartdalton5772eaa2016-10-11 18:28:54 -0700524 sk_sp<SkPicture> skp;
Nathaniel Nifongfa408512019-07-26 09:44:46 -0400525 std::unique_ptr<MultiFrameSkp> mskp; // populated if the file is multi frame.
Chris Daltona4f5ce02018-06-26 10:13:06 -0600526 SkString srcname;
527 if (0 == strcmp(FLAGS_src[0], "warmup")) {
csmartdalton5772eaa2016-10-11 18:28:54 -0700528 skp = create_warmup_skp();
Chris Daltona4f5ce02018-06-26 10:13:06 -0600529 srcname = "warmup";
csmartdalton5772eaa2016-10-11 18:28:54 -0700530 } else {
Chris Daltona4f5ce02018-06-26 10:13:06 -0600531 SkString srcfile(FLAGS_src[0]);
532 std::unique_ptr<SkStream> srcstream(SkStream::MakeFromFile(srcfile.c_str()));
533 if (!srcstream) {
534 exitf(ExitErr::kIO, "failed to open file %s", srcfile.c_str());
csmartdalton5772eaa2016-10-11 18:28:54 -0700535 }
Chris Daltona4f5ce02018-06-26 10:13:06 -0600536 if (srcfile.endsWith(".svg")) {
537 skp = create_skp_from_svg(srcstream.get(), srcfile.c_str());
Nathaniel Nifongfa408512019-07-26 09:44:46 -0400538 } else if (srcfile.endsWith(".mskp")) {
539 mskp = MultiFrameSkp::MakeFromFile(srcfile);
540 // populate skp with it's first frame, for width height determination.
541 skp = mskp->frame(0);
Chris Daltona4f5ce02018-06-26 10:13:06 -0600542 } else {
543 skp = SkPicture::MakeFromStream(srcstream.get());
544 }
csmartdalton5772eaa2016-10-11 18:28:54 -0700545 if (!skp) {
Chris Daltona4f5ce02018-06-26 10:13:06 -0600546 exitf(ExitErr::kData, "failed to parse file %s", srcfile.c_str());
csmartdalton5772eaa2016-10-11 18:28:54 -0700547 }
Chris Daltona4f5ce02018-06-26 10:13:06 -0600548 srcname = SkOSPath::Basename(srcfile.c_str());
csmartdalton4b5179b2016-09-19 11:03:58 -0700549 }
Brian Osman788b9162020-02-07 10:36:46 -0500550 int width = std::min(SkScalarCeilToInt(skp->cullRect().width()), 2048),
551 height = std::min(SkScalarCeilToInt(skp->cullRect().height()), 2048);
csmartdaltond7a9db62016-09-22 05:10:02 -0700552 if (FLAGS_verbosity >= 3 &&
csmartdalton4b5179b2016-09-19 11:03:58 -0700553 (width != skp->cullRect().width() || height != skp->cullRect().height())) {
csmartdaltond7a9db62016-09-22 05:10:02 -0700554 fprintf(stderr, "%s is too large (%ix%i), cropping to %ix%i.\n",
Chris Daltona4f5ce02018-06-26 10:13:06 -0600555 srcname.c_str(), SkScalarCeilToInt(skp->cullRect().width()),
csmartdalton4b5179b2016-09-19 11:03:58 -0700556 SkScalarCeilToInt(skp->cullRect().height()), width, height);
557 }
Chris Dalton84c87872020-01-16 14:45:57 -0700558 if (FLAGS_scale != 1) {
559 width *= FLAGS_scale;
560 height *= FLAGS_scale;
561 if (FLAGS_verbosity >= 3) {
562 fprintf(stderr, "Scale factor of %.2f: scaling to %ix%i.\n",
563 FLAGS_scale, width, height);
564 }
565 }
csmartdalton4b5179b2016-09-19 11:03:58 -0700566
Brian Salomonf865b052018-03-09 09:01:53 -0500567 if (config->getSurfType() != SkCommandLineConfigGpu::SurfType::kDefault) {
568 exitf(ExitErr::kUnavailable, "This tool only supports the default surface type. (%s)",
569 config->getTag().c_str());
570 }
571
csmartdalton4b5179b2016-09-19 11:03:58 -0700572 // Create a context.
csmartdalton008b9d82017-02-22 12:00:42 -0700573 GrContextOptions ctxOptions;
Kevin Lubick8f4e5602021-10-14 09:50:00 -0400574 CommonFlags::SetCtxOptions(&ctxOptions);
csmartdalton008b9d82017-02-22 12:00:42 -0700575 sk_gpu_test::GrContextFactory factory(ctxOptions);
csmartdalton4b5179b2016-09-19 11:03:58 -0700576 sk_gpu_test::ContextInfo ctxInfo =
csmartdaltone812d492017-02-21 12:36:05 -0700577 factory.getContextInfo(config->getContextType(), config->getContextOverrides());
Robert Phillips7b4e43c2020-07-01 16:59:17 -0400578 auto ctx = ctxInfo.directContext();
csmartdalton4b5179b2016-09-19 11:03:58 -0700579 if (!ctx) {
580 exitf(ExitErr::kUnavailable, "failed to create context for config %s",
581 config->getTag().c_str());
582 }
Brian Osman788b9162020-02-07 10:36:46 -0500583 if (ctx->maxRenderTargetSize() < std::max(width, height)) {
csmartdalton4b5179b2016-09-19 11:03:58 -0700584 exitf(ExitErr::kUnavailable, "render target size %ix%i not supported by platform (max: %i)",
Brian Salomonc7fe0f72018-05-11 10:14:21 -0400585 width, height, ctx->maxRenderTargetSize());
csmartdalton4b5179b2016-09-19 11:03:58 -0700586 }
Greg Daniel6fa62e22019-08-07 15:52:37 -0400587 GrBackendFormat format = ctx->defaultBackendFormat(config->getColorType(), GrRenderable::kYes);
588 if (!format.isValid()) {
589 exitf(ExitErr::kUnavailable, "failed to get GrBackendFormat from SkColorType: %d",
Greg Daniel0a7aa142018-02-21 13:02:32 -0500590 config->getColorType());
591 }
Robert Phillips9da87e02019-02-04 13:26:26 -0500592 int supportedSampleCount = ctx->priv().caps()->getRenderTargetSampleCount(
Greg Daniel6fa62e22019-08-07 15:52:37 -0400593 config->getSamples(), format);
Greg Daniel81e7bf82017-07-19 14:47:42 -0400594 if (supportedSampleCount != config->getSamples()) {
595 exitf(ExitErr::kUnavailable, "sample count %i not supported by platform",
596 config->getSamples());
csmartdalton4b5179b2016-09-19 11:03:58 -0700597 }
598 sk_gpu_test::TestContext* testCtx = ctxInfo.testContext();
599 if (!testCtx) {
600 exitf(ExitErr::kSoftware, "testContext is null");
601 }
602 if (!testCtx->fenceSyncSupport()) {
603 exitf(ExitErr::kUnavailable, "GPU does not support fence sync");
604 }
605
606 // Create a render target.
Brian Osmanfcb50232021-08-12 10:51:48 -0400607 SkImageInfo info = SkImageInfo::Make(
608 width, height, config->getColorType(), config->getAlphaType(), config->refColorSpace());
Chris Dalton180b4a12021-03-16 20:49:15 -0600609 SkSurfaceProps props(config->getSurfaceFlags(), kRGB_H_SkPixelGeometry);
Kevin Lubick5c93acf2023-05-09 12:11:43 -0400610 sk_sp<SkSurface> surface =
611 SkSurfaces::RenderTarget(ctx, skgpu::Budgeted::kNo, info, config->getSamples(), &props);
csmartdalton4b5179b2016-09-19 11:03:58 -0700612 if (!surface) {
613 exitf(ExitErr::kUnavailable, "failed to create %ix%i render target for config %s",
614 width, height, config->getTag().c_str());
615 }
616
csmartdalton5772eaa2016-10-11 18:28:54 -0700617 // Run the benchmark.
csmartdalton4b5179b2016-09-19 11:03:58 -0700618 std::vector<Sample> samples;
csmartdalton037adf32016-09-28 13:56:01 -0700619 if (FLAGS_sampleMs > 0) {
620 // +1 because we might take one more sample in order to have an odd number.
621 samples.reserve(1 + (FLAGS_duration + FLAGS_sampleMs - 1) / FLAGS_sampleMs);
622 } else {
623 samples.reserve(2 * FLAGS_duration);
624 }
csmartdalton4b5179b2016-09-19 11:03:58 -0700625 SkCanvas* canvas = surface->getCanvas();
626 canvas->translate(-skp->cullRect().x(), -skp->cullRect().y());
Chris Dalton84c87872020-01-16 14:45:57 -0700627 if (FLAGS_scale != 1) {
628 canvas->scale(FLAGS_scale, FLAGS_scale);
629 }
csmartdaltonc6618dd2016-10-05 08:42:03 -0700630 if (!FLAGS_gpuClock) {
Robert Phillips96601082018-05-29 16:13:26 -0400631 if (FLAGS_ddl) {
Adlai Hollere5c379d2020-05-18 10:21:53 -0400632 run_ddl_benchmark(testCtx, ctx, surface, skp.get(), &samples);
Nathaniel Nifongfa408512019-07-26 09:44:46 -0400633 } else if (!mskp) {
634 auto s = std::make_unique<StaticSkp>(skp);
Kevin Lubick03771992023-05-24 11:09:36 -0400635 run_benchmark(ctx, surface, s.get(), &samples);
Robert Phillips96601082018-05-29 16:13:26 -0400636 } else {
Kevin Lubick03771992023-05-24 11:09:36 -0400637 run_benchmark(ctx, surface, mskp.get(), &samples);
Robert Phillips96601082018-05-29 16:13:26 -0400638 }
csmartdaltonc6618dd2016-10-05 08:42:03 -0700639 } else {
Robert Phillips96601082018-05-29 16:13:26 -0400640 if (FLAGS_ddl) {
641 exitf(ExitErr::kUnavailable, "DDL: GPU-only timing not supported");
642 }
csmartdaltonc6618dd2016-10-05 08:42:03 -0700643 if (!testCtx->gpuTimingSupport()) {
644 exitf(ExitErr::kUnavailable, "GPU does not support timing");
645 }
Kevin Lubick03771992023-05-24 11:09:36 -0400646 run_gpu_time_benchmark(testCtx->gpuTimer(), ctx, surface, skp.get(), &samples);
csmartdaltonc6618dd2016-10-05 08:42:03 -0700647 }
Chris Daltona4f5ce02018-06-26 10:13:06 -0600648 print_result(samples, config->getTag().c_str(), srcname.c_str());
csmartdalton4b5179b2016-09-19 11:03:58 -0700649
650 // Save a proof (if one was requested).
651 if (!FLAGS_png.isEmpty()) {
652 SkBitmap bmp;
Mike Reed12e946b2017-04-17 10:53:29 -0400653 bmp.allocPixels(info);
654 if (!surface->getCanvas()->readPixels(bmp, 0, 0)) {
csmartdalton4b5179b2016-09-19 11:03:58 -0700655 exitf(ExitErr::kUnavailable, "failed to read canvas pixels for png");
656 }
Brian Osmane581aaa2018-08-09 09:46:53 -0400657 if (!mkdir_p(SkOSPath::Dirname(FLAGS_png[0]))) {
658 exitf(ExitErr::kIO, "failed to create directory for png \"%s\"", FLAGS_png[0]);
csmartdalton4b5179b2016-09-19 11:03:58 -0700659 }
Kevin Lubickf48d2b02023-04-11 18:37:34 -0400660 if (!ToolUtils::EncodeImageToPngFile(FLAGS_png[0], bmp)) {
csmartdalton4b5179b2016-09-19 11:03:58 -0700661 exitf(ExitErr::kIO, "failed to save png to \"%s\"", FLAGS_png[0]);
662 }
663 }
664
Greg Danield78a9b42019-12-16 15:37:19 -0500665 return(0);
csmartdalton4b5179b2016-09-19 11:03:58 -0700666}
667
Robert Phillips00f78de2020-07-01 16:09:43 -0400668static void flush_with_sync(GrDirectContext* context, GpuSync& gpuSync) {
Greg Daniel02497d42020-02-21 15:46:27 -0500669 gpuSync.waitIfNeeded();
670
671 GrFlushInfo flushInfo;
672 flushInfo.fFinishedProc = sk_gpu_test::FlushFinishTracker::FlushFinished;
673 flushInfo.fFinishedContext = gpuSync.newFlushTracker(context);
674
675 context->flush(flushInfo);
Greg Daniel0a2464f2020-05-14 15:45:44 -0400676 context->submit();
Greg Daniel02497d42020-02-21 15:46:27 -0500677}
678
Robert Phillips00f78de2020-07-01 16:09:43 -0400679static void draw_skp_and_flush_with_sync(GrDirectContext* context, SkSurface* surface,
Greg Daniel02497d42020-02-21 15:46:27 -0500680 const SkPicture* skp, GpuSync& gpuSync) {
Robert Phillips9882dae2019-03-04 11:00:10 -0500681 auto canvas = surface->getCanvas();
csmartdalton4b5179b2016-09-19 11:03:58 -0700682 canvas->drawPicture(skp);
Greg Daniel02497d42020-02-21 15:46:27 -0500683
684 flush_with_sync(context, gpuSync);
csmartdalton4b5179b2016-09-19 11:03:58 -0700685}
686
csmartdalton5772eaa2016-10-11 18:28:54 -0700687static sk_sp<SkPicture> create_warmup_skp() {
688 static constexpr SkRect bounds{0, 0, 500, 500};
689 SkPictureRecorder recorder;
690 SkCanvas* recording = recorder.beginRecording(bounds);
691
692 recording->clear(SK_ColorWHITE);
693
694 SkPaint stroke;
695 stroke.setStyle(SkPaint::kStroke_Style);
696 stroke.setStrokeWidth(2);
697
698 // Use a big path to (theoretically) warmup the CPU.
Kevin Lubickf541ddf2021-10-13 16:02:56 -0400699 SkPath bigPath = BenchUtils::make_big_path();
csmartdalton5772eaa2016-10-11 18:28:54 -0700700 recording->drawPath(bigPath, stroke);
701
702 // Use a perlin shader to warmup the GPU.
703 SkPaint perlin;
Kevin Lubick8fdbbca2023-06-07 16:14:51 -0400704 perlin.setShader(SkShaders::MakeTurbulence(0.1f, 0.1f, 1, 0, nullptr));
csmartdalton5772eaa2016-10-11 18:28:54 -0700705 recording->drawRect(bounds, perlin);
706
707 return recorder.finishRecordingAsPicture();
708}
709
Chris Daltona4f5ce02018-06-26 10:13:06 -0600710static sk_sp<SkPicture> create_skp_from_svg(SkStream* stream, const char* filename) {
Robert Phillips2af13c12021-09-01 16:47:01 +0000711#if defined(SK_ENABLE_SVG)
Kevin Lubickee6dbb42023-11-03 09:19:25 -0400712 sk_sp<SkSVGDOM> svg =
713 SkSVGDOM::Builder().setFontManager(ToolUtils::TestFontMgr()).make(*stream);
Chris Daltona4f5ce02018-06-26 10:13:06 -0600714 if (!svg) {
715 exitf(ExitErr::kData, "failed to build svg dom from file %s", filename);
716 }
717
718 static constexpr SkRect bounds{0, 0, 1200, 1200};
719 SkPictureRecorder recorder;
720 SkCanvas* recording = recorder.beginRecording(bounds);
721
722 svg->setContainerSize(SkSize::Make(recording->getBaseLayerSize()));
723 svg->render(recording);
724
725 return recorder.finishRecordingAsPicture();
726#endif
Robert Phillips2af13c12021-09-01 16:47:01 +0000727 exitf(ExitErr::kData, "SK_ENABLE_SVG is disabled; cannot open svg file %s", filename);
Florin Malita5d3ff432018-07-31 16:38:43 -0400728 return nullptr;
Chris Daltona4f5ce02018-06-26 10:13:06 -0600729}
730
csmartdalton4b5179b2016-09-19 11:03:58 -0700731bool mkdir_p(const SkString& dirname) {
Chris Dalton1d460d02019-04-01 12:40:06 -0600732 if (dirname.isEmpty() || dirname == SkString("/")) {
csmartdalton4b5179b2016-09-19 11:03:58 -0700733 return true;
734 }
735 return mkdir_p(SkOSPath::Dirname(dirname.c_str())) && sk_mkdir(dirname.c_str());
736}
737
Mike Klein88544fb2019-03-20 10:50:33 -0500738static SkString join(const CommandLineFlags::StringArray& stringArray) {
csmartdalton4b5179b2016-09-19 11:03:58 -0700739 SkString joined;
Herb Derby6a0a4c42022-09-30 16:43:14 -0400740 for (int i = 0; i < stringArray.size(); ++i) {
csmartdalton5772eaa2016-10-11 18:28:54 -0700741 joined.appendf(i ? " %s" : "%s", stringArray[i]);
csmartdalton4b5179b2016-09-19 11:03:58 -0700742 }
743 return joined;
744}
745
John Stiles87aa7a92022-02-04 18:17:59 -0500746static void exitf(ExitErr err, const char* format, ...) SK_PRINTF_LIKE(2, 3);
747
csmartdalton4b5179b2016-09-19 11:03:58 -0700748static void exitf(ExitErr err, const char* format, ...) {
749 fprintf(stderr, ExitErr::kSoftware == err ? "INTERNAL ERROR: " : "ERROR: ");
750 va_list args;
751 va_start(args, format);
752 vfprintf(stderr, format, args);
753 va_end(args);
754 fprintf(stderr, ExitErr::kSoftware == err ? "; this should never happen.\n": ".\n");
755 exit((int)err);
756}
csmartdaltone0384892016-09-28 14:53:07 -0700757
Greg Daniel02497d42020-02-21 15:46:27 -0500758void GpuSync::waitIfNeeded() {
759 if (fFinishTrackers[fCurrentFlushIdx]) {
760 fFinishTrackers[fCurrentFlushIdx]->waitTillFinished();
761 }
csmartdaltone0384892016-09-28 14:53:07 -0700762}
763
Robert Phillips00f78de2020-07-01 16:09:43 -0400764sk_gpu_test::FlushFinishTracker* GpuSync::newFlushTracker(GrDirectContext* context) {
Greg Daniel02497d42020-02-21 15:46:27 -0500765 fFinishTrackers[fCurrentFlushIdx].reset(new sk_gpu_test::FlushFinishTracker(context));
csmartdaltone0384892016-09-28 14:53:07 -0700766
Greg Daniel02497d42020-02-21 15:46:27 -0500767 sk_gpu_test::FlushFinishTracker* tracker = fFinishTrackers[fCurrentFlushIdx].get();
768 // We add an additional ref to the current flush tracker here. This ref is owned by the finish
769 // callback on the flush call. The finish callback will unref the tracker when called.
770 tracker->ref();
csmartdaltone0384892016-09-28 14:53:07 -0700771
Herb Derbyb780fc22022-06-28 08:27:35 -0400772 fCurrentFlushIdx = (fCurrentFlushIdx + 1) % std::size(fFinishTrackers);
Greg Daniel02497d42020-02-21 15:46:27 -0500773 return tracker;
csmartdaltone0384892016-09-28 14:53:07 -0700774}