• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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/SkStrike.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     cache_diff_canvas.drawTextBlob(serverBlob.get(), 0, 0, paint);
215 
216     std::vector<uint8_t> serverStrikeData;
217     server.writeStrikeData(&serverStrikeData);
218 
219     // Client.
220     auto clientTf = client.deserializeTypeface(serverTfData->data(), serverTfData->size());
221     REPORTER_ASSERT(reporter,
222                     client.readStrikeData(serverStrikeData.data(), serverStrikeData.size()));
223     auto clientBlob = buildTextBlob(clientTf, glyphCount);
224 
225     SkBitmap expected = RasterBlob(serverBlob, 10, 10, paint, ctxInfo.grContext());
226     SkBitmap actual = RasterBlob(clientBlob, 10, 10, paint, ctxInfo.grContext());
227     compare_blobs(expected, actual, reporter);
228     REPORTER_ASSERT(reporter, !discardableManager->hasCacheMiss());
229 
230     // Must unlock everything on termination, otherwise valgrind complains about memory leaks.
231     discardableManager->unlockAndDeleteAll();
232 }
233 
DEF_GPUTEST_FOR_RENDERING_CONTEXTS(SkRemoteGlyphCache_ReleaseTypeFace,reporter,ctxInfo)234 DEF_GPUTEST_FOR_RENDERING_CONTEXTS(SkRemoteGlyphCache_ReleaseTypeFace, reporter, ctxInfo) {
235     sk_sp<DiscardableManager> discardableManager = sk_make_sp<DiscardableManager>();
236     SkStrikeServer server(discardableManager.get());
237     SkStrikeClient client(discardableManager, false);
238 
239     // Server.
240     auto serverTf     = TestEmptyTypeface::Make();
241     auto serverTfData = server.serializeTypeface(serverTf.get());
242     REPORTER_ASSERT(reporter, serverTf->unique());
243 
244     {
245         const SkPaint paint;
246         int glyphCount = 10;
247         auto serverBlob = buildTextBlob(serverTf, glyphCount);
248         const SkSurfaceProps props(SkSurfaceProps::kLegacyFontHost_InitType);
249         SkTextBlobCacheDiffCanvas cache_diff_canvas(
250                 10, 10, props, &server, ctxInfo.grContext()->supportsDistanceFieldText());
251         cache_diff_canvas.drawTextBlob(serverBlob.get(), 0, 0, paint);
252         REPORTER_ASSERT(reporter, !serverTf->unique());
253 
254         std::vector<uint8_t> serverStrikeData;
255         server.writeStrikeData(&serverStrikeData);
256     }
257     REPORTER_ASSERT(reporter, serverTf->unique());
258 
259     // Must unlock everything on termination, otherwise valgrind complains about memory leaks.
260     discardableManager->unlockAndDeleteAll();
261 }
262 
DEF_TEST(SkRemoteGlyphCache_StrikeLockingServer,reporter)263 DEF_TEST(SkRemoteGlyphCache_StrikeLockingServer, reporter) {
264     sk_sp<DiscardableManager> discardableManager = sk_make_sp<DiscardableManager>();
265     SkStrikeServer server(discardableManager.get());
266     SkStrikeClient client(discardableManager, false);
267 
268     auto serverTf = SkTypeface::MakeFromName("monospace", SkFontStyle());
269     server.serializeTypeface(serverTf.get());
270     int glyphCount = 10;
271     auto serverBlob = buildTextBlob(serverTf, glyphCount);
272 
273     const SkSurfaceProps props(SkSurfaceProps::kLegacyFontHost_InitType);
274     SkTextBlobCacheDiffCanvas cache_diff_canvas(10, 10, props, &server);
275     SkPaint paint;
276     cache_diff_canvas.drawTextBlob(serverBlob.get(), 0, 0, paint);
277 
278     // The strike from the blob should be locked after it has been drawn on the canvas.
279     REPORTER_ASSERT(reporter, discardableManager->handleCount() == 1u);
280     REPORTER_ASSERT(reporter, discardableManager->lockedHandles().count() == 1u);
281 
282     // Write the strike data and unlock everything. Re-analyzing the blob should lock the handle
283     // again.
284     std::vector<uint8_t> fontData;
285     server.writeStrikeData(&fontData);
286     discardableManager->unlockAll();
287     REPORTER_ASSERT(reporter, discardableManager->lockedHandles().count() == 0u);
288 
289     cache_diff_canvas.drawTextBlob(serverBlob.get(), 0, 0, paint);
290     REPORTER_ASSERT(reporter, discardableManager->handleCount() == 1u);
291     REPORTER_ASSERT(reporter, discardableManager->lockedHandles().count() == 1u);
292 
293     // Must unlock everything on termination, otherwise valgrind complains about memory leaks.
294     discardableManager->unlockAndDeleteAll();
295 }
296 
DEF_TEST(SkRemoteGlyphCache_StrikeDeletionServer,reporter)297 DEF_TEST(SkRemoteGlyphCache_StrikeDeletionServer, reporter) {
298     sk_sp<DiscardableManager> discardableManager = sk_make_sp<DiscardableManager>();
299     SkStrikeServer server(discardableManager.get());
300     SkStrikeClient client(discardableManager, false);
301 
302     auto serverTf = SkTypeface::MakeFromName("monospace", SkFontStyle());
303     server.serializeTypeface(serverTf.get());
304     int glyphCount = 10;
305     auto serverBlob = buildTextBlob(serverTf, glyphCount);
306 
307     const SkSurfaceProps props(SkSurfaceProps::kLegacyFontHost_InitType);
308     SkTextBlobCacheDiffCanvas cache_diff_canvas(10, 10, props, &server);
309     SkPaint paint;
310     cache_diff_canvas.drawTextBlob(serverBlob.get(), 0, 0, paint);
311     REPORTER_ASSERT(reporter, discardableManager->handleCount() == 1u);
312 
313     // Write the strike data and delete all the handles. Re-analyzing the blob should create new
314     // handles.
315     std::vector<uint8_t> fontData;
316     server.writeStrikeData(&fontData);
317     discardableManager->unlockAndDeleteAll();
318     cache_diff_canvas.drawTextBlob(serverBlob.get(), 0, 0, paint);
319     REPORTER_ASSERT(reporter, discardableManager->handleCount() == 2u);
320 
321     // Must unlock everything on termination, otherwise valgrind complains about memory leaks.
322     discardableManager->unlockAndDeleteAll();
323 }
324 
DEF_TEST(SkRemoteGlyphCache_StrikePinningClient,reporter)325 DEF_TEST(SkRemoteGlyphCache_StrikePinningClient, reporter) {
326     sk_sp<DiscardableManager> discardableManager = sk_make_sp<DiscardableManager>();
327     SkStrikeServer server(discardableManager.get());
328     SkStrikeClient client(discardableManager, false);
329 
330     // Server.
331     auto serverTf = SkTypeface::MakeFromName("monospace", SkFontStyle());
332     auto serverTfData = server.serializeTypeface(serverTf.get());
333 
334     int glyphCount = 10;
335     auto serverBlob = buildTextBlob(serverTf, glyphCount);
336 
337     const SkSurfaceProps props(SkSurfaceProps::kLegacyFontHost_InitType);
338     SkTextBlobCacheDiffCanvas cache_diff_canvas(10, 10, props, &server);
339     SkPaint paint;
340     cache_diff_canvas.drawTextBlob(serverBlob.get(), 0, 0, paint);
341 
342     std::vector<uint8_t> serverStrikeData;
343     server.writeStrikeData(&serverStrikeData);
344 
345     // Client.
346     REPORTER_ASSERT(reporter,
347                     client.readStrikeData(serverStrikeData.data(), serverStrikeData.size()));
348     auto* clientTf = client.deserializeTypeface(serverTfData->data(), serverTfData->size()).get();
349 
350     // The cache remains alive until it is pinned in the discardable manager.
351     SkGraphics::PurgeFontCache();
352     REPORTER_ASSERT(reporter, !clientTf->unique());
353 
354     // Once the strike is unpinned and purged, SkStrikeClient should be the only owner of the
355     // clientTf.
356     discardableManager->unlockAndDeleteAll();
357     SkGraphics::PurgeFontCache();
358     REPORTER_ASSERT(reporter, clientTf->unique());
359 
360     // Must unlock everything on termination, otherwise valgrind complains about memory leaks.
361     discardableManager->unlockAndDeleteAll();
362 }
363 
DEF_TEST(SkRemoteGlyphCache_ClientMemoryAccounting,reporter)364 DEF_TEST(SkRemoteGlyphCache_ClientMemoryAccounting, reporter) {
365     sk_sp<DiscardableManager> discardableManager = sk_make_sp<DiscardableManager>();
366     SkStrikeServer server(discardableManager.get());
367     SkStrikeClient client(discardableManager, false);
368 
369     // Server.
370     auto serverTf = SkTypeface::MakeFromName("monospace", SkFontStyle());
371     auto serverTfData = server.serializeTypeface(serverTf.get());
372 
373     int glyphCount = 10;
374     auto serverBlob = buildTextBlob(serverTf, glyphCount);
375 
376     const SkSurfaceProps props(SkSurfaceProps::kLegacyFontHost_InitType);
377     SkTextBlobCacheDiffCanvas cache_diff_canvas(10, 10, props, &server);
378     SkPaint paint;
379     cache_diff_canvas.drawTextBlob(serverBlob.get(), 0, 0, paint);
380 
381     std::vector<uint8_t> serverStrikeData;
382     server.writeStrikeData(&serverStrikeData);
383 
384     // Client.
385     REPORTER_ASSERT(reporter,
386                     client.readStrikeData(serverStrikeData.data(), serverStrikeData.size()));
387     SkStrikeCache::ValidateGlyphCacheDataSize();
388 
389     // Must unlock everything on termination, otherwise valgrind complains about memory leaks.
390     discardableManager->unlockAndDeleteAll();
391 }
392 
DEF_TEST(SkRemoteGlyphCache_PurgesServerEntries,reporter)393 DEF_TEST(SkRemoteGlyphCache_PurgesServerEntries, reporter) {
394     sk_sp<DiscardableManager> discardableManager = sk_make_sp<DiscardableManager>();
395     SkStrikeServer server(discardableManager.get());
396     server.setMaxEntriesInDescriptorMapForTesting(1u);
397     SkStrikeClient client(discardableManager, false);
398 
399     {
400         auto serverTf = SkTypeface::MakeFromName("monospace", SkFontStyle());
401         int glyphCount = 10;
402         auto serverBlob = buildTextBlob(serverTf, glyphCount);
403 
404         const SkSurfaceProps props(SkSurfaceProps::kLegacyFontHost_InitType);
405         SkTextBlobCacheDiffCanvas cache_diff_canvas(10, 10, props, &server);
406         SkPaint paint;
407         REPORTER_ASSERT(reporter, server.remoteGlyphStateMapSizeForTesting() == 0u);
408         cache_diff_canvas.drawTextBlob(serverBlob.get(), 0, 0, paint);
409         REPORTER_ASSERT(reporter, server.remoteGlyphStateMapSizeForTesting() == 1u);
410     }
411 
412     // Serialize to release the lock from the strike server and delete all current
413     // handles.
414     std::vector<uint8_t> fontData;
415     server.writeStrikeData(&fontData);
416     discardableManager->unlockAndDeleteAll();
417 
418     // Use a different typeface. Creating a new strike should evict the previous
419     // one.
420     {
421         auto serverTf = SkTypeface::MakeFromName("Georgia", SkFontStyle());
422         int glyphCount = 10;
423         auto serverBlob = buildTextBlob(serverTf, glyphCount);
424 
425         const SkSurfaceProps props(SkSurfaceProps::kLegacyFontHost_InitType);
426         SkTextBlobCacheDiffCanvas cache_diff_canvas(10, 10, props, &server);
427         SkPaint paint;
428         REPORTER_ASSERT(reporter, server.remoteGlyphStateMapSizeForTesting() == 1u);
429         cache_diff_canvas.drawTextBlob(serverBlob.get(), 0, 0, paint);
430         REPORTER_ASSERT(reporter, server.remoteGlyphStateMapSizeForTesting() == 1u);
431     }
432 
433     // Must unlock everything on termination, otherwise valgrind complains about memory leaks.
434     discardableManager->unlockAndDeleteAll();
435 }
436 
DEF_GPUTEST_FOR_RENDERING_CONTEXTS(SkRemoteGlyphCache_DrawTextAsPath,reporter,ctxInfo)437 DEF_GPUTEST_FOR_RENDERING_CONTEXTS(SkRemoteGlyphCache_DrawTextAsPath, reporter, ctxInfo) {
438     sk_sp<DiscardableManager> discardableManager = sk_make_sp<DiscardableManager>();
439     SkStrikeServer server(discardableManager.get());
440     SkStrikeClient client(discardableManager, false);
441     SkPaint paint;
442     paint.setStyle(SkPaint::kStroke_Style);
443     paint.setStrokeWidth(0);
444     REPORTER_ASSERT(reporter,
445             SkStrikeSpec::ShouldDrawAsPath(paint, SkFont(), SkMatrix::I()));
446 
447     // Server.
448     auto serverTf = SkTypeface::MakeFromName("monospace", SkFontStyle());
449     auto serverTfData = server.serializeTypeface(serverTf.get());
450 
451     int glyphCount = 10;
452     auto serverBlob = buildTextBlob(serverTf, glyphCount);
453     auto props = FindSurfaceProps(ctxInfo.grContext());
454     SkTextBlobCacheDiffCanvas cache_diff_canvas(
455             10, 10, props, &server, ctxInfo.grContext()->supportsDistanceFieldText());
456     cache_diff_canvas.drawTextBlob(serverBlob.get(), 0, 0, paint);
457 
458     std::vector<uint8_t> serverStrikeData;
459     server.writeStrikeData(&serverStrikeData);
460 
461     // Client.
462     auto clientTf = client.deserializeTypeface(serverTfData->data(), serverTfData->size());
463     REPORTER_ASSERT(reporter,
464                     client.readStrikeData(serverStrikeData.data(), serverStrikeData.size()));
465     auto clientBlob = buildTextBlob(clientTf, glyphCount);
466 
467     SkBitmap expected = RasterBlob(serverBlob, 10, 10, paint, ctxInfo.grContext());
468     SkBitmap actual = RasterBlob(clientBlob, 10, 10, paint, ctxInfo.grContext());
469     compare_blobs(expected, actual, reporter, 1);
470     REPORTER_ASSERT(reporter, !discardableManager->hasCacheMiss());
471     SkStrikeCache::ValidateGlyphCacheDataSize();
472 
473     // Must unlock everything on termination, otherwise valgrind complains about memory leaks.
474     discardableManager->unlockAndDeleteAll();
475 }
476 
make_blob_causing_fallback(sk_sp<SkTypeface> targetTf,const SkTypeface * glyphTf,skiatest::Reporter * reporter)477 sk_sp<SkTextBlob> make_blob_causing_fallback(
478         sk_sp<SkTypeface> targetTf, const SkTypeface* glyphTf, skiatest::Reporter* reporter) {
479     SkFont font;
480     font.setSubpixel(true);
481     font.setSize(96);
482     font.setHinting(SkFontHinting::kNormal);
483     font.setTypeface(targetTf);
484 
485     REPORTER_ASSERT(reporter,
486             !SkStrikeSpec::ShouldDrawAsPath(SkPaint(), font, SkMatrix::I()));
487 
488     char s[] = "Skia";
489     int runSize = strlen(s);
490 
491     SkTextBlobBuilder builder;
492     SkRect bounds = SkRect::MakeIWH(100, 100);
493     const auto& runBuffer = builder.allocRunPosH(font, runSize, 10, &bounds);
494     SkASSERT(runBuffer.utf8text == nullptr);
495     SkASSERT(runBuffer.clusters == nullptr);
496 
497     SkFont(sk_ref_sp(glyphTf)).textToGlyphs(s, strlen(s), SkTextEncoding::kUTF8,
498                                             runBuffer.glyphs, runSize);
499 
500     SkRect glyphBounds;
501     font.getWidths(runBuffer.glyphs, 1, nullptr, &glyphBounds);
502 
503     REPORTER_ASSERT(reporter, glyphBounds.width() > SkStrikeCommon::kSkSideTooBigForAtlas);
504 
505     for (int i = 0; i < runSize; i++) {
506         runBuffer.pos[i] = i * 10;
507     }
508 
509     return builder.make();
510 }
511 
DEF_GPUTEST_FOR_RENDERING_CONTEXTS(SkRemoteGlyphCache_DrawTextAsMaskWithPathFallback,reporter,ctxInfo)512 DEF_GPUTEST_FOR_RENDERING_CONTEXTS(SkRemoteGlyphCache_DrawTextAsMaskWithPathFallback,
513         reporter, ctxInfo) {
514     sk_sp<DiscardableManager> discardableManager = sk_make_sp<DiscardableManager>();
515     SkStrikeServer server(discardableManager.get());
516     SkStrikeClient client(discardableManager, false);
517 
518     SkPaint paint;
519 
520     auto serverTf = MakeResourceAsTypeface("fonts/HangingS.ttf");
521     // TODO: when the cq bots can handle this font remove the check.
522     if (serverTf == nullptr) {
523         return;
524     }
525     auto serverTfData = server.serializeTypeface(serverTf.get());
526 
527     auto serverBlob = make_blob_causing_fallback(serverTf, serverTf.get(), reporter);
528 
529     auto props = FindSurfaceProps(ctxInfo.grContext());
530     SkTextBlobCacheDiffCanvas cache_diff_canvas(
531             10, 10, props, &server, ctxInfo.grContext()->supportsDistanceFieldText());
532     cache_diff_canvas.drawTextBlob(serverBlob.get(), 0, 0, paint);
533 
534     std::vector<uint8_t> serverStrikeData;
535     server.writeStrikeData(&serverStrikeData);
536 
537     // Client.
538     auto clientTf = client.deserializeTypeface(serverTfData->data(), serverTfData->size());
539     REPORTER_ASSERT(reporter,
540                     client.readStrikeData(serverStrikeData.data(), serverStrikeData.size()));
541 
542     auto clientBlob = make_blob_causing_fallback(clientTf, serverTf.get(), reporter);
543 
544     SkBitmap expected = RasterBlob(serverBlob, 10, 10, paint, ctxInfo.grContext());
545     SkBitmap actual = RasterBlob(clientBlob, 10, 10, paint, ctxInfo.grContext());
546     compare_blobs(expected, actual, reporter);
547     REPORTER_ASSERT(reporter, !discardableManager->hasCacheMiss());
548     SkStrikeCache::ValidateGlyphCacheDataSize();
549 
550     // Must unlock everything on termination, otherwise valgrind complains about memory leaks.
551     discardableManager->unlockAndDeleteAll();
552 }
553 
554 #if 0
555 // TODO: turn this one when I figure out how to deal with the pixel variance from linear
556 //  interpolation from GPU to GPU.
557 DEF_GPUTEST_FOR_RENDERING_CONTEXTS(SkRemoteGlyphCache_DrawTextAsSDFTWithAllARGBFallback,
558                                    reporter, ctxInfo) {
559     sk_sp<DiscardableManager> discardableManager = sk_make_sp<DiscardableManager>();
560     SkStrikeServer server(discardableManager.get());
561     SkStrikeClient client(discardableManager, false);
562 
563     SkPaint paint;
564 
565     auto serverTf = ToolUtils::planet_typeface();
566     // TODO: when the cq bots can handle this font remove the check.
567     if (serverTf == nullptr) {
568         return;
569     }
570     auto serverTfData = server.serializeTypeface(serverTf.get());
571 
572     auto makeBlob = [&reporter](sk_sp<SkTypeface> typeface) {
573         SkFont font;
574         font.setSubpixel(true);
575         font.setSize(96);
576         font.setHinting(SkFontHinting::kNormal);
577         font.setTypeface(typeface);
578 
579         REPORTER_ASSERT(reporter, !SkDraw::ShouldDrawTextAsPaths(font, SkPaint(), SkMatrix::I()));
580 
581         // Mercury to Uranus.
582         SkGlyphID glyphs[] = {1, 2, 3, 4, 5, 6, 7, 8};
583 
584         SkTextBlobBuilder builder;
585         SkRect bounds = SkRect::MakeIWH(100, 100);
586         const auto& runBuffer = builder.allocRunPosH(font, SK_ARRAY_COUNT(glyphs), 100, &bounds);
587         SkASSERT(runBuffer.utf8text == nullptr);
588         SkASSERT(runBuffer.clusters == nullptr);
589 
590         std::copy(std::begin(glyphs), std::end(glyphs), runBuffer.glyphs);
591 
592         for (size_t i = 0; i < SK_ARRAY_COUNT(glyphs); i++) {
593             runBuffer.pos[i] = i * 100;
594         }
595 
596         return builder.make();
597     };
598 
599     auto serverBlob = makeBlob(serverTf);
600 
601     auto props = FindSurfaceProps(ctxInfo.grContext());
602     SkTextBlobCacheDiffCanvas cache_diff_canvas(
603             800, 800, props, &server, ctxInfo.grContext()->supportsDistanceFieldText());
604     cache_diff_canvas.drawTextBlob(serverBlob.get(), 0, 400, paint);
605 
606     std::vector<uint8_t> serverStrikeData;
607     server.writeStrikeData(&serverStrikeData);
608 
609     // Client.
610     auto clientTf = client.deserializeTypeface(serverTfData->data(), serverTfData->size());
611     REPORTER_ASSERT(reporter,
612                     client.readStrikeData(serverStrikeData.data(), serverStrikeData.size()));
613 
614     auto clientBlob = makeBlob(clientTf);
615 
616     SkBitmap expected = RasterBlob(serverBlob, 800, 800, paint, ctxInfo.grContext());
617     SkBitmap actual = RasterBlob(clientBlob, 800, 800, paint, ctxInfo.grContext());
618 
619     // Pixel variance can be high because of the atlas placement, and large scaling in the linear
620     // interpolation.
621     compare_blobs(expected, actual, reporter, 36);
622     REPORTER_ASSERT(reporter, !discardableManager->hasCacheMiss());
623     SkStrikeCache::ValidateGlyphCacheDataSize();
624 
625     // Must unlock everything on termination, otherwise valgrind complains about memory leaks.
626     discardableManager->unlockAndDeleteAll();
627 }
628 #endif
629 
DEF_GPUTEST_FOR_RENDERING_CONTEXTS(SkRemoteGlyphCache_DrawTextXY,reporter,ctxInfo)630 DEF_GPUTEST_FOR_RENDERING_CONTEXTS(SkRemoteGlyphCache_DrawTextXY, reporter, ctxInfo) {
631     sk_sp<DiscardableManager> discardableManager = sk_make_sp<DiscardableManager>();
632     SkStrikeServer server(discardableManager.get());
633     SkStrikeClient client(discardableManager, false);
634     SkPaint paint;
635     paint.setAntiAlias(true);
636 
637     // Server.
638     auto serverTf = SkTypeface::MakeFromName("monospace", SkFontStyle());
639     auto serverTfData = server.serializeTypeface(serverTf.get());
640 
641     int glyphCount = 10;
642     auto serverBlob = buildTextBlob(serverTf, glyphCount);
643     auto props = FindSurfaceProps(ctxInfo.grContext());
644     SkTextBlobCacheDiffCanvas cache_diff_canvas(
645             10, 10, props, &server, ctxInfo.grContext()->supportsDistanceFieldText());
646     cache_diff_canvas.drawTextBlob(serverBlob.get(), 0.5, 0, paint);
647 
648     std::vector<uint8_t> serverStrikeData;
649     server.writeStrikeData(&serverStrikeData);
650 
651     // Client.
652     auto clientTf = client.deserializeTypeface(serverTfData->data(), serverTfData->size());
653     REPORTER_ASSERT(reporter,
654                     client.readStrikeData(serverStrikeData.data(), serverStrikeData.size()));
655     auto clientBlob = buildTextBlob(clientTf, glyphCount);
656 
657     SkBitmap expected = RasterBlob(serverBlob, 10, 10, paint, ctxInfo.grContext(), nullptr, 0.5);
658     SkBitmap actual = RasterBlob(clientBlob, 10, 10, paint, ctxInfo.grContext(), nullptr, 0.5);
659     compare_blobs(expected, actual, reporter);
660     REPORTER_ASSERT(reporter, !discardableManager->hasCacheMiss());
661     SkStrikeCache::ValidateGlyphCacheDataSize();
662 
663     // Must unlock everything on termination, otherwise valgrind complains about memory leaks.
664     discardableManager->unlockAndDeleteAll();
665 }
666 
DEF_GPUTEST_FOR_RENDERING_CONTEXTS(SkRemoteGlyphCache_DrawTextAsDFT,reporter,ctxInfo)667 DEF_GPUTEST_FOR_RENDERING_CONTEXTS(SkRemoteGlyphCache_DrawTextAsDFT, reporter, ctxInfo) {
668     sk_sp<DiscardableManager> discardableManager = sk_make_sp<DiscardableManager>();
669     SkStrikeServer server(discardableManager.get());
670     SkStrikeClient client(discardableManager, false);
671     SkPaint paint;
672     SkFont font;
673 
674     // A perspective transform forces fallback to dft.
675     SkMatrix matrix = SkMatrix::I();
676     matrix[SkMatrix::kMPersp0] = 0.5f;
677     REPORTER_ASSERT(reporter, matrix.hasPerspective());
678     SkSurfaceProps surfaceProps(0, kUnknown_SkPixelGeometry);
679     GrTextContext::Options options;
680     GrTextContext::SanitizeOptions(&options);
681     REPORTER_ASSERT(reporter, GrTextContext::CanDrawAsDistanceFields(
682                                       paint, font, matrix, surfaceProps, true, options));
683 
684     // Server.
685     auto serverTf = SkTypeface::MakeFromName("monospace", SkFontStyle());
686     auto serverTfData = server.serializeTypeface(serverTf.get());
687 
688     int glyphCount = 10;
689     auto serverBlob = buildTextBlob(serverTf, glyphCount);
690     const SkSurfaceProps props(SkSurfaceProps::kLegacyFontHost_InitType);
691     SkTextBlobCacheDiffCanvas cache_diff_canvas(
692             10, 10, props, &server, ctxInfo.grContext()->supportsDistanceFieldText());
693     cache_diff_canvas.concat(matrix);
694     cache_diff_canvas.drawTextBlob(serverBlob.get(), 0, 0, paint);
695 
696     std::vector<uint8_t> serverStrikeData;
697     server.writeStrikeData(&serverStrikeData);
698 
699     // Client.
700     auto clientTf = client.deserializeTypeface(serverTfData->data(), serverTfData->size());
701     REPORTER_ASSERT(reporter,
702                     client.readStrikeData(serverStrikeData.data(), serverStrikeData.size()));
703     auto clientBlob = buildTextBlob(clientTf, glyphCount);
704 
705     SkBitmap expected = RasterBlob(serverBlob, 10, 10, paint, ctxInfo.grContext(), &matrix);
706     SkBitmap actual = RasterBlob(clientBlob, 10, 10, paint, ctxInfo.grContext(), &matrix);
707     compare_blobs(expected, actual, reporter);
708     REPORTER_ASSERT(reporter, !discardableManager->hasCacheMiss());
709     SkStrikeCache::ValidateGlyphCacheDataSize();
710 
711     // Must unlock everything on termination, otherwise valgrind complains about memory leaks.
712     discardableManager->unlockAndDeleteAll();
713 }
714 
DEF_GPUTEST_FOR_RENDERING_CONTEXTS(SkRemoteGlyphCache_CacheMissReporting,reporter,ctxInfo)715 DEF_GPUTEST_FOR_RENDERING_CONTEXTS(SkRemoteGlyphCache_CacheMissReporting, reporter, ctxInfo) {
716     sk_sp<DiscardableManager> discardableManager = sk_make_sp<DiscardableManager>();
717     SkStrikeServer server(discardableManager.get());
718     SkStrikeClient client(discardableManager, false);
719 
720     auto serverTf = SkTypeface::MakeFromName("monospace", SkFontStyle());
721     auto tfData = server.serializeTypeface(serverTf.get());
722     auto clientTf = client.deserializeTypeface(tfData->data(), tfData->size());
723     REPORTER_ASSERT(reporter, clientTf);
724     int glyphCount = 10;
725     auto clientBlob = buildTextBlob(clientTf, glyphCount);
726 
727     // Raster the client-side blob without the glyph data, we should get cache miss notifications.
728     SkPaint paint;
729     SkMatrix matrix = SkMatrix::I();
730     RasterBlob(clientBlob, 10, 10, paint, ctxInfo.grContext(), &matrix);
731     REPORTER_ASSERT(reporter,
732                     discardableManager->cacheMissCount(SkStrikeClient::kFontMetrics) == 1);
733     REPORTER_ASSERT(reporter,
734                     discardableManager->cacheMissCount(SkStrikeClient::kGlyphMetrics) == 10);
735 
736     // There shouldn't be any image or path requests, since we mark the glyph as empty on a cache
737     // miss.
738     REPORTER_ASSERT(reporter, discardableManager->cacheMissCount(SkStrikeClient::kGlyphImage) == 0);
739     REPORTER_ASSERT(reporter, discardableManager->cacheMissCount(SkStrikeClient::kGlyphPath) == 0);
740 
741     // Must unlock everything on termination, otherwise valgrind complains about memory leaks.
742     discardableManager->unlockAndDeleteAll();
743 }
744 
MakeEmojiBlob(sk_sp<SkTypeface> serverTf,SkScalar textSize,sk_sp<SkTypeface> clientTf=nullptr)745 sk_sp<SkTextBlob> MakeEmojiBlob(sk_sp<SkTypeface> serverTf, SkScalar textSize,
746                                 sk_sp<SkTypeface> clientTf = nullptr) {
747     SkFont font;
748     font.setTypeface(serverTf);
749     font.setSize(textSize);
750 
751     const char* text = ToolUtils::emoji_sample_text();
752     SkFont serverFont = font;
753     auto blob = SkTextBlob::MakeFromText(text, strlen(text), font);
754     if (clientTf == nullptr) return blob;
755 
756     SkSerialProcs s_procs;
757     s_procs.fTypefaceProc = [](SkTypeface*, void* ctx) -> sk_sp<SkData> {
758         return SkData::MakeUninitialized(1u);
759     };
760     auto serialized = blob->serialize(s_procs);
761 
762     SkDeserialProcs d_procs;
763     d_procs.fTypefaceCtx = &clientTf;
764     d_procs.fTypefaceProc = [](const void* data, size_t length, void* ctx) -> sk_sp<SkTypeface> {
765         return *(static_cast<sk_sp<SkTypeface>*>(ctx));
766     };
767     return SkTextBlob::Deserialize(serialized->data(), serialized->size(), d_procs);
768 }
769 
DEF_GPUTEST_FOR_RENDERING_CONTEXTS(SkRemoteGlyphCache_TypefaceWithNoPaths,reporter,ctxInfo)770 DEF_GPUTEST_FOR_RENDERING_CONTEXTS(SkRemoteGlyphCache_TypefaceWithNoPaths, reporter, ctxInfo) {
771     sk_sp<DiscardableManager> discardableManager = sk_make_sp<DiscardableManager>();
772     SkStrikeServer server(discardableManager.get());
773     SkStrikeClient client(discardableManager, false);
774 
775     auto serverTf = ToolUtils::emoji_typeface();
776     auto serverTfData = server.serializeTypeface(serverTf.get());
777     auto clientTf = client.deserializeTypeface(serverTfData->data(), serverTfData->size());
778 
779     for (SkScalar textSize : { 70, 180, 270, 340}) {
780         auto serverBlob = MakeEmojiBlob(serverTf, textSize);
781         auto props = FindSurfaceProps(ctxInfo.grContext());
782         SkTextBlobCacheDiffCanvas cache_diff_canvas(
783                 500, 500, props, &server, ctxInfo.grContext()->supportsDistanceFieldText());
784         SkPaint paint;
785         cache_diff_canvas.drawTextBlob(serverBlob.get(), 100, 100, paint);
786 
787         std::vector<uint8_t> serverStrikeData;
788         server.writeStrikeData(&serverStrikeData);
789 
790         REPORTER_ASSERT(reporter,
791                         client.readStrikeData(serverStrikeData.data(), serverStrikeData.size()));
792         auto clientBlob = MakeEmojiBlob(serverTf, textSize, clientTf);
793         REPORTER_ASSERT(reporter, clientBlob);
794 
795         RasterBlob(clientBlob, 500, 500, paint, ctxInfo.grContext());
796         REPORTER_ASSERT(reporter, !discardableManager->hasCacheMiss());
797         SkStrikeCache::ValidateGlyphCacheDataSize();
798         discardableManager->resetCacheMissCounts();
799     }
800 
801     // Must unlock everything on termination, otherwise valgrind complains about memory leaks.
802     discardableManager->unlockAndDeleteAll();
803 }
804 
DEF_TEST(SkRemoteGlyphCache_SearchOfDesperation,reporter)805 DEF_TEST(SkRemoteGlyphCache_SearchOfDesperation, reporter) {
806     // Build proxy typeface on the client for initializing the cache.
807     sk_sp<DiscardableManager> discardableManager = sk_make_sp<DiscardableManager>();
808     SkStrikeServer server(discardableManager.get());
809     SkStrikeClient client(discardableManager, false);
810 
811     auto serverTf = SkTypeface::MakeFromName("monospace", SkFontStyle());
812     auto tfData = server.serializeTypeface(serverTf.get());
813     auto clientTf = client.deserializeTypeface(tfData->data(), tfData->size());
814     REPORTER_ASSERT(reporter, clientTf);
815 
816     SkFont font;
817     font.setTypeface(clientTf);
818     font.setSubpixel(true);
819     SkPaint paint;
820     paint.setAntiAlias(true);
821     paint.setColor(SK_ColorRED);
822 
823     auto lostGlyphID = SkPackedGlyphID(1, SK_FixedHalf, SK_FixedHalf);
824     const uint8_t glyphImage[] = {0xFF, 0xFF};
825 
826     SkStrikeCache strikeCache;
827 
828     // Build a fallback cache.
829     {
830         SkAutoDescriptor ad;
831         SkScalerContextRec rec;
832         SkScalerContextEffects effects;
833         SkScalerContextFlags flags = SkScalerContextFlags::kFakeGammaAndBoostContrast;
834         SkScalerContext::MakeRecAndEffects(
835                 font, paint, SkSurfacePropsCopyOrDefault(nullptr), flags,
836                 SkMatrix::I(), &rec, &effects);
837         auto desc = SkScalerContext::AutoDescriptorGivenRecAndEffects(rec, effects, &ad);
838 
839         auto fallbackCache = strikeCache.findOrCreateStrikeExclusive(*desc, effects, *clientTf);
840         SkGlyphPrototype proto = {lostGlyphID, 0.f, 0.f, 2, 1, 0, 0, SkMask::kA8_Format, false};
841         fallbackCache->glyphFromPrototype(proto, (void*)glyphImage);
842     }
843 
844     // Make sure we can find the fall back cache.
845     {
846         SkAutoDescriptor ad;
847         SkScalerContextRec rec;
848         SkScalerContextEffects effects;
849         SkScalerContextFlags flags = SkScalerContextFlags::kFakeGammaAndBoostContrast;
850         SkScalerContext::MakeRecAndEffects(
851                 font, paint, SkSurfacePropsCopyOrDefault(nullptr), flags,
852                 SkMatrix::I(), &rec, &effects);
853         auto desc = SkScalerContext::AutoDescriptorGivenRecAndEffects(rec, effects, &ad);
854         auto testCache = strikeCache.findStrikeExclusive(*desc);
855         REPORTER_ASSERT(reporter, !(testCache == nullptr));
856     }
857 
858     // Create the target cache.
859     SkExclusiveStrikePtr testCache;
860     SkAutoDescriptor ad;
861     SkScalerContextRec rec;
862     SkScalerContextEffects effects;
863     SkScalerContextFlags flags = SkScalerContextFlags::kNone;
864     SkScalerContext::MakeRecAndEffects(
865             font, paint, SkSurfacePropsCopyOrDefault(nullptr), flags,
866             SkMatrix::I(), &rec, &effects);
867     auto desc = SkScalerContext::AutoDescriptorGivenRecAndEffects(rec, effects, &ad);
868     testCache = strikeCache.findStrikeExclusive(*desc);
869     REPORTER_ASSERT(reporter, testCache == nullptr);
870     testCache = strikeCache.createStrikeExclusive(*desc,
871                                                      clientTf->createScalerContext(effects, desc));
872     auto scalerProxy = static_cast<SkScalerContextProxy*>(testCache->getScalerContext());
873     scalerProxy->initCache(testCache.get(), &strikeCache);
874 
875     // Look for the lost glyph.
876     {
877         SkPoint pt{SkFixedToScalar(lostGlyphID.getSubXFixed()),
878                    SkFixedToScalar(lostGlyphID.getSubYFixed())};
879         SkGlyph* lostGlyph = testCache->glyph(lostGlyphID.code(), pt);
880         testCache->prepareImage(lostGlyph);
881 
882         REPORTER_ASSERT(reporter, lostGlyph->height() == 1);
883         REPORTER_ASSERT(reporter, lostGlyph->width() == 2);
884         REPORTER_ASSERT(reporter, lostGlyph->maskFormat() == SkMask::kA8_Format);
885         REPORTER_ASSERT(reporter, memcmp(lostGlyph->image(), glyphImage, sizeof(glyphImage)) == 0);
886     }
887 
888     // Look for the lost glyph with a different sub-pix position.
889     {
890         SkPoint pt{SkFixedToScalar(SK_FixedQuarter),
891                    SkFixedToScalar(SK_FixedQuarter)};
892         SkGlyph* lostGlyph = testCache->glyph(lostGlyphID.code(), pt);
893         testCache->prepareImage(lostGlyph);
894 
895         REPORTER_ASSERT(reporter, lostGlyph->height() == 1);
896         REPORTER_ASSERT(reporter, lostGlyph->width() == 2);
897         REPORTER_ASSERT(reporter, lostGlyph->maskFormat() == SkMask::kA8_Format);
898         REPORTER_ASSERT(reporter, memcmp(lostGlyph->image(), glyphImage, sizeof(glyphImage)) == 0);
899     }
900 
901     for (uint32_t i = 0; i <= SkStrikeClient::CacheMissType::kLast; ++i) {
902         if (i == SkStrikeClient::CacheMissType::kGlyphMetricsFallback ||
903             i == SkStrikeClient::CacheMissType::kFontMetrics) {
904             REPORTER_ASSERT(reporter, discardableManager->cacheMissCount(i) == 2);
905         } else {
906             REPORTER_ASSERT(reporter, discardableManager->cacheMissCount(i) == 0);
907         }
908     }
909     strikeCache.validateGlyphCacheDataSize();
910 
911     // Must unlock everything on termination, otherwise valgrind complains about memory leaks.
912     discardableManager->unlockAndDeleteAll();
913 }
914 
DEF_TEST(SkRemoteGlyphCache_ReWriteGlyph,reporter)915 DEF_TEST(SkRemoteGlyphCache_ReWriteGlyph, reporter) {
916     // Build proxy typeface on the client for initializing the cache.
917     sk_sp<DiscardableManager> discardableManager = sk_make_sp<DiscardableManager>();
918     SkStrikeServer server(discardableManager.get());
919     SkStrikeClient client(discardableManager, false);
920 
921     auto serverTf = SkTypeface::MakeFromName("monospace", SkFontStyle());
922     auto tfData = server.serializeTypeface(serverTf.get());
923     auto clientTf = client.deserializeTypeface(tfData->data(), tfData->size());
924     REPORTER_ASSERT(reporter, clientTf);
925 
926     SkFont font;
927     font.setEdging(SkFont::Edging::kAntiAlias);
928     SkPaint paint;
929     paint.setColor(SK_ColorRED);
930 
931     auto lostGlyphID = SkPackedGlyphID(1, SK_FixedHalf, SK_FixedHalf);
932     const uint8_t glyphImage[] = {0xFF, 0xFF};
933     SkMask::Format realMask;
934     SkMask::Format fakeMask;
935 
936     SkStrikeCache strikeCache;
937 
938     {
939         SkAutoDescriptor ad;
940         SkScalerContextRec rec;
941         SkScalerContextEffects effects;
942         SkScalerContextFlags flags = SkScalerContextFlags::kFakeGammaAndBoostContrast;
943         font.setTypeface(serverTf);
944         SkScalerContext::MakeRecAndEffects(
945                 font, paint, SkSurfacePropsCopyOrDefault(nullptr), flags,
946                 SkMatrix::I(), &rec, &effects);
947         auto desc = SkScalerContext::AutoDescriptorGivenRecAndEffects(rec, effects, &ad);
948 
949         auto context = serverTf->createScalerContext(effects, desc, false);
950         SkGlyph glyph{lostGlyphID};
951         context->getMetrics(&glyph);
952         realMask = glyph.maskFormat();
953     }
954 
955     // Build a fallback cache.
956     {
957         SkAutoDescriptor ad;
958         SkScalerContextRec rec;
959         SkScalerContextEffects effects;
960         SkScalerContextFlags flags = SkScalerContextFlags::kFakeGammaAndBoostContrast;
961         font.setTypeface(clientTf);
962         SkScalerContext::MakeRecAndEffects(
963                 font, paint, SkSurfacePropsCopyOrDefault(nullptr), flags,
964                 SkMatrix::I(), &rec, &effects);
965         auto desc = SkScalerContext::AutoDescriptorGivenRecAndEffects(rec, effects, &ad);
966 
967         auto fallbackCache = strikeCache.findOrCreateStrikeExclusive(*desc, effects, *clientTf);
968         fakeMask = (realMask == SkMask::kA8_Format) ? SkMask::kBW_Format : SkMask::kA8_Format;
969         SkGlyphPrototype proto = {lostGlyphID, 0.f, 0.f, 2, 1, 0, 0, fakeMask, false};
970         fallbackCache->glyphFromPrototype(proto, (void *)glyphImage);
971     }
972 
973     // Send over the real glyph and make sure the client cache stays intact.
974     {
975         SkAutoDescriptor ad;
976         SkScalerContextEffects effects;
977         SkScalerContextFlags flags = SkScalerContextFlags::kFakeGammaAndBoostContrast;
978         font.setTypeface(serverTf);
979         auto* cacheState = server.getOrCreateCache(
980                 paint, font, SkSurfacePropsCopyOrDefault(nullptr),
981                 SkMatrix::I(), flags, &effects);
982         SkStrikeServer::AddGlyphForTesting(cacheState, lostGlyphID, false);
983 
984         std::vector<uint8_t> serverStrikeData;
985         server.writeStrikeData(&serverStrikeData);
986         REPORTER_ASSERT(reporter,
987                         client.readStrikeData(
988                                 serverStrikeData.data(),
989                                 serverStrikeData.size()));
990     }
991 
992     {
993         SkAutoDescriptor ad;
994         SkScalerContextRec rec;
995         SkScalerContextEffects effects;
996         SkScalerContextFlags flags = SkScalerContextFlags::kFakeGammaAndBoostContrast;
997         font.setTypeface(clientTf);
998         SkScalerContext::MakeRecAndEffects(
999                 font, paint, SkSurfaceProps(0, kUnknown_SkPixelGeometry), flags,
1000                 SkMatrix::I(), &rec, &effects);
1001         auto desc = SkScalerContext::AutoDescriptorGivenRecAndEffects(rec, effects, &ad);
1002 
1003         auto fallbackCache = strikeCache.findStrikeExclusive(*desc);
1004         REPORTER_ASSERT(reporter, fallbackCache.get() != nullptr);
1005         auto glyph = fallbackCache->glyphOrNull(lostGlyphID);
1006         REPORTER_ASSERT(reporter, glyph && glyph->maskFormat() == fakeMask);
1007         if (glyph) {
1008             REPORTER_ASSERT(reporter,
1009                             memcmp(glyph->image(), glyphImage, glyph->imageSize()) == 0);
1010         }
1011     }
1012 
1013     strikeCache.validateGlyphCacheDataSize();
1014 
1015     // Must unlock everything on termination, otherwise valgrind complains about memory leaks.
1016     discardableManager->unlockAndDeleteAll();
1017 }
1018