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