Flutter Engine
The Flutter Engine
Classes | Enumerations | Functions | Variables
skpbench.cpp File Reference
#include "bench/BigPath.h"
#include "include/core/SkCanvas.h"
#include "include/core/SkGraphics.h"
#include "include/core/SkPicture.h"
#include "include/core/SkPictureRecorder.h"
#include "include/core/SkStream.h"
#include "include/core/SkSurface.h"
#include "include/core/SkSurfaceProps.h"
#include "include/docs/SkMultiPictureDocument.h"
#include "include/effects/SkPerlinNoiseShader.h"
#include "include/gpu/GrDirectContext.h"
#include "include/gpu/ganesh/SkSurfaceGanesh.h"
#include "include/private/chromium/GrDeferredDisplayList.h"
#include "src/core/SkOSFile.h"
#include "src/core/SkTaskGroup.h"
#include "src/gpu/ganesh/GrCaps.h"
#include "src/gpu/ganesh/GrDirectContextPriv.h"
#include "src/gpu/ganesh/SkGr.h"
#include "src/gpu/ganesh/image/GrImageUtils.h"
#include "src/utils/SkOSPath.h"
#include "tools/DDLPromiseImageHelper.h"
#include "tools/DDLTileHelper.h"
#include "tools/EncodeUtils.h"
#include "tools/SkSharingProc.h"
#include "tools/flags/CommandLineFlags.h"
#include "tools/flags/CommonFlags.h"
#include "tools/flags/CommonFlagsConfig.h"
#include "tools/fonts/FontToolUtils.h"
#include "tools/gpu/FlushFinishTracker.h"
#include "tools/gpu/GpuTimer.h"
#include "tools/gpu/GrContextFactory.h"
#include <stdlib.h>
#include <algorithm>
#include <array>
#include <chrono>
#include <cinttypes>
#include <cmath>
#include <vector>

Go to the source code of this file.

Classes

struct  Sample
 
class  GpuSync
 
class  SkpProducer
 
class  StaticSkp
 
class  MultiFrameSkp
 

Enumerations

enum class  ExitErr {
  kOk = 0 , kUsage = 64 , kData = 65 , kUnavailable = 69 ,
  kIO = 74 , kSoftware = 70
}
 

Functions

static DEFINE_bool (ddl, false, "record the skp into DDLs before rendering")
 
static DEFINE_int (ddlNumRecordingThreads, 0, "number of DDL recording threads (0=num_cores)")
 
static DEFINE_int (ddlTilingWidthHeight, 0, "number of tiles along one edge when in DDL mode")
 
static DEFINE_bool (comparableDDL, false, "render in a way that is comparable to 'comparableSKP'")
 
static DEFINE_bool (comparableSKP, false, "report in a way that is comparable to 'comparableDDL'")
 
static DEFINE_int (duration, 5000, "number of milliseconds to run the benchmark")
 
static DEFINE_int (sampleMs, 50, "minimum duration of a sample")
 
static DEFINE_bool (gpuClock, false, "time on the gpu clock (gpu work only)")
 
static DEFINE_bool (fps, false, "use fps instead of ms")
 
static DEFINE_string (src, "", "path to a single .skp or .svg file, or 'warmup' for a builtin warmup run")
 
static DEFINE_string (png, "", "if set, save a .png proof to disk at this file location")
 
static DEFINE_int (verbosity, 4, "level of verbosity (0=none to 5=debug)")
 
static DEFINE_bool (suppressHeader, false, "don't print a header row before the results")
 
static DEFINE_double (scale, 1, "Scale the size of the canvas and the zoom level by this factor.")
 
static DEFINE_bool (dumpSamples, false, "print the individual samples to stdout")
 
static void flush_with_sync (GrDirectContext *, GpuSync &)
 
static void draw_skp_and_flush_with_sync (GrDirectContext *, SkSurface *, const SkPicture *, GpuSync &)
 
static sk_sp< SkPicturecreate_warmup_skp ()
 
static sk_sp< SkPicturecreate_skp_from_svg (SkStream *, const char *filename)
 
static bool mkdir_p (const SkString &name)
 
static SkString join (const CommandLineFlags::StringArray &)
 
static void exitf (ExitErr, const char *format,...)
 
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_ddl_benchmark (sk_gpu_test::TestContext *testContext, GrDirectContext *dContext, sk_sp< SkSurface > dstSurface, SkPicture *inputPicture, std::vector< Sample > *samples)
 
static void run_benchmark (GrDirectContext *context, sk_sp< SkSurface > surface, SkpProducer *skpp, std::vector< Sample > *samples)
 
static void run_gpu_time_benchmark (sk_gpu_test::GpuTimer *gpuTimer, GrDirectContext *context, sk_sp< SkSurface > surface, const SkPicture *skp, std::vector< Sample > *samples)
 
void print_result (const std::vector< Sample > &samples, const char *config, const char *bench)
 
int main (int argc, char **argv)
 

Variables

static const char header []
 
static const char resultFormat []
 
static constexpr int kNumFlushesToPrimeCache = 3
 

Enumeration Type Documentation

◆ ExitErr

