• 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/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