Flutter Engine
The Flutter Engine
GaneshBenchmarkTarget.cpp
Go to the documentation of this file.
1/*
2 * Copyright 2023 Google LLC
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
8#include "bench/Benchmark.h"
18
19// Based on flags found here:
20// https://skia.googlesource.com/skia/+/a063eaeaf1e09e4d6f42e0f44a5723622a46d21c/tools/flags/CommonFlagsGpu.cpp
21//
22// These are the only flags used in CI tasks at the time of writing (2023-09-29), but we can
23// always backport more flags from //tools/flags/CommonFlagsGpu.cpp as needed.
25 gpuMs,
26 5,
27 "While auto-tuning the number of benchmark runs per sample, increase the number of runs "
28 "until a single sample takes this many milliseconds. Do this for each benchmark.");
29
30static DEFINE_bool(gpuStats, false, "Print GPU stats after each GPU benchmark.");
31
32static DEFINE_bool(gpuStatsDump,
33 false,
34 "Dump GPU stats after each benchmark into the "
35 "results.json output file, which can be ingested by Perf.");
36
37static DEFINE_bool(dmsaaStatsDump,
38 false,
39 "Dump DMSAA stats after each benchmark into the "
40 "results.json output file, which can be ingested by Perf.");
41
42// Estimated maximum number of frames the GPU allows to lag, if unknown.
43//
44// Based on:
45// https://skia.googlesource.com/skia/+/a063eaeaf1e09e4d6f42e0f44a5723622a46d21c/bench/nanobench.cpp#131
46//
47// TODO(lovisolo): This value is overridden by //bench/nanobench.cpp based on the --loops flag, see
48// https://skia.googlesource.com/skia/+/a063eaeaf1e09e4d6f42e0f44a5723622a46d21c/bench/nanobench.cpp#1361.
49static constexpr int ESTIMATED_GPU_FRAME_LAG = 5;
50
51GrRecordingContextPriv::DMSAAStats combinedDMSAAStats;
52
54 if (FLAGS_dmsaaStatsDump) {
55 TestRunner::Log("<<Total Combined DMSAA Stats>>");
56 combinedDMSAAStats.dump();
57 }
58}
59
60// Based on
61// https://skia.googlesource.com/skia/+/a063eaeaf1e09e4d6f42e0f44a5723622a46d21c/bench/nanobench.cpp#240.
63public:
64 GaneshBenchmarkTarget(std::unique_ptr<SurfaceManager> surfaceManager, Benchmark* benchmark)
65 : BenchmarkTarget(std::move(surfaceManager), benchmark) {}
66
68 // For Vulkan we need to release all our refs to the GrContext before destroy the vulkan
69 // context which happens at the end of this destructor. Thus we need to release the surface
70 // here which holds a ref to the GrContext.
71 fSurfaceManager->getSurface().reset();
72 }
73
75
76 // TODO(lovisolo): Do we still need this?
77 void setup() const override {
78 fSurfaceManager->getGaneshContextInfo()->testContext()->makeCurrent();
79 // Make sure we're done with whatever came before.
80 fSurfaceManager->getGaneshContextInfo()->testContext()->finish();
81
83 }
84
85 // TODO(lovisolo): Do we still need this?
86 void onAfterDraw() const override {
87 if (fSurfaceManager->getGaneshContextInfo()->testContext()) {
88 fSurfaceManager->getGaneshContextInfo()->testContext()->flushAndWaitOnSync(
89 fSurfaceManager->getGaneshContextInfo()->directContext());
90 }
91 }
92
93 // Based on nanobench's setup_gpu_bench():
94 // https://skia.googlesource.com/skia/+/a063eaeaf1e09e4d6f42e0f44a5723622a46d21c/bench/nanobench.cpp#510.
95 std::tuple<int, bool> autoTuneLoops() const override {
96 int maxFrameLag = computeMaxFrameLag();
97
98 // First, figure out how many loops it'll take to get a frame up to FLAGS_gpuMs.
99 int loops = 1;
100 double elapsed = 0;
101 do {
102 if (1 << 30 == loops) {
103 // We're about to wrap. Something's wrong with the bench.
104 loops = 0;
105 break;
106 }
107 loops *= 2;
108 // If the GPU lets frames lag at all, we need to make sure we're timing
109 // _this_ round, not still timing last round.
110 for (int i = 0; i < maxFrameLag; i++) {
111 elapsed = time(loops);
112 }
113 } while (elapsed < FLAGS_gpuMs);
114
115 // We've overshot at least a little. Scale back linearly.
116 loops = (int)ceil(loops * FLAGS_gpuMs / elapsed);
117
118 // Make sure we're not still timing our calibration.
119 fSurfaceManager->getGaneshContextInfo()->testContext()->finish();
120
121 return std::make_tuple(loops, true);
122 }
123
124 // Based on
125 // https://skia.googlesource.com/skia/+/a063eaeaf1e09e4d6f42e0f44a5723622a46d21c/bench/nanobench.cpp#539.
126 void warmUp(int loops) const override {
127 // Pretty much the same deal as the calibration: do some warmup to make
128 // sure we're timing steady-state pipelined frames.
129 int maxFrameLag = computeMaxFrameLag();
130 for (int i = 0; i < maxFrameLag; i++) {
131 time(loops);
132 }
133 }
134
136 skia_private::TArray<double>* values) const override {
137 if (FLAGS_gpuStatsDump) {
138 // TODO cache stats
140 }
141 if (FLAGS_dmsaaStatsDump && fBenchmark->getDMSAAStats(getCanvas()->recordingContext())) {
142 const auto& dmsaaStats = getCanvas()->recordingContext()->priv().dmsaaStats();
143 dmsaaStats.dumpKeyValuePairs(keys, values);
144 dmsaaStats.dump();
145 combinedDMSAAStats.merge(dmsaaStats);
146 }
147 }
148
149 void printStats() const override {
150 if (FLAGS_gpuStats) {
151 auto context = fSurfaceManager->getGaneshContextInfo()->directContext();
152
153 context->priv().printCacheStats();
154 context->priv().printGpuStats();
155 context->priv().printContextStats();
156 }
157 }
158
159private:
160 // Based on nanobench's GPUTarget::needsFrameTiming():
161 // https://skia.googlesource.com/skia/+/a063eaeaf1e09e4d6f42e0f44a5723622a46d21c/bench/nanobench.cpp#264.
162 int computeMaxFrameLag() const {
163 int maxFrameLag;
164 if (!fSurfaceManager->getGaneshContextInfo()->testContext()->getMaxGpuFrameLag(
165 &maxFrameLag)) {
166 // Frame lag is unknown.
167 maxFrameLag = ESTIMATED_GPU_FRAME_LAG;
168 }
169 return maxFrameLag;
170 }
171};
172
173std::unique_ptr<BenchmarkTarget> BenchmarkTarget::FromConfig(std::string surfaceConfig,
175 SurfaceOptions surfaceOptions = {
176 .width = benchmark->getSize().width(),
177 .height = benchmark->getSize().height(),
178 .modifyGrContextOptions = [&](GrContextOptions* grContextOptions) {
179 benchmark->modifyGrContextOptions(grContextOptions);
180 }};
181 std::unique_ptr<SurfaceManager> surfaceManager =
182 SurfaceManager::FromConfig(surfaceConfig, surfaceOptions);
183 if (surfaceManager == nullptr) {
184 SK_ABORT("Unknown --surfaceConfig flag value: %s.", surfaceConfig.c_str());
185 }
186
187 if (surfaceManager->getGaneshContextInfo()->testContext()->fenceSyncSupport()) {
189 "WARNING: GL context for config \"%s\" does not support fence sync. "
190 "Timings might not be accurate.",
191 surfaceConfig.c_str());
192 }
193
194 return std::make_unique<GaneshBenchmarkTarget>(std::move(surfaceManager), benchmark);
195}
static constexpr int ESTIMATED_GPU_FRAME_LAG
GrRecordingContextPriv::DMSAAStats combinedDMSAAStats
static DEFINE_bool(gpuStats, false, "Print GPU stats after each GPU benchmark.")
static DEFINE_double(gpuMs, 5, "While auto-tuning the number of benchmark runs per sample, increase the number of runs " "until a single sample takes this many milliseconds. Do this for each benchmark.")
#define SK_ABORT(message,...)
Definition: SkAssert.h:70
virtual void setup() const
static std::unique_ptr< BenchmarkTarget > FromConfig(std::string surfaceConfig, Benchmark *benchmark)
std::unique_ptr< SurfaceManager > fSurfaceManager
Benchmark * fBenchmark
static void printGlobalStats()
double time(int loops) const
SkCanvas * getCanvas() const
virtual void getGpuStats(SkCanvas *, skia_private::TArray< SkString > *keys, skia_private::TArray< double > *values)
Definition: Benchmark.h:84
virtual bool getDMSAAStats(GrRecordingContext *)
Definition: Benchmark.h:89
void setup() const override
void printStats() const override
Benchmark::Backend getBackend() const override
void warmUp(int loops) const override
void onAfterDraw() const override
void dumpStats(skia_private::TArray< SkString > *keys, skia_private::TArray< double > *values) const override
std::tuple< int, bool > autoTuneLoops() const override
GaneshBenchmarkTarget(std::unique_ptr< SurfaceManager > surfaceManager, Benchmark *benchmark)
GrRecordingContextPriv priv()
virtual GrRecordingContext * recordingContext() const
Definition: SkCanvas.cpp:1637
static std::unique_ptr< SurfaceManager > FromConfig(std::string config, SurfaceOptions surfaceOptions)
void Log(const char *format,...) SK_PRINTF_LIKE(1
Definition: TestRunner.cpp:137
SIN Vec< N, float > ceil(const Vec< N, float > &x)
Definition: SkVx.h:702
Definition: ref_ptr.h:256