enum class ExitErr
strong
Enumerator
kOk 
kUsage 
kData 
kUnavailable 
kIO 
kSoftware 

Definition at line 124 of file skpbench.cpp.

124 {
125 kOk = 0,
126 kUsage = 64,
127 kData = 65,
128 kUnavailable = 69,
129 kIO = 74,
130 kSoftware = 70
131};
@ kSoftware
Definition: embedder.h:81

Function Documentation

◆ create_skp_from_svg()

static sk_sp< SkPicture > create_skp_from_svg ( SkStream stream,
const char *  filename 
)
static

Definition at line 711 of file skpbench.cpp.

711 {
712#if defined(SK_ENABLE_SVG)
714 .setFontManager(ToolUtils::TestFontMgr())
715 .setTextShapingFactory(SkShapers::BestAvailable())
716 .make(*stream);
717 if (!svg) {
718 exitf(ExitErr::kData, "failed to build svg dom from file %s", filename);
719 }
720
721 static constexpr SkRect bounds{0, 0, 1200, 1200};
722 SkPictureRecorder recorder;
723 SkCanvas* recording = recorder.beginRecording(bounds);
724
725 svg->setContainerSize(SkSize::Make(recording->getBaseLayerSize()));
726 svg->render(recording);
727
728 return recorder.finishRecordingAsPicture();
729#endif
730 exitf(ExitErr::kData, "SK_ENABLE_SVG is disabled; cannot open svg file %s", filename);
731 return nullptr;
732}
virtual SkISize getBaseLayerSize() const
Definition: SkCanvas.cpp:369
SkCanvas * beginRecording(const SkRect &bounds, sk_sp< SkBBoxHierarchy > bbh)
sk_sp< SkPicture > finishRecordingAsPicture()
Optional< SkRect > bounds
Definition: SkRecords.h:189
sk_sp< Factory > BestAvailable()
sk_sp< SkFontMgr > TestFontMgr()
DlVertices::Builder Builder
static void exitf(ExitErr, const char *format,...)
Definition: skpbench.cpp:751
static constexpr SkSize Make(SkScalar w, SkScalar h)
Definition: SkSize.h:56
const char * svg

◆ create_warmup_skp()

static sk_sp< SkPicture > create_warmup_skp ( )
static

Definition at line 688 of file skpbench.cpp.

688 {
689 static constexpr SkRect bounds{0, 0, 500, 500};
690 SkPictureRecorder recorder;
691 SkCanvas* recording = recorder.beginRecording(bounds);
692
693 recording->clear(SK_ColorWHITE);
694
697 stroke.setStrokeWidth(2);
698
699 // Use a big path to (theoretically) warmup the CPU.
701 recording->drawPath(bigPath, stroke);
702
703 // Use a perlin shader to warmup the GPU.
704 SkPaint perlin;
705 perlin.setShader(SkShaders::MakeTurbulence(0.1f, 0.1f, 1, 0, nullptr));
706 recording->drawRect(bounds, perlin);
707
708 return recorder.finishRecordingAsPicture();
709}
constexpr SkColor SK_ColorWHITE
Definition: SkColor.h:122
void drawRect(const SkRect &rect, const SkPaint &paint)
Definition: SkCanvas.cpp:1673
void clear(SkColor color)
Definition: SkCanvas.h:1199
void drawPath(const SkPath &path, const SkPaint &paint)
Definition: SkCanvas.cpp:1747
@ kStroke_Style
set to stroke geometry
Definition: SkPaint.h:194
void setShader(sk_sp< SkShader > shader)
Definition: SkPath.h:59
SkPath make_big_path()
Definition: BigPath.cpp:19
SK_API sk_sp< SkShader > MakeTurbulence(SkScalar baseFrequencyX, SkScalar baseFrequencyY, int numOctaves, SkScalar seed, const SkISize *tileSize=nullptr)

◆ ddl_sample()

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

Definition at line 215 of file skpbench.cpp.

218 {
219 using clock = std::chrono::high_resolution_clock;
220
221 clock::time_point start = *startStopTime;
222
223 if (FLAGS_comparableDDL) {
224 SkASSERT(!FLAGS_comparableSKP);
225
226 // In this mode we simply alternate between creating a DDL and drawing it - all on one
227 // thread. The interleaving is so that we don't starve the GPU.
228 // One unfortunate side effect of this is that we can't delete the DDLs until after
229 // the GPU work is flushed.
230 tiles->interleaveDDLCreationAndDraw(dContext, picture);
231 } else if (FLAGS_comparableSKP) {
232 // In this mode simply draw the re-inflated per-tile SKPs directly to the GPU w/o going
233 // through a DDL.
234 tiles->drawAllTilesDirectly(dContext, picture);
235 } else {
236 tiles->kickOffThreadedWork(recordingTaskGroup, gpuTaskGroup, dContext, picture);
237 recordingTaskGroup->wait();
238 }
239
240 if (gpuTaskGroup) {
241 gpuTaskGroup->add([&]{
242 flush_with_sync(dContext, gpuSync);
243 });
244 gpuTaskGroup->wait();
245 } else {
246 flush_with_sync(dContext, gpuSync);
247 }
248
249 *startStopTime = clock::now();
250
251 if (sample) {
252 sample->fDuration += *startStopTime - start;
253 sample->fFrames++;
254 }
255}
#define SkASSERT(cond)
Definition: SkAssert.h:116
void add(std::function< void(void)> fn)
Definition: SkTaskGroup.cpp:16
sk_sp< const SkPicture > picture
Definition: SkRecords.h:299
constexpr struct @263 tiles[]
static void flush_with_sync(GrDirectContext *, GpuSync &)
Definition: skpbench.cpp:669
duration fDuration
Definition: skpbench.cpp:106
int fFrames
Definition: skpbench.cpp:105

