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