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