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