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