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