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