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
30class DiscardableManager : public SkStrikeServer::DiscardableHandleManager,
31                           public SkStrikeClient::DiscardableHandleManager {
32public:
33    DiscardableManager() { sk_bzero(&fCacheMissCount, sizeof(fCacheMissCount)); }
34    ~DiscardableManager() override = default;
35
36    // Server implementation.
37    SkDiscardableHandleId createHandle() override {
38        SkAutoMutexExclusive l(fMutex);
39
40        // Handles starts as locked.
41        fLockedHandles.add(++fNextHandleId);
42        return fNextHandleId;
43    }
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.
53    bool deleteHandle(SkDiscardableHandleId id) override {
54        SkAutoMutexExclusive l(fMutex);
55
56        return id <= fLastDeletedHandleId;
57    }
58
59    void notifyCacheMiss(SkStrikeClient::CacheMissType type, int fontSize) override {
60        SkAutoMutexExclusive l(fMutex);
61
62        fCacheMissCount[type]++;
63    }
64    bool isHandleDeleted(SkDiscardableHandleId id) override {
65        SkAutoMutexExclusive l(fMutex);
66
67        return id <= fLastDeletedHandleId;
68    }
69
70    void unlockAll() {
71        SkAutoMutexExclusive l(fMutex);
72
73        fLockedHandles.reset();
74    }
75    void unlockAndDeleteAll() {
76        SkAutoMutexExclusive l(fMutex);
77
78        fLockedHandles.reset();
79        fLastDeletedHandleId = fNextHandleId;
80    }
81    const SkTHashSet<SkDiscardableHandleId>& lockedHandles() const {
82        SkAutoMutexExclusive l(fMutex);
83
84        return fLockedHandles;
85    }
86    SkDiscardableHandleId handleCount() {
87        SkAutoMutexExclusive l(fMutex);
88
89        return fNextHandleId;
90    }
91    int cacheMissCount(uint32_t type) {
92        SkAutoMutexExclusive l(fMutex);
93
94        return fCacheMissCount[type];
95    }
96    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    }
104    void resetCacheMissCounts() {
105        SkAutoMutexExclusive l(fMutex);
106        sk_bzero(&fCacheMissCount, sizeof(fCacheMissCount));
107    }
108
109private:
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
121sk_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
142static 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
163sk_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
169SkSurfaceProps FindSurfaceProps(GrRecordingContext* rContext) {
170    auto surface = MakeSurface(1, 1, rContext);
171    return surface->props();
172}
173
174SkBitmap 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
186DEF_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
203DEF_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
239DEF_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
268#ifdef SKIA_COMPILE_DM_ALL
269DEF_TEST(SkRemoteGlyphCache_StrikeLockingServer, reporter) {
270    sk_sp<DiscardableManager> discardableManager = sk_make_sp<DiscardableManager>();
271    SkStrikeServer server(discardableManager.get());
272    SkStrikeClient client(discardableManager, false);
273
274    auto serverTf = SkTypeface::MakeFromName("monospace", SkFontStyle());
275    server.serializeTypeface(serverTf.get());
276    int glyphCount = 10;
277    auto serverBlob = buildTextBlob(serverTf, glyphCount);
278
279    const SkSurfaceProps props;
280    std::unique_ptr<SkCanvas> cache_diff_canvas =
281            server.makeAnalysisCanvas(10, 10, props, nullptr, true);
282    SkPaint paint;
283    cache_diff_canvas->drawTextBlob(serverBlob.get(), 0, 0, paint);
284
285    // The strike from the blob should be locked after it has been drawn on the canvas.
286    REPORTER_ASSERT(reporter, discardableManager->handleCount() == 1u);
287    REPORTER_ASSERT(reporter, discardableManager->lockedHandles().count() == 1u);
288
289    // Write the strike data and unlock everything. Re-analyzing the blob should lock the handle
290    // again.
291    std::vector<uint8_t> fontData;
292    server.writeStrikeData(&fontData);
293    discardableManager->unlockAll();
294    REPORTER_ASSERT(reporter, discardableManager->lockedHandles().count() == 0u);
295
296    cache_diff_canvas->drawTextBlob(serverBlob.get(), 0, 0, paint);
297    REPORTER_ASSERT(reporter, discardableManager->handleCount() == 1u);
298    REPORTER_ASSERT(reporter, discardableManager->lockedHandles().count() == 1u);
299
300    // Must unlock everything on termination, otherwise valgrind complains about memory leaks.
301    discardableManager->unlockAndDeleteAll();
302}
303#endif
304
305#ifdef SKIA_COMPILE_DM_ALL
306DEF_TEST(SkRemoteGlyphCache_StrikeDeletionServer, reporter) {
307    sk_sp<DiscardableManager> discardableManager = sk_make_sp<DiscardableManager>();
308    SkStrikeServer server(discardableManager.get());
309    SkStrikeClient client(discardableManager, false);
310
311    auto serverTf = SkTypeface::MakeFromName("monospace", SkFontStyle());
312    server.serializeTypeface(serverTf.get());
313    int glyphCount = 10;
314    auto serverBlob = buildTextBlob(serverTf, glyphCount);
315
316    const SkSurfaceProps props;
317    std::unique_ptr<SkCanvas> cache_diff_canvas =
318            server.makeAnalysisCanvas(10, 10, props, nullptr, true);
319    SkPaint paint;
320    cache_diff_canvas->drawTextBlob(serverBlob.get(), 0, 0, paint);
321    REPORTER_ASSERT(reporter, discardableManager->handleCount() == 1u);
322
323    // Write the strike data and delete all the handles. Re-analyzing the blob should create new
324    // handles.
325    std::vector<uint8_t> fontData;
326    server.writeStrikeData(&fontData);
327
328    // Another analysis pass, to ensure that deleting handles after a complete cache hit still
329    // works. This is a regression test for crbug.com/999682.
330    cache_diff_canvas->drawTextBlob(serverBlob.get(), 0, 0, paint);
331    server.writeStrikeData(&fontData);
332    REPORTER_ASSERT(reporter, discardableManager->handleCount() == 1u);
333
334    discardableManager->unlockAndDeleteAll();
335    cache_diff_canvas->drawTextBlob(serverBlob.get(), 0, 0, paint);
336    REPORTER_ASSERT(reporter, discardableManager->handleCount() == 2u);
337
338    // Must unlock everything on termination, otherwise valgrind complains about memory leaks.
339    discardableManager->unlockAndDeleteAll();
340}
341#endif
342
343#ifdef SKIA_COMPILE_DM_ALL
344DEF_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;
357    std::unique_ptr<SkCanvas> cache_diff_canvas =
358            server.makeAnalysisCanvas(10, 10, props, nullptr, true);
359    SkPaint paint;
360    cache_diff_canvas->drawTextBlob(serverBlob.get(), 0, 0, paint);
361
362    std::vector<uint8_t> serverStrikeData;
363    server.writeStrikeData(&serverStrikeData);
364
365    // Client.
366    REPORTER_ASSERT(reporter,
367                    client.readStrikeData(serverStrikeData.data(), serverStrikeData.size()));
368    auto* clientTf = client.deserializeTypeface(serverTfData->data(), serverTfData->size()).get();
369
370    // The cache remains alive until it is pinned in the discardable manager.
371    SkGraphics::PurgeFontCache();
372    REPORTER_ASSERT(reporter, !clientTf->unique());
373
374    // Once the strike is unpinned and purged, SkStrikeClient should be the only owner of the
375    // clientTf.
376    discardableManager->unlockAndDeleteAll();
377    SkGraphics::PurgeFontCache();
378    REPORTER_ASSERT(reporter, clientTf->unique());
379
380    // Must unlock everything on termination, otherwise valgrind complains about memory leaks.
381    discardableManager->unlockAndDeleteAll();
382}
383#endif
384
385#ifdef SKIA_COMPILE_DM_ALL
386DEF_TEST(SkRemoteGlyphCache_ClientMemoryAccounting, reporter) {
387    sk_sp<DiscardableManager> discardableManager = sk_make_sp<DiscardableManager>();
388    SkStrikeServer server(discardableManager.get());
389    SkStrikeClient client(discardableManager, false);
390
391    // Server.
392    auto serverTf = SkTypeface::MakeFromName("monospace", SkFontStyle());
393    auto serverTfData = server.serializeTypeface(serverTf.get());
394
395    int glyphCount = 10;
396    auto serverBlob = buildTextBlob(serverTf, glyphCount);
397
398    const SkSurfaceProps props;
399    std::unique_ptr<SkCanvas> cache_diff_canvas =
400            server.makeAnalysisCanvas(10, 10, props, nullptr, true);
401    SkPaint paint;
402    cache_diff_canvas->drawTextBlob(serverBlob.get(), 0, 0, paint);
403
404    std::vector<uint8_t> serverStrikeData;
405    server.writeStrikeData(&serverStrikeData);
406
407    // Client.
408    REPORTER_ASSERT(reporter,
409                    client.readStrikeData(serverStrikeData.data(), serverStrikeData.size()));
410
411    // Must unlock everything on termination, otherwise valgrind complains about memory leaks.
412    discardableManager->unlockAndDeleteAll();
413}
414#endif
415
416DEF_TEST(SkRemoteGlyphCache_PurgesServerEntries, reporter) {
417    sk_sp<DiscardableManager> discardableManager = sk_make_sp<DiscardableManager>();
418    SkStrikeServer server(discardableManager.get());
419    server.setMaxEntriesInDescriptorMapForTesting(1u);
420    SkStrikeClient client(discardableManager, false);
421
422    {
423        auto serverTf = SkTypeface::MakeFromName("monospace", SkFontStyle());
424        int glyphCount = 10;
425        auto serverBlob = buildTextBlob(serverTf, glyphCount);
426
427        const SkSurfaceProps props;
428        std::unique_ptr<SkCanvas> cache_diff_canvas =
429            server.makeAnalysisCanvas(10, 10, props, nullptr, true);
430        SkPaint paint;
431        REPORTER_ASSERT(reporter, server.remoteStrikeMapSizeForTesting() == 0u);
432        cache_diff_canvas->drawTextBlob(serverBlob.get(), 0, 0, paint);
433        REPORTER_ASSERT(reporter, server.remoteStrikeMapSizeForTesting() == 1u);
434    }
435
436    // Serialize to release the lock from the strike server and delete all current
437    // handles.
438    std::vector<uint8_t> fontData;
439    server.writeStrikeData(&fontData);
440    discardableManager->unlockAndDeleteAll();
441
442    // Use a different typeface. Creating a new strike should evict the previous
443    // one.
444    {
445        auto serverTf = SkTypeface::MakeFromName("Georgia", SkFontStyle());
446        int glyphCount = 10;
447        auto serverBlob = buildTextBlob(serverTf, glyphCount);
448
449        const SkSurfaceProps props;
450        std::unique_ptr<SkCanvas> cache_diff_canvas =
451            server.makeAnalysisCanvas(10, 10, props, nullptr, true);
452        SkPaint paint;
453        REPORTER_ASSERT(reporter, server.remoteStrikeMapSizeForTesting() == 1u);
454        cache_diff_canvas->drawTextBlob(serverBlob.get(), 0, 0, paint);
455        REPORTER_ASSERT(reporter, server.remoteStrikeMapSizeForTesting() == 1u);
456    }
457
458    // Must unlock everything on termination, otherwise valgrind complains about memory leaks.
459    discardableManager->unlockAndDeleteAll();
460}
461
462DEF_GPUTEST_FOR_RENDERING_CONTEXTS(SkRemoteGlyphCache_DrawTextAsPath, reporter, ctxInfo) {
463    auto direct = ctxInfo.directContext();
464    sk_sp<DiscardableManager> discardableManager = sk_make_sp<DiscardableManager>();
465    SkStrikeServer server(discardableManager.get());
466    SkStrikeClient client(discardableManager, false);
467    SkPaint paint;
468    paint.setStyle(SkPaint::kStroke_Style);
469    paint.setStrokeWidth(0);
470    REPORTER_ASSERT(reporter,
471            SkStrikeSpec::ShouldDrawAsPath(paint, SkFont(), SkMatrix::I()));
472
473    // Server.
474    auto serverTf = SkTypeface::MakeFromName("monospace", SkFontStyle());
475    auto serverTfData = server.serializeTypeface(serverTf.get());
476
477    int glyphCount = 10;
478    auto serverBlob = buildTextBlob(serverTf, glyphCount);
479    auto props = FindSurfaceProps(direct);
480    std::unique_ptr<SkCanvas> cache_diff_canvas = server.makeAnalysisCanvas(
481            10, 10, props, nullptr, direct->supportsDistanceFieldText());
482    cache_diff_canvas->drawTextBlob(serverBlob.get(), 0, 0, paint);
483
484    std::vector<uint8_t> serverStrikeData;
485    server.writeStrikeData(&serverStrikeData);
486
487    // Client.
488    auto clientTf = client.deserializeTypeface(serverTfData->data(), serverTfData->size());
489    REPORTER_ASSERT(reporter,
490                    client.readStrikeData(serverStrikeData.data(), serverStrikeData.size()));
491    auto clientBlob = buildTextBlob(clientTf, glyphCount);
492
493    SkBitmap expected = RasterBlob(serverBlob, 10, 10, paint, direct);
494    SkBitmap actual = RasterBlob(clientBlob, 10, 10, paint, direct);
495    compare_blobs(expected, actual, reporter, 1);
496    REPORTER_ASSERT(reporter, !discardableManager->hasCacheMiss());
497
498    // Must unlock everything on termination, otherwise valgrind complains about memory leaks.
499    discardableManager->unlockAndDeleteAll();
500}
501
502sk_sp<SkTextBlob> make_blob_causing_fallback(
503        sk_sp<SkTypeface> targetTf, const SkTypeface* glyphTf, skiatest::Reporter* reporter) {
504    SkFont font;
505    font.setSubpixel(true);
506    font.setSize(96);
507    font.setHinting(SkFontHinting::kNormal);
508    font.setTypeface(targetTf);
509
510    REPORTER_ASSERT(reporter,
511            !SkStrikeSpec::ShouldDrawAsPath(SkPaint(), font, SkMatrix::I()));
512
513    char s[] = "Skia";
514    int runSize = strlen(s);
515
516    SkTextBlobBuilder builder;
517    SkRect bounds = SkRect::MakeIWH(100, 100);
518    const auto& runBuffer = builder.allocRunPosH(font, runSize, 10, &bounds);
519    SkASSERT(runBuffer.utf8text == nullptr);
520    SkASSERT(runBuffer.clusters == nullptr);
521
522    SkFont(sk_ref_sp(glyphTf)).textToGlyphs(s, strlen(s), SkTextEncoding::kUTF8,
523                                            runBuffer.glyphs, runSize);
524
525    SkRect glyphBounds;
526    font.getWidths(runBuffer.glyphs, 1, nullptr, &glyphBounds);
527
528    REPORTER_ASSERT(reporter, glyphBounds.width() > SkStrikeCommon::kSkSideTooBigForAtlas);
529
530    for (int i = 0; i < runSize; i++) {
531        runBuffer.pos[i] = i * 10;
532    }
533
534    return builder.make();
535}
536
537DEF_GPUTEST_FOR_RENDERING_CONTEXTS(SkRemoteGlyphCache_DrawTextAsMaskWithPathFallback,
538        reporter, ctxInfo) {
539    auto direct = ctxInfo.directContext();
540    sk_sp<DiscardableManager> discardableManager = sk_make_sp<DiscardableManager>();
541    SkStrikeServer server(discardableManager.get());
542    SkStrikeClient client(discardableManager, false);
543
544    SkPaint paint;
545
546    auto serverTf = MakeResourceAsTypeface("fonts/HangingS.ttf");
547    // TODO: when the cq bots can handle this font remove the check.
548    if (serverTf == nullptr) {
549        return;
550    }
551    auto serverTfData = server.serializeTypeface(serverTf.get());
552
553    auto serverBlob = make_blob_causing_fallback(serverTf, serverTf.get(), reporter);
554
555    auto props = FindSurfaceProps(direct);
556    std::unique_ptr<SkCanvas> cache_diff_canvas = server.makeAnalysisCanvas(
557            10, 10, props, nullptr, direct->supportsDistanceFieldText());
558    cache_diff_canvas->drawTextBlob(serverBlob.get(), 0, 0, paint);
559
560    std::vector<uint8_t> serverStrikeData;
561    server.writeStrikeData(&serverStrikeData);
562
563    // Client.
564    auto clientTf = client.deserializeTypeface(serverTfData->data(), serverTfData->size());
565    REPORTER_ASSERT(reporter,
566                    client.readStrikeData(serverStrikeData.data(), serverStrikeData.size()));
567
568    auto clientBlob = make_blob_causing_fallback(clientTf, serverTf.get(), reporter);
569
570    SkBitmap expected = RasterBlob(serverBlob, 10, 10, paint, direct);
571    SkBitmap actual = RasterBlob(clientBlob, 10, 10, paint, direct);
572    compare_blobs(expected, actual, reporter);
573    REPORTER_ASSERT(reporter, !discardableManager->hasCacheMiss());
574
575    // Must unlock everything on termination, otherwise valgrind complains about memory leaks.
576    discardableManager->unlockAndDeleteAll();
577}
578
579#if 0
580// TODO: turn this one when I figure out how to deal with the pixel variance from linear
581//  interpolation from GPU to GPU.
582DEF_GPUTEST_FOR_RENDERING_CONTEXTS(SkRemoteGlyphCache_DrawTextAsSDFTWithAllARGBFallback,
583                                   reporter, ctxInfo) {
584    sk_sp<DiscardableManager> discardableManager = sk_make_sp<DiscardableManager>();
585    SkStrikeServer server(discardableManager.get());
586    SkStrikeClient client(discardableManager, false);
587
588    SkPaint paint;
589
590    auto serverTf = ToolUtils::planet_typeface();
591    // TODO: when the cq bots can handle this font remove the check.
592    if (serverTf == nullptr) {
593        return;
594    }
595    auto serverTfData = server.serializeTypeface(serverTf.get());
596
597    auto makeBlob = [&reporter](sk_sp<SkTypeface> typeface) {
598        SkFont font;
599        font.setSubpixel(true);
600        font.setSize(96);
601        font.setHinting(SkFontHinting::kNormal);
602        font.setTypeface(typeface);
603
604        REPORTER_ASSERT(reporter, !SkDraw::ShouldDrawTextAsPaths(font, SkPaint(), SkMatrix::I()));
605
606        // Mercury to Uranus.
607        SkGlyphID glyphs[] = {1, 2, 3, 4, 5, 6, 7, 8};
608
609        SkTextBlobBuilder builder;
610        SkRect bounds = SkRect::MakeIWH(100, 100);
611        const auto& runBuffer = builder.allocRunPosH(font, SK_ARRAY_COUNT(glyphs), 100, &bounds);
612        SkASSERT(runBuffer.utf8text == nullptr);
613        SkASSERT(runBuffer.clusters == nullptr);
614
615        std::copy(std::begin(glyphs), std::end(glyphs), runBuffer.glyphs);
616
617        for (size_t i = 0; i < SK_ARRAY_COUNT(glyphs); i++) {
618            runBuffer.pos[i] = i * 100;
619        }
620
621        return builder.make();
622    };
623
624    auto serverBlob = makeBlob(serverTf);
625
626    auto props = FindSurfaceProps(ctxInfo.grContext());
627    std::unique_ptr<SkCanvas> cache_diff_canvas = server.makeAnalysisCanvas(
628            10, 10, props, nullptr, ctxInfo.directContext()->supportsDistanceFieldText());
629    cache_diff_canvas->drawTextBlob(serverBlob.get(), 0, 400, paint);
630
631    std::vector<uint8_t> serverStrikeData;
632    server.writeStrikeData(&serverStrikeData);
633
634    // Client.
635    auto clientTf = client.deserializeTypeface(serverTfData->data(), serverTfData->size());
636    REPORTER_ASSERT(reporter,
637                    client.readStrikeData(serverStrikeData.data(), serverStrikeData.size()));
638
639    auto clientBlob = makeBlob(clientTf);
640
641    SkBitmap expected = RasterBlob(serverBlob, 800, 800, paint, ctxInfo.grContext());
642    SkBitmap actual = RasterBlob(clientBlob, 800, 800, paint, ctxInfo.grContext());
643
644    // Pixel variance can be high because of the atlas placement, and large scaling in the linear
645    // interpolation.
646    compare_blobs(expected, actual, reporter, 36);
647    REPORTER_ASSERT(reporter, !discardableManager->hasCacheMiss());
648
649    // Must unlock everything on termination, otherwise valgrind complains about memory leaks.
650    discardableManager->unlockAndDeleteAll();
651}
652#endif
653
654DEF_GPUTEST_FOR_RENDERING_CONTEXTS(SkRemoteGlyphCache_DrawTextXY, reporter, ctxInfo) {
655    auto direct = ctxInfo.directContext();
656    sk_sp<DiscardableManager> discardableManager = sk_make_sp<DiscardableManager>();
657    SkStrikeServer server(discardableManager.get());
658    SkStrikeClient client(discardableManager, false);
659    SkPaint paint;
660    paint.setAntiAlias(true);
661
662    // Server.
663    auto serverTf = SkTypeface::MakeFromName("monospace", SkFontStyle());
664    auto serverTfData = server.serializeTypeface(serverTf.get());
665
666    int glyphCount = 10;
667    auto serverBlob = buildTextBlob(serverTf, glyphCount);
668    auto props = FindSurfaceProps(direct);
669    std::unique_ptr<SkCanvas> cache_diff_canvas = server.makeAnalysisCanvas(
670            10, 10, props, nullptr, direct->supportsDistanceFieldText());
671    cache_diff_canvas->drawTextBlob(serverBlob.get(), 0.5, 0, paint);
672
673    std::vector<uint8_t> serverStrikeData;
674    server.writeStrikeData(&serverStrikeData);
675
676    // Client.
677    auto clientTf = client.deserializeTypeface(serverTfData->data(), serverTfData->size());
678    REPORTER_ASSERT(reporter,
679                    client.readStrikeData(serverStrikeData.data(), serverStrikeData.size()));
680    auto clientBlob = buildTextBlob(clientTf, glyphCount);
681
682    SkBitmap expected = RasterBlob(serverBlob, 10, 10, paint, direct, nullptr, 0.5);
683    SkBitmap actual = RasterBlob(clientBlob, 10, 10, paint, direct, nullptr, 0.5);
684    compare_blobs(expected, actual, reporter);
685    REPORTER_ASSERT(reporter, !discardableManager->hasCacheMiss());
686
687    // Must unlock everything on termination, otherwise valgrind complains about memory leaks.
688    discardableManager->unlockAndDeleteAll();
689}
690
691DEF_GPUTEST_FOR_RENDERING_CONTEXTS(SkRemoteGlyphCache_DrawTextAsDFT, reporter, ctxInfo) {
692    auto direct = ctxInfo.directContext();
693    if (!direct->priv().caps()->shaderCaps()->supportsDistanceFieldText()) {
694        return;
695    }
696    sk_sp<DiscardableManager> discardableManager = sk_make_sp<DiscardableManager>();
697    SkStrikeServer server(discardableManager.get());
698    SkStrikeClient client(discardableManager, false);
699    SkPaint paint;
700    SkFont font;
701
702    // A scale transform forces fallback to dft.
703    SkMatrix matrix = SkMatrix::Scale(16, 16);
704    GrSDFTControl control = direct->priv().asRecordingContext()->priv().getSDFTControl(true);
705    REPORTER_ASSERT(reporter, control.drawingType(font, paint, matrix) == GrSDFTControl::kSDFT);
706
707    // Server.
708    auto serverTf = SkTypeface::MakeFromName("monospace", SkFontStyle());
709    auto serverTfData = server.serializeTypeface(serverTf.get());
710
711    int glyphCount = 10;
712    auto serverBlob = buildTextBlob(serverTf, glyphCount);
713    const SkSurfaceProps props;
714    std::unique_ptr<SkCanvas> cache_diff_canvas = server.makeAnalysisCanvas(
715            10, 10, props, nullptr, direct->supportsDistanceFieldText());
716    cache_diff_canvas->concat(matrix);
717    cache_diff_canvas->drawTextBlob(serverBlob.get(), 0, 0, paint);
718
719    std::vector<uint8_t> serverStrikeData;
720    server.writeStrikeData(&serverStrikeData);
721
722    // Client.
723    auto clientTf = client.deserializeTypeface(serverTfData->data(), serverTfData->size());
724    REPORTER_ASSERT(reporter,
725                    client.readStrikeData(serverStrikeData.data(), serverStrikeData.size()));
726    auto clientBlob = buildTextBlob(clientTf, glyphCount);
727
728    SkBitmap expected = RasterBlob(serverBlob, 10, 10, paint, direct, &matrix);
729    SkBitmap actual = RasterBlob(clientBlob, 10, 10, paint, direct, &matrix);
730    compare_blobs(expected, actual, reporter);
731    REPORTER_ASSERT(reporter, !discardableManager->hasCacheMiss());
732
733    // Must unlock everything on termination, otherwise valgrind complains about memory leaks.
734    discardableManager->unlockAndDeleteAll();
735}
736
737DEF_GPUTEST_FOR_RENDERING_CONTEXTS(SkRemoteGlyphCache_CacheMissReporting, reporter, ctxInfo) {
738    sk_sp<DiscardableManager> discardableManager = sk_make_sp<DiscardableManager>();
739    SkStrikeServer server(discardableManager.get());
740    SkStrikeClient client(discardableManager, false);
741
742    auto serverTf = SkTypeface::MakeFromName("monospace", SkFontStyle());
743    auto tfData = server.serializeTypeface(serverTf.get());
744    auto clientTf = client.deserializeTypeface(tfData->data(), tfData->size());
745    REPORTER_ASSERT(reporter, clientTf);
746    int glyphCount = 10;
747    auto clientBlob = buildTextBlob(clientTf, glyphCount);
748
749    // Raster the client-side blob without the glyph data, we should get cache miss notifications.
750    SkPaint paint;
751    SkMatrix matrix = SkMatrix::I();
752    RasterBlob(clientBlob, 10, 10, paint, ctxInfo.directContext(), &matrix);
753    REPORTER_ASSERT(reporter,
754                    discardableManager->cacheMissCount(SkStrikeClient::kFontMetrics) == 1);
755    REPORTER_ASSERT(reporter,
756                    discardableManager->cacheMissCount(SkStrikeClient::kGlyphMetrics) == 10);
757
758    // There shouldn't be any image or path requests, since we mark the glyph as empty on a cache
759    // miss.
760    REPORTER_ASSERT(reporter, discardableManager->cacheMissCount(SkStrikeClient::kGlyphImage) == 0);
761    REPORTER_ASSERT(reporter, discardableManager->cacheMissCount(SkStrikeClient::kGlyphPath) == 0);
762
763    // Must unlock everything on termination, otherwise valgrind complains about memory leaks.
764    discardableManager->unlockAndDeleteAll();
765}
766
767sk_sp<SkTextBlob> MakeEmojiBlob(sk_sp<SkTypeface> serverTf, SkScalar textSize,
768                                sk_sp<SkTypeface> clientTf = nullptr) {
769    SkFont font;
770    font.setTypeface(serverTf);
771    font.setSize(textSize);
772
773    const char* text = ToolUtils::emoji_sample_text();
774    SkFont serverFont = font;
775    auto blob = SkTextBlob::MakeFromText(text, strlen(text), font);
776    if (clientTf == nullptr) return blob;
777
778    SkSerialProcs s_procs;
779    s_procs.fTypefaceProc = [](SkTypeface*, void* ctx) -> sk_sp<SkData> {
780        return SkData::MakeUninitialized(1u);
781    };
782    auto serialized = blob->serialize(s_procs);
783
784    SkDeserialProcs d_procs;
785    d_procs.fTypefaceCtx = &clientTf;
786    d_procs.fTypefaceProc = [](const void* data, size_t length, void* ctx) -> sk_sp<SkTypeface> {
787        return *(static_cast<sk_sp<SkTypeface>*>(ctx));
788    };
789    return SkTextBlob::Deserialize(serialized->data(), serialized->size(), d_procs);
790}
791
792DEF_GPUTEST_FOR_RENDERING_CONTEXTS(SkRemoteGlyphCache_TypefaceWithNoPaths, reporter, ctxInfo) {
793    auto direct = ctxInfo.directContext();
794    sk_sp<DiscardableManager> discardableManager = sk_make_sp<DiscardableManager>();
795    SkStrikeServer server(discardableManager.get());
796    SkStrikeClient client(discardableManager, false);
797
798    auto serverTf = ToolUtils::emoji_typeface();
799    auto serverTfData = server.serializeTypeface(serverTf.get());
800    auto clientTf = client.deserializeTypeface(serverTfData->data(), serverTfData->size());
801
802    auto props = FindSurfaceProps(direct);
803    std::unique_ptr<SkCanvas> cache_diff_canvas = server.makeAnalysisCanvas(
804            500, 500, props, nullptr, direct->supportsDistanceFieldText());
805    for (SkScalar textSize : { 70, 180, 270, 340}) {
806        auto serverBlob = MakeEmojiBlob(serverTf, textSize);
807
808        SkPaint paint;
809        cache_diff_canvas->drawTextBlob(serverBlob.get(), 100, 100, paint);
810
811        std::vector<uint8_t> serverStrikeData;
812        server.writeStrikeData(&serverStrikeData);
813        if (!serverStrikeData.empty()) {
814            REPORTER_ASSERT(reporter,
815                            client.readStrikeData(serverStrikeData.data(),
816                                                  serverStrikeData.size()));
817        }
818        auto clientBlob = MakeEmojiBlob(serverTf, textSize, clientTf);
819        REPORTER_ASSERT(reporter, clientBlob);
820
821        RasterBlob(clientBlob, 500, 500, paint, direct);
822        REPORTER_ASSERT(reporter, !discardableManager->hasCacheMiss());
823        discardableManager->resetCacheMissCounts();
824    }
825
826    // Must unlock everything on termination, otherwise valgrind complains about memory leaks.
827    discardableManager->unlockAndDeleteAll();
828}
829