1 /*
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
8 #include "include/core/SkGraphics.h"
9 #include "include/core/SkSurface.h"
10 #include "include/core/SkTextBlob.h"
11 #include "include/private/SkMutex.h"
12 #include "src/core/SkDraw.h"
13 #include "src/core/SkRemoteGlyphCache.h"
14 #include "src/core/SkScalerCache.h"
15 #include "src/core/SkStrikeCache.h"
16 #include "src/core/SkStrikeSpec.h"
17 #include "src/core/SkSurfacePriv.h"
18 #include "src/core/SkTypeface_remote.h"
19 #include "tests/Test.h"
20 #include "tools/Resources.h"
21 #include "tools/ToolUtils.h"
22 #include "tools/fonts/TestEmptyTypeface.h"
23
24 #include "src/gpu/text/GrTextContext.h"
25
26 class DiscardableManager : public SkStrikeServer::DiscardableHandleManager,
27 public SkStrikeClient::DiscardableHandleManager {
28 public:
DiscardableManager()29 DiscardableManager() { sk_bzero(&fCacheMissCount, sizeof(fCacheMissCount)); }
30 ~DiscardableManager() override = default;
31
32 // Server implementation.
createHandle()33 SkDiscardableHandleId createHandle() override {
34 SkAutoMutexExclusive l(fMutex);
35
36 // Handles starts as locked.
37 fLockedHandles.add(++fNextHandleId);
38 return fNextHandleId;
39 }
lockHandle(SkDiscardableHandleId id)40 bool lockHandle(SkDiscardableHandleId id) override {
41 SkAutoMutexExclusive l(fMutex);
42
43 if (id <= fLastDeletedHandleId) return false;
44 fLockedHandles.add(id);
45 return true;
46 }
47
48 // Client implementation.
deleteHandle(SkDiscardableHandleId id)49 bool deleteHandle(SkDiscardableHandleId id) override {
50 SkAutoMutexExclusive l(fMutex);
51
52 return id <= fLastDeletedHandleId;
53 }
54
notifyCacheMiss(SkStrikeClient::CacheMissType type)55 void notifyCacheMiss(SkStrikeClient::CacheMissType type) override {
56 SkAutoMutexExclusive l(fMutex);
57
58 fCacheMissCount[type]++;
59 }
isHandleDeleted(SkDiscardableHandleId id)60 bool isHandleDeleted(SkDiscardableHandleId id) override {
61 SkAutoMutexExclusive l(fMutex);
62
63 return id <= fLastDeletedHandleId;
64 }
65
unlockAll()66 void unlockAll() {
67 SkAutoMutexExclusive l(fMutex);
68
69 fLockedHandles.reset();
70 }
unlockAndDeleteAll()71 void unlockAndDeleteAll() {
72 SkAutoMutexExclusive l(fMutex);
73
74 fLockedHandles.reset();
75 fLastDeletedHandleId = fNextHandleId;
76 }
lockedHandles() const77 const SkTHashSet<SkDiscardableHandleId>& lockedHandles() const {
78 SkAutoMutexExclusive l(fMutex);
79
80 return fLockedHandles;
81 }
handleCount()82 SkDiscardableHandleId handleCount() {
83 SkAutoMutexExclusive l(fMutex);
84
85 return fNextHandleId;
86 }
cacheMissCount(uint32_t type)87 int cacheMissCount(uint32_t type) {
88 SkAutoMutexExclusive l(fMutex);
89
90 return fCacheMissCount[type];
91 }
hasCacheMiss() const92 bool hasCacheMiss() const {
93 SkAutoMutexExclusive l(fMutex);
94
95 for (uint32_t i = 0; i <= SkStrikeClient::CacheMissType::kLast; ++i) {
96 if (fCacheMissCount[i] > 0) { return true; }
97 }
98 return false;
99 }
resetCacheMissCounts()100 void resetCacheMissCounts() {
101 SkAutoMutexExclusive l(fMutex);
102 sk_bzero(&fCacheMissCount, sizeof(fCacheMissCount));
103 }
104
105 private:
106 // The tests below run in parallel on multiple threads and use the same
107 // process global SkStrikeCache. So the implementation needs to be
108 // thread-safe.
109 mutable SkMutex fMutex;
110
111 SkDiscardableHandleId fNextHandleId = 0u;
112 SkDiscardableHandleId fLastDeletedHandleId = 0u;
113 SkTHashSet<SkDiscardableHandleId> fLockedHandles;
114 int fCacheMissCount[SkStrikeClient::CacheMissType::kLast + 1u];
115 };
116
buildTextBlob(sk_sp<SkTypeface> tf,int glyphCount)117 sk_sp<SkTextBlob> buildTextBlob(sk_sp<SkTypeface> tf, int glyphCount) {
118 SkFont font;
119 font.setTypeface(tf);
120 font.setHinting(SkFontHinting::kNormal);
121 font.setSize(1u);
122 font.setEdging(SkFont::Edging::kAntiAlias);
123 font.setSubpixel(true);
124
125 SkTextBlobBuilder builder;
126 SkRect bounds = SkRect::MakeWH(10, 10);
127 const auto& runBuffer = builder.allocRunPosH(font, glyphCount, 0, &bounds);
128 SkASSERT(runBuffer.utf8text == nullptr);
129 SkASSERT(runBuffer.clusters == nullptr);
130
131 for (int i = 0; i < glyphCount; i++) {
132 runBuffer.glyphs[i] = static_cast<SkGlyphID>(i);
133 runBuffer.pos[i] = SkIntToScalar(i);
134 }
135 return builder.make();
136 }
137
compare_blobs(const SkBitmap & expected,const SkBitmap & actual,skiatest::Reporter * reporter,int tolerance=0)138 static void compare_blobs(const SkBitmap& expected, const SkBitmap& actual,
139 skiatest::Reporter* reporter, int tolerance = 0) {
140 SkASSERT(expected.width() == actual.width());
141 SkASSERT(expected.height() == actual.height());
142 for (int i = 0; i < expected.width(); ++i) {
143 for (int j = 0; j < expected.height(); ++j) {
144 SkColor expectedColor = expected.getColor(i, j);
145 SkColor actualColor = actual.getColor(i, j);
146 if (0 == tolerance) {
147 REPORTER_ASSERT(reporter, expectedColor == actualColor);
148 } else {
149 for (int k = 0; k < 4; ++k) {
150 int expectedChannel = (expectedColor >> (k*8)) & 0xff;
151 int actualChannel = (actualColor >> (k*8)) & 0xff;
152 REPORTER_ASSERT(reporter, abs(expectedChannel - actualChannel) <= tolerance);
153 }
154 }
155 }
156 }
157 }
158
MakeSurface(int width,int height,GrContext * context)159 sk_sp<SkSurface> MakeSurface(int width, int height, GrContext* context) {
160 const SkImageInfo info =
161 SkImageInfo::Make(width, height, kN32_SkColorType, kPremul_SkAlphaType);
162 return SkSurface::MakeRenderTarget(context, SkBudgeted::kNo, info);
163 }
164
FindSurfaceProps(GrContext * context)165 const SkSurfaceProps FindSurfaceProps(GrContext* context) {
166 auto surface = MakeSurface(1, 1, context);
167 return surface->props();
168 }
169
RasterBlob(sk_sp<SkTextBlob> blob,int width,int height,const SkPaint & paint,GrContext * context,const SkMatrix * matrix=nullptr,SkScalar x=0)170 SkBitmap RasterBlob(sk_sp<SkTextBlob> blob, int width, int height, const SkPaint& paint,
171 GrContext* context, const SkMatrix* matrix = nullptr,
172 SkScalar x = 0) {
173 auto surface = MakeSurface(width, height, context);
174 if (matrix) surface->getCanvas()->concat(*matrix);
175 surface->getCanvas()->drawTextBlob(blob.get(), x, height/2, paint);
176 SkBitmap bitmap;
177 bitmap.allocN32Pixels(width, height);
178 surface->readPixels(bitmap, 0, 0);
179 return bitmap;
180 }
181
DEF_TEST(SkRemoteGlyphCache_TypefaceSerialization,reporter)182 DEF_TEST(SkRemoteGlyphCache_TypefaceSerialization, reporter) {
183 sk_sp<DiscardableManager> discardableManager = sk_make_sp<DiscardableManager>();
184 SkStrikeServer server(discardableManager.get());
185 SkStrikeClient client(discardableManager, false);
186
187 auto server_tf = SkTypeface::MakeDefault();
188 auto tf_data = server.serializeTypeface(server_tf.get());
189
190 auto client_tf = client.deserializeTypeface(tf_data->data(), tf_data->size());
191 REPORTER_ASSERT(reporter, client_tf);
192 REPORTER_ASSERT(reporter, static_cast<SkTypefaceProxy*>(client_tf.get())->remoteTypefaceID() ==
193 server_tf->uniqueID());
194
195 // Must unlock everything on termination, otherwise valgrind complains about memory leaks.
196 discardableManager->unlockAndDeleteAll();
197 }
198
DEF_GPUTEST_FOR_RENDERING_CONTEXTS(SkRemoteGlyphCache_StrikeSerialization,reporter,ctxInfo)199 DEF_GPUTEST_FOR_RENDERING_CONTEXTS(SkRemoteGlyphCache_StrikeSerialization, reporter, ctxInfo) {
200 sk_sp<DiscardableManager> discardableManager = sk_make_sp<DiscardableManager>();
201 SkStrikeServer server(discardableManager.get());
202 SkStrikeClient client(discardableManager, false);
203 const SkPaint paint;
204
205 // Server.
206 auto serverTf = SkTypeface::MakeFromName("monospace", SkFontStyle());
207 auto serverTfData = server.serializeTypeface(serverTf.get());
208
209 int glyphCount = 10;
210 auto serverBlob = buildTextBlob(serverTf, glyphCount);
211 auto props = FindSurfaceProps(ctxInfo.grContext());
212 SkTextBlobCacheDiffCanvas cache_diff_canvas(10, 10, props, &server,
213 ctxInfo.grContext()->supportsDistanceFieldText());
214 #ifdef SK_CAPTURE_DRAW_TEXT_BLOB
215 {
216 SkDynamicMemoryWStream wStream;
217 server.fCapture.reset(new SkTextBlobTrace::Capture);
218 cache_diff_canvas.drawTextBlob(serverBlob.get(), 0, 0, paint);
219 server.fCapture->dump(&wStream);
220 std::unique_ptr<SkStreamAsset> stream = wStream.detachAsStream();
221 std::vector<SkTextBlobTrace::Record> trace = SkTextBlobTrace::CreateBlobTrace(stream.get());
222 REPORTER_ASSERT(reporter, trace.size() == 1);
223 }
224 #else
225 cache_diff_canvas.drawTextBlob(serverBlob.get(), 0, 0, paint);
226 #endif
227
228 std::vector<uint8_t> serverStrikeData;
229 server.writeStrikeData(&serverStrikeData);
230
231 // Client.
232 auto clientTf = client.deserializeTypeface(serverTfData->data(), serverTfData->size());
233 REPORTER_ASSERT(reporter,
234 client.readStrikeData(serverStrikeData.data(), serverStrikeData.size()));
235 auto clientBlob = buildTextBlob(clientTf, glyphCount);
236
237 SkBitmap expected = RasterBlob(serverBlob, 10, 10, paint, ctxInfo.grContext());
238 SkBitmap actual = RasterBlob(clientBlob, 10, 10, paint, ctxInfo.grContext());
239 compare_blobs(expected, actual, reporter);
240 REPORTER_ASSERT(reporter, !discardableManager->hasCacheMiss());
241
242 // Must unlock everything on termination, otherwise valgrind complains about memory leaks.
243 discardableManager->unlockAndDeleteAll();
244 }
245
DEF_GPUTEST_FOR_RENDERING_CONTEXTS(SkRemoteGlyphCache_ReleaseTypeFace,reporter,ctxInfo)246 DEF_GPUTEST_FOR_RENDERING_CONTEXTS(SkRemoteGlyphCache_ReleaseTypeFace, reporter, ctxInfo) {
247 sk_sp<DiscardableManager> discardableManager = sk_make_sp<DiscardableManager>();
248 SkStrikeServer server(discardableManager.get());
249 SkStrikeClient client(discardableManager, false);
250
251 // Server.
252 auto serverTf = TestEmptyTypeface::Make();
253 auto serverTfData = server.serializeTypeface(serverTf.get());
254 REPORTER_ASSERT(reporter, serverTf->unique());
255
256 {
257 const SkPaint paint;
258 int glyphCount = 10;
259 auto serverBlob = buildTextBlob(serverTf, glyphCount);
260 const SkSurfaceProps props(SkSurfaceProps::kLegacyFontHost_InitType);
261 SkTextBlobCacheDiffCanvas cache_diff_canvas(
262 10, 10, props, &server, ctxInfo.grContext()->supportsDistanceFieldText());
263 cache_diff_canvas.drawTextBlob(serverBlob.get(), 0, 0, paint);
264 REPORTER_ASSERT(reporter, !serverTf->unique());
265
266 std::vector<uint8_t> serverStrikeData;
267 server.writeStrikeData(&serverStrikeData);
268 }
269 REPORTER_ASSERT(reporter, serverTf->unique());
270
271 // Must unlock everything on termination, otherwise valgrind complains about memory leaks.
272 discardableManager->unlockAndDeleteAll();
273 }
274
DEF_TEST(SkRemoteGlyphCache_StrikeLockingServer,reporter)275 DEF_TEST(SkRemoteGlyphCache_StrikeLockingServer, reporter) {
276 sk_sp<DiscardableManager> discardableManager = sk_make_sp<DiscardableManager>();
277 SkStrikeServer server(discardableManager.get());
278 SkStrikeClient client(discardableManager, false);
279
280 auto serverTf = SkTypeface::MakeFromName("monospace", SkFontStyle());
281 server.serializeTypeface(serverTf.get());
282 int glyphCount = 10;
283 auto serverBlob = buildTextBlob(serverTf, glyphCount);
284
285 const SkSurfaceProps props(SkSurfaceProps::kLegacyFontHost_InitType);
286 SkTextBlobCacheDiffCanvas cache_diff_canvas(10, 10, props, &server);
287 SkPaint paint;
288 cache_diff_canvas.drawTextBlob(serverBlob.get(), 0, 0, paint);
289
290 // The strike from the blob should be locked after it has been drawn on the canvas.
291 REPORTER_ASSERT(reporter, discardableManager->handleCount() == 1u);
292 REPORTER_ASSERT(reporter, discardableManager->lockedHandles().count() == 1u);
293
294 // Write the strike data and unlock everything. Re-analyzing the blob should lock the handle
295 // again.
296 std::vector<uint8_t> fontData;
297 server.writeStrikeData(&fontData);
298 discardableManager->unlockAll();
299 REPORTER_ASSERT(reporter, discardableManager->lockedHandles().count() == 0u);
300
301 cache_diff_canvas.drawTextBlob(serverBlob.get(), 0, 0, paint);
302 REPORTER_ASSERT(reporter, discardableManager->handleCount() == 1u);
303 REPORTER_ASSERT(reporter, discardableManager->lockedHandles().count() == 1u);
304
305 // Must unlock everything on termination, otherwise valgrind complains about memory leaks.
306 discardableManager->unlockAndDeleteAll();
307 }
308
DEF_TEST(SkRemoteGlyphCache_StrikeDeletionServer,reporter)309 DEF_TEST(SkRemoteGlyphCache_StrikeDeletionServer, reporter) {
310 sk_sp<DiscardableManager> discardableManager = sk_make_sp<DiscardableManager>();
311 SkStrikeServer server(discardableManager.get());
312 SkStrikeClient client(discardableManager, false);
313
314 auto serverTf = SkTypeface::MakeFromName("monospace", SkFontStyle());
315 server.serializeTypeface(serverTf.get());
316 int glyphCount = 10;
317 auto serverBlob = buildTextBlob(serverTf, glyphCount);
318
319 const SkSurfaceProps props(SkSurfaceProps::kLegacyFontHost_InitType);
320 SkTextBlobCacheDiffCanvas cache_diff_canvas(10, 10, props, &server);
321 SkPaint paint;
322 cache_diff_canvas.drawTextBlob(serverBlob.get(), 0, 0, paint);
323 REPORTER_ASSERT(reporter, discardableManager->handleCount() == 1u);
324
325 // Write the strike data and delete all the handles. Re-analyzing the blob should create new
326 // handles.
327 std::vector<uint8_t> fontData;
328 server.writeStrikeData(&fontData);
329
330 // Another analysis pass, to ensure that deleting handles after a complete cache hit still
331 // works. This is a regression test for crbug.com/999682.
332 cache_diff_canvas.drawTextBlob(serverBlob.get(), 0, 0, paint);
333 server.writeStrikeData(&fontData);
334 REPORTER_ASSERT(reporter, discardableManager->handleCount() == 1u);
335
336 discardableManager->unlockAndDeleteAll();
337 cache_diff_canvas.drawTextBlob(serverBlob.get(), 0, 0, paint);
338 REPORTER_ASSERT(reporter, discardableManager->handleCount() == 2u);
339
340 // Must unlock everything on termination, otherwise valgrind complains about memory leaks.
341 discardableManager->unlockAndDeleteAll();
342 }
343
DEF_TEST(SkRemoteGlyphCache_StrikePinningClient,reporter)344 DEF_TEST(SkRemoteGlyphCache_StrikePinningClient, reporter) {
345 sk_sp<DiscardableManager> discardableManager = sk_make_sp<DiscardableManager>();
346 SkStrikeServer server(discardableManager.get());
347 SkStrikeClient client(discardableManager, false);
348
349 // Server.
350 auto serverTf = SkTypeface::MakeFromName("monospace", SkFontStyle());
351 auto serverTfData = server.serializeTypeface(serverTf.get());
352
353 int glyphCount = 10;
354 auto serverBlob = buildTextBlob(serverTf, glyphCount);
355
356 const SkSurfaceProps props(SkSurfaceProps::kLegacyFontHost_InitType);
357 SkTextBlobCacheDiffCanvas cache_diff_canvas(10, 10, props, &server);
358 SkPaint paint;
359 cache_diff_canvas.drawTextBlob(serverBlob.get(), 0, 0, paint);
360
361 std::vector<uint8_t> serverStrikeData;
362 server.writeStrikeData(&serverStrikeData);
363
364 // Client.
365 REPORTER_ASSERT(reporter,
366 client.readStrikeData(serverStrikeData.data(), serverStrikeData.size()));
367 auto* clientTf = client.deserializeTypeface(serverTfData->data(), serverTfData->size()).get();
368
369 // The cache remains alive until it is pinned in the discardable manager.
370 SkGraphics::PurgeFontCache();
371 REPORTER_ASSERT(reporter, !clientTf->unique());
372
373 // Once the strike is unpinned and purged, SkStrikeClient should be the only owner of the
374 // clientTf.
375 discardableManager->unlockAndDeleteAll();
376 SkGraphics::PurgeFontCache();
377 REPORTER_ASSERT(reporter, clientTf->unique());
378
379 // Must unlock everything on termination, otherwise valgrind complains about memory leaks.
380 discardableManager->unlockAndDeleteAll();
381 }
382
DEF_TEST(SkRemoteGlyphCache_ClientMemoryAccounting,reporter)383 DEF_TEST(SkRemoteGlyphCache_ClientMemoryAccounting, reporter) {
384 sk_sp<DiscardableManager> discardableManager = sk_make_sp<DiscardableManager>();
385 SkStrikeServer server(discardableManager.get());
386 SkStrikeClient client(discardableManager, false);
387
388 // Server.
389 auto serverTf = SkTypeface::MakeFromName("monospace", SkFontStyle());
390 auto serverTfData = server.serializeTypeface(serverTf.get());
391
392 int glyphCount = 10;
393 auto serverBlob = buildTextBlob(serverTf, glyphCount);
394
395 const SkSurfaceProps props(SkSurfaceProps::kLegacyFontHost_InitType);
396 SkTextBlobCacheDiffCanvas cache_diff_canvas(10, 10, props, &server);
397 SkPaint paint;
398 cache_diff_canvas.drawTextBlob(serverBlob.get(), 0, 0, paint);
399
400 std::vector<uint8_t> serverStrikeData;
401 server.writeStrikeData(&serverStrikeData);
402
403 // Client.
404 REPORTER_ASSERT(reporter,
405 client.readStrikeData(serverStrikeData.data(), serverStrikeData.size()));
406
407 // Must unlock everything on termination, otherwise valgrind complains about memory leaks.
408 discardableManager->unlockAndDeleteAll();
409 }
410
DEF_TEST(SkRemoteGlyphCache_PurgesServerEntries,reporter)411 DEF_TEST(SkRemoteGlyphCache_PurgesServerEntries, reporter) {
412 sk_sp<DiscardableManager> discardableManager = sk_make_sp<DiscardableManager>();
413 SkStrikeServer server(discardableManager.get());
414 server.setMaxEntriesInDescriptorMapForTesting(1u);
415 SkStrikeClient client(discardableManager, false);
416
417 {
418 auto serverTf = SkTypeface::MakeFromName("monospace", SkFontStyle());
419 int glyphCount = 10;
420 auto serverBlob = buildTextBlob(serverTf, glyphCount);
421
422 const SkSurfaceProps props(SkSurfaceProps::kLegacyFontHost_InitType);
423 SkTextBlobCacheDiffCanvas cache_diff_canvas(10, 10, props, &server);
424 SkPaint paint;
425 REPORTER_ASSERT(reporter, server.remoteStrikeMapSizeForTesting() == 0u);
426 cache_diff_canvas.drawTextBlob(serverBlob.get(), 0, 0, paint);
427 REPORTER_ASSERT(reporter, server.remoteStrikeMapSizeForTesting() == 1u);
428 }
429
430 // Serialize to release the lock from the strike server and delete all current
431 // handles.
432 std::vector<uint8_t> fontData;
433 server.writeStrikeData(&fontData);
434 discardableManager->unlockAndDeleteAll();
435
436 // Use a different typeface. Creating a new strike should evict the previous
437 // one.
438 {
439 auto serverTf = SkTypeface::MakeFromName("Georgia", SkFontStyle());
440 int glyphCount = 10;
441 auto serverBlob = buildTextBlob(serverTf, glyphCount);
442
443 const SkSurfaceProps props(SkSurfaceProps::kLegacyFontHost_InitType);
444 SkTextBlobCacheDiffCanvas cache_diff_canvas(10, 10, props, &server);
445 SkPaint paint;
446 REPORTER_ASSERT(reporter, server.remoteStrikeMapSizeForTesting() == 1u);
447 cache_diff_canvas.drawTextBlob(serverBlob.get(), 0, 0, paint);
448 REPORTER_ASSERT(reporter, server.remoteStrikeMapSizeForTesting() == 1u);
449 }
450
451 // Must unlock everything on termination, otherwise valgrind complains about memory leaks.
452 discardableManager->unlockAndDeleteAll();
453 }
454
DEF_GPUTEST_FOR_RENDERING_CONTEXTS(SkRemoteGlyphCache_DrawTextAsPath,reporter,ctxInfo)455 DEF_GPUTEST_FOR_RENDERING_CONTEXTS(SkRemoteGlyphCache_DrawTextAsPath, reporter, ctxInfo) {
456 sk_sp<DiscardableManager> discardableManager = sk_make_sp<DiscardableManager>();
457 SkStrikeServer server(discardableManager.get());
458 SkStrikeClient client(discardableManager, false);
459 SkPaint paint;
460 paint.setStyle(SkPaint::kStroke_Style);
461 paint.setStrokeWidth(0);
462 REPORTER_ASSERT(reporter,
463 SkStrikeSpec::ShouldDrawAsPath(paint, SkFont(), SkMatrix::I()));
464
465 // Server.
466 auto serverTf = SkTypeface::MakeFromName("monospace", SkFontStyle());
467 auto serverTfData = server.serializeTypeface(serverTf.get());
468
469 int glyphCount = 10;
470 auto serverBlob = buildTextBlob(serverTf, glyphCount);
471 auto props = FindSurfaceProps(ctxInfo.grContext());
472 SkTextBlobCacheDiffCanvas cache_diff_canvas(
473 10, 10, props, &server, ctxInfo.grContext()->supportsDistanceFieldText());
474 cache_diff_canvas.drawTextBlob(serverBlob.get(), 0, 0, paint);
475
476 std::vector<uint8_t> serverStrikeData;
477 server.writeStrikeData(&serverStrikeData);
478
479 // Client.
480 auto clientTf = client.deserializeTypeface(serverTfData->data(), serverTfData->size());
481 REPORTER_ASSERT(reporter,
482 client.readStrikeData(serverStrikeData.data(), serverStrikeData.size()));
483 auto clientBlob = buildTextBlob(clientTf, glyphCount);
484
485 SkBitmap expected = RasterBlob(serverBlob, 10, 10, paint, ctxInfo.grContext());
486 SkBitmap actual = RasterBlob(clientBlob, 10, 10, paint, ctxInfo.grContext());
487 compare_blobs(expected, actual, reporter, 1);
488 REPORTER_ASSERT(reporter, !discardableManager->hasCacheMiss());
489
490 // Must unlock everything on termination, otherwise valgrind complains about memory leaks.
491 discardableManager->unlockAndDeleteAll();
492 }
493
make_blob_causing_fallback(sk_sp<SkTypeface> targetTf,const SkTypeface * glyphTf,skiatest::Reporter * reporter)494 sk_sp<SkTextBlob> make_blob_causing_fallback(
495 sk_sp<SkTypeface> targetTf, const SkTypeface* glyphTf, skiatest::Reporter* reporter) {
496 SkFont font;
497 font.setSubpixel(true);
498 font.setSize(96);
499 font.setHinting(SkFontHinting::kNormal);
500 font.setTypeface(targetTf);
501
502 REPORTER_ASSERT(reporter,
503 !SkStrikeSpec::ShouldDrawAsPath(SkPaint(), font, SkMatrix::I()));
504
505 char s[] = "Skia";
506 int runSize = strlen(s);
507
508 SkTextBlobBuilder builder;
509 SkRect bounds = SkRect::MakeIWH(100, 100);
510 const auto& runBuffer = builder.allocRunPosH(font, runSize, 10, &bounds);
511 SkASSERT(runBuffer.utf8text == nullptr);
512 SkASSERT(runBuffer.clusters == nullptr);
513
514 SkFont(sk_ref_sp(glyphTf)).textToGlyphs(s, strlen(s), SkTextEncoding::kUTF8,
515 runBuffer.glyphs, runSize);
516
517 SkRect glyphBounds;
518 font.getWidths(runBuffer.glyphs, 1, nullptr, &glyphBounds);
519
520 REPORTER_ASSERT(reporter, glyphBounds.width() > SkStrikeCommon::kSkSideTooBigForAtlas);
521
522 for (int i = 0; i < runSize; i++) {
523 runBuffer.pos[i] = i * 10;
524 }
525
526 return builder.make();
527 }
528
DEF_GPUTEST_FOR_RENDERING_CONTEXTS(SkRemoteGlyphCache_DrawTextAsMaskWithPathFallback,reporter,ctxInfo)529 DEF_GPUTEST_FOR_RENDERING_CONTEXTS(SkRemoteGlyphCache_DrawTextAsMaskWithPathFallback,
530 reporter, ctxInfo) {
531 sk_sp<DiscardableManager> discardableManager = sk_make_sp<DiscardableManager>();
532 SkStrikeServer server(discardableManager.get());
533 SkStrikeClient client(discardableManager, false);
534
535 SkPaint paint;
536
537 auto serverTf = MakeResourceAsTypeface("fonts/HangingS.ttf");
538 // TODO: when the cq bots can handle this font remove the check.
539 if (serverTf == nullptr) {
540 return;
541 }
542 auto serverTfData = server.serializeTypeface(serverTf.get());
543
544 auto serverBlob = make_blob_causing_fallback(serverTf, serverTf.get(), reporter);
545
546 auto props = FindSurfaceProps(ctxInfo.grContext());
547 SkTextBlobCacheDiffCanvas cache_diff_canvas(
548 10, 10, props, &server, ctxInfo.grContext()->supportsDistanceFieldText());
549 cache_diff_canvas.drawTextBlob(serverBlob.get(), 0, 0, paint);
550
551 std::vector<uint8_t> serverStrikeData;
552 server.writeStrikeData(&serverStrikeData);
553
554 // Client.
555 auto clientTf = client.deserializeTypeface(serverTfData->data(), serverTfData->size());
556 REPORTER_ASSERT(reporter,
557 client.readStrikeData(serverStrikeData.data(), serverStrikeData.size()));
558
559 auto clientBlob = make_blob_causing_fallback(clientTf, serverTf.get(), reporter);
560
561 SkBitmap expected = RasterBlob(serverBlob, 10, 10, paint, ctxInfo.grContext());
562 SkBitmap actual = RasterBlob(clientBlob, 10, 10, paint, ctxInfo.grContext());
563 compare_blobs(expected, actual, reporter);
564 REPORTER_ASSERT(reporter, !discardableManager->hasCacheMiss());
565
566 // Must unlock everything on termination, otherwise valgrind complains about memory leaks.
567 discardableManager->unlockAndDeleteAll();
568 }
569
570 #if 0
571 // TODO: turn this one when I figure out how to deal with the pixel variance from linear
572 // interpolation from GPU to GPU.
573 DEF_GPUTEST_FOR_RENDERING_CONTEXTS(SkRemoteGlyphCache_DrawTextAsSDFTWithAllARGBFallback,
574 reporter, ctxInfo) {
575 sk_sp<DiscardableManager> discardableManager = sk_make_sp<DiscardableManager>();
576 SkStrikeServer server(discardableManager.get());
577 SkStrikeClient client(discardableManager, false);
578
579 SkPaint paint;
580
581 auto serverTf = ToolUtils::planet_typeface();
582 // TODO: when the cq bots can handle this font remove the check.
583 if (serverTf == nullptr) {
584 return;
585 }
586 auto serverTfData = server.serializeTypeface(serverTf.get());
587
588 auto makeBlob = [&reporter](sk_sp<SkTypeface> typeface) {
589 SkFont font;
590 font.setSubpixel(true);
591 font.setSize(96);
592 font.setHinting(SkFontHinting::kNormal);
593 font.setTypeface(typeface);
594
595 REPORTER_ASSERT(reporter, !SkDraw::ShouldDrawTextAsPaths(font, SkPaint(), SkMatrix::I()));
596
597 // Mercury to Uranus.
598 SkGlyphID glyphs[] = {1, 2, 3, 4, 5, 6, 7, 8};
599
600 SkTextBlobBuilder builder;
601 SkRect bounds = SkRect::MakeIWH(100, 100);
602 const auto& runBuffer = builder.allocRunPosH(font, SK_ARRAY_COUNT(glyphs), 100, &bounds);
603 SkASSERT(runBuffer.utf8text == nullptr);
604 SkASSERT(runBuffer.clusters == nullptr);
605
606 std::copy(std::begin(glyphs), std::end(glyphs), runBuffer.glyphs);
607
608 for (size_t i = 0; i < SK_ARRAY_COUNT(glyphs); i++) {
609 runBuffer.pos[i] = i * 100;
610 }
611
612 return builder.make();
613 };
614
615 auto serverBlob = makeBlob(serverTf);
616
617 auto props = FindSurfaceProps(ctxInfo.grContext());
618 SkTextBlobCacheDiffCanvas cache_diff_canvas(
619 800, 800, props, &server, ctxInfo.grContext()->supportsDistanceFieldText());
620 cache_diff_canvas.drawTextBlob(serverBlob.get(), 0, 400, paint);
621
622 std::vector<uint8_t> serverStrikeData;
623 server.writeStrikeData(&serverStrikeData);
624
625 // Client.
626 auto clientTf = client.deserializeTypeface(serverTfData->data(), serverTfData->size());
627 REPORTER_ASSERT(reporter,
628 client.readStrikeData(serverStrikeData.data(), serverStrikeData.size()));
629
630 auto clientBlob = makeBlob(clientTf);
631
632 SkBitmap expected = RasterBlob(serverBlob, 800, 800, paint, ctxInfo.grContext());
633 SkBitmap actual = RasterBlob(clientBlob, 800, 800, paint, ctxInfo.grContext());
634
635 // Pixel variance can be high because of the atlas placement, and large scaling in the linear
636 // interpolation.
637 compare_blobs(expected, actual, reporter, 36);
638 REPORTER_ASSERT(reporter, !discardableManager->hasCacheMiss());
639
640 // Must unlock everything on termination, otherwise valgrind complains about memory leaks.
641 discardableManager->unlockAndDeleteAll();
642 }
643 #endif
644
DEF_GPUTEST_FOR_RENDERING_CONTEXTS(SkRemoteGlyphCache_DrawTextXY,reporter,ctxInfo)645 DEF_GPUTEST_FOR_RENDERING_CONTEXTS(SkRemoteGlyphCache_DrawTextXY, reporter, ctxInfo) {
646 sk_sp<DiscardableManager> discardableManager = sk_make_sp<DiscardableManager>();
647 SkStrikeServer server(discardableManager.get());
648 SkStrikeClient client(discardableManager, false);
649 SkPaint paint;
650 paint.setAntiAlias(true);
651
652 // Server.
653 auto serverTf = SkTypeface::MakeFromName("monospace", SkFontStyle());
654 auto serverTfData = server.serializeTypeface(serverTf.get());
655
656 int glyphCount = 10;
657 auto serverBlob = buildTextBlob(serverTf, glyphCount);
658 auto props = FindSurfaceProps(ctxInfo.grContext());
659 SkTextBlobCacheDiffCanvas cache_diff_canvas(
660 10, 10, props, &server, ctxInfo.grContext()->supportsDistanceFieldText());
661 cache_diff_canvas.drawTextBlob(serverBlob.get(), 0.5, 0, paint);
662
663 std::vector<uint8_t> serverStrikeData;
664 server.writeStrikeData(&serverStrikeData);
665
666 // Client.
667 auto clientTf = client.deserializeTypeface(serverTfData->data(), serverTfData->size());
668 REPORTER_ASSERT(reporter,
669 client.readStrikeData(serverStrikeData.data(), serverStrikeData.size()));
670 auto clientBlob = buildTextBlob(clientTf, glyphCount);
671
672 SkBitmap expected = RasterBlob(serverBlob, 10, 10, paint, ctxInfo.grContext(), nullptr, 0.5);
673 SkBitmap actual = RasterBlob(clientBlob, 10, 10, paint, ctxInfo.grContext(), nullptr, 0.5);
674 compare_blobs(expected, actual, reporter);
675 REPORTER_ASSERT(reporter, !discardableManager->hasCacheMiss());
676
677 // Must unlock everything on termination, otherwise valgrind complains about memory leaks.
678 discardableManager->unlockAndDeleteAll();
679 }
680
DEF_GPUTEST_FOR_RENDERING_CONTEXTS(SkRemoteGlyphCache_DrawTextAsDFT,reporter,ctxInfo)681 DEF_GPUTEST_FOR_RENDERING_CONTEXTS(SkRemoteGlyphCache_DrawTextAsDFT, reporter, ctxInfo) {
682 sk_sp<DiscardableManager> discardableManager = sk_make_sp<DiscardableManager>();
683 SkStrikeServer server(discardableManager.get());
684 SkStrikeClient client(discardableManager, false);
685 SkPaint paint;
686 SkFont font;
687
688 // A scale transform forces fallback to dft.
689 SkMatrix matrix = SkMatrix::MakeScale(16);
690 SkSurfaceProps surfaceProps(0, kUnknown_SkPixelGeometry);
691 GrTextContext::Options options;
692 GrTextContext::SanitizeOptions(&options);
693 REPORTER_ASSERT(reporter, GrTextContext::CanDrawAsDistanceFields(
694 paint, font, matrix, surfaceProps, true, options));
695
696 // Server.
697 auto serverTf = SkTypeface::MakeFromName("monospace", SkFontStyle());
698 auto serverTfData = server.serializeTypeface(serverTf.get());
699
700 int glyphCount = 10;
701 auto serverBlob = buildTextBlob(serverTf, glyphCount);
702 const SkSurfaceProps props(SkSurfaceProps::kLegacyFontHost_InitType);
703 SkTextBlobCacheDiffCanvas cache_diff_canvas(
704 10, 10, props, &server, ctxInfo.grContext()->supportsDistanceFieldText());
705 cache_diff_canvas.concat(matrix);
706 cache_diff_canvas.drawTextBlob(serverBlob.get(), 0, 0, paint);
707
708 std::vector<uint8_t> serverStrikeData;
709 server.writeStrikeData(&serverStrikeData);
710
711 // Client.
712 auto clientTf = client.deserializeTypeface(serverTfData->data(), serverTfData->size());
713 REPORTER_ASSERT(reporter,
714 client.readStrikeData(serverStrikeData.data(), serverStrikeData.size()));
715 auto clientBlob = buildTextBlob(clientTf, glyphCount);
716
717 SkBitmap expected = RasterBlob(serverBlob, 10, 10, paint, ctxInfo.grContext(), &matrix);
718 SkBitmap actual = RasterBlob(clientBlob, 10, 10, paint, ctxInfo.grContext(), &matrix);
719 compare_blobs(expected, actual, reporter);
720 REPORTER_ASSERT(reporter, !discardableManager->hasCacheMiss());
721
722 // Must unlock everything on termination, otherwise valgrind complains about memory leaks.
723 discardableManager->unlockAndDeleteAll();
724 }
725
DEF_GPUTEST_FOR_RENDERING_CONTEXTS(SkRemoteGlyphCache_CacheMissReporting,reporter,ctxInfo)726 DEF_GPUTEST_FOR_RENDERING_CONTEXTS(SkRemoteGlyphCache_CacheMissReporting, reporter, ctxInfo) {
727 sk_sp<DiscardableManager> discardableManager = sk_make_sp<DiscardableManager>();
728 SkStrikeServer server(discardableManager.get());
729 SkStrikeClient client(discardableManager, false);
730
731 auto serverTf = SkTypeface::MakeFromName("monospace", SkFontStyle());
732 auto tfData = server.serializeTypeface(serverTf.get());
733 auto clientTf = client.deserializeTypeface(tfData->data(), tfData->size());
734 REPORTER_ASSERT(reporter, clientTf);
735 int glyphCount = 10;
736 auto clientBlob = buildTextBlob(clientTf, glyphCount);
737
738 // Raster the client-side blob without the glyph data, we should get cache miss notifications.
739 SkPaint paint;
740 SkMatrix matrix = SkMatrix::I();
741 RasterBlob(clientBlob, 10, 10, paint, ctxInfo.grContext(), &matrix);
742 REPORTER_ASSERT(reporter,
743 discardableManager->cacheMissCount(SkStrikeClient::kFontMetrics) == 1);
744 REPORTER_ASSERT(reporter,
745 discardableManager->cacheMissCount(SkStrikeClient::kGlyphMetrics) == 10);
746
747 // There shouldn't be any image or path requests, since we mark the glyph as empty on a cache
748 // miss.
749 REPORTER_ASSERT(reporter, discardableManager->cacheMissCount(SkStrikeClient::kGlyphImage) == 0);
750 REPORTER_ASSERT(reporter, discardableManager->cacheMissCount(SkStrikeClient::kGlyphPath) == 0);
751
752 // Must unlock everything on termination, otherwise valgrind complains about memory leaks.
753 discardableManager->unlockAndDeleteAll();
754 }
755
MakeEmojiBlob(sk_sp<SkTypeface> serverTf,SkScalar textSize,sk_sp<SkTypeface> clientTf=nullptr)756 sk_sp<SkTextBlob> MakeEmojiBlob(sk_sp<SkTypeface> serverTf, SkScalar textSize,
757 sk_sp<SkTypeface> clientTf = nullptr) {
758 SkFont font;
759 font.setTypeface(serverTf);
760 font.setSize(textSize);
761
762 const char* text = ToolUtils::emoji_sample_text();
763 SkFont serverFont = font;
764 auto blob = SkTextBlob::MakeFromText(text, strlen(text), font);
765 if (clientTf == nullptr) return blob;
766
767 SkSerialProcs s_procs;
768 s_procs.fTypefaceProc = [](SkTypeface*, void* ctx) -> sk_sp<SkData> {
769 return SkData::MakeUninitialized(1u);
770 };
771 auto serialized = blob->serialize(s_procs);
772
773 SkDeserialProcs d_procs;
774 d_procs.fTypefaceCtx = &clientTf;
775 d_procs.fTypefaceProc = [](const void* data, size_t length, void* ctx) -> sk_sp<SkTypeface> {
776 return *(static_cast<sk_sp<SkTypeface>*>(ctx));
777 };
778 return SkTextBlob::Deserialize(serialized->data(), serialized->size(), d_procs);
779 }
780
DEF_GPUTEST_FOR_RENDERING_CONTEXTS(SkRemoteGlyphCache_TypefaceWithNoPaths,reporter,ctxInfo)781 DEF_GPUTEST_FOR_RENDERING_CONTEXTS(SkRemoteGlyphCache_TypefaceWithNoPaths, reporter, ctxInfo) {
782 sk_sp<DiscardableManager> discardableManager = sk_make_sp<DiscardableManager>();
783 SkStrikeServer server(discardableManager.get());
784 SkStrikeClient client(discardableManager, false);
785
786 auto serverTf = ToolUtils::emoji_typeface();
787 auto serverTfData = server.serializeTypeface(serverTf.get());
788 auto clientTf = client.deserializeTypeface(serverTfData->data(), serverTfData->size());
789
790 for (SkScalar textSize : { 70, 180, 270, 340}) {
791 auto serverBlob = MakeEmojiBlob(serverTf, textSize);
792 auto props = FindSurfaceProps(ctxInfo.grContext());
793 SkTextBlobCacheDiffCanvas cache_diff_canvas(
794 500, 500, props, &server, ctxInfo.grContext()->supportsDistanceFieldText());
795 SkPaint paint;
796 cache_diff_canvas.drawTextBlob(serverBlob.get(), 100, 100, paint);
797
798 std::vector<uint8_t> serverStrikeData;
799 server.writeStrikeData(&serverStrikeData);
800 if (!serverStrikeData.empty()) {
801 REPORTER_ASSERT(reporter,
802 client.readStrikeData(serverStrikeData.data(),
803 serverStrikeData.size()));
804 }
805 auto clientBlob = MakeEmojiBlob(serverTf, textSize, clientTf);
806 REPORTER_ASSERT(reporter, clientBlob);
807
808 RasterBlob(clientBlob, 500, 500, paint, ctxInfo.grContext());
809 REPORTER_ASSERT(reporter, !discardableManager->hasCacheMiss());
810 discardableManager->resetCacheMissCounts();
811 }
812
813 // Must unlock everything on termination, otherwise valgrind complains about memory leaks.
814 discardableManager->unlockAndDeleteAll();
815 }
816
817