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