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 30class DiscardableManager : public SkStrikeServer::DiscardableHandleManager, 31 public SkStrikeClient::DiscardableHandleManager { 32public: 33 DiscardableManager() { sk_bzero(&fCacheMissCount, sizeof(fCacheMissCount)); } 34 ~DiscardableManager() override = default; 35 36 // Server implementation. 37 SkDiscardableHandleId createHandle() override { 38 SkAutoMutexExclusive l(fMutex); 39 40 // Handles starts as locked. 41 fLockedHandles.add(++fNextHandleId); 42 return fNextHandleId; 43 } 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. 53 bool deleteHandle(SkDiscardableHandleId id) override { 54 SkAutoMutexExclusive l(fMutex); 55 56 return id <= fLastDeletedHandleId; 57 } 58 59 void notifyCacheMiss(SkStrikeClient::CacheMissType type, int fontSize) override { 60 SkAutoMutexExclusive l(fMutex); 61 62 fCacheMissCount[type]++; 63 } 64 bool isHandleDeleted(SkDiscardableHandleId id) override { 65 SkAutoMutexExclusive l(fMutex); 66 67 return id <= fLastDeletedHandleId; 68 } 69 70 void unlockAll() { 71 SkAutoMutexExclusive l(fMutex); 72 73 fLockedHandles.reset(); 74 } 75 void unlockAndDeleteAll() { 76 SkAutoMutexExclusive l(fMutex); 77 78 fLockedHandles.reset(); 79 fLastDeletedHandleId = fNextHandleId; 80 } 81 const SkTHashSet<SkDiscardableHandleId>& lockedHandles() const { 82 SkAutoMutexExclusive l(fMutex); 83 84 return fLockedHandles; 85 } 86 SkDiscardableHandleId handleCount() { 87 SkAutoMutexExclusive l(fMutex); 88 89 return fNextHandleId; 90 } 91 int cacheMissCount(uint32_t type) { 92 SkAutoMutexExclusive l(fMutex); 93 94 return fCacheMissCount[type]; 95 } 96 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 } 104 void resetCacheMissCounts() { 105 SkAutoMutexExclusive l(fMutex); 106 sk_bzero(&fCacheMissCount, sizeof(fCacheMissCount)); 107 } 108 109private: 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 121sk_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 142static 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 163sk_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 169SkSurfaceProps FindSurfaceProps(GrRecordingContext* rContext) { 170 auto surface = MakeSurface(1, 1, rContext); 171 return surface->props(); 172} 173 174SkBitmap 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 186DEF_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 203DEF_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 239DEF_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 269DEF_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 306DEF_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 344DEF_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 386DEF_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 416DEF_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 462DEF_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 502sk_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 537DEF_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. 582DEF_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 654DEF_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 691DEF_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 737DEF_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 767sk_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 792DEF_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