◆ DEFINE_bool() [1/7]

static DEFINE_bool ( comparableDDL  ,
false  ,
"render in a way that is comparable to 'comparableSKP'"   
)
static

◆ DEFINE_bool() [2/7]

static DEFINE_bool ( comparableSKP  ,
false  ,
"report in a way that is comparable to 'comparableDDL'"   
)
static

◆ DEFINE_bool() [3/7]

static DEFINE_bool ( ddl  ,
false  ,
"record the skp into DDLs before rendering"   
)
static

This is a minimalist program whose sole purpose is to open a .skp or .svg file, benchmark it on a single config, and exit. It is intended to be used through skpbench.py rather than invoked directly. Limiting the entire process to a single config/skp pair helps to keep the results repeatable.

No tiling, looping, or other fanciness is used; it just draws the skp whole into a size-matched render target and syncs the GPU after each draw.

Well, maybe a little fanciness, MSKP's can be loaded and played. The animation is played as many times as necessary to reach the target sample duration and FPS is reported.

Currently, only GPU configs are supported.

◆ DEFINE_bool() [4/7]

static DEFINE_bool ( dumpSamples  ,
false  ,
"print the individual samples to stdout"   
)
static

◆ DEFINE_bool() [5/7]

static DEFINE_bool ( fps  ,
false  ,
"use fps instead of ms"   
)
static

◆ DEFINE_bool() [6/7]

static DEFINE_bool ( gpuClock  ,
false  ,
"time on the gpu clock (gpu work only)"   
)
static

◆ DEFINE_bool() [7/7]

static DEFINE_bool ( suppressHeader  ,
false  ,
"don't print a header row before the results"   
)
static

◆ DEFINE_double()

static DEFINE_double ( scale  ,
,
"Scale the size of the canvas and the zoom level by this factor."   
)
static

◆ DEFINE_int() [1/5]

static DEFINE_int ( ddlNumRecordingThreads  ,
,
"number of DDL recording threads (0=num_cores)"   
)
static

◆ DEFINE_int() [2/5]

static DEFINE_int ( ddlTilingWidthHeight  ,
,
"number of tiles along one edge when in DDL mode"   
)
static

◆ DEFINE_int() [3/5]

static DEFINE_int ( duration  ,
5000  ,
"number of milliseconds to run the benchmark"   
)
static

◆ DEFINE_int() [4/5]

static DEFINE_int ( sampleMs  ,
50  ,
"minimum duration of a sample"   
)
static

◆ DEFINE_int() [5/5]

static DEFINE_int ( verbosity  ,
,
"level of verbosity (0=none to 5=debug)"   
)
static

◆ DEFINE_string() [1/2]

static DEFINE_string ( png  ,
""  ,
"if  set,
save a .png proof to disk at this file location"   
)
static

◆ DEFINE_string() [2/2]

static DEFINE_string ( src  ,
""  ,
"path to a single .skp or .svg  file,
or 'warmup' for a builtin warmup run"   
)
static

◆ draw_skp_and_flush_with_sync()

static void draw_skp_and_flush_with_sync ( GrDirectContext context,
SkSurface surface,
const SkPicture skp,
GpuSync gpuSync 
)
static

Definition at line 680 of file skpbench.cpp.

681 {
682 auto canvas = surface->getCanvas();
683 canvas->drawPicture(skp);
684
685 flush_with_sync(context, gpuSync);
686}
VkSurfaceKHR surface
Definition: main.cc:49
Definition: __init__.py:1

◆ exitf()

static void static void exitf ( ExitErr  err,
const char *  format,
  ... 
)
static

Definition at line 751 of file skpbench.cpp.

751 {
752 fprintf(stderr, ExitErr::kSoftware == err ? "INTERNAL ERROR: " : "ERROR: ");
753 va_list args;
755 vfprintf(stderr, format, args);
756 va_end(args);
757 fprintf(stderr, ExitErr::kSoftware == err ? "; this should never happen.\n": ".\n");
758 exit((int)err);
759}
G_BEGIN_DECLS G_MODULE_EXPORT FlValue * args
uint32_t uint32_t * format
va_start(args, format)
exit(kErrorExitCode)
va_end(args)

◆ flush_with_sync()

static void flush_with_sync ( GrDirectContext context,
GpuSync gpuSync 
)
static

Definition at line 669 of file skpbench.cpp.

