• 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 "Resources.h"
9 #include "SkDraw.h"
10 #include "SkGraphics.h"
11 #include "SkMutex.h"
12 #include "SkRemoteGlyphCache.h"
13 #include "SkRemoteGlyphCacheImpl.h"
14 #include "SkStrike.h"
15 #include "SkStrikeCache.h"
16 #include "SkSurface.h"
17 #include "SkSurfacePriv.h"
18 #include "SkTestEmptyTypeface.h"
19 #include "SkTextBlob.h"
20 #include "SkTypeface_remote.h"
21 #include "Test.h"
22 
23 #include "text/GrTextContext.h"
24 
25 class DiscardableManager : public SkStrikeServer::DiscardableHandleManager,
26                            public SkStrikeClient::DiscardableHandleManager {
27 public:
DiscardableManager()28     DiscardableManager() { sk_bzero(&fCacheMissCount, sizeof(fCacheMissCount)); }
29     ~DiscardableManager() override = default;
30 
31     // Server implementation.
createHandle()32     SkDiscardableHandleId createHandle() override {
33         // Handles starts as locked.
34         fLockedHandles.add(++fNextHandleId);
35         return fNextHandleId;
36     }
lockHandle(SkDiscardableHandleId id)37     bool lockHandle(SkDiscardableHandleId id) override {
38         if (id <= fLastDeletedHandleId) return false;
39         fLockedHandles.add(id);
40         return true;
41     }
42 
43     // Client implementation.
deleteHandle(SkDiscardableHandleId id)44     bool deleteHandle(SkDiscardableHandleId id) override { return id <= fLastDeletedHandleId; }
notifyCacheMiss(SkStrikeClient::CacheMissType type)45     void notifyCacheMiss(SkStrikeClient::CacheMissType type) override { fCacheMissCount[type]++; }
isHandleDeleted(SkDiscardableHandleId id)46     bool isHandleDeleted(SkDiscardableHandleId id) override { return id <= fLastDeletedHandleId; }
47 
unlockAll()48     void unlockAll() { fLockedHandles.reset(); }
unlockAndDeleteAll()49     void unlockAndDeleteAll() {
50         unlockAll();
51         fLastDeletedHandleId = fNextHandleId;
52     }
lockedHandles() const53     const SkTHashSet<SkDiscardableHandleId>& lockedHandles() const { return fLockedHandles; }
handleCount()54     SkDiscardableHandleId handleCount() { return fNextHandleId; }
cacheMissCount(uint32_t type)55     int cacheMissCount(uint32_t type) { return fCacheMissCount[type]; }
hasCacheMiss() const56     bool hasCacheMiss() const {
57         for (uint32_t i = 0; i <= SkStrikeClient::CacheMissType::kLast; ++i) {
58             if (fCacheMissCount[i] > 0) return true;
59         }
60         return false;
61     }
62 
63 private:
64     SkDiscardableHandleId fNextHandleId = 0u;
65     SkDiscardableHandleId fLastDeletedHandleId = 0u;
66     SkTHashSet<SkDiscardableHandleId> fLockedHandles;
67     int fCacheMissCount[SkStrikeClient::CacheMissType::kLast + 1u];
68 };
69 
buildTextBlob(sk_sp<SkTypeface> tf,int glyphCount)70 sk_sp<SkTextBlob> buildTextBlob(sk_sp<SkTypeface> tf, int glyphCount) {
71     SkFont font;
72     font.setTypeface(tf);
73     font.setHinting(kNormal_SkFontHinting);
74     font.setSize(1u);
75     font.setEdging(SkFont::Edging::kAntiAlias);
76     font.setSubpixel(true);
77 
78     SkTextBlobBuilder builder;
79     SkRect bounds = SkRect::MakeWH(10, 10);
80     const auto& runBuffer = builder.allocRunPosH(font, glyphCount, 0, &bounds);
81     SkASSERT(runBuffer.utf8text == nullptr);
82     SkASSERT(runBuffer.clusters == nullptr);
83 
84     for (int i = 0; i < glyphCount; i++) {
85         runBuffer.glyphs[i] = static_cast<SkGlyphID>(i);
86         runBuffer.pos[i] = SkIntToScalar(i);
87     }
88     return builder.make();
89 }
90 
compare_blobs(const SkBitmap & expected,const SkBitmap & actual,skiatest::Reporter * reporter,int tolerance=0)91 static void compare_blobs(const SkBitmap& expected, const SkBitmap& actual,
92                           skiatest::Reporter* reporter, int tolerance = 0) {
93     SkASSERT(expected.width() == actual.width());
94     SkASSERT(expected.height() == actual.height());
95     for (int i = 0; i < expected.width(); ++i) {
96         for (int j = 0; j < expected.height(); ++j) {
97             SkColor expectedColor = expected.getColor(i, j);
98             SkColor actualColor = actual.getColor(i, j);
99             if (0 == tolerance) {
100                 REPORTER_ASSERT(reporter, expectedColor == actualColor);
101             } else {
102                 for (int k = 0; k < 4; ++k) {
103                     int expectedChannel = (expectedColor >> (k*8)) & 0xff;
104                     int actualChannel = (actualColor >> (k*8)) & 0xff;
105                     REPORTER_ASSERT(reporter, abs(expectedChannel - actualChannel) <= tolerance);
106                 }
107             }
108         }
109     }
110 }
111 
MakeSettings(GrContext * context)112 SkTextBlobCacheDiffCanvas::Settings MakeSettings(GrContext* context) {
113     SkTextBlobCacheDiffCanvas::Settings settings;
114     settings.fContextSupportsDistanceFieldText = context->supportsDistanceFieldText();
115     settings.fMaxTextureSize = context->maxTextureSize();
116     settings.fMaxTextureBytes = GrContextOptions().fGlyphCacheTextureMaximumBytes;
117     return settings;
118 }
119 
MakeSurface(int width,int height,GrContext * context)120 sk_sp<SkSurface> MakeSurface(int width, int height, GrContext* context) {
121     const SkImageInfo info =
122             SkImageInfo::Make(width, height, kN32_SkColorType, kPremul_SkAlphaType);
123     return SkSurface::MakeRenderTarget(context, SkBudgeted::kNo, info);
124 }
125 
FindSurfaceProps(GrContext * context)126 const SkSurfaceProps FindSurfaceProps(GrContext* context) {
127     auto surface = MakeSurface(1, 1, context);
128     return surface->props();
129 }
130 
RasterBlob(sk_sp<SkTextBlob> blob,int width,int height,const SkPaint & paint,GrContext * context,const SkMatrix * matrix=nullptr,SkScalar x=0)131 SkBitmap RasterBlob(sk_sp<SkTextBlob> blob, int width, int height, const SkPaint& paint,
132                     GrContext* context, const SkMatrix* matrix = nullptr,
133                     SkScalar x = 0) {
134     auto surface = MakeSurface(width, height, context);
135     if (matrix) surface->getCanvas()->concat(*matrix);
136     surface->getCanvas()->drawTextBlob(blob.get(), x, 0, paint);
137     SkBitmap bitmap;
138     bitmap.allocN32Pixels(width, height);
139     surface->readPixels(bitmap, 0, 0);
140     return bitmap;
141 }
142 
DEF_TEST(SkRemoteGlyphCache_TypefaceSerialization,reporter)143 DEF_TEST(SkRemoteGlyphCache_TypefaceSerialization, reporter) {
144     sk_sp<DiscardableManager> discardableManager = sk_make_sp<DiscardableManager>();
145     SkStrikeServer server(discardableManager.get());
146     SkStrikeClient client(discardableManager, false);
147 
148     auto server_tf = SkTypeface::MakeDefault();
149     auto tf_data = server.serializeTypeface(server_tf.get());
150 
151     auto client_tf = client.deserializeTypeface(tf_data->data(), tf_data->size());
152     REPORTER_ASSERT(reporter, client_tf);
153     REPORTER_ASSERT(reporter, static_cast<SkTypefaceProxy*>(client_tf.get())->remoteTypefaceID() ==
154                                       server_tf->uniqueID());
155 
156     // Must unlock everything on termination, otherwise valgrind complains about memory leaks.
157     discardableManager->unlockAndDeleteAll();
158 }
159 
DEF_GPUTEST_FOR_RENDERING_CONTEXTS(SkRemoteGlyphCache_StrikeSerialization,reporter,ctxInfo)160 DEF_GPUTEST_FOR_RENDERING_CONTEXTS(SkRemoteGlyphCache_StrikeSerialization, reporter, ctxInfo) {
161     sk_sp<DiscardableManager> discardableManager = sk_make_sp<DiscardableManager>();
162     SkStrikeServer server(discardableManager.get());
163     SkStrikeClient client(discardableManager, false);
164     const SkPaint paint;
165 
166     // Server.
167     auto serverTf = SkTypeface::MakeFromName("monospace", SkFontStyle());
168     auto serverTfData = server.serializeTypeface(serverTf.get());
169 
170     int glyphCount = 10;
171     auto serverBlob = buildTextBlob(serverTf, glyphCount);
172     auto props = FindSurfaceProps(ctxInfo.grContext());
173     SkTextBlobCacheDiffCanvas cache_diff_canvas(10, 10, SkMatrix::I(), props, &server,
174                                                 MakeSettings(ctxInfo.grContext()));
175     cache_diff_canvas.drawTextBlob(serverBlob.get(), 0, 0, paint);
176 
177     std::vector<uint8_t> serverStrikeData;
178     server.writeStrikeData(&serverStrikeData);
179 
180     // Client.
181     auto clientTf = client.deserializeTypeface(serverTfData->data(), serverTfData->size());
182     REPORTER_ASSERT(reporter,
183                     client.readStrikeData(serverStrikeData.data(), serverStrikeData.size()));
184     auto clientBlob = buildTextBlob(clientTf, glyphCount);
185 
186     SkBitmap expected = RasterBlob(serverBlob, 10, 10, paint, ctxInfo.grContext());
187     SkBitmap actual = RasterBlob(clientBlob, 10, 10, paint, ctxInfo.grContext());
188     compare_blobs(expected, actual, reporter);
189     REPORTER_ASSERT(reporter, !discardableManager->hasCacheMiss());
190 
191     // Must unlock everything on termination, otherwise valgrind complains about memory leaks.
192     discardableManager->unlockAndDeleteAll();
193 }
194 
DEF_GPUTEST_FOR_RENDERING_CONTEXTS(SkRemoteGlyphCache_ReleaseTypeFace,reporter,ctxInfo)195 DEF_GPUTEST_FOR_RENDERING_CONTEXTS(SkRemoteGlyphCache_ReleaseTypeFace, reporter, ctxInfo) {
196     sk_sp<DiscardableManager> discardableManager = sk_make_sp<DiscardableManager>();
197     SkStrikeServer server(discardableManager.get());
198     SkStrikeClient client(discardableManager, false);
199 
200     // Server.
201     auto serverTf = SkTestEmptyTypeface::Make();
202     auto serverTfData = server.serializeTypeface(serverTf.get());
203     REPORTER_ASSERT(reporter, serverTf->unique());
204 
205     {
206         const SkPaint paint;
207         int glyphCount = 10;
208         auto serverBlob = buildTextBlob(serverTf, glyphCount);
209         const SkSurfaceProps props(SkSurfaceProps::kLegacyFontHost_InitType);
210         SkTextBlobCacheDiffCanvas cache_diff_canvas(10, 10, SkMatrix::I(), props, &server,
211                                                     MakeSettings(ctxInfo.grContext()));
212         cache_diff_canvas.drawTextBlob(serverBlob.get(), 0, 0, paint);
213         REPORTER_ASSERT(reporter, !serverTf->unique());
214 
215         std::vector<uint8_t> serverStrikeData;
216         server.writeStrikeData(&serverStrikeData);
217     }
218     REPORTER_ASSERT(reporter, serverTf->unique());
219 
220     // Must unlock everything on termination, otherwise valgrind complains about memory leaks.
221     discardableManager->unlockAndDeleteAll();
222 }
223 
DEF_TEST(SkRemoteGlyphCache_StrikeLockingServer,reporter)224 DEF_TEST(SkRemoteGlyphCache_StrikeLockingServer, reporter) {
225     sk_sp<DiscardableManager> discardableManager = sk_make_sp<DiscardableManager>();
226     SkStrikeServer server(discardableManager.get());
227     SkStrikeClient client(discardableManager, false);
228 
229     auto serverTf = SkTypeface::MakeFromName("monospace", SkFontStyle());
230     server.serializeTypeface(serverTf.get());
231     int glyphCount = 10;
232     auto serverBlob = buildTextBlob(serverTf, glyphCount);
233 
234     const SkSurfaceProps props(SkSurfaceProps::kLegacyFontHost_InitType);
235     SkTextBlobCacheDiffCanvas cache_diff_canvas(10, 10, SkMatrix::I(), props, &server);
236     SkPaint paint;
237     cache_diff_canvas.drawTextBlob(serverBlob.get(), 0, 0, paint);
238 
239     // The strike from the blob should be locked after it has been drawn on the canvas.
240     REPORTER_ASSERT(reporter, discardableManager->handleCount() == 1u);
241     REPORTER_ASSERT(reporter, discardableManager->lockedHandles().count() == 1u);
242 
243     // Write the strike data and unlock everything. Re-analyzing the blob should lock the handle
244     // again.
245     std::vector<uint8_t> fontData;
246     server.writeStrikeData(&fontData);
247     discardableManager->unlockAll();
248     REPORTER_ASSERT(reporter, discardableManager->lockedHandles().count() == 0u);
249 
250     cache_diff_canvas.drawTextBlob(serverBlob.get(), 0, 0, paint);
251     REPORTER_ASSERT(reporter, discardableManager->handleCount() == 1u);
252     REPORTER_ASSERT(reporter, discardableManager->lockedHandles().count() == 1u);
253 
254     // Must unlock everything on termination, otherwise valgrind complains about memory leaks.
255     discardableManager->unlockAndDeleteAll();
256 }
257 
DEF_TEST(SkRemoteGlyphCache_StrikeDeletionServer,reporter)258 DEF_TEST(SkRemoteGlyphCache_StrikeDeletionServer, reporter) {
259     sk_sp<DiscardableManager> discardableManager = sk_make_sp<DiscardableManager>();
260     SkStrikeServer server(discardableManager.get());
261     SkStrikeClient client(discardableManager, false);
262 
263     auto serverTf = SkTypeface::MakeFromName("monospace", SkFontStyle());
264     server.serializeTypeface(serverTf.get());
265     int glyphCount = 10;
266     auto serverBlob = buildTextBlob(serverTf, glyphCount);
267 
268     const SkSurfaceProps props(SkSurfaceProps::kLegacyFontHost_InitType);
269     SkTextBlobCacheDiffCanvas cache_diff_canvas(10, 10, SkMatrix::I(), props, &server);
270     SkPaint paint;
271     cache_diff_canvas.drawTextBlob(serverBlob.get(), 0, 0, paint);
272     REPORTER_ASSERT(reporter, discardableManager->handleCount() == 1u);
273 
274     // Write the strike data and delete all the handles. Re-analyzing the blob should create new
275     // handles.
276     std::vector<uint8_t> fontData;
277     server.writeStrikeData(&fontData);
278     discardableManager->unlockAndDeleteAll();
279     cache_diff_canvas.drawTextBlob(serverBlob.get(), 0, 0, paint);
280     REPORTER_ASSERT(reporter, discardableManager->handleCount() == 2u);
281 
282     // Must unlock everything on termination, otherwise valgrind complains about memory leaks.
283     discardableManager->unlockAndDeleteAll();
284 }
285 
DEF_TEST(SkRemoteGlyphCache_StrikePinningClient,reporter)286 DEF_TEST(SkRemoteGlyphCache_StrikePinningClient, reporter) {
287     sk_sp<DiscardableManager> discardableManager = sk_make_sp<DiscardableManager>();
288     SkStrikeServer server(discardableManager.get());
289     SkStrikeClient client(discardableManager, false);
290 
291     // Server.
292     auto serverTf = SkTypeface::MakeFromName("monospace", SkFontStyle());
293     auto serverTfData = server.serializeTypeface(serverTf.get());
294 
295     int glyphCount = 10;
296     auto serverBlob = buildTextBlob(serverTf, glyphCount);
297 
298     const SkSurfaceProps props(SkSurfaceProps::kLegacyFontHost_InitType);
299     SkTextBlobCacheDiffCanvas cache_diff_canvas(10, 10, SkMatrix::I(), props, &server);
300     SkPaint paint;
301     cache_diff_canvas.drawTextBlob(serverBlob.get(), 0, 0, paint);
302 
303     std::vector<uint8_t> serverStrikeData;
304     server.writeStrikeData(&serverStrikeData);
305 
306     // Client.
307     REPORTER_ASSERT(reporter,
308                     client.readStrikeData(serverStrikeData.data(), serverStrikeData.size()));
309     auto* clientTf = client.deserializeTypeface(serverTfData->data(), serverTfData->size()).get();
310 
311     // The cache remains alive until it is pinned in the discardable manager.
312     SkGraphics::PurgeFontCache();
313     REPORTER_ASSERT(reporter, !clientTf->unique());
314 
315     // Once the strike is unpinned and purged, SkStrikeClient should be the only owner of the
316     // clientTf.
317     discardableManager->unlockAndDeleteAll();
318     SkGraphics::PurgeFontCache();
319     REPORTER_ASSERT(reporter, clientTf->unique());
320 
321     // Must unlock everything on termination, otherwise valgrind complains about memory leaks.
322     discardableManager->unlockAndDeleteAll();
323 }
324 
DEF_TEST(SkRemoteGlyphCache_ClientMemoryAccounting,reporter)325 DEF_TEST(SkRemoteGlyphCache_ClientMemoryAccounting, 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, SkMatrix::I(), 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     SkStrikeCache::ValidateGlyphCacheDataSize();
349 
350     // Must unlock everything on termination, otherwise valgrind complains about memory leaks.
351     discardableManager->unlockAndDeleteAll();
352 }
353 
DEF_TEST(SkRemoteGlyphCache_PurgesServerEntries,reporter)354 DEF_TEST(SkRemoteGlyphCache_PurgesServerEntries, reporter) {
355     sk_sp<DiscardableManager> discardableManager = sk_make_sp<DiscardableManager>();
356     SkStrikeServer server(discardableManager.get());
357     server.setMaxEntriesInDescriptorMapForTesting(1u);
358     SkStrikeClient client(discardableManager, false);
359 
360     {
361         auto serverTf = SkTypeface::MakeFromName("monospace", SkFontStyle());
362         int glyphCount = 10;
363         auto serverBlob = buildTextBlob(serverTf, glyphCount);
364 
365         const SkSurfaceProps props(SkSurfaceProps::kLegacyFontHost_InitType);
366         SkTextBlobCacheDiffCanvas cache_diff_canvas(10, 10, SkMatrix::I(), props, &server);
367         SkPaint paint;
368         REPORTER_ASSERT(reporter, server.remoteGlyphStateMapSizeForTesting() == 0u);
369         cache_diff_canvas.drawTextBlob(serverBlob.get(), 0, 0, paint);
370         REPORTER_ASSERT(reporter, server.remoteGlyphStateMapSizeForTesting() == 1u);
371     }
372 
373     // Serialize to release the lock from the strike server and delete all current
374     // handles.
375     std::vector<uint8_t> fontData;
376     server.writeStrikeData(&fontData);
377     discardableManager->unlockAndDeleteAll();
378 
379     // Use a different typeface. Creating a new strike should evict the previous
380     // one.
381     {
382         auto serverTf = SkTypeface::MakeFromName("Georgia", SkFontStyle());
383         int glyphCount = 10;
384         auto serverBlob = buildTextBlob(serverTf, glyphCount);
385 
386         const SkSurfaceProps props(SkSurfaceProps::kLegacyFontHost_InitType);
387         SkTextBlobCacheDiffCanvas cache_diff_canvas(10, 10, SkMatrix::I(), props, &server);
388         SkPaint paint;
389         REPORTER_ASSERT(reporter, server.remoteGlyphStateMapSizeForTesting() == 1u);
390         cache_diff_canvas.drawTextBlob(serverBlob.get(), 0, 0, paint);
391         REPORTER_ASSERT(reporter, server.remoteGlyphStateMapSizeForTesting() == 1u);
392     }
393 
394     // Must unlock everything on termination, otherwise valgrind complains about memory leaks.
395     discardableManager->unlockAndDeleteAll();
396 }
397 
DEF_GPUTEST_FOR_RENDERING_CONTEXTS(SkRemoteGlyphCache_DrawTextAsPath,reporter,ctxInfo)398 DEF_GPUTEST_FOR_RENDERING_CONTEXTS(SkRemoteGlyphCache_DrawTextAsPath, reporter, ctxInfo) {
399     sk_sp<DiscardableManager> discardableManager = sk_make_sp<DiscardableManager>();
400     SkStrikeServer server(discardableManager.get());
401     SkStrikeClient client(discardableManager, false);
402     SkPaint paint;
403     paint.setStyle(SkPaint::kStroke_Style);
404     paint.setStrokeWidth(0);
405     REPORTER_ASSERT(reporter, SkDraw::ShouldDrawTextAsPaths(SkFont(), paint, SkMatrix::I()));
406 
407     // Server.
408     auto serverTf = SkTypeface::MakeFromName("monospace", SkFontStyle());
409     auto serverTfData = server.serializeTypeface(serverTf.get());
410 
411     int glyphCount = 10;
412     auto serverBlob = buildTextBlob(serverTf, glyphCount);
413     auto props = FindSurfaceProps(ctxInfo.grContext());
414     SkTextBlobCacheDiffCanvas cache_diff_canvas(10, 10, SkMatrix::I(), props, &server,
415                                                 MakeSettings(ctxInfo.grContext()));
416     cache_diff_canvas.drawTextBlob(serverBlob.get(), 0, 0, paint);
417 
418     std::vector<uint8_t> serverStrikeData;
419     server.writeStrikeData(&serverStrikeData);
420 
421     // Client.
422     auto clientTf = client.deserializeTypeface(serverTfData->data(), serverTfData->size());
423     REPORTER_ASSERT(reporter,
424                     client.readStrikeData(serverStrikeData.data(), serverStrikeData.size()));
425     auto clientBlob = buildTextBlob(clientTf, glyphCount);
426 
427     SkBitmap expected = RasterBlob(serverBlob, 10, 10, paint, ctxInfo.grContext());
428     SkBitmap actual = RasterBlob(clientBlob, 10, 10, paint, ctxInfo.grContext());
429     compare_blobs(expected, actual, reporter, 1);
430     REPORTER_ASSERT(reporter, !discardableManager->hasCacheMiss());
431     SkStrikeCache::ValidateGlyphCacheDataSize();
432 
433     // Must unlock everything on termination, otherwise valgrind complains about memory leaks.
434     discardableManager->unlockAndDeleteAll();
435 }
436 
make_blob_causing_fallback(sk_sp<SkTypeface> targetTf,const SkTypeface * glyphTf,skiatest::Reporter * reporter)437 sk_sp<SkTextBlob> make_blob_causing_fallback(
438         sk_sp<SkTypeface> targetTf, const SkTypeface* glyphTf, skiatest::Reporter* reporter) {
439     SkFont font;
440     font.setSubpixel(true);
441     font.setSize(96);
442     font.setHinting(kNormal_SkFontHinting);
443     font.setTypeface(targetTf);
444 
445     REPORTER_ASSERT(reporter, !SkDraw::ShouldDrawTextAsPaths(font, SkPaint(), SkMatrix::I()));
446 
447     char s[] = "Skia";
448     int runSize = strlen(s);
449 
450     SkTextBlobBuilder builder;
451     SkRect bounds = SkRect::MakeIWH(100, 100);
452     const auto& runBuffer = builder.allocRunPosH(font, runSize, 10, &bounds);
453     SkASSERT(runBuffer.utf8text == nullptr);
454     SkASSERT(runBuffer.clusters == nullptr);
455 
456     glyphTf->charsToGlyphs(s, SkTypeface::kUTF8_Encoding, runBuffer.glyphs, runSize);
457 
458     SkRect glyphBounds;
459     font.getWidths(runBuffer.glyphs, 1, nullptr, &glyphBounds);
460 
461     REPORTER_ASSERT(reporter, glyphBounds.width() > SkStrikeCommon::kSkSideTooBigForAtlas);
462 
463     for (int i = 0; i < runSize; i++) {
464         runBuffer.pos[i] = i * 10;
465     }
466 
467     return builder.make();
468 }
469 
DEF_GPUTEST_FOR_RENDERING_CONTEXTS(SkRemoteGlyphCache_DrawTextAsMaskWithPathFallback,reporter,ctxInfo)470 DEF_GPUTEST_FOR_RENDERING_CONTEXTS(SkRemoteGlyphCache_DrawTextAsMaskWithPathFallback,
471         reporter, ctxInfo) {
472     sk_sp<DiscardableManager> discardableManager = sk_make_sp<DiscardableManager>();
473     SkStrikeServer server(discardableManager.get());
474     SkStrikeClient client(discardableManager, false);
475 
476     SkPaint paint;
477 
478     auto serverTf = MakeResourceAsTypeface("fonts/HangingS.ttf");
479     // TODO: when the cq bots can handle this font remove the check.
480     if (serverTf == nullptr) {
481         return;
482     }
483     auto serverTfData = server.serializeTypeface(serverTf.get());
484 
485     auto serverBlob = make_blob_causing_fallback(serverTf, serverTf.get(), reporter);
486 
487     auto props = FindSurfaceProps(ctxInfo.grContext());
488     SkTextBlobCacheDiffCanvas cache_diff_canvas(10, 10, SkMatrix::I(), props, &server,
489                                                 MakeSettings(ctxInfo.grContext()));
490     cache_diff_canvas.drawTextBlob(serverBlob.get(), 0, 0, paint);
491 
492     std::vector<uint8_t> serverStrikeData;
493     server.writeStrikeData(&serverStrikeData);
494 
495     // Client.
496     auto clientTf = client.deserializeTypeface(serverTfData->data(), serverTfData->size());
497     REPORTER_ASSERT(reporter,
498                     client.readStrikeData(serverStrikeData.data(), serverStrikeData.size()));
499 
500     auto clientBlob = make_blob_causing_fallback(clientTf, serverTf.get(), reporter);
501 
502     SkBitmap expected = RasterBlob(serverBlob, 10, 10, paint, ctxInfo.grContext());
503     SkBitmap actual = RasterBlob(clientBlob, 10, 10, paint, ctxInfo.grContext());
504     compare_blobs(expected, actual, reporter);
505     REPORTER_ASSERT(reporter, !discardableManager->hasCacheMiss());
506     SkStrikeCache::ValidateGlyphCacheDataSize();
507 
508     // Must unlock everything on termination, otherwise valgrind complains about memory leaks.
509     discardableManager->unlockAndDeleteAll();
510 }
511 
DEF_GPUTEST_FOR_RENDERING_CONTEXTS(SkRemoteGlyphCache_DrawTextXY,reporter,ctxInfo)512 DEF_GPUTEST_FOR_RENDERING_CONTEXTS(SkRemoteGlyphCache_DrawTextXY, reporter, ctxInfo) {
513     sk_sp<DiscardableManager> discardableManager = sk_make_sp<DiscardableManager>();
514     SkStrikeServer server(discardableManager.get());
515     SkStrikeClient client(discardableManager, false);
516     SkPaint paint;
517     paint.setAntiAlias(true);
518 
519     // Server.
520     auto serverTf = SkTypeface::MakeFromName("monospace", SkFontStyle());
521     auto serverTfData = server.serializeTypeface(serverTf.get());
522 
523     int glyphCount = 10;
524     auto serverBlob = buildTextBlob(serverTf, glyphCount);
525     auto props = FindSurfaceProps(ctxInfo.grContext());
526     SkTextBlobCacheDiffCanvas cache_diff_canvas(10, 10, SkMatrix::I(), props, &server,
527                                                 MakeSettings(ctxInfo.grContext()));
528     cache_diff_canvas.drawTextBlob(serverBlob.get(), 0.5, 0, paint);
529 
530     std::vector<uint8_t> serverStrikeData;
531     server.writeStrikeData(&serverStrikeData);
532 
533     // Client.
534     auto clientTf = client.deserializeTypeface(serverTfData->data(), serverTfData->size());
535     REPORTER_ASSERT(reporter,
536                     client.readStrikeData(serverStrikeData.data(), serverStrikeData.size()));
537     auto clientBlob = buildTextBlob(clientTf, glyphCount);
538 
539     SkBitmap expected = RasterBlob(serverBlob, 10, 10, paint, ctxInfo.grContext(), nullptr, 0.5);
540     SkBitmap actual = RasterBlob(clientBlob, 10, 10, paint, ctxInfo.grContext(), nullptr, 0.5);
541     compare_blobs(expected, actual, reporter);
542     REPORTER_ASSERT(reporter, !discardableManager->hasCacheMiss());
543     SkStrikeCache::ValidateGlyphCacheDataSize();
544 
545     // Must unlock everything on termination, otherwise valgrind complains about memory leaks.
546     discardableManager->unlockAndDeleteAll();
547 }
548 
DEF_GPUTEST_FOR_RENDERING_CONTEXTS(SkRemoteGlyphCache_DrawTextAsDFT,reporter,ctxInfo)549 DEF_GPUTEST_FOR_RENDERING_CONTEXTS(SkRemoteGlyphCache_DrawTextAsDFT, reporter, ctxInfo) {
550     sk_sp<DiscardableManager> discardableManager = sk_make_sp<DiscardableManager>();
551     SkStrikeServer server(discardableManager.get());
552     SkStrikeClient client(discardableManager, false);
553     SkPaint paint;
554     SkFont font;
555 
556     // A perspective transform forces fallback to dft.
557     SkMatrix matrix = SkMatrix::I();
558     matrix[SkMatrix::kMPersp0] = 0.5f;
559     REPORTER_ASSERT(reporter, matrix.hasPerspective());
560     SkSurfaceProps surfaceProps(0, kUnknown_SkPixelGeometry);
561     GrTextContext::Options options;
562     GrTextContext::SanitizeOptions(&options);
563     REPORTER_ASSERT(reporter, GrTextContext::CanDrawAsDistanceFields(
564                                       paint, font, matrix, surfaceProps, true, options));
565 
566     // Server.
567     auto serverTf = SkTypeface::MakeFromName("monospace", SkFontStyle());
568     auto serverTfData = server.serializeTypeface(serverTf.get());
569 
570     int glyphCount = 10;
571     auto serverBlob = buildTextBlob(serverTf, glyphCount);
572     const SkSurfaceProps props(SkSurfaceProps::kLegacyFontHost_InitType);
573     SkTextBlobCacheDiffCanvas cache_diff_canvas(10, 10, SkMatrix::I(), props, &server,
574                                                 MakeSettings(ctxInfo.grContext()));
575     cache_diff_canvas.concat(matrix);
576     cache_diff_canvas.drawTextBlob(serverBlob.get(), 0, 0, paint);
577 
578     std::vector<uint8_t> serverStrikeData;
579     server.writeStrikeData(&serverStrikeData);
580 
581     // Client.
582     auto clientTf = client.deserializeTypeface(serverTfData->data(), serverTfData->size());
583     REPORTER_ASSERT(reporter,
584                     client.readStrikeData(serverStrikeData.data(), serverStrikeData.size()));
585     auto clientBlob = buildTextBlob(clientTf, glyphCount);
586 
587     SkBitmap expected = RasterBlob(serverBlob, 10, 10, paint, ctxInfo.grContext(), &matrix);
588     SkBitmap actual = RasterBlob(clientBlob, 10, 10, paint, ctxInfo.grContext(), &matrix);
589     compare_blobs(expected, actual, reporter);
590     REPORTER_ASSERT(reporter, !discardableManager->hasCacheMiss());
591     SkStrikeCache::ValidateGlyphCacheDataSize();
592 
593     // Must unlock everything on termination, otherwise valgrind complains about memory leaks.
594     discardableManager->unlockAndDeleteAll();
595 }
596 
DEF_GPUTEST_FOR_RENDERING_CONTEXTS(SkRemoteGlyphCache_CacheMissReporting,reporter,ctxInfo)597 DEF_GPUTEST_FOR_RENDERING_CONTEXTS(SkRemoteGlyphCache_CacheMissReporting, reporter, ctxInfo) {
598     sk_sp<DiscardableManager> discardableManager = sk_make_sp<DiscardableManager>();
599     SkStrikeServer server(discardableManager.get());
600     SkStrikeClient client(discardableManager, false);
601 
602     auto serverTf = SkTypeface::MakeFromName("monospace", SkFontStyle());
603     auto tfData = server.serializeTypeface(serverTf.get());
604     auto clientTf = client.deserializeTypeface(tfData->data(), tfData->size());
605     REPORTER_ASSERT(reporter, clientTf);
606     int glyphCount = 10;
607     auto clientBlob = buildTextBlob(clientTf, glyphCount);
608 
609     // Raster the client-side blob without the glyph data, we should get cache miss notifications.
610     SkPaint paint;
611     SkMatrix matrix = SkMatrix::I();
612     RasterBlob(clientBlob, 10, 10, paint, ctxInfo.grContext(), &matrix);
613     REPORTER_ASSERT(reporter,
614                     discardableManager->cacheMissCount(SkStrikeClient::kFontMetrics) == 1);
615     REPORTER_ASSERT(reporter,
616                     discardableManager->cacheMissCount(SkStrikeClient::kGlyphMetrics) == 10);
617 
618     // There shouldn't be any image or path requests, since we mark the glyph as empty on a cache
619     // miss.
620     REPORTER_ASSERT(reporter, discardableManager->cacheMissCount(SkStrikeClient::kGlyphImage) == 0);
621     REPORTER_ASSERT(reporter, discardableManager->cacheMissCount(SkStrikeClient::kGlyphPath) == 0);
622 
623     // Must unlock everything on termination, otherwise valgrind complains about memory leaks.
624     discardableManager->unlockAndDeleteAll();
625 }
626 
DEF_TEST(SkRemoteGlyphCache_SearchOfDesperation,reporter)627 DEF_TEST(SkRemoteGlyphCache_SearchOfDesperation, reporter) {
628     // Build proxy typeface on the client for initializing the cache.
629     sk_sp<DiscardableManager> discardableManager = sk_make_sp<DiscardableManager>();
630     SkStrikeServer server(discardableManager.get());
631     SkStrikeClient client(discardableManager, false);
632 
633     auto serverTf = SkTypeface::MakeFromName("monospace", SkFontStyle());
634     auto tfData = server.serializeTypeface(serverTf.get());
635     auto clientTf = client.deserializeTypeface(tfData->data(), tfData->size());
636     REPORTER_ASSERT(reporter, clientTf);
637 
638     SkFont font;
639     font.setTypeface(clientTf);
640     SkPaint paint;
641     paint.setAntiAlias(true);
642     paint.setColor(SK_ColorRED);
643 
644     auto lostGlyphID = SkPackedGlyphID(1, SK_FixedHalf, SK_FixedHalf);
645     const uint8_t glyphImage[] = {0xFF, 0xFF};
646 
647     SkStrikeCache strikeCache;
648 
649     // Build a fallback cache.
650     {
651         SkAutoDescriptor ad;
652         SkScalerContextRec rec;
653         SkScalerContextEffects effects;
654         SkScalerContextFlags flags = SkScalerContextFlags::kFakeGammaAndBoostContrast;
655         SkScalerContext::MakeRecAndEffects(
656                 font, paint, SkSurfacePropsCopyOrDefault(nullptr), flags,
657                 SkMatrix::I(), &rec, &effects, false);
658         auto desc = SkScalerContext::AutoDescriptorGivenRecAndEffects(rec, effects, &ad);
659 
660         auto fallbackCache = strikeCache.findOrCreateStrikeExclusive(*desc, effects, *clientTf);
661         auto glyph = fallbackCache->getRawGlyphByID(lostGlyphID);
662         glyph->fMaskFormat = SkMask::kA8_Format;
663         glyph->fHeight = 1;
664         glyph->fWidth = 2;
665         fallbackCache->initializeImage(glyphImage, glyph->computeImageSize(), glyph);
666         glyph->fImage = (void *)glyphImage;
667     }
668 
669     // Make sure we can find the fall back cache.
670     {
671         SkAutoDescriptor ad;
672         SkScalerContextRec rec;
673         SkScalerContextEffects effects;
674         SkScalerContextFlags flags = SkScalerContextFlags::kFakeGammaAndBoostContrast;
675         SkScalerContext::MakeRecAndEffects(
676                 font, paint, SkSurfacePropsCopyOrDefault(nullptr), flags,
677                 SkMatrix::I(), &rec, &effects, false);
678         auto desc = SkScalerContext::AutoDescriptorGivenRecAndEffects(rec, effects, &ad);
679         auto testCache = strikeCache.findStrikeExclusive(*desc);
680         REPORTER_ASSERT(reporter, !(testCache == nullptr));
681     }
682 
683     // Create the target cache.
684     SkExclusiveStrikePtr testCache;
685     SkAutoDescriptor ad;
686     SkScalerContextRec rec;
687     SkScalerContextEffects effects;
688     SkScalerContextFlags flags = SkScalerContextFlags::kNone;
689     SkScalerContext::MakeRecAndEffects(
690             font, paint, SkSurfacePropsCopyOrDefault(nullptr), flags,
691             SkMatrix::I(),
692             &rec, &effects, false);
693     auto desc = SkScalerContext::AutoDescriptorGivenRecAndEffects(rec, effects, &ad);
694     testCache = strikeCache.findStrikeExclusive(*desc);
695     REPORTER_ASSERT(reporter, testCache == nullptr);
696     testCache = strikeCache.createStrikeExclusive(*desc,
697                                                      clientTf->createScalerContext(effects, desc));
698     auto scalerProxy = static_cast<SkScalerContextProxy*>(testCache->getScalerContext());
699     scalerProxy->initCache(testCache.get(), &strikeCache);
700 
701     // Look for the lost glyph.
702     {
703         const auto& lostGlyph = testCache->getGlyphIDMetrics(
704                 lostGlyphID.code(), lostGlyphID.getSubXFixed(), lostGlyphID.getSubYFixed());
705         testCache->findImage(lostGlyph);
706 
707         REPORTER_ASSERT(reporter, lostGlyph.fHeight == 1);
708         REPORTER_ASSERT(reporter, lostGlyph.fWidth == 2);
709         REPORTER_ASSERT(reporter, lostGlyph.fMaskFormat == SkMask::kA8_Format);
710         REPORTER_ASSERT(reporter, memcmp(lostGlyph.fImage, glyphImage, sizeof(glyphImage)) == 0);
711     }
712 
713     // Look for the lost glyph with a different sub-pix position.
714     {
715         const auto& lostGlyph =
716                 testCache->getGlyphIDMetrics(lostGlyphID.code(), SK_FixedQuarter, SK_FixedQuarter);
717         testCache->findImage(lostGlyph);
718 
719         REPORTER_ASSERT(reporter, lostGlyph.fHeight == 1);
720         REPORTER_ASSERT(reporter, lostGlyph.fWidth == 2);
721         REPORTER_ASSERT(reporter, lostGlyph.fMaskFormat == SkMask::kA8_Format);
722         REPORTER_ASSERT(reporter, memcmp(lostGlyph.fImage, glyphImage, sizeof(glyphImage)) == 0);
723     }
724 
725     for (uint32_t i = 0; i <= SkStrikeClient::CacheMissType::kLast; ++i) {
726         if (i == SkStrikeClient::CacheMissType::kGlyphMetricsFallback ||
727             i == SkStrikeClient::CacheMissType::kFontMetrics) {
728             REPORTER_ASSERT(reporter, discardableManager->cacheMissCount(i) == 2);
729         } else {
730             REPORTER_ASSERT(reporter, discardableManager->cacheMissCount(i) == 0);
731         }
732     }
733     strikeCache.validateGlyphCacheDataSize();
734 
735     // Must unlock everything on termination, otherwise valgrind complains about memory leaks.
736     discardableManager->unlockAndDeleteAll();
737 }
738 
DEF_TEST(SkRemoteGlyphCache_ReWriteGlyph,reporter)739 DEF_TEST(SkRemoteGlyphCache_ReWriteGlyph, reporter) {
740     // Build proxy typeface on the client for initializing the cache.
741     sk_sp<DiscardableManager> discardableManager = sk_make_sp<DiscardableManager>();
742     SkStrikeServer server(discardableManager.get());
743     SkStrikeClient client(discardableManager, false);
744 
745     auto serverTf = SkTypeface::MakeFromName("monospace", SkFontStyle());
746     auto tfData = server.serializeTypeface(serverTf.get());
747     auto clientTf = client.deserializeTypeface(tfData->data(), tfData->size());
748     REPORTER_ASSERT(reporter, clientTf);
749 
750     SkFont font;
751     font.setEdging(SkFont::Edging::kAntiAlias);
752     SkPaint paint;
753     paint.setColor(SK_ColorRED);
754 
755     auto lostGlyphID = SkPackedGlyphID(1, SK_FixedHalf, SK_FixedHalf);
756     const uint8_t glyphImage[] = {0xFF, 0xFF};
757     uint32_t realMask;
758     uint32_t fakeMask;
759 
760     SkStrikeCache strikeCache;
761 
762     {
763         SkAutoDescriptor ad;
764         SkScalerContextRec rec;
765         SkScalerContextEffects effects;
766         SkScalerContextFlags flags = SkScalerContextFlags::kFakeGammaAndBoostContrast;
767         font.setTypeface(serverTf);
768         SkScalerContext::MakeRecAndEffects(
769                 font, paint, SkSurfacePropsCopyOrDefault(nullptr), flags,
770                 SkMatrix::I(), &rec, &effects, false);
771         auto desc = SkScalerContext::AutoDescriptorGivenRecAndEffects(rec, effects, &ad);
772 
773         auto context = serverTf->createScalerContext(effects, desc, false);
774         SkGlyph glyph{lostGlyphID};
775         context->getMetrics(&glyph);
776         realMask = glyph.fMaskFormat;
777         REPORTER_ASSERT(reporter, realMask != MASK_FORMAT_UNKNOWN);
778     }
779 
780     // Build a fallback cache.
781     {
782         SkAutoDescriptor ad;
783         SkScalerContextRec rec;
784         SkScalerContextEffects effects;
785         SkScalerContextFlags flags = SkScalerContextFlags::kFakeGammaAndBoostContrast;
786         font.setTypeface(clientTf);
787         SkScalerContext::MakeRecAndEffects(
788                 font, paint, SkSurfacePropsCopyOrDefault(nullptr), flags,
789                 SkMatrix::I(), &rec, &effects, false);
790         auto desc = SkScalerContext::AutoDescriptorGivenRecAndEffects(rec, effects, &ad);
791 
792         auto fallbackCache = strikeCache.findOrCreateStrikeExclusive(*desc, effects, *clientTf);
793         auto glyph = fallbackCache->getRawGlyphByID(lostGlyphID);
794         fakeMask = (realMask == SkMask::kA8_Format) ? SkMask::kBW_Format : SkMask::kA8_Format;
795         glyph->fMaskFormat = fakeMask;
796         glyph->fHeight = 1;
797         glyph->fWidth = 2;
798         fallbackCache->initializeImage(glyphImage, glyph->computeImageSize(), glyph);
799     }
800 
801     // Send over the real glyph and make sure the client cache stays intact.
802     {
803         SkAutoDescriptor ad;
804         SkScalerContextEffects effects;
805         SkScalerContextFlags flags = SkScalerContextFlags::kFakeGammaAndBoostContrast;
806         font.setTypeface(serverTf);
807         auto* cacheState = server.getOrCreateCache(
808                 paint, font, SkSurfacePropsCopyOrDefault(nullptr),
809                 SkMatrix::I(), flags, &effects);
810         cacheState->addGlyph(lostGlyphID, false);
811 
812         std::vector<uint8_t> serverStrikeData;
813         server.writeStrikeData(&serverStrikeData);
814         REPORTER_ASSERT(reporter,
815                         client.readStrikeData(
816                                 serverStrikeData.data(),
817                                 serverStrikeData.size()));
818     }
819 
820     {
821         SkAutoDescriptor ad;
822         SkScalerContextRec rec;
823         SkScalerContextEffects effects;
824         SkScalerContextFlags flags = SkScalerContextFlags::kFakeGammaAndBoostContrast;
825         font.setTypeface(clientTf);
826         SkScalerContext::MakeRecAndEffects(
827                 font, paint, SkSurfaceProps(0, kUnknown_SkPixelGeometry), flags,
828                 SkMatrix::I(), &rec, &effects, false);
829         auto desc = SkScalerContext::AutoDescriptorGivenRecAndEffects(rec, effects, &ad);
830 
831         auto fallbackCache = strikeCache.findStrikeExclusive(*desc);
832         REPORTER_ASSERT(reporter, fallbackCache.get() != nullptr);
833         auto glyph = fallbackCache->getRawGlyphByID(lostGlyphID);
834         REPORTER_ASSERT(reporter, glyph->fMaskFormat == fakeMask);
835 
836         // Try overriding the image, it should stay the same.
837         REPORTER_ASSERT(reporter,
838                         memcmp(glyph->fImage, glyphImage, glyph->computeImageSize()) == 0);
839         const uint8_t newGlyphImage[] = {0, 0};
840         fallbackCache->initializeImage(newGlyphImage, glyph->computeImageSize(), glyph);
841         REPORTER_ASSERT(reporter,
842                         memcmp(glyph->fImage, glyphImage, glyph->computeImageSize()) == 0);
843     }
844 
845     strikeCache.validateGlyphCacheDataSize();
846 
847     // Must unlock everything on termination, otherwise valgrind complains about memory leaks.
848     discardableManager->unlockAndDeleteAll();
849 }
850