Flutter Engine
The Flutter Engine
Loading...
Searching...
No Matches
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};
@ kUnavailable

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)
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:373
SkCanvas * beginRecording(const SkRect &bounds, sk_sp< SkBBoxHierarchy > bbh)
sk_sp< SkPicture > finishRecordingAsPicture()
Builder & setTextShapingFactory(sk_sp< SkShapers::Factory >)
Definition SkSVGDOM.cpp:400
sk_sp< SkSVGDOM > make(SkStream &) const
Definition SkSVGDOM.cpp:405
Builder & setFontManager(sk_sp< SkFontMgr >)
Definition SkSVGDOM.cpp:390
Optional< SkRect > bounds
Definition SkRecords.h:189
sk_sp< Factory > BestAvailable()
sk_sp< SkFontMgr > TestFontMgr()
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
695 SkPaint stroke;
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)
void clear(SkColor color)
Definition SkCanvas.h:1199
void drawPath(const SkPath &path, const SkPaint &paint)
void setStyle(Style style)
Definition SkPaint.cpp:105
@ kStroke_Style
set to stroke geometry
Definition SkPaint.h:194
void setShader(sk_sp< SkShader > shader)
void setStrokeWidth(SkScalar width)
Definition SkPaint.cpp:159
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)
constexpr struct @268 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.");
498 CommandLineFlags::Parse(argc, argv);
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)
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 void Init()
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)
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
void SetCtxOptions(struct GrContextOptions *)
std::string printf(const char *fmt,...) SK_PRINTF_LIKE(1
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)
SINT Vec< 2 *N, T > join(const Vec< N, T > &lo, const Vec< N, T > &hi)
Definition SkVx.h:242
int32_t height
int32_t width
static const char header[]
Definition skpbench.cpp:88
static bool mkdir_p(const SkString &name)
Definition skpbench.cpp:734
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 void bench(NanoJSONResultsWriter *log, const char *name, int bytes)
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
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
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}
#define SkAssertResult(cond)
Definition SkAssert.h:123
void flushAndSubmit(GrSyncCpu sync=GrSyncCpu::kNo)
static std::unique_ptr< SkExecutor > MakeFIFOThreadPool(int threads=0, bool allowBorrowing=true)
bool characterize(GrSurfaceCharacterization *characterization) const
virtual SkImageInfo imageInfo() const
Definition SkSurface.h:188
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
SkIRect bounds() const

◆ 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;
424 case QueryStatus::kInvalid:
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}
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 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.