1 /*
2 * Copyright 2015 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 "src/core/SkStrike.h"
9
10 #include "bench/Benchmark.h"
11 #include "include/core/SkCanvas.h"
12 #include "include/core/SkColorSpace.h"
13 #include "include/core/SkFontMgr.h"
14 #include "include/core/SkGraphics.h"
15 #include "include/core/SkTypeface.h"
16 #include "include/private/chromium/SkChromeRemoteGlyphCache.h"
17 #include "src/base/SkTLazy.h"
18 #include "src/core/SkStrikeSpec.h"
19 #include "src/core/SkTaskGroup.h"
20 #include "tools/Resources.h"
21 #include "tools/ToolUtils.h"
22 #include "tools/fonts/FontToolUtils.h"
23 #include "tools/text/SkTextBlobTrace.h"
24
25 using namespace skia_private;
26
do_font_stuff(SkFont * font)27 static void do_font_stuff(SkFont* font) {
28 SkPaint defaultPaint;
29 for (SkScalar i = 8; i < 64; i++) {
30 font->setSize(i);
31 auto strikeSpec = SkStrikeSpec::MakeMask(
32 *font, defaultPaint, SkSurfaceProps(0, kUnknown_SkPixelGeometry),
33 SkScalerContextFlags::kNone, SkMatrix::I());
34 SkPackedGlyphID glyphs['z'];
35 for (int c = ' '; c < 'z'; c++) {
36 glyphs[c] = SkPackedGlyphID{font->unicharToGlyph(c)};
37 }
38 constexpr size_t glyphCount = 'z' - ' ';
39 SkSpan<const SkPackedGlyphID> glyphIDs{&glyphs[SkTo<int>(' ')], glyphCount};
40 SkBulkGlyphMetricsAndImages images{strikeSpec};
41 for (int lookups = 0; lookups < 10; lookups++) {
42 (void)images.glyphs(glyphIDs);
43 }
44 }
45 }
46
47 class SkGlyphCacheBasic : public Benchmark {
48 public:
SkGlyphCacheBasic(size_t cacheSize)49 explicit SkGlyphCacheBasic(size_t cacheSize) : fCacheSize(cacheSize) { }
50
51 protected:
onGetName()52 const char* onGetName() override {
53 fName.printf("SkGlyphCacheBasic%dK", (int)(fCacheSize >> 10));
54 return fName.c_str();
55 }
56
isSuitableFor(Backend backend)57 bool isSuitableFor(Backend backend) override {
58 return backend == Backend::kNonRendering;
59 }
60
onDraw(int loops,SkCanvas *)61 void onDraw(int loops, SkCanvas*) override {
62 size_t oldCacheLimitSize = SkGraphics::GetFontCacheLimit();
63 SkGraphics::SetFontCacheLimit(fCacheSize);
64 SkFont font = ToolUtils::DefaultFont();
65 font.setEdging(SkFont::Edging::kAntiAlias);
66 font.setSubpixel(true);
67 font.setTypeface(ToolUtils::CreatePortableTypeface("serif", SkFontStyle::Italic()));
68
69 for (int work = 0; work < loops; work++) {
70 do_font_stuff(&font);
71 }
72 SkGraphics::SetFontCacheLimit(oldCacheLimitSize);
73 }
74
75 private:
76 using INHERITED = Benchmark;
77 const size_t fCacheSize;
78 SkString fName;
79 };
80
81 class SkGlyphCacheStressTest : public Benchmark {
82 public:
SkGlyphCacheStressTest(int cacheSize)83 explicit SkGlyphCacheStressTest(int cacheSize) : fCacheSize(cacheSize) { }
84
85 protected:
onGetName()86 const char* onGetName() override {
87 fName.printf("SkGlyphCacheStressTest%dK", (int)(fCacheSize >> 10));
88 return fName.c_str();
89 }
90
isSuitableFor(Backend backend)91 bool isSuitableFor(Backend backend) override {
92 return backend == Backend::kNonRendering;
93 }
94
onDraw(int loops,SkCanvas *)95 void onDraw(int loops, SkCanvas*) override {
96 size_t oldCacheLimitSize = SkGraphics::GetFontCacheLimit();
97 SkGraphics::SetFontCacheLimit(fCacheSize);
98 sk_sp<SkTypeface> typefaces[] = {
99 ToolUtils::CreatePortableTypeface("serif", SkFontStyle::Italic()),
100 ToolUtils::CreatePortableTypeface("sans-serif", SkFontStyle::Italic())};
101
102 for (int work = 0; work < loops; work++) {
103 SkTaskGroup().batch(16, [&](int threadIndex) {
104 SkFont font = ToolUtils::DefaultFont();
105 font.setEdging(SkFont::Edging::kAntiAlias);
106 font.setSubpixel(true);
107 font.setTypeface(typefaces[threadIndex % 2]);
108 do_font_stuff(&font);
109 });
110 }
111 SkGraphics::SetFontCacheLimit(oldCacheLimitSize);
112 }
113
114 private:
115 using INHERITED = Benchmark;
116 const size_t fCacheSize;
117 SkString fName;
118 };
119
120 DEF_BENCH( return new SkGlyphCacheBasic(256 * 1024); )
121 DEF_BENCH( return new SkGlyphCacheBasic(32 * 1024 * 1024); )
122 DEF_BENCH( return new SkGlyphCacheStressTest(256 * 1024); )
123 DEF_BENCH( return new SkGlyphCacheStressTest(32 * 1024 * 1024); )
124
125 namespace {
126 class DiscardableManager : public SkStrikeServer::DiscardableHandleManager,
127 public SkStrikeClient::DiscardableHandleManager {
128 public:
DiscardableManager()129 DiscardableManager() { sk_bzero(&fCacheMissCount, sizeof(fCacheMissCount)); }
130 ~DiscardableManager() override = default;
131
132 // Server implementation.
createHandle()133 SkDiscardableHandleId createHandle() override {
134 SkAutoMutexExclusive l(fMutex);
135
136 // Handles starts as locked.
137 fLockedHandles.add(++fNextHandleId);
138 return fNextHandleId;
139 }
lockHandle(SkDiscardableHandleId id)140 bool lockHandle(SkDiscardableHandleId id) override {
141 SkAutoMutexExclusive l(fMutex);
142
143 if (id <= fLastDeletedHandleId) return false;
144 fLockedHandles.add(id);
145 return true;
146 }
147
148 // Client implementation.
deleteHandle(SkDiscardableHandleId id)149 bool deleteHandle(SkDiscardableHandleId id) override {
150 SkAutoMutexExclusive l(fMutex);
151
152 return id <= fLastDeletedHandleId;
153 }
154
notifyCacheMiss(SkStrikeClient::CacheMissType type,int fontSize)155 void notifyCacheMiss(SkStrikeClient::CacheMissType type, int fontSize) override {
156 SkAutoMutexExclusive l(fMutex);
157
158 fCacheMissCount[type]++;
159 }
isHandleDeleted(SkDiscardableHandleId id)160 bool isHandleDeleted(SkDiscardableHandleId id) override {
161 SkAutoMutexExclusive l(fMutex);
162
163 return id <= fLastDeletedHandleId;
164 }
165
unlockAll()166 void unlockAll() {
167 SkAutoMutexExclusive l(fMutex);
168
169 fLockedHandles.reset();
170 }
unlockAndDeleteAll()171 void unlockAndDeleteAll() {
172 SkAutoMutexExclusive l(fMutex);
173
174 fLockedHandles.reset();
175 fLastDeletedHandleId = fNextHandleId;
176 }
lockedHandles() const177 const THashSet<SkDiscardableHandleId>& lockedHandles() const {
178 SkAutoMutexExclusive l(fMutex);
179
180 return fLockedHandles;
181 }
handleCount()182 SkDiscardableHandleId handleCount() {
183 SkAutoMutexExclusive l(fMutex);
184
185 return fNextHandleId;
186 }
cacheMissCount(uint32_t type)187 int cacheMissCount(uint32_t type) {
188 SkAutoMutexExclusive l(fMutex);
189
190 return fCacheMissCount[type];
191 }
hasCacheMiss() const192 bool hasCacheMiss() const {
193 SkAutoMutexExclusive l(fMutex);
194
195 for (uint32_t i = 0; i <= SkStrikeClient::CacheMissType::kLast; ++i) {
196 if (fCacheMissCount[i] > 0) return true;
197 }
198 return false;
199 }
resetCacheMissCounts()200 void resetCacheMissCounts() {
201 SkAutoMutexExclusive l(fMutex);
202 sk_bzero(&fCacheMissCount, sizeof(fCacheMissCount));
203 }
204
205 private:
206 // The tests below run in parallel on multiple threads and use the same
207 // process global SkStrikeCache. So the implementation needs to be
208 // thread-safe.
209 mutable SkMutex fMutex;
210
211 SkDiscardableHandleId fNextHandleId = 0u;
212 SkDiscardableHandleId fLastDeletedHandleId = 0u;
213 THashSet<SkDiscardableHandleId> fLockedHandles;
214 int fCacheMissCount[SkStrikeClient::CacheMissType::kLast + 1u];
215 };
216
217 class DiffCanvasBench : public Benchmark {
218 SkString fBenchName;
219 std::function<std::unique_ptr<SkStreamAsset>()> fDataProvider;
220 std::vector<SkTextBlobTrace::Record> fTrace;
221 sk_sp<DiscardableManager> fDiscardableManager;
222 SkTLazy<SkStrikeServer> fServer;
223
onGetName()224 const char* onGetName() override { return fBenchName.c_str(); }
225
isSuitableFor(Backend b)226 bool isSuitableFor(Backend b) override { return b == Backend::kNonRendering; }
227
onDraw(int loops,SkCanvas * modelCanvas)228 void onDraw(int loops, SkCanvas* modelCanvas) override {
229 SkSurfaceProps props;
230 if (modelCanvas) { modelCanvas->getProps(&props); }
231 std::unique_ptr<SkCanvas> canvas = fServer->makeAnalysisCanvas(1024, 1024, props,
232 nullptr, true, true);
233 loops *= 100;
234 while (loops --> 0) {
235 for (const auto& record : fTrace) {
236 canvas->drawTextBlob(
237 record.blob.get(), record.offset.x(), record.offset.y(),record.paint);
238 }
239 }
240 }
241
onDelayedSetup()242 void onDelayedSetup() override {
243 auto stream = fDataProvider();
244 fDiscardableManager = sk_make_sp<DiscardableManager>();
245 fServer.init(fDiscardableManager.get());
246 fTrace = SkTextBlobTrace::CreateBlobTrace(stream.get(), nullptr);
247 }
248
249 public:
DiffCanvasBench(SkString n,std::function<std::unique_ptr<SkStreamAsset> ()> f)250 DiffCanvasBench(SkString n, std::function<std::unique_ptr<SkStreamAsset>()> f)
251 : fBenchName(std::move(n)), fDataProvider(std::move(f)) {}
252 };
253 } // namespace
254
CreateDiffCanvasBench(SkString name,std::function<std::unique_ptr<SkStreamAsset> ()> dataSrc)255 Benchmark* CreateDiffCanvasBench(
256 SkString name, std::function<std::unique_ptr<SkStreamAsset>()> dataSrc) {
257 return new DiffCanvasBench(std::move(name), std::move(dataSrc));
258 }
259
260 DEF_BENCH( return CreateDiffCanvasBench(
261 SkString("SkDiffBench-lorem_ipsum"),
__anond70192340302()262 [](){ return GetResourceAsStream("diff_canvas_traces/lorem_ipsum.trace"); }));
263