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
onDrawFrame(int loops,SkCanvas * canvas,std::function<void ()> submitFrame)120 void SKPBench::onDrawFrame(int loops, SkCanvas* canvas, std::function<void()> submitFrame) {
121 SkASSERT(fDoLooping || 1 == loops);
122 for (int i = 0; i < loops; ++i) {
123 this->drawPicture();
124 if (submitFrame) {
125 submitFrame();
126 }
127 }
128 }
129
drawMPDPicture()130 void SKPBench::drawMPDPicture() {
131 // TODO: remove me
132 }
133
drawPicture()134 void SKPBench::drawPicture() {
135 for (int j = 0; j < fTileRects.size(); ++j) {
136 const SkMatrix trans = SkMatrix::Translate(-fTileRects[j].fLeft / fScale,
137 -fTileRects[j].fTop / fScale);
138 fSurfaces[j]->getCanvas()->drawPicture(fPic.get(), &trans, nullptr);
139 }
140
141 for (int j = 0; j < fTileRects.size(); ++j) {
142 skgpu::Flush(fSurfaces[j].get());
143 }
144 }
145
draw_pic_for_stats(SkCanvas * canvas,GrDirectContext * dContext,const SkPicture * picture,TArray<SkString> * keys,TArray<double> * values)146 static void draw_pic_for_stats(SkCanvas* canvas,
147 GrDirectContext* dContext,
148 const SkPicture* picture,
149 TArray<SkString>* keys,
150 TArray<double>* values) {
151 dContext->priv().resetGpuStats();
152 dContext->priv().resetContextStats();
153 canvas->drawPicture(picture);
154 dContext->flush();
155
156 dContext->priv().dumpGpuStatsKeyValuePairs(keys, values);
157 dContext->priv().dumpCacheStatsKeyValuePairs(keys, values);
158 dContext->priv().dumpContextStatsKeyValuePairs(keys, values);
159 }
160
getGpuStats(SkCanvas * canvas,TArray<SkString> * keys,TArray<double> * values)161 void SKPBench::getGpuStats(SkCanvas* canvas, TArray<SkString>* keys, TArray<double>* values) {
162 // we do a special single draw and then dump the key / value pairs
163 auto direct = canvas->recordingContext() ? canvas->recordingContext()->asDirectContext()
164 : nullptr;
165 if (!direct) {
166 return;
167 }
168
169 // TODO refactor this out if we want to test other subclasses of skpbench
170 direct->flushAndSubmit();
171 direct->freeGpuResources();
172 direct->resetContext();
173 direct->priv().getGpu()->resetShaderCacheForTesting();
174 draw_pic_for_stats(canvas, direct, fPic.get(), keys, values);
175 }
176
getDMSAAStats(GrRecordingContext * rContext)177 bool SKPBench::getDMSAAStats(GrRecordingContext* rContext) {
178 if (!rContext || !rContext->asDirectContext()) {
179 return false;
180 }
181 // Clear the current DMSAA stats then do a single tiled draw that resets them to the specific
182 // values for our SKP.
183 rContext->asDirectContext()->flushAndSubmit();
184 rContext->priv().dmsaaStats() = {};
185 this->drawPicture(); // Draw tiled for DMSAA stats.
186 rContext->asDirectContext()->flush();
187 return true;
188 }
189