blob: 7370bdafdbcc8b1c743cf7944b85d2bc9843d240 [file] [log] [blame]
Khushal38a08432018-05-02 10:29:37 -07001/*
2 * Copyright 2018 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
Khushal51371a42018-05-17 10:41:40 -07008#include "SkDraw.h"
Khushal38a08432018-05-02 10:29:37 -07009#include "SkGraphics.h"
10#include "SkMutex.h"
11#include "SkRemoteGlyphCache.h"
12#include "SkStrikeCache.h"
13#include "SkSurface.h"
14#include "SkTextBlob.h"
15#include "SkTypeface_remote.h"
16#include "Test.h"
17
Khushal3e7548c2018-05-23 15:45:01 -070018#if SK_SUPPORT_GPU
Herb Derby26cbe512018-05-24 14:39:01 -040019#include "text/GrTextContext.h"
Khushal3e7548c2018-05-23 15:45:01 -070020#endif
21
Khushal38a08432018-05-02 10:29:37 -070022class DiscardableManager : public SkStrikeServer::DiscardableHandleManager,
23 public SkStrikeClient::DiscardableHandleManager {
24public:
Khushald4160832018-05-23 18:16:00 -070025 DiscardableManager() { sk_bzero(&fCacheMissCount, sizeof(fCacheMissCount)); }
Khushal38a08432018-05-02 10:29:37 -070026 ~DiscardableManager() override = default;
27
28 // Server implementation.
29 SkDiscardableHandleId createHandle() override {
30 // Handles starts as locked.
31 fLockedHandles.add(++fNextHandleId);
32 return fNextHandleId;
33 }
34 bool lockHandle(SkDiscardableHandleId id) override {
35 if (id <= fLastDeletedHandleId) return false;
36 fLockedHandles.add(id);
37 return true;
38 }
39
40 // Client implementation.
41 bool deleteHandle(SkDiscardableHandleId id) override { return id <= fLastDeletedHandleId; }
Khushald4160832018-05-23 18:16:00 -070042 void NotifyCacheMiss(SkStrikeClient::CacheMissType type) override { fCacheMissCount[type]++; }
Khushal38a08432018-05-02 10:29:37 -070043
44 void unlockAll() { fLockedHandles.reset(); }
45 void unlockAndDeleteAll() {
46 unlockAll();
47 fLastDeletedHandleId = fNextHandleId;
48 }
49 const SkTHashSet<SkDiscardableHandleId>& lockedHandles() const { return fLockedHandles; }
50 SkDiscardableHandleId handleCount() { return fNextHandleId; }
Khushald4160832018-05-23 18:16:00 -070051 int cacheMissCount(SkStrikeClient::CacheMissType type) { return fCacheMissCount[type]; }
Khushal38a08432018-05-02 10:29:37 -070052
53private:
54 SkDiscardableHandleId fNextHandleId = 0u;
55 SkDiscardableHandleId fLastDeletedHandleId = 0u;
56 SkTHashSet<SkDiscardableHandleId> fLockedHandles;
Khushald4160832018-05-23 18:16:00 -070057 int fCacheMissCount[SkStrikeClient::CacheMissType::kLast + 1u];
Khushal38a08432018-05-02 10:29:37 -070058};
59
60sk_sp<SkTextBlob> buildTextBlob(sk_sp<SkTypeface> tf, int glyphCount) {
61 SkPaint font;
62 font.setTypeface(tf);
63 font.setTextEncoding(SkPaint::kGlyphID_TextEncoding);
64 font.setTextAlign(SkPaint::kLeft_Align);
65 font.setStyle(SkPaint::kFill_Style);
66 font.setHinting(SkPaint::kNormal_Hinting);
67 font.setTextSize(1u);
68
69 SkTextBlobBuilder builder;
70 SkRect bounds = SkRect::MakeWH(10, 10);
71 const auto& runBuffer = builder.allocRunPosH(font, glyphCount, 0, &bounds);
72 SkASSERT(runBuffer.utf8text == nullptr);
73 SkASSERT(runBuffer.clusters == nullptr);
74
75 for (int i = 0; i < glyphCount; i++) {
76 runBuffer.glyphs[i] = static_cast<SkGlyphID>(i);
77 runBuffer.pos[i] = SkIntToScalar(i);
78 }
79 return builder.make();
80}
81
Khushal51371a42018-05-17 10:41:40 -070082#define COMPARE_BLOBS(expected, actual, reporter) \
83 for (int i = 0; i < expected.width(); ++i) { \
84 for (int j = 0; j < expected.height(); ++j) { \
85 REPORTER_ASSERT(reporter, expected.getColor(i, j) == actual.getColor(i, j)); \
86 } \
87 }
88
Khushal3e7548c2018-05-23 15:45:01 -070089#if SK_SUPPORT_GPU
90SkTextBlobCacheDiffCanvas::Settings MakeSettings(GrContext* context) {
91 SkTextBlobCacheDiffCanvas::Settings settings;
92 settings.fContextSupportsDistanceFieldText = context->supportsDistanceFieldText();
93 return settings;
94}
95
96SkBitmap RasterBlob(sk_sp<SkTextBlob> blob, int width, int height, const SkPaint& paint,
97 GrContext* context, const SkMatrix* matrix = nullptr) {
98 const SkImageInfo info =
99 SkImageInfo::Make(width, height, kN32_SkColorType, kPremul_SkAlphaType);
100 auto surface = SkSurface::MakeRenderTarget(context, SkBudgeted::kNo, info);
101 if (matrix) surface->getCanvas()->concat(*matrix);
Khushal38a08432018-05-02 10:29:37 -0700102 surface->getCanvas()->drawTextBlob(blob.get(), 0u, 0u, paint);
103 SkBitmap bitmap;
104 bitmap.allocN32Pixels(width, height);
105 surface->readPixels(bitmap, 0, 0);
106 return bitmap;
107}
Khushal3e7548c2018-05-23 15:45:01 -0700108#endif
Khushal38a08432018-05-02 10:29:37 -0700109
110DEF_TEST(SkRemoteGlyphCache_TypefaceSerialization, reporter) {
111 sk_sp<DiscardableManager> discardableManager = sk_make_sp<DiscardableManager>();
112 SkStrikeServer server(discardableManager.get());
113 SkStrikeClient client(discardableManager);
114
115 auto server_tf = SkTypeface::MakeDefault();
116 auto tf_data = server.serializeTypeface(server_tf.get());
117
118 auto client_tf = client.deserializeTypeface(tf_data->data(), tf_data->size());
119 REPORTER_ASSERT(reporter, client_tf);
Khushald4160832018-05-23 18:16:00 -0700120 REPORTER_ASSERT(reporter, static_cast<SkTypefaceProxy*>(client_tf.get())->remoteTypefaceID() ==
Khushal38a08432018-05-02 10:29:37 -0700121 server_tf->uniqueID());
Khushal3e7548c2018-05-23 15:45:01 -0700122
123 // Must unlock everything on termination, otherwise valgrind complains about memory leaks.
124 discardableManager->unlockAndDeleteAll();
Khushal38a08432018-05-02 10:29:37 -0700125}
126
Khushal3e7548c2018-05-23 15:45:01 -0700127#if SK_SUPPORT_GPU
128DEF_GPUTEST_FOR_RENDERING_CONTEXTS(SkRemoteGlyphCache_StrikeSerialization, reporter, ctxInfo) {
Khushal38a08432018-05-02 10:29:37 -0700129 sk_sp<DiscardableManager> discardableManager = sk_make_sp<DiscardableManager>();
130 SkStrikeServer server(discardableManager.get());
131 SkStrikeClient client(discardableManager);
Khushal51371a42018-05-17 10:41:40 -0700132 const SkPaint paint;
Khushal38a08432018-05-02 10:29:37 -0700133
134 // Server.
135 auto serverTf = SkTypeface::MakeFromName("monospace", SkFontStyle());
136 auto serverTfData = server.serializeTypeface(serverTf.get());
137
138 int glyphCount = 10;
139 auto serverBlob = buildTextBlob(serverTf, glyphCount);
140 const SkSurfaceProps props(SkSurfaceProps::kLegacyFontHost_InitType);
Khushal3e7548c2018-05-23 15:45:01 -0700141 SkTextBlobCacheDiffCanvas cache_diff_canvas(10, 10, SkMatrix::I(), props, &server,
142 MakeSettings(ctxInfo.grContext()));
Khushal38a08432018-05-02 10:29:37 -0700143 cache_diff_canvas.drawTextBlob(serverBlob.get(), 0, 0, paint);
144
145 std::vector<uint8_t> serverStrikeData;
146 server.writeStrikeData(&serverStrikeData);
147
148 // Client.
149 auto clientTf = client.deserializeTypeface(serverTfData->data(), serverTfData->size());
150 REPORTER_ASSERT(reporter,
151 client.readStrikeData(serverStrikeData.data(), serverStrikeData.size()));
152 auto clientBlob = buildTextBlob(clientTf, glyphCount);
153
Khushal3e7548c2018-05-23 15:45:01 -0700154 SkBitmap expected = RasterBlob(serverBlob, 10, 10, paint, ctxInfo.grContext());
155 SkBitmap actual = RasterBlob(clientBlob, 10, 10, paint, ctxInfo.grContext());
Khushal51371a42018-05-17 10:41:40 -0700156 COMPARE_BLOBS(expected, actual, reporter);
Khushal3e7548c2018-05-23 15:45:01 -0700157
158 // Must unlock everything on termination, otherwise valgrind complains about memory leaks.
159 discardableManager->unlockAndDeleteAll();
Khushal38a08432018-05-02 10:29:37 -0700160}
Khushal3e7548c2018-05-23 15:45:01 -0700161#endif
Khushal38a08432018-05-02 10:29:37 -0700162
163DEF_TEST(SkRemoteGlyphCache_StrikeLockingServer, reporter) {
164 sk_sp<DiscardableManager> discardableManager = sk_make_sp<DiscardableManager>();
165 SkStrikeServer server(discardableManager.get());
166 SkStrikeClient client(discardableManager);
167
168 auto serverTf = SkTypeface::MakeFromName("monospace", SkFontStyle());
169 server.serializeTypeface(serverTf.get());
170 int glyphCount = 10;
171 auto serverBlob = buildTextBlob(serverTf, glyphCount);
172
173 const SkSurfaceProps props(SkSurfaceProps::kLegacyFontHost_InitType);
174 SkTextBlobCacheDiffCanvas cache_diff_canvas(10, 10, SkMatrix::I(), props, &server);
175 SkPaint paint;
176 cache_diff_canvas.drawTextBlob(serverBlob.get(), 0, 0, paint);
177
178 // The strike from the blob should be locked after it has been drawn on the canvas.
179 REPORTER_ASSERT(reporter, discardableManager->handleCount() == 1u);
180 REPORTER_ASSERT(reporter, discardableManager->lockedHandles().count() == 1u);
181
182 // Write the strike data and unlock everything. Re-analyzing the blob should lock the handle
183 // again.
184 std::vector<uint8_t> fontData;
185 server.writeStrikeData(&fontData);
186 discardableManager->unlockAll();
187 REPORTER_ASSERT(reporter, discardableManager->lockedHandles().count() == 0u);
188
189 cache_diff_canvas.drawTextBlob(serverBlob.get(), 0, 0, paint);
190 REPORTER_ASSERT(reporter, discardableManager->handleCount() == 1u);
191 REPORTER_ASSERT(reporter, discardableManager->lockedHandles().count() == 1u);
Khushal3e7548c2018-05-23 15:45:01 -0700192
193 // Must unlock everything on termination, otherwise valgrind complains about memory leaks.
194 discardableManager->unlockAndDeleteAll();
Khushal38a08432018-05-02 10:29:37 -0700195}
196
197DEF_TEST(SkRemoteGlyphCache_StrikeDeletionServer, reporter) {
198 sk_sp<DiscardableManager> discardableManager = sk_make_sp<DiscardableManager>();
199 SkStrikeServer server(discardableManager.get());
200 SkStrikeClient client(discardableManager);
201
202 auto serverTf = SkTypeface::MakeFromName("monospace", SkFontStyle());
203 server.serializeTypeface(serverTf.get());
204 int glyphCount = 10;
205 auto serverBlob = buildTextBlob(serverTf, glyphCount);
206
207 const SkSurfaceProps props(SkSurfaceProps::kLegacyFontHost_InitType);
208 SkTextBlobCacheDiffCanvas cache_diff_canvas(10, 10, SkMatrix::I(), props, &server);
209 SkPaint paint;
210 cache_diff_canvas.drawTextBlob(serverBlob.get(), 0, 0, paint);
211 REPORTER_ASSERT(reporter, discardableManager->handleCount() == 1u);
212
213 // Write the strike data and delete all the handles. Re-analyzing the blob should create new
214 // handles.
215 std::vector<uint8_t> fontData;
216 server.writeStrikeData(&fontData);
217 discardableManager->unlockAndDeleteAll();
218 cache_diff_canvas.drawTextBlob(serverBlob.get(), 0, 0, paint);
Khushal38a08432018-05-02 10:29:37 -0700219 REPORTER_ASSERT(reporter, discardableManager->handleCount() == 2u);
Khushal3e7548c2018-05-23 15:45:01 -0700220
221 // Must unlock everything on termination, otherwise valgrind complains about memory leaks.
222 discardableManager->unlockAndDeleteAll();
Khushal38a08432018-05-02 10:29:37 -0700223}
224
225DEF_TEST(SkRemoteGlyphCache_StrikePinningClient, reporter) {
226 sk_sp<DiscardableManager> discardableManager = sk_make_sp<DiscardableManager>();
227 SkStrikeServer server(discardableManager.get());
228 SkStrikeClient client(discardableManager);
229
230 // Server.
231 auto serverTf = SkTypeface::MakeFromName("monospace", SkFontStyle());
232 auto serverTfData = server.serializeTypeface(serverTf.get());
233
234 int glyphCount = 10;
235 auto serverBlob = buildTextBlob(serverTf, glyphCount);
236
237 const SkSurfaceProps props(SkSurfaceProps::kLegacyFontHost_InitType);
238 SkTextBlobCacheDiffCanvas cache_diff_canvas(10, 10, SkMatrix::I(), props, &server);
239 SkPaint paint;
240 cache_diff_canvas.drawTextBlob(serverBlob.get(), 0, 0, paint);
241
242 std::vector<uint8_t> serverStrikeData;
243 server.writeStrikeData(&serverStrikeData);
244
245 // Client.
246 REPORTER_ASSERT(reporter,
247 client.readStrikeData(serverStrikeData.data(), serverStrikeData.size()));
248 auto* clientTf = client.deserializeTypeface(serverTfData->data(), serverTfData->size()).get();
249
250 // The cache remains alive until it is pinned in the discardable manager.
251 SkGraphics::PurgeFontCache();
252 REPORTER_ASSERT(reporter, !clientTf->unique());
253
254 // Once the strike is unpinned and purged, SkStrikeClient should be the only owner of the
255 // clientTf.
256 discardableManager->unlockAndDeleteAll();
257 SkGraphics::PurgeFontCache();
258 REPORTER_ASSERT(reporter, clientTf->unique());
Khushal3e7548c2018-05-23 15:45:01 -0700259
260 // Must unlock everything on termination, otherwise valgrind complains about memory leaks.
261 discardableManager->unlockAndDeleteAll();
Khushal38a08432018-05-02 10:29:37 -0700262}
Khushalb2e71272018-05-15 12:59:48 -0700263
264DEF_TEST(SkRemoteGlyphCache_ClientMemoryAccounting, reporter) {
265 sk_sp<DiscardableManager> discardableManager = sk_make_sp<DiscardableManager>();
266 SkStrikeServer server(discardableManager.get());
267 SkStrikeClient client(discardableManager);
268
269 // Server.
270 auto serverTf = SkTypeface::MakeFromName("monospace", SkFontStyle());
271 auto serverTfData = server.serializeTypeface(serverTf.get());
272
273 int glyphCount = 10;
274 auto serverBlob = buildTextBlob(serverTf, glyphCount);
275
276 const SkSurfaceProps props(SkSurfaceProps::kLegacyFontHost_InitType);
277 SkTextBlobCacheDiffCanvas cache_diff_canvas(10, 10, SkMatrix::I(), props, &server);
278 SkPaint paint;
279 cache_diff_canvas.drawTextBlob(serverBlob.get(), 0, 0, paint);
280
281 std::vector<uint8_t> serverStrikeData;
282 server.writeStrikeData(&serverStrikeData);
283
284 // Client.
285 REPORTER_ASSERT(reporter,
286 client.readStrikeData(serverStrikeData.data(), serverStrikeData.size()));
287 SkStrikeCache::Validate();
Khushal3e7548c2018-05-23 15:45:01 -0700288
289 // Must unlock everything on termination, otherwise valgrind complains about memory leaks.
290 discardableManager->unlockAndDeleteAll();
Khushalb2e71272018-05-15 12:59:48 -0700291}
Khushal51371a42018-05-17 10:41:40 -0700292
Khushal3e7548c2018-05-23 15:45:01 -0700293#if SK_SUPPORT_GPU
294DEF_GPUTEST_FOR_RENDERING_CONTEXTS(SkRemoteGlyphCache_DrawTextAsPath, reporter, ctxInfo) {
Khushal51371a42018-05-17 10:41:40 -0700295 sk_sp<DiscardableManager> discardableManager = sk_make_sp<DiscardableManager>();
296 SkStrikeServer server(discardableManager.get());
297 SkStrikeClient client(discardableManager);
298 SkPaint paint;
299 paint.setStyle(SkPaint::kStroke_Style);
300 paint.setStrokeWidth(0);
301 REPORTER_ASSERT(reporter, SkDraw::ShouldDrawTextAsPaths(paint, SkMatrix::I()));
302
303 // Server.
304 auto serverTf = SkTypeface::MakeFromName("monospace", SkFontStyle());
305 auto serverTfData = server.serializeTypeface(serverTf.get());
306
307 int glyphCount = 10;
308 auto serverBlob = buildTextBlob(serverTf, glyphCount);
309 const SkSurfaceProps props(SkSurfaceProps::kLegacyFontHost_InitType);
Khushal3e7548c2018-05-23 15:45:01 -0700310 SkTextBlobCacheDiffCanvas cache_diff_canvas(10, 10, SkMatrix::I(), props, &server,
311 MakeSettings(ctxInfo.grContext()));
Khushal51371a42018-05-17 10:41:40 -0700312 cache_diff_canvas.drawTextBlob(serverBlob.get(), 0, 0, paint);
313
314 std::vector<uint8_t> serverStrikeData;
315 server.writeStrikeData(&serverStrikeData);
316
317 // Client.
318 auto clientTf = client.deserializeTypeface(serverTfData->data(), serverTfData->size());
319 REPORTER_ASSERT(reporter,
320 client.readStrikeData(serverStrikeData.data(), serverStrikeData.size()));
321 auto clientBlob = buildTextBlob(clientTf, glyphCount);
322
Khushal3e7548c2018-05-23 15:45:01 -0700323 SkBitmap expected = RasterBlob(serverBlob, 10, 10, paint, ctxInfo.grContext());
324 SkBitmap actual = RasterBlob(clientBlob, 10, 10, paint, ctxInfo.grContext());
Khushal51371a42018-05-17 10:41:40 -0700325 COMPARE_BLOBS(expected, actual, reporter);
326 SkStrikeCache::Validate();
Khushal3e7548c2018-05-23 15:45:01 -0700327
328 // Must unlock everything on termination, otherwise valgrind complains about memory leaks.
329 discardableManager->unlockAndDeleteAll();
Khushal51371a42018-05-17 10:41:40 -0700330}
Khushal3e7548c2018-05-23 15:45:01 -0700331#endif
332
333#if SK_SUPPORT_GPU
334DEF_GPUTEST_FOR_RENDERING_CONTEXTS(SkRemoteGlyphCache_DrawTextAsDFT, reporter, ctxInfo) {
335 sk_sp<DiscardableManager> discardableManager = sk_make_sp<DiscardableManager>();
336 SkStrikeServer server(discardableManager.get());
337 SkStrikeClient client(discardableManager);
338 SkPaint paint;
339
340 // A perspective transform forces fallback to dft.
341 SkMatrix matrix = SkMatrix::I();
342 matrix[SkMatrix::kMPersp0] = 0.5f;
343 REPORTER_ASSERT(reporter, matrix.hasPerspective());
344 SkSurfaceProps surfaceProps(0, kUnknown_SkPixelGeometry);
Herb Derby26cbe512018-05-24 14:39:01 -0400345 GrTextContext::Options options;
346 GrTextContext::SanitizeOptions(&options);
347 REPORTER_ASSERT(reporter, GrTextContext::CanDrawAsDistanceFields(
Khushal3e7548c2018-05-23 15:45:01 -0700348 paint, matrix, surfaceProps, true, options));
349
350 // Server.
351 auto serverTf = SkTypeface::MakeFromName("monospace", SkFontStyle());
352 auto serverTfData = server.serializeTypeface(serverTf.get());
353
354 int glyphCount = 10;
355 auto serverBlob = buildTextBlob(serverTf, glyphCount);
356 const SkSurfaceProps props(SkSurfaceProps::kLegacyFontHost_InitType);
357 SkTextBlobCacheDiffCanvas cache_diff_canvas(10, 10, SkMatrix::I(), props, &server,
358 MakeSettings(ctxInfo.grContext()));
359 cache_diff_canvas.concat(matrix);
360 cache_diff_canvas.drawTextBlob(serverBlob.get(), 0, 0, paint);
361
362 std::vector<uint8_t> serverStrikeData;
363 server.writeStrikeData(&serverStrikeData);
364
365 // Client.
366 auto clientTf = client.deserializeTypeface(serverTfData->data(), serverTfData->size());
367 REPORTER_ASSERT(reporter,
368 client.readStrikeData(serverStrikeData.data(), serverStrikeData.size()));
369 auto clientBlob = buildTextBlob(clientTf, glyphCount);
370
371 SkBitmap expected = RasterBlob(serverBlob, 10, 10, paint, ctxInfo.grContext(), &matrix);
372 SkBitmap actual = RasterBlob(clientBlob, 10, 10, paint, ctxInfo.grContext(), &matrix);
373 COMPARE_BLOBS(expected, actual, reporter);
374 SkStrikeCache::Validate();
375
376 // Must unlock everything on termination, otherwise valgrind complains about memory leaks.
377 discardableManager->unlockAndDeleteAll();
378}
Khushald4160832018-05-23 18:16:00 -0700379
380DEF_GPUTEST_FOR_RENDERING_CONTEXTS(SkRemoteGlyphCache_CacheMissReporting, reporter, ctxInfo) {
381 sk_sp<DiscardableManager> discardableManager = sk_make_sp<DiscardableManager>();
382 SkStrikeServer server(discardableManager.get());
383 SkStrikeClient client(discardableManager);
384
385 auto serverTf = SkTypeface::MakeFromName("monospace", SkFontStyle());
386 auto tfData = server.serializeTypeface(serverTf.get());
387 auto clientTf = client.deserializeTypeface(tfData->data(), tfData->size());
388 REPORTER_ASSERT(reporter, clientTf);
389 int glyphCount = 10;
390 auto clientBlob = buildTextBlob(clientTf, glyphCount);
391
392 // Raster the client-side blob without the glyph data, we should get cache miss notifications.
393 SkPaint paint;
394 SkMatrix matrix = SkMatrix::I();
395 RasterBlob(clientBlob, 10, 10, paint, ctxInfo.grContext(), &matrix);
396 REPORTER_ASSERT(reporter,
397 discardableManager->cacheMissCount(SkStrikeClient::kFontMetrics) == 1);
398 REPORTER_ASSERT(reporter,
399 discardableManager->cacheMissCount(SkStrikeClient::kGlyphMetrics) == 10);
400
401 // There shouldn't be any image or path requests, since we mark the glyph as empty on a cache
402 // miss.
403 REPORTER_ASSERT(reporter, discardableManager->cacheMissCount(SkStrikeClient::kGlyphImage) == 0);
404 REPORTER_ASSERT(reporter, discardableManager->cacheMissCount(SkStrikeClient::kGlyphPath) == 0);
405
406 // Must unlock everything on termination, otherwise valgrind complains about memory leaks.
407 discardableManager->unlockAndDeleteAll();
408}
Khushal3e7548c2018-05-23 15:45:01 -0700409#endif