xref: /aosp_15_r20/external/skia/tools/testrunners/benchmark/target/GaneshBenchmarkTarget.cpp (revision c8dee2aa9b3f27cf6c858bd81872bdeb2c07ed17)
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