669 {
670 gpuSync.waitIfNeeded();
671
672 GrFlushInfo flushInfo;
674 flushInfo.fFinishedContext = gpuSync.newFlushTracker(context);
675
676 context->flush(flushInfo);
677 context->submit();
678}
sk_gpu_test::FlushFinishTracker * newFlushTracker(GrDirectContext *context)
Definition: skpbench.cpp:767
void waitIfNeeded()
Definition: skpbench.cpp:761
bool submit(GrSyncCpu sync=GrSyncCpu::kNo)
GrSemaphoresSubmitted flush(const GrFlushInfo &info)
static void FlushFinished(void *finishedContext)
GrGpuFinishedContext fFinishedContext
Definition: GrTypes.h:220
GrGpuFinishedProc fFinishedProc
Definition: GrTypes.h:219

◆ join()

static SkString join ( const CommandLineFlags::StringArray stringArray)
static

Definition at line 741 of file skpbench.cpp.

741 {
742 SkString joined;
743 for (int i = 0; i < stringArray.size(); ++i) {
744 joined.appendf(i ? " %s" : "%s", stringArray[i]);
745 }
746 return joined;
747}
void void void appendf(const char format[],...) SK_PRINTF_LIKE(2
Definition: SkString.cpp:550

◆ main()

int main ( int  argc,
char **  argv 
)

Definition at line 494 of file skpbench.cpp.

494 {
496 "Use skpbench.py instead. "
497 "You usually don't want to use this program directly.");
499
500 if (!FLAGS_suppressHeader) {
501 printf("%s\n", header);
502 }
503 if (FLAGS_duration <= 0) {
504 exit(0); // This can be used to print the header and quit.
505 }
506
507 // Parse the config.
508 const SkCommandLineConfigGpu* config = nullptr; // Initialize for spurious warning.
510 ParseConfigs(FLAGS_config, &configs);
511 if (configs.size() != 1 || !(config = configs[0]->asConfigGpu())) {
512 exitf(ExitErr::kUsage, "invalid config '%s': must specify one (and only one) GPU config",
513 join(FLAGS_config).c_str());
514 }
515
516 // Parse the skp.
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());
521 }
522
524
526 std::unique_ptr<MultiFrameSkp> mskp; // populated if the file is multi frame.
527 SkString srcname;
528 if (0 == strcmp(FLAGS_src[0], "warmup")) {
530 srcname = "warmup";
531 } else {
532 SkString srcfile(FLAGS_src[0]);
533 std::unique_ptr<SkStream> srcstream(SkStream::MakeFromFile(srcfile.c_str()));
534 if (!srcstream) {
535 exitf(ExitErr::kIO, "failed to open file %s", srcfile.c_str());
536 }
537 if (srcfile.endsWith(".svg")) {
538 skp = create_skp_from_svg(srcstream.get(), srcfile.c_str());
539 } else if (srcfile.endsWith(".mskp")) {
540 mskp = MultiFrameSkp::MakeFromFile(srcfile);
541 // populate skp with it's first frame, for width height determination.
542 skp = mskp->frame(0);
543 } else {
544 skp = SkPicture::MakeFromStream(srcstream.get());
545 }
546 if (!skp) {
547 exitf(ExitErr::kData, "failed to parse file %s", srcfile.c_str());
548 }
549 srcname = SkOSPath::Basename(srcfile.c_str());
550 }
551 int width = std::min(SkScalarCeilToInt(skp->cullRect().width()), 2048),
552 height = std::min(SkScalarCeilToInt(skp->cullRect().height()), 2048);
553 if (FLAGS_verbosity >= 3 &&
554 (width != skp->cullRect().width() || height != skp->cullRect().height())) {
555 fprintf(stderr, "%s is too large (%ix%i), cropping to %ix%i.\n",
556 srcname.c_str(), SkScalarCeilToInt(skp->cullRect().width()),
557 SkScalarCeilToInt(skp->cullRect().height()), width, height);
558 }
559 if (FLAGS_scale != 1) {
560 width *= FLAGS_scale;
561 height *= FLAGS_scale;
562 if (FLAGS_verbosity >= 3) {
563 fprintf(stderr, "Scale factor of %.2f: scaling to %ix%i.\n",
564 FLAGS_scale, width, height);
565 }
566 }
567
569 exitf(ExitErr::kUnavailable, "This tool only supports the default surface type. (%s)",
570 config->getTag().c_str());
571 }
572
573 // Create a context.
574 GrContextOptions ctxOptions;
575 CommonFlags::SetCtxOptions(&ctxOptions);
576 sk_gpu_test::GrContextFactory factory(ctxOptions);
578 factory.getContextInfo(config->getContextType(), config->getContextOverrides());
579 auto ctx = ctxInfo.directContext();
580 if (!ctx) {
581 exitf(ExitErr::kUnavailable, "failed to create context for config %s",
582 config->getTag().c_str());
583 }
584 if (ctx->maxRenderTargetSize() < std::max(width, height)) {
585 exitf(ExitErr::kUnavailable, "render target size %ix%i not supported by platform (max: %i)",
586 width, height, ctx->maxRenderTargetSize());
587 }
588 GrBackendFormat format = ctx->defaultBackendFormat(config->getColorType(), GrRenderable::kYes);
589 if (!format.isValid()) {
590 exitf(ExitErr::kUnavailable, "failed to get GrBackendFormat from SkColorType: %d",
591 config->getColorType());
592 }
593 int supportedSampleCount = ctx->priv().caps()->getRenderTargetSampleCount(
594 config->getSamples(), format);
595 if (supportedSampleCount != config->getSamples()) {
596 exitf(ExitErr::kUnavailable, "sample count %i not supported by platform",
597 config->getSamples());
598 }
599 sk_gpu_test::TestContext* testCtx = ctxInfo.testContext();
600 if (!testCtx) {
601 exitf(ExitErr::kSoftware, "testContext is null");
602 }
603 if (!testCtx->fenceSyncSupport()) {
604 exitf(ExitErr::kUnavailable, "GPU does not support fence sync");
605 }
606
607 // Create a render target.
609 width, height, config->getColorType(), config->getAlphaType(), config->refColorSpace());
613 if (!surface) {
614 exitf(ExitErr::kUnavailable, "failed to create %ix%i render target for config %s",
615 width, height, config->getTag().c_str());
616 }
617
618 // Run the benchmark.
619 std::vector<Sample> samples;
620 if (FLAGS_sampleMs > 0) {
621 // +1 because we might take one more sample in order to have an odd number.
622 samples.reserve(1 + (FLAGS_duration + FLAGS_sampleMs - 1) / FLAGS_sampleMs);
623 } else {
624 samples.reserve(2 * FLAGS_duration);
625 }
626 SkCanvas* canvas = surface->getCanvas();
627 canvas->translate(-skp->cullRect().x(), -skp->cullRect().y());
628 if (FLAGS_scale != 1) {
629 canvas->scale(FLAGS_scale, FLAGS_scale);
630 }
631 if (!FLAGS_gpuClock) {
632 if (FLAGS_ddl) {
633 run_ddl_benchmark(testCtx, ctx, surface, skp.get(), &samples);
634 } else if (!mskp) {
635 auto s = std::make_unique<StaticSkp>(skp);
636 run_benchmark(ctx, surface, s.get(), &samples);
637 } else {
638 run_benchmark(ctx, surface, mskp.get(), &samples);
639 }
640 } else {
641 if (FLAGS_ddl) {
642 exitf(ExitErr::kUnavailable, "DDL: GPU-only timing not supported");
643 }
644 if (!testCtx->gpuTimingSupport()) {
645 exitf(ExitErr::kUnavailable, "GPU does not support timing");
646 }
647 run_gpu_time_benchmark(testCtx->gpuTimer(), ctx, surface, skp.get(), &samples);
648 }
649 print_result(samples, config->getTag().c_str(), srcname.c_str());
650
651 // Save a proof (if one was requested).
652 if (!FLAGS_png.isEmpty()) {
653 SkBitmap bmp;
654 bmp.allocPixels(info);
655 if (!surface->getCanvas()->readPixels(bmp, 0, 0)) {
656 exitf(ExitErr::kUnavailable, "failed to read canvas pixels for png");
657 }
658 if (!mkdir_p(SkOSPath::Dirname(FLAGS_png[0]))) {
659 exitf(ExitErr::kIO, "failed to create directory for png \"%s\"", FLAGS_png[0]);
660 }
661 if (!ToolUtils::EncodeImageToPngFile(FLAGS_png[0], bmp)) {
662 exitf(ExitErr::kIO, "failed to save png to \"%s\"", FLAGS_png[0]);
663 }
664 }
665
666 return(0);
667}
void ParseConfigs(const CommandLineFlags::StringArray &configs, SkCommandLineConfigArray *outResult)
static void info(const char *fmt,...) SK_PRINTF_LIKE(1
Definition: DM.cpp:213
#define SkScalarCeilToInt(x)
Definition: SkScalar.h:36
@ kRGB_H_SkPixelGeometry
static void Parse(int argc, const char *const *argv)
static void SetUsage(const char *usage)
static std::unique_ptr< MultiFrameSkp > MakeFromFile(const SkString &path)
Definition: skpbench.cpp:173
void allocPixels(const SkImageInfo &info, size_t rowBytes)
Definition: SkBitmap.cpp:258
void translate(SkScalar dx, SkScalar dy)
Definition: SkCanvas.cpp:1278
void scale(SkScalar sx, SkScalar sy)
Definition: SkCanvas.cpp:1289
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 void Init()
Definition: SkGraphics.cpp:22
static SkString Basename(const char *fullPath)
Definition: SkOSPath.cpp:23
static SkString Dirname(const char *fullPath)
Definition: SkOSPath.cpp:36
static sk_sp< SkPicture > MakeFromStream(SkStream *stream, const SkDeserialProcs *procs=nullptr)
Definition: SkPicture.cpp:147
static std::unique_ptr< SkStreamAsset > MakeFromFile(const char path[])
Definition: SkStream.cpp:922
const char * c_str() const
Definition: SkString.h:133
GrDirectContext * directContext() const
TestContext * testContext() const
bool gpuTimingSupport() const
Definition: TestContext.h:37
GpuTimer * gpuTimer() const
Definition: TestContext.h:38
bool fenceSyncSupport() const
Definition: TestContext.h:35
struct MyStruct s
static float max(float r, float g, float b)
Definition: hsl.cpp:49
static float min(float r, float g, float b)
Definition: hsl.cpp:48
char ** argv
Definition: library.h:9
void SetCtxOptions(struct GrContextOptions *)
std::string printf(const char *fmt,...) SK_PRINTF_LIKE(1
Definition: SkSLString.cpp:83
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)
bool EncodeImageToPngFile(const char *path, const SkBitmap &src)
Definition: EncodeUtils.cpp:60
int32_t height
int32_t width
static const char header[]
Definition: skpbench.cpp:88
@ kUnavailable
static bool mkdir_p(const SkString &name)
Definition: skpbench.cpp:734
static SkString join(const CommandLineFlags::StringArray &)
Definition: skpbench.cpp:741
static void run_benchmark(GrDirectContext *context, sk_sp< SkSurface > surface, SkpProducer *skpp, std::vector< Sample > *samples)
Definition: skpbench.cpp:349
void print_result(const std::vector< Sample > &samples, const char *config, const char *bench)
Definition: skpbench.cpp:454
static void run_gpu_time_benchmark(sk_gpu_test::GpuTimer *gpuTimer, GrDirectContext *context, sk_sp< SkSurface > surface, const SkPicture *skp, std::vector< Sample > *samples)
Definition: skpbench.cpp:384
static void run_ddl_benchmark(sk_gpu_test::TestContext *testContext, GrDirectContext *dContext, sk_sp< SkSurface > dstSurface, SkPicture *inputPicture, std::vector< Sample > *samples)
Definition: skpbench.cpp:257
static sk_sp< SkPicture > create_skp_from_svg(SkStream *, const char *filename)
Definition: skpbench.cpp:711
static sk_sp< SkPicture > create_warmup_skp()
Definition: skpbench.cpp:688
static SkImageInfo Make(int width, int height, SkColorType ct, SkAlphaType at)

◆ mkdir_p()

bool mkdir_p ( const SkString name)
static

Definition at line 734 of file skpbench.cpp.

734 {
735 if (dirname.isEmpty() || dirname == SkString("/")) {
736 return true;
737 }
738 return mkdir_p(SkOSPath::Dirname(dirname.c_str())) && sk_mkdir(dirname.c_str());
739}
bool sk_mkdir(const char *path)

◆ print_result()

void print_result ( const std::vector< Sample > &  samples,
const char *  config,
const char *  bench 
)

Definition at line 454 of file skpbench.cpp.

454 {
455 if (0 == (samples.size() % 2)) {
456 exitf(ExitErr::kSoftware, "attempted to gather stats on even number of samples");
457 }
458
459 if (FLAGS_dumpSamples) {
460 printf("Samples: ");
461 for (const Sample& sample : samples) {
462 printf("%" PRId64 " ", static_cast<int64_t>(sample.fDuration.count()));
463 }
464 printf("%s\n", bench);
465 }
466
467 Sample accum = Sample();
468 std::vector<double> values;
469 values.reserve(samples.size());
470 for (const Sample& sample : samples) {
471 accum.fFrames += sample.fFrames;
472 accum.fDuration += sample.fDuration;
473 values.push_back(sample.value());
474 }
475 std::sort(values.begin(), values.end());
476
477 const double accumValue = accum.value();
478 double variance = 0;
479 for (double value : values) {
480 const double delta = value - accumValue;
481 variance += delta * delta;
482 }
483 variance /= values.size();
484 // Technically, this is the relative standard deviation.
485 const double stddev = 100/*%*/ * sqrt(variance) / accumValue;
486
487 printf(resultFormat, accumValue, values[values.size() / 2], values.back(), values.front(),
488 stddev, values.size(), FLAGS_sampleMs, FLAGS_gpuClock ? "gpu" : "cpu", Sample::metric(),
489 config, bench);
490 printf("\n");
491 fflush(stdout);
492}
static std::vector< SkPDFIndirectReference > sort(const THashSet< SkPDFIndirectReference > &src)
static void bench(NanoJSONResultsWriter *log, const char *name, int bytes)
Definition: SkSLBench.cpp:285
uint8_t value
SIN Vec< N, float > sqrt(const Vec< N, float > &x)
Definition: SkVx.h:706
static const char resultFormat[]
Definition: skpbench.cpp:91
static const char * metric()
Definition: skpbench.cpp:103
double value() const
Definition: skpbench.cpp:102

◆ run_benchmark()

static void run_benchmark ( GrDirectContext context,
sk_sp< SkSurface surface,
SkpProducer skpp,
std::vector< Sample > *  samples 
)
static

Definition at line 349 of file skpbench.cpp.

352 {
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);
356
357 GpuSync gpuSync;
358 int i = 0;
359 do {
360 i += skpp->drawAndFlushAndSync(context, surface.get(), gpuSync);
361 } while(i < kNumFlushesToPrimeCache);
362
363 clock::time_point now = clock::now();
364 const clock::time_point endTime = now + benchDuration;
365
366 do {
367 clock::time_point sampleStart = now;
368 samples->emplace_back();
369 Sample& sample = samples->back();
370
371 do {
372 sample.fFrames += skpp->drawAndFlushAndSync(context, surface.get(), gpuSync);
373 now = clock::now();
374 sample.fDuration = now - sampleStart;
375 } while (sample.fDuration < sampleDuration);
376 } while (now < endTime || 0 == samples->size() % 2);
377
378 // Make sure the gpu has finished all its work before we exit this function and delete the
379 // fence.
380 context->flush(surface.get());
381 context->submit(GrSyncCpu::kYes);
382}
virtual int drawAndFlushAndSync(GrDirectContext *, SkSurface *surface, GpuSync &gpuSync)=0
double duration
Definition: examples.cpp:30
it will be possible to load the file into Perfetto s trace viewer disable asset Prevents usage of any non test fonts unless they were explicitly Loaded via prefetched default font Indicates whether the embedding started a prefetch of the default font manager before creating the engine run In non interactive keep the shell running after the Dart script has completed enable serial On low power devices with low core running concurrent GC tasks on threads can cause them to contend with the UI thread which could potentially lead to jank This option turns off all concurrent GC activities domain network JSON encoded network policy per domain This overrides the DisallowInsecureConnections switch Embedder can specify whether to allow or disallow insecure connections at a domain level old gen heap size
Definition: switches.h:259
static constexpr int kNumFlushesToPrimeCache
Definition: skpbench.cpp:94
std::chrono::nanoseconds duration
Definition: skpbench.cpp:97

