40#if defined(SK_ENABLE_SVG)
69static DEFINE_bool(ddl,
false,
"record the skp into DDLs before rendering");
70static DEFINE_int(ddlNumRecordingThreads, 0,
"number of DDL recording threads (0=num_cores)");
71static DEFINE_int(ddlTilingWidthHeight, 0,
"number of tiles along one edge when in DDL mode");
73static DEFINE_bool(comparableDDL,
false,
"render in a way that is comparable to 'comparableSKP'");
74static DEFINE_bool(comparableSKP,
false,
"report in a way that is comparable to 'comparableDDL'");
77static DEFINE_int(sampleMs, 50,
"minimum duration of a sample");
78static DEFINE_bool(gpuClock,
false,
"time on the gpu clock (gpu work only)");
81 "path to a single .skp or .svg file, or 'warmup' for a builtin warmup run");
82static DEFINE_string(png,
"",
"if set, save a .png proof to disk at this file location");
83static DEFINE_int(verbosity, 4,
"level of verbosity (0=none to 5=debug)");
84static DEFINE_bool(suppressHeader,
false,
"don't print a header row before the results");
85static DEFINE_double(
scale, 1,
"Scale the size of the canvas and the zoom level by this factor.");
86static DEFINE_bool(dumpSamples,
false,
"print the individual samples to stdout");
89" accum median max min stddev samples sample_ms clock metric config bench";
92"%8.4g %8.4g %8.4g %8.4g %6.3g%% %7zu %9i %-5s %-6s %-9s %s";
101 double ms()
const {
return std::chrono::duration<double, std::milli>(
fDuration).count(); }
103 static const char*
metric() {
return FLAGS_fps ?
"fps" :
"ms"; }
119 enum { kMaxFrameLag = 3 };
121 int fCurrentFlushIdx = 0;
171 MultiFrameSkp(
const std::vector<SkDocumentPage>& frames) : fFrames(frames){}
176 if (!stream) {
return nullptr; }
179 auto deserialContext = std::make_unique<SkSharingDeserialContext>();
190 std::vector<SkDocumentPage> frames(page_count);
195 return std::make_unique<MultiFrameSkp>(frames);
202 for (
int i=0; i<this->
count(); i++){
205 return this->
count();
210 int count()
const {
return fFrames.size(); }
212 std::vector<SkDocumentPage> fFrames;
217 std::chrono::high_resolution_clock::time_point* startStopTime,
219 using clock = std::chrono::high_resolution_clock;
221 clock::time_point
start = *startStopTime;
223 if (FLAGS_comparableDDL) {
230 tiles->interleaveDDLCreationAndDraw(dContext, picture);
231 }
else if (FLAGS_comparableSKP) {
234 tiles->drawAllTilesDirectly(dContext, picture);
236 tiles->kickOffThreadedWork(recordingTaskGroup, gpuTaskGroup, dContext, picture);
237 recordingTaskGroup->
wait();
241 gpuTaskGroup->
add([&]{
244 gpuTaskGroup->
wait();
249 *startStopTime = clock::now();
261 std::vector<Sample>* samples) {
262 using clock = std::chrono::high_resolution_clock;
263 const Sample::duration sampleDuration = std::chrono::milliseconds(FLAGS_sampleMs);
264 const clock::duration benchDuration = std::chrono::milliseconds(FLAGS_duration);
269 SkIRect viewport = dstSurface->imageInfo().bounds();
281 FLAGS_ddlTilingWidthHeight, FLAGS_ddlTilingWidthHeight,
284 tiles.createBackendTextures(
nullptr, dContext);
288 std::unique_ptr<SkExecutor> gpuThread;
289 std::unique_ptr<SkTaskGroup> gpuTaskGroup;
290 std::unique_ptr<SkExecutor> recordingThreadPool;
291 std::unique_ptr<SkTaskGroup> recordingTaskGroup;
292 if (!FLAGS_comparableDDL && !FLAGS_comparableSKP) {
294 gpuTaskGroup = std::make_unique<SkTaskGroup>(*gpuThread);
296 recordingTaskGroup = std::make_unique<SkTaskGroup>(*recordingThreadPool);
298 gpuTaskGroup->add([=]{ testContext->
makeCurrent(); });
301 clock::time_point startStopTime = clock::now();
304 ddl_sample(dContext, &
tiles, gpuSync,
nullptr, recordingTaskGroup.get(),
305 gpuTaskGroup.get(), &startStopTime, newSKP.
get());
307 clock::duration cumulativeDuration = std::chrono::milliseconds(0);
310 samples->emplace_back();
311 Sample& sample = samples->back();
314 tiles.resetAllTiles();
315 ddl_sample(dContext, &
tiles, gpuSync, &sample, recordingTaskGroup.get(),
316 gpuTaskGroup.get(), &startStopTime, newSKP.
get());
317 }
while (sample.
fDuration < sampleDuration);
320 }
while (cumulativeDuration < benchDuration || 0 == samples->size() % 2);
324 gpuTaskGroup->add([=]{
327 gpuTaskGroup->wait();
331 if (!FLAGS_png.isEmpty()) {
337 tiles.resetAllTiles();
346 tiles.deleteBackendTextures(
nullptr, dContext);
352 std::vector<Sample>* samples) {
353 using clock = std::chrono::high_resolution_clock;
354 const Sample::duration sampleDuration = std::chrono::milliseconds(FLAGS_sampleMs);
355 const clock::duration benchDuration = std::chrono::milliseconds(FLAGS_duration);
363 clock::time_point now = clock::now();
364 const clock::time_point endTime = now + benchDuration;
367 clock::time_point sampleStart = now;
368 samples->emplace_back();
369 Sample& sample = samples->back();
375 }
while (sample.
fDuration < sampleDuration);
376 }
while (now < endTime || 0 == samples->size() % 2);
388 std::vector<Sample>* samples) {
390 using clock = std::chrono::steady_clock;
391 const clock::duration sampleDuration = std::chrono::milliseconds(FLAGS_sampleMs);
392 const clock::duration benchDuration = std::chrono::milliseconds(FLAGS_duration);
395 fprintf(stderr,
"WARNING: GPU timer cannot detect disjoint operations; "
396 "results may be unreliable\n");
402 PlatformTimerQuery previousTime = 0;
409 clock::time_point now = clock::now();
410 const clock::time_point endTime = now + benchDuration;
413 const clock::time_point sampleEndTime = now + sampleDuration;
414 samples->emplace_back();
415 Sample& sample = samples->back();
420 PlatformTimerQuery time = gpuTimer->
queueStop();
424 case QueryStatus::kInvalid:
427 case QueryStatus::kPending:
430 case QueryStatus::kDisjoint:
431 if (FLAGS_verbosity >= 4) {
432 fprintf(stderr,
"discarding timer query due to disjoint operations.\n");
435 case QueryStatus::kAccurate:
443 }
while (now < sampleEndTime || 0 == sample.
fFrames);
444 }
while (now < endTime || 0 == samples->size() % 2);
455 if (0 == (samples.size() % 2)) {
459 if (FLAGS_dumpSamples) {
461 for (
const Sample& sample : samples) {
462 printf(
"%" PRId64
" ",
static_cast<int64_t
>(sample.fDuration.count()));
464 printf(
"%s\n",
bench);
468 std::vector<double> values;
469 values.reserve(samples.size());
470 for (
const Sample& sample : samples) {
471 accum.
fFrames += sample.fFrames;
473 values.push_back(sample.value());
475 std::sort(values.begin(), values.end());
477 const double accumValue = accum.
value();
479 for (
double value : values) {
480 const double delta =
value - accumValue;
481 variance += delta * delta;
483 variance /= values.size();
485 const double stddev = 100 * sqrt(variance) / accumValue;
487 printf(
resultFormat, accumValue, values[values.size() / 2], values.back(), values.front(),
488 stddev, values.size(), FLAGS_sampleMs, FLAGS_gpuClock ?
"gpu" :
"cpu",
Sample::metric(),
494int main(
int argc,
char** argv) {
496 "Use skpbench.py instead. "
497 "You usually don't want to use this program directly.");
500 if (!FLAGS_suppressHeader) {
503 if (FLAGS_duration <= 0) {
511 if (configs.size() != 1 || !(config = configs[0]->asConfigGpu())) {
513 join(FLAGS_config).c_str());
517 if (FLAGS_src.size() != 1) {
519 "invalid input '%s': must specify a single .skp or .svg file, or 'warmup'",
520 join(FLAGS_src).c_str());
526 std::unique_ptr<MultiFrameSkp> mskp;
528 if (0 == strcmp(FLAGS_src[0],
"warmup")) {
539 }
else if (srcfile.
endsWith(
".mskp")) {
542 skp = mskp->frame(0);
553 if (FLAGS_verbosity >= 3 &&
555 fprintf(stderr,
"%s is too large (%ix%i), cropping to %ix%i.\n",
559 if (FLAGS_scale != 1) {
560 width *= FLAGS_scale;
562 if (FLAGS_verbosity >= 3) {
563 fprintf(stderr,
"Scale factor of %.2f: scaling to %ix%i.\n",
584 if (ctx->maxRenderTargetSize() < std::max(
width,
height)) {
593 int supportedSampleCount = ctx->priv().caps()->getRenderTargetSampleCount(
595 if (supportedSampleCount != config->
getSamples()) {
619 std::vector<Sample> samples;
620 if (FLAGS_sampleMs > 0) {
622 samples.reserve(1 + (FLAGS_duration + FLAGS_sampleMs - 1) / FLAGS_sampleMs);
624 samples.reserve(2 * FLAGS_duration);
628 if (FLAGS_scale != 1) {
629 canvas->
scale(FLAGS_scale, FLAGS_scale);
631 if (!FLAGS_gpuClock) {
635 auto s = std::make_unique<StaticSkp>(
skp);
652 if (!FLAGS_png.isEmpty()) {
655 if (!
surface->getCanvas()->readPixels(bmp, 0, 0)) {
676 context->
flush(flushInfo);
682 auto canvas =
surface->getCanvas();
683 canvas->drawPicture(
skp);
689 static constexpr SkRect bounds{0, 0, 500, 500};
701 recording->
drawPath(bigPath, stroke);
706 recording->
drawRect(bounds, perlin);
712#if defined(SK_ENABLE_SVG)
721 static constexpr SkRect bounds{0, 0, 1200, 1200};
726 svg->render(recording);
743 for (
int i = 0; i < stringArray.
size(); ++i) {
744 joined.
appendf(i ?
" %s" :
"%s", stringArray[i]);
762 if (fFinishTrackers[fCurrentFlushIdx]) {
775 fCurrentFlushIdx = (fCurrentFlushIdx + 1) % std::size(fFinishTrackers);
#define DEFINE_bool(name, defaultValue, helpString)
#define DEFINE_int(name, defaultValue, helpString)
#define DEFINE_double(name, defaultValue, helpString)
#define DEFINE_string(name, defaultValue, helpString)
void ParseConfigs(const CommandLineFlags::StringArray &configs, SkCommandLineConfigArray *outResult)
static void info(const char *fmt,...) SK_PRINTF_LIKE(1
#define SkAssertResult(cond)
#define SK_PRINTF_LIKE(A, B)
constexpr SkColor SK_ColorWHITE
bool sk_mkdir(const char *path)
static void bench(NanoJSONResultsWriter *log, const char *name, int bytes)
#define SkScalarCeilToInt(x)
static void Parse(int argc, const char *const *argv)
static void SetUsage(const char *usage)
sk_sp< SkPicture > recreateSKP(GrDirectContext *, SkPicture *)
void deleteAllFromGPU(SkTaskGroup *, GrDirectContext *)
void uploadAllToGPU(SkTaskGroup *, GrDirectContext *)
sk_gpu_test::FlushFinishTracker * newFlushTracker(GrDirectContext *context)
bool submit(GrSyncCpu sync=GrSyncCpu::kNo)
void flushAndSubmit(GrSyncCpu sync=GrSyncCpu::kNo)
GrSemaphoresSubmitted flush(const GrFlushInfo &info)
int drawAndFlushAndSync(GrDirectContext *context, SkSurface *surface, GpuSync &gpuSync) override
MultiFrameSkp(const std::vector< SkDocumentPage > &frames)
static std::unique_ptr< MultiFrameSkp > MakeFromFile(const SkString &path)
sk_sp< SkPicture > frame(int n) const
void allocPixels(const SkImageInfo &info, size_t rowBytes)
void drawRect(const SkRect &rect, const SkPaint &paint)
void translate(SkScalar dx, SkScalar dy)
virtual SkISize getBaseLayerSize() const
void clear(SkColor color)
void drawPath(const SkPath &path, const SkPaint &paint)
void scale(SkScalar sx, SkScalar sy)
SurfType getSurfType() const
ContextType getContextType() const
ContextOverrides getContextOverrides() const
SkColorType getColorType() const
SkAlphaType getAlphaType() const
uint32_t getSurfaceFlags() const
sk_sp< SkColorSpace > refColorSpace() const
const SkString & getTag() const
static std::unique_ptr< SkExecutor > MakeFIFOThreadPool(int threads=0, bool allowBorrowing=true)
static SkString Basename(const char *fullPath)
static SkString Dirname(const char *fullPath)
void setStyle(Style style)
@ kStroke_Style
set to stroke geometry
void setShader(sk_sp< SkShader > shader)
void setStrokeWidth(SkScalar width)
SkCanvas * beginRecording(const SkRect &bounds, sk_sp< SkBBoxHierarchy > bbh)
sk_sp< SkPicture > finishRecordingAsPicture()
static sk_sp< SkPicture > MakeFromStream(SkStream *stream, const SkDeserialProcs *procs=nullptr)
Builder & setTextShapingFactory(sk_sp< SkShapers::Factory >)
sk_sp< SkSVGDOM > make(SkStream &) const
Builder & setFontManager(sk_sp< SkFontMgr >)
static std::unique_ptr< SkStreamAsset > MakeFromFile(const char path[])
bool endsWith(const char suffixStr[]) const
const char * c_str() const
void void void appendf(const char format[],...) SK_PRINTF_LIKE(2
void add(std::function< void(void)> fn)
virtual int drawAndFlushAndSync(GrDirectContext *, SkSurface *surface, GpuSync &gpuSync)=0
StaticSkp(sk_sp< SkPicture > skp)
int drawAndFlushAndSync(GrDirectContext *context, SkSurface *surface, GpuSync &gpuSync) override
GrDirectContext * directContext() const
TestContext * testContext() const
static void FlushFinished(void *finishedContext)
void waitTillFinished(std::function< void()> tick={})
virtual std::chrono::nanoseconds getTimeElapsed(PlatformTimerQuery)=0
PlatformTimerQuery queueStop()
virtual QueryStatus checkQueryStatus(PlatformTimerQuery)=0
bool disjointSupport() const
virtual void deleteQuery(PlatformTimerQuery)=0
ContextInfo getContextInfo(ContextType type, ContextOverrides=ContextOverrides::kNone)
bool gpuTimingSupport() const
GpuTimer * gpuTimer() const
void makeNotCurrent() const
bool fenceSyncSupport() const
void reset(T *ptr=nullptr)
G_BEGIN_DECLS G_MODULE_EXPORT FlValue * args
uint32_t uint32_t * format
void SetCtxOptions(struct GrContextOptions *)
SK_API bool Read(SkStreamSeekable *src, SkDocumentPage *dstArray, int dstArrayCount, const SkDeserialProcs *=nullptr)
SK_API int ReadPageCount(SkStreamSeekable *src)
SK_API sk_sp< SkShader > MakeTurbulence(SkScalar baseFrequencyX, SkScalar baseFrequencyY, int numOctaves, SkScalar seed, const SkISize *tileSize=nullptr)
sk_sp< Factory > BestAvailable()
SK_API sk_sp< SkSurface > RenderTarget(GrRecordingContext *context, skgpu::Budgeted budgeted, const SkImageInfo &imageInfo, int sampleCount, GrSurfaceOrigin surfaceOrigin, const SkSurfaceProps *surfaceProps, bool shouldCreateWithMips=false, bool isProtected=false)
uint64_t PlatformTimerQuery
SK_API bool DrawDDL(SkSurface *, sk_sp< const GrDeferredDisplayList > ddl)
SkYUVAPixmapInfo::SupportedDataTypes SupportedTextureFormats(const GrImageContext &context)
SINT Vec< 2 *N, T > join(const Vec< N, T > &lo, const Vec< N, T > &hi)
constexpr struct @268 tiles[]
static const char header[]
static bool mkdir_p(const SkString &name)
static void exitf(ExitErr, const char *format,...)
static void flush_with_sync(GrDirectContext *, GpuSync &)
static void draw_skp_and_flush_with_sync(GrDirectContext *, SkSurface *, const SkPicture *, GpuSync &)
static void ddl_sample(GrDirectContext *dContext, DDLTileHelper *tiles, GpuSync &gpuSync, Sample *sample, SkTaskGroup *recordingTaskGroup, SkTaskGroup *gpuTaskGroup, std::chrono::high_resolution_clock::time_point *startStopTime, SkPicture *picture)
static void run_benchmark(GrDirectContext *context, sk_sp< SkSurface > surface, SkpProducer *skpp, std::vector< Sample > *samples)
void print_result(const std::vector< Sample > &samples, const char *config, const char *bench)
static void run_gpu_time_benchmark(sk_gpu_test::GpuTimer *gpuTimer, GrDirectContext *context, sk_sp< SkSurface > surface, const SkPicture *skp, std::vector< Sample > *samples)
static void run_ddl_benchmark(sk_gpu_test::TestContext *testContext, GrDirectContext *dContext, sk_sp< SkSurface > dstSurface, SkPicture *inputPicture, std::vector< Sample > *samples)
static sk_sp< SkPicture > create_skp_from_svg(SkStream *, const char *filename)
static constexpr int kNumFlushesToPrimeCache
static sk_sp< SkPicture > create_warmup_skp()
static const char resultFormat[]
GrGpuFinishedContext fFinishedContext
GrGpuFinishedProc fFinishedProc
static const char * metric()
std::chrono::nanoseconds duration
SkDeserialImageProc fImageProc
static SkImageInfo Make(int width, int height, SkColorType ct, SkAlphaType at)
static sk_sp< SkImage > deserializeImage(const void *data, size_t length, void *ctx)
static constexpr SkSize Make(SkScalar w, SkScalar h)