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/SkAlphaType.h"
9 #include "include/core/SkBitmap.h"
10 #include "include/core/SkCanvas.h"
11 #include "include/core/SkColor.h"
12 #include "include/core/SkColorSpace.h"
13 #include "include/core/SkColorType.h"
14 #include "include/core/SkData.h"
15 #include "include/core/SkFont.h"
16 #include "include/core/SkFontStyle.h"
17 #include "include/core/SkFontTypes.h"
18 #include "include/core/SkGraphics.h"
19 #include "include/core/SkImageInfo.h"
20 #include "include/core/SkMatrix.h"
21 #include "include/core/SkPaint.h"
22 #include "include/core/SkRect.h"
23 #include "include/core/SkRefCnt.h"
24 #include "include/core/SkScalar.h"
25 #include "include/core/SkSerialProcs.h"
26 #include "include/core/SkSurface.h"
27 #include "include/core/SkSurfaceProps.h"
28 #include "include/core/SkTextBlob.h"
29 #include "include/core/SkTypeface.h"
30 #include "include/core/SkTypes.h"
31 #include "include/gpu/GpuTypes.h"
32 #include "include/gpu/GrContextOptions.h"
33 #include "include/gpu/GrDirectContext.h"
34 #include "include/gpu/GrRecordingContext.h"
35 #include "src/core/SkTHash.h"
36 #include "include/private/base/SkMalloc.h"
37 #include "include/private/base/SkMutex.h"
38 #include "include/private/chromium/SkChromeRemoteGlyphCache.h"
39 #include "include/private/chromium/Slug.h"
40 #include "src/core/SkFontPriv.h"
41 #include "src/core/SkGlyph.h"
42 #include "src/core/SkStrikeSpec.h"
43 #include "src/core/SkTypeface_remote.h"
44 #include "src/gpu/ganesh/GrCaps.h"
45 #include "src/gpu/ganesh/GrDirectContextPriv.h"
46 #include "src/gpu/ganesh/GrRecordingContextPriv.h"
47 #include "src/gpu/ganesh/GrShaderCaps.h"
48 #include "src/text/gpu/SDFTControl.h"
49 #include "tests/CtsEnforcement.h"
50 #include "tests/Test.h"
51 #include "tools/Resources.h"
52 #include "tools/ToolUtils.h"
53 #include "tools/fonts/TestEmptyTypeface.h"
54 
55 #include <cmath>
56 #include <cstdint>
57 #include <cstring>
58 #include <initializer_list>
59 #include <memory>
60 #include <vector>
61 
62 // Since SkRemoteGlyphCache is not re-entrant, we can't use it while drawing slugs to simulate
63 // text blobs in the GPU stack.
64 #if !defined(SK_EXPERIMENTAL_SIMULATE_DRAWGLYPHRUNLIST_WITH_SLUG_STRIKE_SERIALIZE)
65 
66 using Slug = sktext::gpu::Slug;
67 
68 class DiscardableManager : public SkStrikeServer::DiscardableHandleManager,
69                            public SkStrikeClient::DiscardableHandleManager {
70 public:
DiscardableManager()71     DiscardableManager() { sk_bzero(&fCacheMissCount, sizeof(fCacheMissCount)); }
72     ~DiscardableManager() override = default;
73 
74     // Server implementation.
createHandle()75     SkDiscardableHandleId createHandle() override {
76         SkAutoMutexExclusive l(fMutex);
77 
78         // Handles starts as locked.
79         fLockedHandles.add(++fNextHandleId);
80         return fNextHandleId;
81     }
lockHandle(SkDiscardableHandleId id)82     bool lockHandle(SkDiscardableHandleId id) override {
83         SkAutoMutexExclusive l(fMutex);
84 
85         if (id <= fLastDeletedHandleId) return false;
86         fLockedHandles.add(id);
87         return true;
88     }
89 
90     // Client implementation.
deleteHandle(SkDiscardableHandleId id)91     bool deleteHandle(SkDiscardableHandleId id) override {
92         SkAutoMutexExclusive l(fMutex);
93 
94         return id <= fLastDeletedHandleId;
95     }
96 
notifyCacheMiss(SkStrikeClient::CacheMissType type,int fontSize)97     void notifyCacheMiss(SkStrikeClient::CacheMissType type, int fontSize) override {
98         SkAutoMutexExclusive l(fMutex);
99 
100         fCacheMissCount[type]++;
101     }
isHandleDeleted(SkDiscardableHandleId id)102     bool isHandleDeleted(SkDiscardableHandleId id) override {
103         SkAutoMutexExclusive l(fMutex);
104 
105         return id <= fLastDeletedHandleId;
106     }
107 
unlockAll()108     void unlockAll() {
109         SkAutoMutexExclusive l(fMutex);
110 
111         fLockedHandles.reset();
112     }
unlockAndDeleteAll()113     void unlockAndDeleteAll() {
114         SkAutoMutexExclusive l(fMutex);
115 
116         fLockedHandles.reset();
117         fLastDeletedHandleId = fNextHandleId;
118     }
lockedHandles() const119     const SkTHashSet<SkDiscardableHandleId>& lockedHandles() const {
120         SkAutoMutexExclusive l(fMutex);
121 
122         return fLockedHandles;
123     }
handleCount()124     SkDiscardableHandleId handleCount() {
125         SkAutoMutexExclusive l(fMutex);
126 
127         return fNextHandleId;
128     }
cacheMissCount(uint32_t type)129     int cacheMissCount(uint32_t type) {
130         SkAutoMutexExclusive l(fMutex);
131 
132         return fCacheMissCount[type];
133     }
hasCacheMiss() const134     bool hasCacheMiss() const {
135         SkAutoMutexExclusive l(fMutex);
136 
137         for (uint32_t i = 0; i <= SkStrikeClient::CacheMissType::kLast; ++i) {
138             if (fCacheMissCount[i] > 0) { return true; }
139         }
140         return false;
141     }
resetCacheMissCounts()142     void resetCacheMissCounts() {
143         SkAutoMutexExclusive l(fMutex);
144         sk_bzero(&fCacheMissCount, sizeof(fCacheMissCount));
145     }
146 
147 private:
148     // The tests below run in parallel on multiple threads and use the same
149     // process global SkStrikeCache. So the implementation needs to be
150     // thread-safe.
151     mutable SkMutex fMutex;
152 
153     SkDiscardableHandleId fNextHandleId = 0u;
154     SkDiscardableHandleId fLastDeletedHandleId = 0u;
155     SkTHashSet<SkDiscardableHandleId> fLockedHandles;
156     int fCacheMissCount[SkStrikeClient::CacheMissType::kLast + 1u];
157 };
158 
buildTextBlob(sk_sp<SkTypeface> tf,int glyphCount,int textSize=1)159 sk_sp<SkTextBlob> buildTextBlob(sk_sp<SkTypeface> tf, int glyphCount, int textSize = 1) {
160     SkFont font;
161     font.setTypeface(tf);
162     font.setHinting(SkFontHinting::kNormal);
163     font.setSize(textSize);
164     font.setEdging(SkFont::Edging::kAntiAlias);
165     font.setSubpixel(true);
166 
167     SkTextBlobBuilder builder;
168     SkRect bounds = SkRect::MakeWH(10, 10);
169     const auto& runBuffer = builder.allocRunPosH(font, glyphCount, 0, &bounds);
170     SkASSERT(runBuffer.utf8text == nullptr);
171     SkASSERT(runBuffer.clusters == nullptr);
172 
173     for (int i = 0; i < glyphCount; i++) {
174         runBuffer.glyphs[i] = static_cast<SkGlyphID>(i);
175         runBuffer.pos[i] = SkIntToScalar(i);
176     }
177     return builder.make();
178 }
179 
compare_blobs(const SkBitmap & expected,const SkBitmap & actual,skiatest::Reporter * reporter,int tolerance=0)180 static void compare_blobs(const SkBitmap& expected, const SkBitmap& actual,
181                           skiatest::Reporter* reporter, int tolerance = 0) {
182     SkASSERT(expected.width() == actual.width());
183     SkASSERT(expected.height() == actual.height());
184     for (int i = 0; i < expected.width(); ++i) {
185         for (int j = 0; j < expected.height(); ++j) {
186             SkColor expectedColor = expected.getColor(i, j);
187             SkColor actualColor = actual.getColor(i, j);
188             if (0 == tolerance) {
189                 REPORTER_ASSERT(reporter, expectedColor == actualColor);
190             } else {
191                 for (int k = 0; k < 4; ++k) {
192                     int expectedChannel = (expectedColor >> (k*8)) & 0xff;
193                     int actualChannel = (actualColor >> (k*8)) & 0xff;
194                     REPORTER_ASSERT(reporter, abs(expectedChannel - actualChannel) <= tolerance);
195                 }
196             }
197         }
198     }
199 }
200 
MakeSurface(int width,int height,GrRecordingContext * rContext)201 sk_sp<SkSurface> MakeSurface(int width, int height, GrRecordingContext* rContext) {
202     const SkImageInfo info =
203             SkImageInfo::Make(width, height, kN32_SkColorType, kPremul_SkAlphaType);
204     return SkSurface::MakeRenderTarget(rContext, skgpu::Budgeted::kNo, info);
205 }
206 
FindSurfaceProps(GrRecordingContext * rContext)207 SkSurfaceProps FindSurfaceProps(GrRecordingContext* rContext) {
208     auto surface = MakeSurface(1, 1, rContext);
209     return surface->props();
210 }
211 
RasterBlob(sk_sp<SkTextBlob> blob,int width,int height,const SkPaint & paint,GrRecordingContext * rContext,const SkMatrix * matrix=nullptr,SkScalar x=0)212 SkBitmap RasterBlob(sk_sp<SkTextBlob> blob, int width, int height, const SkPaint& paint,
213                     GrRecordingContext* rContext, const SkMatrix* matrix = nullptr,
214                     SkScalar x = 0) {
215     auto surface = MakeSurface(width, height, rContext);
216     if (matrix) {
217         surface->getCanvas()->concat(*matrix);
218     }
219     surface->getCanvas()->drawTextBlob(blob.get(), x, height/2, paint);
220     SkBitmap bitmap;
221     bitmap.allocN32Pixels(width, height);
222     surface->readPixels(bitmap, 0, 0);
223     return bitmap;
224 }
225 
RasterBlobThroughSlug(sk_sp<SkTextBlob> blob,int width,int height,const SkPaint & paint,GrRecordingContext * rContext,const SkMatrix * matrix=nullptr,SkScalar x=0)226 SkBitmap RasterBlobThroughSlug(sk_sp<SkTextBlob> blob, int width, int height, const SkPaint& paint,
227                                GrRecordingContext* rContext, const SkMatrix* matrix = nullptr,
228                                SkScalar x = 0) {
229     auto surface = MakeSurface(width, height, rContext);
230     if (matrix) {
231         surface->getCanvas()->concat(*matrix);
232     }
233     auto canvas = surface->getCanvas();
234     auto slug = Slug::ConvertBlob(canvas, *blob, {x, height/2.0f}, paint);
235     slug->draw(canvas);
236     SkBitmap bitmap;
237     bitmap.allocN32Pixels(width, height);
238     surface->readPixels(bitmap, 0, 0);
239     return bitmap;
240 }
241 
RasterSlug(sk_sp<Slug> slug,int width,int height,const SkPaint & paint,GrRecordingContext * rContext,const SkMatrix * matrix=nullptr,SkScalar x=0)242 SkBitmap RasterSlug(sk_sp<Slug> slug, int width, int height, const SkPaint& paint,
243                     GrRecordingContext* rContext, const SkMatrix* matrix = nullptr,
244                     SkScalar x = 0) {
245     auto surface = MakeSurface(width, height, rContext);
246     auto canvas = surface->getCanvas();
247     if (matrix) {
248         canvas->concat(*matrix);
249     }
250     slug->draw(canvas);
251     SkBitmap bitmap;
252     bitmap.allocN32Pixels(width, height);
253     surface->readPixels(bitmap, 0, 0);
254     return bitmap;
255 }
256 
DEF_TEST(SkRemoteGlyphCache_TypefaceSerialization,reporter)257 DEF_TEST(SkRemoteGlyphCache_TypefaceSerialization, reporter) {
258     sk_sp<DiscardableManager> discardableManager = sk_make_sp<DiscardableManager>();
259     SkStrikeServer server(discardableManager.get());
260     SkStrikeClient client(discardableManager, false);
261 
262     auto server_tf = SkTypeface::MakeDefault();
263     auto tf_data = server.serializeTypeface(server_tf.get());
264 
265     auto client_tf = client.deserializeTypeface(tf_data->data(), tf_data->size());
266     REPORTER_ASSERT(reporter, client_tf);
267     REPORTER_ASSERT(reporter, static_cast<SkTypefaceProxy*>(client_tf.get())->remoteTypefaceID() ==
268                                       server_tf->uniqueID());
269 
270     // Must unlock everything on termination, otherwise valgrind complains about memory leaks.
271     discardableManager->unlockAndDeleteAll();
272 }
273 
DEF_GANESH_TEST_FOR_RENDERING_CONTEXTS(SkRemoteGlyphCache_StrikeSerialization,reporter,ctxInfo,CtsEnforcement::kNever)274 DEF_GANESH_TEST_FOR_RENDERING_CONTEXTS(SkRemoteGlyphCache_StrikeSerialization,
275                                        reporter,
276                                        ctxInfo,
277                                        CtsEnforcement::kNever) {
278     auto dContext = ctxInfo.directContext();
279     sk_sp<DiscardableManager> discardableManager = sk_make_sp<DiscardableManager>();
280     SkStrikeServer server(discardableManager.get());
281     SkStrikeClient client(discardableManager, false);
282     const SkPaint paint;
283 
284     // Server.
285     auto serverTf = SkTypeface::MakeFromName("monospace", SkFontStyle());
286     auto serverTfData = server.serializeTypeface(serverTf.get());
287 
288     int glyphCount = 10;
289     auto serverBlob = buildTextBlob(serverTf, glyphCount);
290     auto props = FindSurfaceProps(dContext);
291     std::unique_ptr<SkCanvas> cache_diff_canvas = server.makeAnalysisCanvas(
292             10, 10, props, nullptr, dContext->supportsDistanceFieldText(),
293             !dContext->priv().caps()->disablePerspectiveSDFText());
294     cache_diff_canvas->drawTextBlob(serverBlob.get(), 0, 0, paint);
295 
296     std::vector<uint8_t> serverStrikeData;
297     server.writeStrikeData(&serverStrikeData);
298 
299     // Client.
300     auto clientTf = client.deserializeTypeface(serverTfData->data(), serverTfData->size());
301     REPORTER_ASSERT(reporter,
302                     client.readStrikeData(serverStrikeData.data(), serverStrikeData.size()));
303     auto clientBlob = buildTextBlob(clientTf, glyphCount);
304 
305     SkBitmap expected = RasterBlob(serverBlob, 10, 10, paint, dContext);
306     SkBitmap actual = RasterBlob(clientBlob, 10, 10, paint, dContext);
307     compare_blobs(expected, actual, reporter);
308     REPORTER_ASSERT(reporter, !discardableManager->hasCacheMiss());
309 
310     // Must unlock everything on termination, otherwise valgrind complains about memory leaks.
311     discardableManager->unlockAndDeleteAll();
312 }
313 
use_padding_options(GrContextOptions * options)314 static void use_padding_options(GrContextOptions* options) {
315     options->fSupportBilerpFromGlyphAtlas = true;
316 }
317 
DEF_GANESH_TEST_FOR_CONTEXTS(SkRemoteGlyphCache_StrikeSerializationSlug,sk_gpu_test::GrContextFactory::IsRenderingContext,reporter,ctxInfo,use_padding_options,CtsEnforcement::kNever)318 DEF_GANESH_TEST_FOR_CONTEXTS(SkRemoteGlyphCache_StrikeSerializationSlug,
319                              sk_gpu_test::GrContextFactory::IsRenderingContext,
320                              reporter,
321                              ctxInfo,
322                              use_padding_options,
323                              CtsEnforcement::kNever) {
324     auto dContext = ctxInfo.directContext();
325     sk_sp<DiscardableManager> discardableManager = sk_make_sp<DiscardableManager>();
326     SkStrikeServer server(discardableManager.get());
327     SkStrikeClient client(discardableManager, false);
328     const SkPaint paint;
329 
330     // Server.
331     auto serverTf = SkTypeface::MakeFromName("monospace", SkFontStyle());
332     auto serverTfData = server.serializeTypeface(serverTf.get());
333 
334     int glyphCount = 10;
335     auto serverBlob = buildTextBlob(serverTf, glyphCount);
336     auto props = FindSurfaceProps(dContext);
337     std::unique_ptr<SkCanvas> analysisCanvas = server.makeAnalysisCanvas(
338             10, 10, props, nullptr, dContext->supportsDistanceFieldText(),
339             !dContext->priv().caps()->disablePerspectiveSDFText());
340 
341     // Generate strike updates.
342     (void)Slug::ConvertBlob(analysisCanvas.get(), *serverBlob, {0, 0}, paint);
343 
344     std::vector<uint8_t> serverStrikeData;
345     server.writeStrikeData(&serverStrikeData);
346 
347     // Client.
348     auto clientTf = client.deserializeTypeface(serverTfData->data(), serverTfData->size());
349     REPORTER_ASSERT(reporter,
350                     client.readStrikeData(serverStrikeData.data(), serverStrikeData.size()));
351     auto clientBlob = buildTextBlob(clientTf, glyphCount);
352 
353     SkBitmap expected = RasterBlobThroughSlug(serverBlob, 10, 10, paint, dContext);
354     SkBitmap actual = RasterBlobThroughSlug(clientBlob, 10, 10, paint, dContext);
355     compare_blobs(expected, actual, reporter);
356     REPORTER_ASSERT(reporter, !discardableManager->hasCacheMiss());
357 
358     // Must unlock everything on termination, otherwise valgrind complains about memory leaks.
359     discardableManager->unlockAndDeleteAll();
360 }
361 
DEF_GANESH_TEST_FOR_CONTEXTS(SkRemoteGlyphCache_StrikeSerializationSlugForcePath,sk_gpu_test::GrContextFactory::IsRenderingContext,reporter,ctxInfo,use_padding_options,CtsEnforcement::kNever)362 DEF_GANESH_TEST_FOR_CONTEXTS(SkRemoteGlyphCache_StrikeSerializationSlugForcePath,
363                              sk_gpu_test::GrContextFactory::IsRenderingContext,
364                              reporter,
365                              ctxInfo,
366                              use_padding_options,
367                              CtsEnforcement::kNever) {
368     auto dContext = ctxInfo.directContext();
369     sk_sp<DiscardableManager> discardableManager = sk_make_sp<DiscardableManager>();
370     SkStrikeServer server(discardableManager.get());
371     SkStrikeClient client(discardableManager, false);
372     const SkPaint paint;
373 
374     // Server.
375     auto serverTf = SkTypeface::MakeFromName("monospace", SkFontStyle());
376     auto serverTfData = server.serializeTypeface(serverTf.get());
377 
378     int glyphCount = 10;
379     auto serverBlob = buildTextBlob(serverTf, glyphCount, 360);
380     auto props = FindSurfaceProps(dContext);
381     std::unique_ptr<SkCanvas> analysisCanvas = server.makeAnalysisCanvas(
382             10, 10, props, nullptr, dContext->supportsDistanceFieldText(),
383             !dContext->priv().caps()->disablePerspectiveSDFText());
384 
385     // Generate strike updates.
386     (void)Slug::ConvertBlob(analysisCanvas.get(), *serverBlob, {0, 0}, paint);
387 
388     std::vector<uint8_t> serverStrikeData;
389     server.writeStrikeData(&serverStrikeData);
390 
391     // Client.
392     auto clientTf = client.deserializeTypeface(serverTfData->data(), serverTfData->size());
393     REPORTER_ASSERT(reporter,
394                     client.readStrikeData(serverStrikeData.data(), serverStrikeData.size()));
395     auto clientBlob = buildTextBlob(clientTf, glyphCount, 360);
396 
397     SkBitmap expected = RasterBlobThroughSlug(serverBlob, 10, 10, paint, dContext);
398     SkBitmap actual = RasterBlobThroughSlug(clientBlob, 10, 10, paint, dContext);
399     compare_blobs(expected, actual, reporter);
400     REPORTER_ASSERT(reporter, !discardableManager->hasCacheMiss());
401 
402     // Must unlock everything on termination, otherwise valgrind complains about memory leaks.
403     discardableManager->unlockAndDeleteAll();
404 }
405 
DEF_GANESH_TEST_FOR_CONTEXTS(SkRemoteGlyphCache_SlugSerialization,sk_gpu_test::GrContextFactory::IsRenderingContext,reporter,ctxInfo,use_padding_options,CtsEnforcement::kNever)406 DEF_GANESH_TEST_FOR_CONTEXTS(SkRemoteGlyphCache_SlugSerialization,
407                              sk_gpu_test::GrContextFactory::IsRenderingContext,
408                              reporter,
409                              ctxInfo,
410                              use_padding_options,
411                              CtsEnforcement::kNever) {
412     auto dContext = ctxInfo.directContext();
413     sk_sp<DiscardableManager> discardableManager = sk_make_sp<DiscardableManager>();
414     SkStrikeServer server(discardableManager.get());
415     SkStrikeClient client(discardableManager, false);
416     const SkPaint paint;
417 
418     // Server.
419     auto serverTf = SkTypeface::MakeFromName("monospace", SkFontStyle());
420     auto serverTfData = server.serializeTypeface(serverTf.get());
421 
422     int glyphCount = 10;
423     auto serverBlob = buildTextBlob(serverTf, glyphCount);
424     auto props = FindSurfaceProps(dContext);
425     std::unique_ptr<SkCanvas> analysisCanvas = server.makeAnalysisCanvas(
426             10, 10, props, nullptr, dContext->supportsDistanceFieldText(),
427             !dContext->priv().caps()->disablePerspectiveSDFText());
428 
429     // Generate strike updates.
430     auto srcSlug = Slug::ConvertBlob(analysisCanvas.get(), *serverBlob, {0.3f, 0}, paint);
431     auto dstSlugData = srcSlug->serialize();
432 
433     std::vector<uint8_t> serverStrikeData;
434     server.writeStrikeData(&serverStrikeData);
435 
436     // Client.
437     REPORTER_ASSERT(reporter,
438                     client.readStrikeData(serverStrikeData.data(), serverStrikeData.size()));
439 
440     SkBitmap expected = RasterSlug(srcSlug, 10, 10, paint, dContext);
441     auto dstSlug = client.deserializeSlug(dstSlugData->data(), dstSlugData->size());
442     REPORTER_ASSERT(reporter, dstSlug != nullptr);
443     SkBitmap actual = RasterSlug(dstSlug, 10, 10, paint, dContext);
444     compare_blobs(expected, actual, reporter);
445     REPORTER_ASSERT(reporter, !discardableManager->hasCacheMiss());
446 
447     // Must unlock everything on termination, otherwise valgrind complains about memory leaks.
448     discardableManager->unlockAndDeleteAll();
449 }
450 
DEF_GANESH_TEST_FOR_RENDERING_CONTEXTS(SkRemoteGlyphCache_ReleaseTypeFace,reporter,ctxInfo,CtsEnforcement::kNever)451 DEF_GANESH_TEST_FOR_RENDERING_CONTEXTS(SkRemoteGlyphCache_ReleaseTypeFace,
452                                        reporter,
453                                        ctxInfo,
454                                        CtsEnforcement::kNever) {
455     sk_sp<DiscardableManager> discardableManager = sk_make_sp<DiscardableManager>();
456     SkStrikeServer server(discardableManager.get());
457     SkStrikeClient client(discardableManager, false);
458 
459     // Server.
460     auto serverTf     = TestEmptyTypeface::Make();
461     auto serverTfData = server.serializeTypeface(serverTf.get());
462     REPORTER_ASSERT(reporter, serverTf->unique());
463 
464     {
465         const SkPaint paint;
466         int glyphCount = 10;
467         auto serverBlob = buildTextBlob(serverTf, glyphCount);
468         const SkSurfaceProps props;
469         std::unique_ptr<SkCanvas> cache_diff_canvas = server.makeAnalysisCanvas(
470                 10, 10, props, nullptr, ctxInfo.directContext()->supportsDistanceFieldText(),
471                 !ctxInfo.directContext()->priv().caps()->disablePerspectiveSDFText());
472         cache_diff_canvas->drawTextBlob(serverBlob.get(), 0, 0, paint);
473         REPORTER_ASSERT(reporter, !serverTf->unique());
474 
475         std::vector<uint8_t> serverStrikeData;
476         server.writeStrikeData(&serverStrikeData);
477     }
478     REPORTER_ASSERT(reporter, serverTf->unique());
479 
480     // Must unlock everything on termination, otherwise valgrind complains about memory leaks.
481     discardableManager->unlockAndDeleteAll();
482 }
483 
DEF_TEST(SkRemoteGlyphCache_StrikeLockingServer,reporter)484 DEF_TEST(SkRemoteGlyphCache_StrikeLockingServer, reporter) {
485     sk_sp<DiscardableManager> discardableManager = sk_make_sp<DiscardableManager>();
486     SkStrikeServer server(discardableManager.get());
487     SkStrikeClient client(discardableManager, false);
488 
489     auto serverTf = SkTypeface::MakeFromName("monospace", SkFontStyle());
490     server.serializeTypeface(serverTf.get());
491     int glyphCount = 10;
492     auto serverBlob = buildTextBlob(serverTf, glyphCount);
493 
494     const SkSurfaceProps props;
495     std::unique_ptr<SkCanvas> cache_diff_canvas =
496             server.makeAnalysisCanvas(10, 10, props, nullptr, true, true);
497     SkPaint paint;
498     cache_diff_canvas->drawTextBlob(serverBlob.get(), 0, 0, paint);
499 
500     // The strike from the blob should be locked after it has been drawn on the canvas.
501     REPORTER_ASSERT(reporter, discardableManager->handleCount() == 1u);
502     REPORTER_ASSERT(reporter, discardableManager->lockedHandles().count() == 1u);
503 
504     // Write the strike data and unlock everything. Re-analyzing the blob should lock the handle
505     // again.
506     std::vector<uint8_t> fontData;
507     server.writeStrikeData(&fontData);
508     discardableManager->unlockAll();
509     REPORTER_ASSERT(reporter, discardableManager->lockedHandles().count() == 0u);
510 
511     cache_diff_canvas->drawTextBlob(serverBlob.get(), 0, 0, paint);
512     REPORTER_ASSERT(reporter, discardableManager->handleCount() == 1u);
513     REPORTER_ASSERT(reporter, discardableManager->lockedHandles().count() == 1u);
514 
515     // Must unlock everything on termination, otherwise valgrind complains about memory leaks.
516     discardableManager->unlockAndDeleteAll();
517 }
518 
DEF_TEST(SkRemoteGlyphCache_StrikeDeletionServer,reporter)519 DEF_TEST(SkRemoteGlyphCache_StrikeDeletionServer, reporter) {
520     sk_sp<DiscardableManager> discardableManager = sk_make_sp<DiscardableManager>();
521     SkStrikeServer server(discardableManager.get());
522     SkStrikeClient client(discardableManager, false);
523 
524     auto serverTf = SkTypeface::MakeFromName("monospace", SkFontStyle());
525     server.serializeTypeface(serverTf.get());
526     int glyphCount = 10;
527     auto serverBlob = buildTextBlob(serverTf, glyphCount);
528 
529     const SkSurfaceProps props;
530     std::unique_ptr<SkCanvas> cache_diff_canvas =
531             server.makeAnalysisCanvas(10, 10, props, nullptr, true, true);
532     SkPaint paint;
533     cache_diff_canvas->drawTextBlob(serverBlob.get(), 0, 0, paint);
534     REPORTER_ASSERT(reporter, discardableManager->handleCount() == 1u);
535 
536     // Write the strike data and delete all the handles. Re-analyzing the blob should create new
537     // handles.
538     std::vector<uint8_t> fontData;
539     server.writeStrikeData(&fontData);
540 
541     // Another analysis pass, to ensure that deleting handles after a complete cache hit still
542     // works. This is a regression test for crbug.com/999682.
543     cache_diff_canvas->drawTextBlob(serverBlob.get(), 0, 0, paint);
544     server.writeStrikeData(&fontData);
545     REPORTER_ASSERT(reporter, discardableManager->handleCount() == 1u);
546 
547     discardableManager->unlockAndDeleteAll();
548     cache_diff_canvas->drawTextBlob(serverBlob.get(), 0, 0, paint);
549     REPORTER_ASSERT(reporter, discardableManager->handleCount() == 2u);
550 
551     // Must unlock everything on termination, otherwise valgrind complains about memory leaks.
552     discardableManager->unlockAndDeleteAll();
553 }
554 
DEF_TEST(SkRemoteGlyphCache_StrikePinningClient,reporter)555 DEF_TEST(SkRemoteGlyphCache_StrikePinningClient, reporter) {
556     sk_sp<DiscardableManager> discardableManager = sk_make_sp<DiscardableManager>();
557     SkStrikeServer server(discardableManager.get());
558     SkStrikeClient client(discardableManager, false);
559 
560     // Server.
561     auto serverTf = SkTypeface::MakeFromName("monospace", SkFontStyle());
562     auto serverTfData = server.serializeTypeface(serverTf.get());
563 
564     int glyphCount = 10;
565     auto serverBlob = buildTextBlob(serverTf, glyphCount);
566 
567     const SkSurfaceProps props;
568     std::unique_ptr<SkCanvas> cache_diff_canvas =
569             server.makeAnalysisCanvas(10, 10, props, nullptr, true, true);
570     SkPaint paint;
571     cache_diff_canvas->drawTextBlob(serverBlob.get(), 0, 0, paint);
572 
573     std::vector<uint8_t> serverStrikeData;
574     server.writeStrikeData(&serverStrikeData);
575 
576     // Client.
577     REPORTER_ASSERT(reporter,
578                     client.readStrikeData(serverStrikeData.data(), serverStrikeData.size()));
579     auto* clientTf = client.deserializeTypeface(serverTfData->data(), serverTfData->size()).get();
580 
581     // The cache remains alive until it is pinned in the discardable manager.
582     SkGraphics::PurgeFontCache();
583     REPORTER_ASSERT(reporter, !clientTf->unique());
584 
585     // Once the strike is unpinned and purged, SkStrikeClient should be the only owner of the
586     // clientTf.
587     discardableManager->unlockAndDeleteAll();
588     SkGraphics::PurgeFontCache();
589     REPORTER_ASSERT(reporter, clientTf->unique());
590 
591     // Must unlock everything on termination, otherwise valgrind complains about memory leaks.
592     discardableManager->unlockAndDeleteAll();
593 }
594 
DEF_TEST(SkRemoteGlyphCache_ClientMemoryAccounting,reporter)595 DEF_TEST(SkRemoteGlyphCache_ClientMemoryAccounting, reporter) {
596     sk_sp<DiscardableManager> discardableManager = sk_make_sp<DiscardableManager>();
597     SkStrikeServer server(discardableManager.get());
598     SkStrikeClient client(discardableManager, false);
599 
600     // Server.
601     auto serverTf = SkTypeface::MakeFromName("monospace", SkFontStyle());
602     auto serverTfData = server.serializeTypeface(serverTf.get());
603 
604     int glyphCount = 10;
605     auto serverBlob = buildTextBlob(serverTf, glyphCount);
606 
607     const SkSurfaceProps props;
608     std::unique_ptr<SkCanvas> cache_diff_canvas =
609             server.makeAnalysisCanvas(10, 10, props, nullptr, true, true);
610     SkPaint paint;
611     cache_diff_canvas->drawTextBlob(serverBlob.get(), 0, 0, paint);
612 
613     std::vector<uint8_t> serverStrikeData;
614     server.writeStrikeData(&serverStrikeData);
615 
616     // Client.
617     REPORTER_ASSERT(reporter,
618                     client.readStrikeData(serverStrikeData.data(), serverStrikeData.size()));
619 
620     // Must unlock everything on termination, otherwise valgrind complains about memory leaks.
621     discardableManager->unlockAndDeleteAll();
622 }
623 
DEF_TEST(SkRemoteGlyphCache_PurgesServerEntries,reporter)624 DEF_TEST(SkRemoteGlyphCache_PurgesServerEntries, reporter) {
625     sk_sp<DiscardableManager> discardableManager = sk_make_sp<DiscardableManager>();
626     SkStrikeServer server(discardableManager.get());
627     server.setMaxEntriesInDescriptorMapForTesting(1u);
628     SkStrikeClient client(discardableManager, false);
629 
630     {
631         auto serverTf = SkTypeface::MakeFromName("monospace", SkFontStyle());
632         int glyphCount = 10;
633         auto serverBlob = buildTextBlob(serverTf, glyphCount);
634 
635         const SkSurfaceProps props;
636         std::unique_ptr<SkCanvas> cache_diff_canvas =
637             server.makeAnalysisCanvas(10, 10, props, nullptr, true, true);
638         SkPaint paint;
639         REPORTER_ASSERT(reporter, server.remoteStrikeMapSizeForTesting() == 0u);
640         cache_diff_canvas->drawTextBlob(serverBlob.get(), 0, 0, paint);
641         REPORTER_ASSERT(reporter, server.remoteStrikeMapSizeForTesting() == 1u);
642     }
643 
644     // Serialize to release the lock from the strike server and delete all current
645     // handles.
646     std::vector<uint8_t> fontData;
647     server.writeStrikeData(&fontData);
648     discardableManager->unlockAndDeleteAll();
649 
650     // Use a different typeface. Creating a new strike should evict the previous
651     // one.
652     {
653         auto serverTf = SkTypeface::MakeFromName("Georgia", SkFontStyle());
654         int glyphCount = 10;
655         auto serverBlob = buildTextBlob(serverTf, glyphCount);
656 
657         const SkSurfaceProps props;
658         std::unique_ptr<SkCanvas> cache_diff_canvas =
659             server.makeAnalysisCanvas(10, 10, props, nullptr, true, true);
660         SkPaint paint;
661         REPORTER_ASSERT(reporter, server.remoteStrikeMapSizeForTesting() == 1u);
662         cache_diff_canvas->drawTextBlob(serverBlob.get(), 0, 0, paint);
663         REPORTER_ASSERT(reporter, server.remoteStrikeMapSizeForTesting() == 1u);
664     }
665 
666     // Must unlock everything on termination, otherwise valgrind complains about memory leaks.
667     discardableManager->unlockAndDeleteAll();
668 }
669 
DEF_GANESH_TEST_FOR_RENDERING_CONTEXTS(SkRemoteGlyphCache_DrawTextAsPath,reporter,ctxInfo,CtsEnforcement::kNever)670 DEF_GANESH_TEST_FOR_RENDERING_CONTEXTS(SkRemoteGlyphCache_DrawTextAsPath,
671                                        reporter,
672                                        ctxInfo,
673                                        CtsEnforcement::kNever) {
674     auto direct = ctxInfo.directContext();
675     sk_sp<DiscardableManager> discardableManager = sk_make_sp<DiscardableManager>();
676     SkStrikeServer server(discardableManager.get());
677     SkStrikeClient client(discardableManager, false);
678     SkPaint paint;
679     paint.setStyle(SkPaint::kStroke_Style);
680     paint.setStrokeWidth(0);
681     REPORTER_ASSERT(reporter,
682             SkStrikeSpec::ShouldDrawAsPath(paint, SkFont(), SkMatrix::I()));
683 
684     // Server.
685     auto serverTf = SkTypeface::MakeFromName("monospace", SkFontStyle());
686     auto serverTfData = server.serializeTypeface(serverTf.get());
687 
688     int glyphCount = 10;
689     auto serverBlob = buildTextBlob(serverTf, glyphCount);
690     auto props = FindSurfaceProps(direct);
691     std::unique_ptr<SkCanvas> cache_diff_canvas = server.makeAnalysisCanvas(
692             10, 10, props, nullptr, direct->supportsDistanceFieldText(),
693             !direct->priv().caps()->disablePerspectiveSDFText());
694     cache_diff_canvas->drawTextBlob(serverBlob.get(), 0, 0, paint);
695 
696     std::vector<uint8_t> serverStrikeData;
697     server.writeStrikeData(&serverStrikeData);
698 
699     // Client.
700     auto clientTf = client.deserializeTypeface(serverTfData->data(), serverTfData->size());
701     REPORTER_ASSERT(reporter,
702                     client.readStrikeData(serverStrikeData.data(), serverStrikeData.size()));
703     auto clientBlob = buildTextBlob(clientTf, glyphCount);
704 
705     SkBitmap expected = RasterBlob(serverBlob, 10, 10, paint, direct);
706     SkBitmap actual = RasterBlob(clientBlob, 10, 10, paint, direct);
707     compare_blobs(expected, actual, reporter, 1);
708     REPORTER_ASSERT(reporter, !discardableManager->hasCacheMiss());
709 
710     // Must unlock everything on termination, otherwise valgrind complains about memory leaks.
711     discardableManager->unlockAndDeleteAll();
712 }
713 
make_blob_causing_fallback(sk_sp<SkTypeface> targetTf,const SkTypeface * glyphTf,skiatest::Reporter * reporter)714 sk_sp<SkTextBlob> make_blob_causing_fallback(
715         sk_sp<SkTypeface> targetTf, const SkTypeface* glyphTf, skiatest::Reporter* reporter) {
716     SkFont font;
717     font.setSubpixel(true);
718     font.setSize(96);
719     font.setHinting(SkFontHinting::kNormal);
720     font.setTypeface(targetTf);
721 
722     REPORTER_ASSERT(reporter,
723             !SkStrikeSpec::ShouldDrawAsPath(SkPaint(), font, SkMatrix::I()));
724 
725     char s[] = "Skia";
726     int runSize = strlen(s);
727 
728     SkTextBlobBuilder builder;
729     SkRect bounds = SkRect::MakeIWH(100, 100);
730     const auto& runBuffer = builder.allocRunPosH(font, runSize, 10, &bounds);
731     SkASSERT(runBuffer.utf8text == nullptr);
732     SkASSERT(runBuffer.clusters == nullptr);
733 
734     SkFont(sk_ref_sp(glyphTf)).textToGlyphs(s, strlen(s), SkTextEncoding::kUTF8,
735                                             runBuffer.glyphs, runSize);
736 
737     SkRect glyphBounds;
738     font.getWidths(runBuffer.glyphs, 1, nullptr, &glyphBounds);
739 
740     REPORTER_ASSERT(reporter, glyphBounds.width() > SkGlyphDigest::kSkSideTooBigForAtlas);
741 
742     for (int i = 0; i < runSize; i++) {
743         runBuffer.pos[i] = i * 10;
744     }
745 
746     return builder.make();
747 }
748 
DEF_GANESH_TEST_FOR_RENDERING_CONTEXTS(SkRemoteGlyphCache_DrawTextAsMaskWithPathFallback,reporter,ctxInfo,CtsEnforcement::kNever)749 DEF_GANESH_TEST_FOR_RENDERING_CONTEXTS(SkRemoteGlyphCache_DrawTextAsMaskWithPathFallback,
750                                        reporter,
751                                        ctxInfo,
752                                        CtsEnforcement::kNever) {
753     auto direct = ctxInfo.directContext();
754     sk_sp<DiscardableManager> discardableManager = sk_make_sp<DiscardableManager>();
755     SkStrikeServer server(discardableManager.get());
756     SkStrikeClient client(discardableManager, false);
757 
758     SkPaint paint;
759 
760     auto serverTf = MakeResourceAsTypeface("fonts/HangingS.ttf");
761     // TODO: when the cq bots can handle this font remove the check.
762     if (serverTf == nullptr) {
763         return;
764     }
765     auto serverTfData = server.serializeTypeface(serverTf.get());
766 
767     auto serverBlob = make_blob_causing_fallback(serverTf, serverTf.get(), reporter);
768 
769     auto props = FindSurfaceProps(direct);
770     std::unique_ptr<SkCanvas> cache_diff_canvas = server.makeAnalysisCanvas(
771             10, 10, props, nullptr, direct->supportsDistanceFieldText(),
772             !direct->priv().caps()->disablePerspectiveSDFText());
773     cache_diff_canvas->drawTextBlob(serverBlob.get(), 0, 0, paint);
774 
775     std::vector<uint8_t> serverStrikeData;
776     server.writeStrikeData(&serverStrikeData);
777 
778     // Client.
779     auto clientTf = client.deserializeTypeface(serverTfData->data(), serverTfData->size());
780     REPORTER_ASSERT(reporter,
781                     client.readStrikeData(serverStrikeData.data(), serverStrikeData.size()));
782 
783     auto clientBlob = make_blob_causing_fallback(clientTf, serverTf.get(), reporter);
784 
785     SkBitmap expected = RasterBlob(serverBlob, 10, 10, paint, direct);
786     SkBitmap actual = RasterBlob(clientBlob, 10, 10, paint, direct);
787     compare_blobs(expected, actual, reporter);
788     REPORTER_ASSERT(reporter, !discardableManager->hasCacheMiss());
789 
790     // Must unlock everything on termination, otherwise valgrind complains about memory leaks.
791     discardableManager->unlockAndDeleteAll();
792 }
793 
794 #if 0
795 // TODO: turn this one when I figure out how to deal with the pixel variance from linear
796 //  interpolation from GPU to GPU.
797 DEF_GANESH_TEST_FOR_RENDERING_CONTEXTS(SkRemoteGlyphCache_DrawTextAsSDFTWithAllARGBFallback,
798                                    reporter, ctxInfo, CtsEnforcement::kNever) {
799     sk_sp<DiscardableManager> discardableManager = sk_make_sp<DiscardableManager>();
800     SkStrikeServer server(discardableManager.get());
801     SkStrikeClient client(discardableManager, false);
802 
803     SkPaint paint;
804 
805     auto serverTf = ToolUtils::planet_typeface();
806     // TODO: when the cq bots can handle this font remove the check.
807     if (serverTf == nullptr) {
808         return;
809     }
810     auto serverTfData = server.serializeTypeface(serverTf.get());
811 
812     auto makeBlob = [&reporter](sk_sp<SkTypeface> typeface) {
813         SkFont font;
814         font.setSubpixel(true);
815         font.setSize(96);
816         font.setHinting(SkFontHinting::kNormal);
817         font.setTypeface(typeface);
818 
819         REPORTER_ASSERT(reporter, !SkDraw::ShouldDrawTextAsPaths(font, SkPaint(), SkMatrix::I()));
820 
821         // Mercury to Uranus.
822         SkGlyphID glyphs[] = {1, 2, 3, 4, 5, 6, 7, 8};
823 
824         SkTextBlobBuilder builder;
825         SkRect bounds = SkRect::MakeIWH(100, 100);
826         const auto& runBuffer = builder.allocRunPosH(font, std::size(glyphs), 100, &bounds);
827         SkASSERT(runBuffer.utf8text == nullptr);
828         SkASSERT(runBuffer.clusters == nullptr);
829 
830         std::copy(std::begin(glyphs), std::end(glyphs), runBuffer.glyphs);
831 
832         for (size_t i = 0; i < std::size(glyphs); i++) {
833             runBuffer.pos[i] = i * 100;
834         }
835 
836         return builder.make();
837     };
838 
839     auto serverBlob = makeBlob(serverTf);
840 
841     auto props = FindSurfaceProps(ctxInfo.grContext());
842     std::unique_ptr<SkCanvas> cache_diff_canvas = server.makeAnalysisCanvas(
843             10, 10, props, nullptr, ctxInfo.directContext()->supportsDistanceFieldText());
844     cache_diff_canvas->drawTextBlob(serverBlob.get(), 0, 400, paint);
845 
846     std::vector<uint8_t> serverStrikeData;
847     server.writeStrikeData(&serverStrikeData);
848 
849     // Client.
850     auto clientTf = client.deserializeTypeface(serverTfData->data(), serverTfData->size());
851     REPORTER_ASSERT(reporter,
852                     client.readStrikeData(serverStrikeData.data(), serverStrikeData.size()));
853 
854     auto clientBlob = makeBlob(clientTf);
855 
856     SkBitmap expected = RasterBlob(serverBlob, 800, 800, paint, ctxInfo.grContext());
857     SkBitmap actual = RasterBlob(clientBlob, 800, 800, paint, ctxInfo.grContext());
858 
859     // Pixel variance can be high because of the atlas placement, and large scaling in the linear
860     // interpolation.
861     compare_blobs(expected, actual, reporter, 36);
862     REPORTER_ASSERT(reporter, !discardableManager->hasCacheMiss());
863 
864     // Must unlock everything on termination, otherwise valgrind complains about memory leaks.
865     discardableManager->unlockAndDeleteAll();
866 }
867 #endif
868 
DEF_GANESH_TEST_FOR_RENDERING_CONTEXTS(SkRemoteGlyphCache_DrawTextXY,reporter,ctxInfo,CtsEnforcement::kNever)869 DEF_GANESH_TEST_FOR_RENDERING_CONTEXTS(SkRemoteGlyphCache_DrawTextXY,
870                                        reporter,
871                                        ctxInfo,
872                                        CtsEnforcement::kNever) {
873     auto direct = ctxInfo.directContext();
874     sk_sp<DiscardableManager> discardableManager = sk_make_sp<DiscardableManager>();
875     SkStrikeServer server(discardableManager.get());
876     SkStrikeClient client(discardableManager, false);
877     SkPaint paint;
878     paint.setAntiAlias(true);
879 
880     // Server.
881     auto serverTf = SkTypeface::MakeFromName("monospace", SkFontStyle());
882     auto serverTfData = server.serializeTypeface(serverTf.get());
883 
884     int glyphCount = 10;
885     auto serverBlob = buildTextBlob(serverTf, glyphCount);
886     auto props = FindSurfaceProps(direct);
887     std::unique_ptr<SkCanvas> cache_diff_canvas = server.makeAnalysisCanvas(
888             10, 10, props, nullptr, direct->supportsDistanceFieldText(),
889             !direct->priv().caps()->disablePerspectiveSDFText());
890     cache_diff_canvas->drawTextBlob(serverBlob.get(), 0.5, 0, paint);
891 
892     std::vector<uint8_t> serverStrikeData;
893     server.writeStrikeData(&serverStrikeData);
894 
895     // Client.
896     auto clientTf = client.deserializeTypeface(serverTfData->data(), serverTfData->size());
897     REPORTER_ASSERT(reporter,
898                     client.readStrikeData(serverStrikeData.data(), serverStrikeData.size()));
899     auto clientBlob = buildTextBlob(clientTf, glyphCount);
900 
901     SkBitmap expected = RasterBlob(serverBlob, 10, 10, paint, direct, nullptr, 0.5);
902     SkBitmap actual = RasterBlob(clientBlob, 10, 10, paint, direct, nullptr, 0.5);
903     compare_blobs(expected, actual, reporter);
904     REPORTER_ASSERT(reporter, !discardableManager->hasCacheMiss());
905 
906     // Must unlock everything on termination, otherwise valgrind complains about memory leaks.
907     discardableManager->unlockAndDeleteAll();
908 }
909 
910 #if !defined(SK_DISABLE_SDF_TEXT)
DEF_GANESH_TEST_FOR_RENDERING_CONTEXTS(SkRemoteGlyphCache_DrawTextAsDFT,reporter,ctxInfo,CtsEnforcement::kNever)911 DEF_GANESH_TEST_FOR_RENDERING_CONTEXTS(SkRemoteGlyphCache_DrawTextAsDFT,
912                                        reporter,
913                                        ctxInfo,
914                                        CtsEnforcement::kNever) {
915     auto direct = ctxInfo.directContext();
916     if (!direct->priv().caps()->shaderCaps()->supportsDistanceFieldText()) {
917         return;
918     }
919     sk_sp<DiscardableManager> discardableManager = sk_make_sp<DiscardableManager>();
920     SkStrikeServer server(discardableManager.get());
921     SkStrikeClient client(discardableManager, false);
922     SkPaint paint;
923     SkFont font;
924 
925     // A scale transform forces fallback to dft.
926     SkMatrix matrix = SkMatrix::Scale(16, 16);
927     sktext::gpu::SDFTControl control =
928             direct->priv().asRecordingContext()->priv().getSDFTControl(true);
929     SkScalar approximateDeviceTextSize = SkFontPriv::ApproximateTransformedTextSize(font, matrix,
930                                                                                     {0, 0});
931     REPORTER_ASSERT(reporter, control.isSDFT(approximateDeviceTextSize, paint, matrix));
932 
933     // Server.
934     auto serverTf = SkTypeface::MakeFromName("monospace", SkFontStyle());
935     auto serverTfData = server.serializeTypeface(serverTf.get());
936 
937     int glyphCount = 10;
938     auto serverBlob = buildTextBlob(serverTf, glyphCount);
939     const SkSurfaceProps props;
940     std::unique_ptr<SkCanvas> cache_diff_canvas = server.makeAnalysisCanvas(
941             10, 10, props, nullptr, direct->supportsDistanceFieldText(),
942             !direct->priv().caps()->disablePerspectiveSDFText());
943     cache_diff_canvas->concat(matrix);
944     cache_diff_canvas->drawTextBlob(serverBlob.get(), 0, 0, paint);
945 
946     std::vector<uint8_t> serverStrikeData;
947     server.writeStrikeData(&serverStrikeData);
948 
949     // Client.
950     auto clientTf = client.deserializeTypeface(serverTfData->data(), serverTfData->size());
951     REPORTER_ASSERT(reporter,
952                     client.readStrikeData(serverStrikeData.data(), serverStrikeData.size()));
953     auto clientBlob = buildTextBlob(clientTf, glyphCount);
954 
955     SkBitmap expected = RasterBlob(serverBlob, 10, 10, paint, direct, &matrix);
956     SkBitmap actual = RasterBlob(clientBlob, 10, 10, paint, direct, &matrix);
957     compare_blobs(expected, actual, reporter);
958     REPORTER_ASSERT(reporter, !discardableManager->hasCacheMiss());
959 
960     // Must unlock everything on termination, otherwise valgrind complains about memory leaks.
961     discardableManager->unlockAndDeleteAll();
962 }
963 #endif // !defined(SK_DISABLE_SDF_TEXT)
964 
DEF_GANESH_TEST_FOR_RENDERING_CONTEXTS(SkRemoteGlyphCache_CacheMissReporting,reporter,ctxInfo,CtsEnforcement::kNever)965 DEF_GANESH_TEST_FOR_RENDERING_CONTEXTS(SkRemoteGlyphCache_CacheMissReporting,
966                                        reporter,
967                                        ctxInfo,
968                                        CtsEnforcement::kNever) {
969     sk_sp<DiscardableManager> discardableManager = sk_make_sp<DiscardableManager>();
970     SkStrikeServer server(discardableManager.get());
971     SkStrikeClient client(discardableManager, false);
972 
973     auto serverTf = SkTypeface::MakeFromName("monospace", SkFontStyle());
974     auto tfData = server.serializeTypeface(serverTf.get());
975     auto clientTf = client.deserializeTypeface(tfData->data(), tfData->size());
976     REPORTER_ASSERT(reporter, clientTf);
977     int glyphCount = 10;
978     auto clientBlob = buildTextBlob(clientTf, glyphCount);
979 
980     // Raster the client-side blob without the glyph data, we should get cache miss notifications.
981     SkPaint paint;
982     SkMatrix matrix = SkMatrix::I();
983     RasterBlob(clientBlob, 10, 10, paint, ctxInfo.directContext(), &matrix);
984     REPORTER_ASSERT(reporter,
985                     discardableManager->cacheMissCount(SkStrikeClient::kFontMetrics) == 1);
986     REPORTER_ASSERT(reporter,
987                     discardableManager->cacheMissCount(SkStrikeClient::kGlyphMetrics) == 10);
988 
989     // There shouldn't be any image or path requests, since we mark the glyph as empty on a cache
990     // miss.
991     REPORTER_ASSERT(reporter, discardableManager->cacheMissCount(SkStrikeClient::kGlyphImage) == 0);
992     REPORTER_ASSERT(reporter, discardableManager->cacheMissCount(SkStrikeClient::kGlyphPath) == 0);
993 
994     // Must unlock everything on termination, otherwise valgrind complains about memory leaks.
995     discardableManager->unlockAndDeleteAll();
996 }
997 
MakeEmojiBlob(sk_sp<SkTypeface> serverTf,SkScalar textSize,sk_sp<SkTypeface> clientTf=nullptr)998 sk_sp<SkTextBlob> MakeEmojiBlob(sk_sp<SkTypeface> serverTf, SkScalar textSize,
999                                 sk_sp<SkTypeface> clientTf = nullptr) {
1000     SkFont font;
1001     font.setTypeface(serverTf);
1002     font.setSize(textSize);
1003 
1004     const char* text = ToolUtils::emoji_sample_text();
1005     auto blob = SkTextBlob::MakeFromText(text, strlen(text), font);
1006     if (clientTf == nullptr) return blob;
1007 
1008     SkSerialProcs s_procs;
1009     s_procs.fTypefaceProc = [](SkTypeface*, void* ctx) -> sk_sp<SkData> {
1010         return SkData::MakeUninitialized(1u);
1011     };
1012     auto serialized = blob->serialize(s_procs);
1013 
1014     SkDeserialProcs d_procs;
1015     d_procs.fTypefaceCtx = &clientTf;
1016     d_procs.fTypefaceProc = [](const void* data, size_t length, void* ctx) -> sk_sp<SkTypeface> {
1017         return *(static_cast<sk_sp<SkTypeface>*>(ctx));
1018     };
1019     return SkTextBlob::Deserialize(serialized->data(), serialized->size(), d_procs);
1020 }
1021 
DEF_GANESH_TEST_FOR_RENDERING_CONTEXTS(SkRemoteGlyphCache_TypefaceWithNoPaths,reporter,ctxInfo,CtsEnforcement::kNever)1022 DEF_GANESH_TEST_FOR_RENDERING_CONTEXTS(SkRemoteGlyphCache_TypefaceWithNoPaths,
1023                                        reporter,
1024                                        ctxInfo,
1025                                        CtsEnforcement::kNever) {
1026     auto direct = ctxInfo.directContext();
1027     sk_sp<DiscardableManager> discardableManager = sk_make_sp<DiscardableManager>();
1028     SkStrikeServer server(discardableManager.get());
1029     SkStrikeClient client(discardableManager, false);
1030 
1031     auto serverTf = ToolUtils::emoji_typeface();
1032     auto serverTfData = server.serializeTypeface(serverTf.get());
1033     auto clientTf = client.deserializeTypeface(serverTfData->data(), serverTfData->size());
1034 
1035     auto props = FindSurfaceProps(direct);
1036     std::unique_ptr<SkCanvas> cache_diff_canvas = server.makeAnalysisCanvas(
1037             500, 500, props, nullptr, direct->supportsDistanceFieldText(),
1038             !direct->priv().caps()->disablePerspectiveSDFText());
1039     for (SkScalar textSize : { 70, 180, 270, 340}) {
1040         auto serverBlob = MakeEmojiBlob(serverTf, textSize);
1041 
1042         SkPaint paint;
1043         cache_diff_canvas->drawTextBlob(serverBlob.get(), 100, 100, paint);
1044 
1045         std::vector<uint8_t> serverStrikeData;
1046         server.writeStrikeData(&serverStrikeData);
1047         if (!serverStrikeData.empty()) {
1048             REPORTER_ASSERT(reporter,
1049                             client.readStrikeData(serverStrikeData.data(),
1050                                                   serverStrikeData.size()));
1051         }
1052         auto clientBlob = MakeEmojiBlob(serverTf, textSize, clientTf);
1053         REPORTER_ASSERT(reporter, clientBlob);
1054 
1055         RasterBlob(clientBlob, 500, 500, paint, direct);
1056         REPORTER_ASSERT(reporter, !discardableManager->hasCacheMiss());
1057         discardableManager->resetCacheMissCounts();
1058     }
1059 
1060     // Must unlock everything on termination, otherwise valgrind complains about memory leaks.
1061     discardableManager->unlockAndDeleteAll();
1062 }
1063 
1064 class SkRemoteGlyphCacheTest {
1065     public:
MakeNormalBlob(SkPaint * paint,sk_sp<SkTypeface> serverTf,bool asPaths,SkScalar textSize,sk_sp<SkTypeface> clientTf=nullptr)1066     static sk_sp<SkTextBlob> MakeNormalBlob(SkPaint* paint,
1067                                             sk_sp<SkTypeface> serverTf, bool asPaths, SkScalar textSize,
1068                                             sk_sp<SkTypeface> clientTf = nullptr) {
1069         SkFont font;
1070         font.setTypeface(serverTf);
1071         font.setSize(textSize);
1072 
1073         const char* text = "Hel lo";
1074         if (asPaths) {
1075             font.setupForAsPaths(paint);
1076         } else {
1077             SkFont font2(font);
1078             font2.setupForAsPaths(paint);
1079         }
1080         auto blob = SkTextBlob::MakeFromText(text, strlen(text), font);
1081         if (clientTf == nullptr) return blob;
1082 
1083         SkSerialProcs s_procs;
1084         s_procs.fTypefaceProc = [](SkTypeface*, void* ctx) -> sk_sp<SkData> {
1085             return SkData::MakeUninitialized(1u);
1086         };
1087         auto serialized = blob->serialize(s_procs);
1088 
1089         SkDeserialProcs d_procs;
1090         d_procs.fTypefaceCtx = &clientTf;
1091         d_procs.fTypefaceProc = [](const void* data, size_t length, void* ctx) -> sk_sp<SkTypeface> {
1092             return *(static_cast<sk_sp<SkTypeface>*>(ctx));
1093         };
1094         return SkTextBlob::Deserialize(serialized->data(), serialized->size(), d_procs);
1095     }
1096 };
DEF_GANESH_TEST_FOR_RENDERING_CONTEXTS(SkRemoteGlyphCache_TypefaceWithPaths_MaskThenPath,reporter,ctxInfo,CtsEnforcement::kNever)1097 DEF_GANESH_TEST_FOR_RENDERING_CONTEXTS(SkRemoteGlyphCache_TypefaceWithPaths_MaskThenPath,
1098                                        reporter,
1099                                        ctxInfo,
1100                                        CtsEnforcement::kNever) {
1101     auto direct = ctxInfo.directContext();
1102     sk_sp<DiscardableManager> discardableManager = sk_make_sp<DiscardableManager>();
1103     SkStrikeServer server(discardableManager.get());
1104     SkStrikeClient client(discardableManager, true);
1105 
1106     auto serverTf = ToolUtils::create_portable_typeface();
1107     auto serverTfData = server.serializeTypeface(serverTf.get());
1108     auto clientTf = client.deserializeTypeface(serverTfData->data(), serverTfData->size());
1109 
1110     auto props = FindSurfaceProps(direct);
1111     std::unique_ptr<SkCanvas> cache_diff_canvas = server.makeAnalysisCanvas(
1112             500, 500, props, nullptr, direct->supportsDistanceFieldText(),
1113             !direct->priv().caps()->disablePerspectiveSDFText());
1114     SkPaint paint;
1115     using Rgct = SkRemoteGlyphCacheTest;
1116 
1117     // Draw from mask out of the strike which provides paths.
1118     {
1119         auto serverBlob = Rgct::MakeNormalBlob(&paint, serverTf, true, 64);
1120         cache_diff_canvas->drawTextBlob(serverBlob.get(), 100, 100, paint);
1121     }
1122     // Draw from path out of the strike which provides paths.
1123     {
1124         auto serverBlob = Rgct::MakeNormalBlob(&paint, serverTf, false, 440);
1125         cache_diff_canvas->drawTextBlob(serverBlob.get(), 100, 100, paint);
1126     }
1127     std::vector<uint8_t> serverStrikeData;
1128     server.writeStrikeData(&serverStrikeData);
1129     if (!serverStrikeData.empty()) {
1130         REPORTER_ASSERT(reporter,
1131                         client.readStrikeData(serverStrikeData.data(),
1132                                               serverStrikeData.size()));
1133     }
1134     {
1135         auto clientBlob = Rgct::MakeNormalBlob(&paint, serverTf, true, 64, clientTf);
1136         REPORTER_ASSERT(reporter, clientBlob);
1137 
1138         RasterBlob(clientBlob, 100, 100, paint, direct);
1139         REPORTER_ASSERT(reporter, !discardableManager->hasCacheMiss());
1140         discardableManager->resetCacheMissCounts();
1141     }
1142     {
1143         auto clientBlob = Rgct::MakeNormalBlob(&paint, serverTf, false, 440, clientTf);
1144         REPORTER_ASSERT(reporter, clientBlob);
1145 
1146         RasterBlob(clientBlob, 100, 100, paint, direct);
1147         REPORTER_ASSERT(reporter, !discardableManager->hasCacheMiss());
1148         discardableManager->resetCacheMissCounts();
1149     }
1150     // Must unlock everything on termination, otherwise valgrind complains about memory leaks.
1151     discardableManager->unlockAndDeleteAll();
1152 }
1153 
DEF_GANESH_TEST_FOR_RENDERING_CONTEXTS(SkRemoteGlyphCache_TypefaceWithPaths_PathThenMask,reporter,ctxInfo,CtsEnforcement::kNever)1154 DEF_GANESH_TEST_FOR_RENDERING_CONTEXTS(SkRemoteGlyphCache_TypefaceWithPaths_PathThenMask,
1155                                        reporter,
1156                                        ctxInfo,
1157                                        CtsEnforcement::kNever) {
1158     auto direct = ctxInfo.directContext();
1159     sk_sp<DiscardableManager> discardableManager = sk_make_sp<DiscardableManager>();
1160     SkStrikeServer server(discardableManager.get());
1161     SkStrikeClient client(discardableManager, true);
1162 
1163     auto serverTf = ToolUtils::create_portable_typeface();
1164     auto serverTfData = server.serializeTypeface(serverTf.get());
1165     auto clientTf = client.deserializeTypeface(serverTfData->data(), serverTfData->size());
1166 
1167     auto props = FindSurfaceProps(direct);
1168     std::unique_ptr<SkCanvas> cache_diff_canvas = server.makeAnalysisCanvas(
1169             500, 500, props, nullptr, direct->supportsDistanceFieldText(),
1170             !direct->priv().caps()->disablePerspectiveSDFText());
1171     SkPaint paint;
1172     using Rgct = SkRemoteGlyphCacheTest;
1173 
1174     // Draw from path out of the strike which provides paths.
1175     {
1176         auto serverBlob = Rgct::MakeNormalBlob(&paint, serverTf, false, 440);
1177         cache_diff_canvas->drawTextBlob(serverBlob.get(), 100, 100, paint);
1178     }
1179     // Draw from mask out of the strike which provides paths.
1180     {
1181         auto serverBlob = Rgct::MakeNormalBlob(&paint, serverTf, true, 64);
1182         cache_diff_canvas->drawTextBlob(serverBlob.get(), 100, 100, paint);
1183     }
1184     std::vector<uint8_t> serverStrikeData;
1185     server.writeStrikeData(&serverStrikeData);
1186     if (!serverStrikeData.empty()) {
1187         REPORTER_ASSERT(reporter,
1188                         client.readStrikeData(serverStrikeData.data(),
1189                                               serverStrikeData.size()));
1190     }
1191     {
1192         auto clientBlob = Rgct::MakeNormalBlob(&paint, serverTf, true, 64, clientTf);
1193         REPORTER_ASSERT(reporter, clientBlob);
1194 
1195         RasterBlob(clientBlob, 100, 100, paint, direct);
1196         REPORTER_ASSERT(reporter, !discardableManager->hasCacheMiss());
1197         discardableManager->resetCacheMissCounts();
1198     }
1199     {
1200         auto clientBlob = Rgct::MakeNormalBlob(&paint, serverTf, false, 440, clientTf);
1201         REPORTER_ASSERT(reporter, clientBlob);
1202 
1203         RasterBlob(clientBlob, 100, 100, paint, direct);
1204         REPORTER_ASSERT(reporter, !discardableManager->hasCacheMiss());
1205         discardableManager->resetCacheMissCounts();
1206     }
1207     // Must unlock everything on termination, otherwise valgrind complains about memory leaks.
1208     discardableManager->unlockAndDeleteAll();
1209 }
1210 #endif
1211