◆ run_ddl_benchmark()

static void run_ddl_benchmark ( sk_gpu_test::TestContext testContext,
GrDirectContext dContext,
sk_sp< SkSurface dstSurface,
SkPicture inputPicture,
std::vector< Sample > *  samples 
)
static

Definition at line 257 of file skpbench.cpp.

261 {
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);
265
266 GrSurfaceCharacterization dstCharacterization;
267 SkAssertResult(dstSurface->characterize(&dstCharacterization));
268
269 SkIRect viewport = dstSurface->imageInfo().bounds();
270
271 auto supportedYUVADataTypes = skgpu::ganesh::SupportedTextureFormats(*dContext);
272 DDLPromiseImageHelper promiseImageHelper(supportedYUVADataTypes);
273 sk_sp<SkPicture> newSKP = promiseImageHelper.recreateSKP(dContext, inputPicture);
274 if (!newSKP) {
275 exitf(ExitErr::kUnavailable, "DDL: conversion of skp failed");
276 }
277
278 promiseImageHelper.uploadAllToGPU(nullptr, dContext);
279
280 DDLTileHelper tiles(dContext, dstCharacterization, viewport,
281 FLAGS_ddlTilingWidthHeight, FLAGS_ddlTilingWidthHeight,
282 /* addRandomPaddingToDst */ false);
283
284 tiles.createBackendTextures(nullptr, dContext);
285
286 // In comparable modes, there is no GPU thread. The following pointers are all null.
287 // Otherwise, we transfer testContext onto the GPU thread until after the bench.
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) {
293 gpuThread = SkExecutor::MakeFIFOThreadPool(1, false);
294 gpuTaskGroup = std::make_unique<SkTaskGroup>(*gpuThread);
295 recordingThreadPool = SkExecutor::MakeFIFOThreadPool(FLAGS_ddlNumRecordingThreads, false);
296 recordingTaskGroup = std::make_unique<SkTaskGroup>(*recordingThreadPool);
297 testContext->makeNotCurrent();
298 gpuTaskGroup->add([=]{ testContext->makeCurrent(); });
299 }
300
301 clock::time_point startStopTime = clock::now();
302
303 GpuSync gpuSync;
304 ddl_sample(dContext, &tiles, gpuSync, nullptr, recordingTaskGroup.get(),
305 gpuTaskGroup.get(), &startStopTime, newSKP.get());
306
307 clock::duration cumulativeDuration = std::chrono::milliseconds(0);
308
309 do {
310 samples->emplace_back();
311 Sample& sample = samples->back();
312
313 do {
314 tiles.resetAllTiles();
315 ddl_sample(dContext, &tiles, gpuSync, &sample, recordingTaskGroup.get(),
316 gpuTaskGroup.get(), &startStopTime, newSKP.get());
317 } while (sample.fDuration < sampleDuration);
318
319 cumulativeDuration += sample.fDuration;
320 } while (cumulativeDuration < benchDuration || 0 == samples->size() % 2);
321
322 // Move the context back to this thread now that we're done benching.
323 if (gpuTaskGroup) {
324 gpuTaskGroup->add([=]{
325 testContext->makeNotCurrent();
326 });
327 gpuTaskGroup->wait();
328 testContext->makeCurrent();
329 }
330
331 if (!FLAGS_png.isEmpty()) {
332 // The user wants to see the final result
333 skgpu::ganesh::DrawDDL(dstSurface, tiles.composeDDL());
334 dContext->flushAndSubmit(dstSurface.get(), GrSyncCpu::kNo);
335 }
336
337 tiles.resetAllTiles();
338
339 // Make sure the gpu has finished all its work before we exit this function and delete the
340 // fence.
341 dContext->flush();
342 dContext->submit(GrSyncCpu::kYes);
343
344 promiseImageHelper.deleteAllFromGPU(nullptr, dContext);
345
346 tiles.deleteBackendTextures(nullptr, dContext);
347}
SkAssertResult(font.textToGlyphs("Hello", 5, SkTextEncoding::kUTF8, glyphs, std::size(glyphs))==count)
void flushAndSubmit(GrSyncCpu sync=GrSyncCpu::kNo)
static std::unique_ptr< SkExecutor > MakeFIFOThreadPool(int threads=0, bool allowBorrowing=true)
Definition: SkExecutor.cpp:146
bool characterize(GrSurfaceCharacterization *characterization) const
Definition: SkSurface.cpp:239
virtual SkImageInfo imageInfo() const
Definition: SkSurface.h:188
void makeNotCurrent() const
Definition: TestContext.cpp:28
T * get() const
Definition: SkRefCnt.h:303
SK_API bool DrawDDL(SkSurface *, sk_sp< const GrDeferredDisplayList > ddl)
SkYUVAPixmapInfo::SupportedDataTypes SupportedTextureFormats(const GrImageContext &context)
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)
Definition: skpbench.cpp:215
Definition: SkRect.h:32
SkIRect bounds() const
Definition: SkImageInfo.h:427

