xref: /aosp_15_r20/external/skia/bench/SKPBench.cpp (revision c8dee2aa9b3f27cf6c858bd81872bdeb2c07ed17)
1 /*
2  * Copyright 2014 Google Inc.
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/GpuTools.h"
9 #include "bench/SKPBench.h"
10 #include "include/core/SkSurface.h"
11 #include "include/gpu/ganesh/GrDirectContext.h"
12 #include "include/gpu/ganesh/SkSurfaceGanesh.h"
13 #include "src/gpu/ganesh/GrDirectContextPriv.h"
14 #include "src/gpu/ganesh/GrGpu.h"
15 #include "tools/flags/CommandLineFlags.h"
16 
17 #if defined(SK_GRAPHITE)
18 #include "include/gpu/graphite/Context.h"
19 #include "include/gpu/graphite/Recorder.h"
20 #include "src/gpu/graphite/RecorderPriv.h"
21 #endif
22 
23 using namespace skia_private;
24 
25 // These CPU tile sizes are not good per se, but they are similar to what Chrome uses.
26 static DEFINE_int(CPUbenchTileW, 256, "Tile width  used for CPU SKP playback.");
27 static DEFINE_int(CPUbenchTileH, 256, "Tile height used for CPU SKP playback.");
28 
29 static DEFINE_int(GPUbenchTileW, 1600, "Tile width  used for GPU SKP playback.");
30 static DEFINE_int(GPUbenchTileH, 512, "Tile height used for GPU SKP playback.");
31 
SKPBench(const char * name,const SkPicture * pic,const SkIRect & clip,SkScalar scale,bool doLooping)32 SKPBench::SKPBench(const char* name, const SkPicture* pic, const SkIRect& clip, SkScalar scale,
33                    bool doLooping)
34     : fPic(SkRef(pic))
35     , fClip(clip)
36     , fScale(scale)
37     , fName(name)
38     , fDoLooping(doLooping) {
39     fUniqueName.printf("%s_%.2g", name, scale);  // Scale makes this unqiue for perf.skia.org traces.
40 }
41 
~SKPBench()42 SKPBench::~SKPBench() {
43     for (int i = 0; i < fSurfaces.size(); ++i) {
44         fSurfaces[i]->unref();
45     }
46 }
47 
onGetName()48 const char* SKPBench::onGetName() {
49     return fName.c_str();
50 }
51 
onGetUniqueName()52 const char* SKPBench::onGetUniqueName() {
53     return fUniqueName.c_str();
54 }
55 
onPerCanvasPreDraw(SkCanvas * canvas)56 void SKPBench::onPerCanvasPreDraw(SkCanvas* canvas) {
57     SkIRect bounds = canvas->getDeviceClipBounds();
58     bounds.intersect(fClip);
59     bounds.intersect(fPic->cullRect().roundOut());
60     SkAssertResult(!bounds.isEmpty());
61 
62 #if defined(SK_GRAPHITE)
63     const bool gpu = canvas->recordingContext() != nullptr || canvas->recorder() != nullptr;
64 #else
65     const bool gpu = canvas->recordingContext() != nullptr;
66 #endif
67     int tileW = gpu ? FLAGS_GPUbenchTileW : FLAGS_CPUbenchTileW,
68         tileH = gpu ? FLAGS_GPUbenchTileH : FLAGS_CPUbenchTileH;
69 
70     tileW = std::min(tileW, bounds.width());
71     tileH = std::min(tileH, bounds.height());
72 
73     int xTiles = SkScalarCeilToInt(bounds.width()  / SkIntToScalar(tileW));
74     int yTiles = SkScalarCeilToInt(bounds.height() / SkIntToScalar(tileH));
75 
76     fSurfaces.reserve_exact(fSurfaces.size() + (xTiles * yTiles));
77     fTileRects.reserve(xTiles * yTiles);
78 
79     SkImageInfo ii = canvas->imageInfo().makeWH(tileW, tileH);
80 
81     for (int y = bounds.fTop; y < bounds.fBottom; y += tileH) {
82         for (int x = bounds.fLeft; x < bounds.fRight; x += tileW) {
83             const SkIRect tileRect = SkIRect::MakeXYWH(x, y, tileW, tileH);
84             *fTileRects.append() = tileRect;
85             fSurfaces.emplace_back(canvas->makeSurface(ii));
86 
87             // Never want the contents of a tile to include stuff the parent
88             // canvas clips out
89             SkRect clip = SkRect::Make(bounds);
90             clip.offset(-SkIntToScalar(tileRect.fLeft), -SkIntToScalar(tileRect.fTop));
91             fSurfaces.back()->getCanvas()->clipRect(clip);
92 
93             fSurfaces.back()->getCanvas()->setMatrix(canvas->getLocalToDevice());
94             fSurfaces.back()->getCanvas()->scale(fScale, fScale);
95         }
96     }
97 }
98 
onPerCanvasPostDraw(SkCanvas * canvas)99 void SKPBench::onPerCanvasPostDraw(SkCanvas* canvas) {
100     // Draw the last set of tiles into the main canvas in case we're
101     // saving the images
102     for (int i = 0; i < fTileRects.size(); ++i) {
103         sk_sp<SkImage> image(fSurfaces[i]->makeImageSnapshot());
104         canvas->drawImage(image,
105                           SkIntToScalar(fTileRects[i].fLeft), SkIntToScalar(fTileRects[i].fTop));
106     }
107 
108     fSurfaces.clear();
109     fTileRects.clear();
110 }
111 
isSuitableFor(Backend backend)112 bool SKPBench::isSuitableFor(Backend backend) {
113     return backend != Backend::kNonRendering;
114 }
115 
onGetSize()116 SkISize SKPBench::onGetSize() {
117     return SkISize::Make(fClip.width(), fClip.height());
118 }
119 
onDraw(int loops,SkCanvas * canvas)120 void SKPBench::onDraw(int loops, SkCanvas* canvas) {
121     SkASSERT(fDoLooping || 1 == loops);
122     while (1) {
123         this->drawPicture();
124         if (0 == --loops) {
125             break;
126         }
127 
128         // Ensure the gpu backends don't combine ops/tasks across draw loops. Also submit each work
129         // to the gpu so we are measuring the cost of gpu submission and not amortizing one
130         // submission across all loops.
131         auto direct = canvas->recordingContext() ? canvas->recordingContext()->asDirectContext()
132                                                  : nullptr;
133         if (direct) {
134             direct->flushAndSubmit();
135         }
136 
137 #if defined(SK_GRAPHITE)
138         skgpu::graphite::Recorder* recorder = canvas->recorder();
139         if (recorder) {
140             std::unique_ptr<skgpu::graphite::Recording> recording = recorder->snap();
141             if (recording) {
142                 skgpu::graphite::InsertRecordingInfo info;
143                 info.fRecording = recording.get();
144                 skgpu::graphite::Context* context = recorder->priv().context();
145                 context->insertRecording(info);
146                 context->submit();
147             }
148         }
149 #endif
150     }
151 }
152 
drawMPDPicture()153 void SKPBench::drawMPDPicture() {
154     // TODO: remove me
155 }
156 
drawPicture()157 void SKPBench::drawPicture() {
158     for (int j = 0; j < fTileRects.size(); ++j) {
159         const SkMatrix trans = SkMatrix::Translate(-fTileRects[j].fLeft / fScale,
160                                                    -fTileRects[j].fTop / fScale);
161         fSurfaces[j]->getCanvas()->drawPicture(fPic.get(), &trans, nullptr);
162     }
163 
164     for (int j = 0; j < fTileRects.size(); ++j) {
165         skgpu::Flush(fSurfaces[j].get());
166     }
167 }
168 
draw_pic_for_stats(SkCanvas * canvas,GrDirectContext * dContext,const SkPicture * picture,TArray<SkString> * keys,TArray<double> * values)169 static void draw_pic_for_stats(SkCanvas* canvas,
170                                GrDirectContext* dContext,
171                                const SkPicture* picture,
172                                TArray<SkString>* keys,
173                                TArray<double>* values) {
174     dContext->priv().resetGpuStats();
175     dContext->priv().resetContextStats();
176     canvas->drawPicture(picture);
177     dContext->flush();
178 
179     dContext->priv().dumpGpuStatsKeyValuePairs(keys, values);
180     dContext->priv().dumpCacheStatsKeyValuePairs(keys, values);
181     dContext->priv().dumpContextStatsKeyValuePairs(keys, values);
182 }
183 
getGpuStats(SkCanvas * canvas,TArray<SkString> * keys,TArray<double> * values)184 void SKPBench::getGpuStats(SkCanvas* canvas, TArray<SkString>* keys, TArray<double>* values) {
185     // we do a special single draw and then dump the key / value pairs
186     auto direct = canvas->recordingContext() ? canvas->recordingContext()->asDirectContext()
187                                              : nullptr;
188     if (!direct) {
189         return;
190     }
191 
192     // TODO refactor this out if we want to test other subclasses of skpbench
193     direct->flushAndSubmit();
194     direct->freeGpuResources();
195     direct->resetContext();
196     direct->priv().getGpu()->resetShaderCacheForTesting();
197     draw_pic_for_stats(canvas, direct, fPic.get(), keys, values);
198 }
199 
getDMSAAStats(GrRecordingContext * rContext)200 bool SKPBench::getDMSAAStats(GrRecordingContext* rContext) {
201     if (!rContext || !rContext->asDirectContext()) {
202         return false;
203     }
204     // Clear the current DMSAA stats then do a single tiled draw that resets them to the specific
205     // values for our SKP.
206     rContext->asDirectContext()->flushAndSubmit();
207     rContext->priv().dmsaaStats() = {};
208     this->drawPicture();  // Draw tiled for DMSAA stats.
209     rContext->asDirectContext()->flush();
210     return true;
211 }
212