◆ run_gpu_time_benchmark()

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

Definition at line 384 of file skpbench.cpp.

388 {
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);
393
394 if (!gpuTimer->disjointSupport()) {
395 fprintf(stderr, "WARNING: GPU timer cannot detect disjoint operations; "
396 "results may be unreliable\n");
397 }
398
399 GpuSync gpuSync;
400 draw_skp_and_flush_with_sync(context, surface.get(), skp, gpuSync);
401
402 PlatformTimerQuery previousTime = 0;
403 for (int i = 1; i < kNumFlushesToPrimeCache; ++i) {
404 gpuTimer->queueStart();
405 draw_skp_and_flush_with_sync(context, surface.get(), skp, gpuSync);
406 previousTime = gpuTimer->queueStop();
407 }
408
409 clock::time_point now = clock::now();
410 const clock::time_point endTime = now + benchDuration;
411
412 do {
413 const clock::time_point sampleEndTime = now + sampleDuration;
414 samples->emplace_back();
415 Sample& sample = samples->back();
416
417 do {
418 gpuTimer->queueStart();
419 draw_skp_and_flush_with_sync(context, surface.get(), skp, gpuSync);
420 PlatformTimerQuery time = gpuTimer->queueStop();
421
422 switch (gpuTimer->checkQueryStatus(previousTime)) {
423 using QueryStatus = sk_gpu_test::GpuTimer::QueryStatus;
425 exitf(ExitErr::kUnavailable, "GPU timer failed");
426 break;
427 case QueryStatus::kPending:
428 exitf(ExitErr::kUnavailable, "timer query still not ready after fence sync");
429 break;
430 case QueryStatus::kDisjoint:
431 if (FLAGS_verbosity >= 4) {
432 fprintf(stderr, "discarding timer query due to disjoint operations.\n");
433 }
434 break;
435 case QueryStatus::kAccurate:
436 sample.fDuration += gpuTimer->getTimeElapsed(previousTime);
437 ++sample.fFrames;
438 break;
439 }
440 gpuTimer->deleteQuery(previousTime);
441 previousTime = time;
442 now = clock::now();
443 } while (now < sampleEndTime || 0 == sample.fFrames);
444 } while (now < endTime || 0 == samples->size() % 2);
445
446 gpuTimer->deleteQuery(previousTime);
447
448 // Make sure the gpu has finished all its work before we exit this function and delete the
449 // fence.
450 context->flush(surface.get());
451 context->submit(GrSyncCpu::kYes);
452}
@ kInvalid
virtual std::chrono::nanoseconds getTimeElapsed(PlatformTimerQuery)=0
PlatformTimerQuery queueStop()
Definition: GpuTimer.h:50
virtual QueryStatus checkQueryStatus(PlatformTimerQuery)=0
bool disjointSupport() const
Definition: GpuTimer.h:35
virtual void deleteQuery(PlatformTimerQuery)=0
uint64_t PlatformTimerQuery
Definition: GpuTimer.h:17
static double time(int loops, Benchmark *bench, Target *target)
Definition: nanobench.cpp:394
static void draw_skp_and_flush_with_sync(GrDirectContext *, SkSurface *, const SkPicture *, GpuSync &)
Definition: skpbench.cpp:680

Variable Documentation

◆ header

const char header[]
static
Initial value:
=
" accum median max min stddev samples sample_ms clock metric config bench"

Definition at line 88 of file skpbench.cpp.

◆ kNumFlushesToPrimeCache

constexpr int kNumFlushesToPrimeCache = 3
staticconstexpr

Definition at line 94 of file skpbench.cpp.

◆ resultFormat

const char resultFormat[]
static
Initial value:
=
"%8.4g %8.4g %8.4g %8.4g %6.3g%% %7zu %9i %-5s %-6s %-9s %s"

Definition at line 91 of file skpbench.cpp.