| /* |
| * Copyright 2018 Google Inc. |
| * |
| * Use of this source code is governed by a BSD-style license that can be |
| * found in the LICENSE file. |
| */ |
| |
| #include "include/core/SkAlphaType.h" |
| #include "include/core/SkBitmap.h" |
| #include "include/core/SkCanvas.h" |
| #include "include/core/SkColor.h" |
| #include "include/core/SkColorSpace.h" |
| #include "include/core/SkColorType.h" |
| #include "include/core/SkData.h" |
| #include "include/core/SkFont.h" |
| #include "include/core/SkFontStyle.h" |
| #include "include/core/SkFontTypes.h" |
| #include "include/core/SkGraphics.h" |
| #include "include/core/SkImageInfo.h" |
| #include "include/core/SkMatrix.h" |
| #include "include/core/SkPaint.h" |
| #include "include/core/SkRect.h" |
| #include "include/core/SkRefCnt.h" |
| #include "include/core/SkScalar.h" |
| #include "include/core/SkSerialProcs.h" |
| #include "include/core/SkSurface.h" |
| #include "include/core/SkSurfaceProps.h" |
| #include "include/core/SkTextBlob.h" |
| #include "include/core/SkTypeface.h" |
| #include "include/core/SkTypes.h" |
| #include "include/gpu/GpuTypes.h" |
| #include "include/gpu/GrContextOptions.h" |
| #include "include/gpu/GrDirectContext.h" |
| #include "include/gpu/GrRecordingContext.h" |
| #include "include/private/base/SkMalloc.h" |
| #include "include/private/base/SkMutex.h" |
| #include "include/private/chromium/SkChromeRemoteGlyphCache.h" |
| #include "include/private/chromium/Slug.h" |
| #include "src/core/SkFontPriv.h" |
| #include "src/core/SkGlyph.h" |
| #include "src/core/SkReadBuffer.h" |
| #include "src/core/SkStrikeSpec.h" |
| #include "src/core/SkTHash.h" |
| #include "src/core/SkTypeface_remote.h" |
| #include "src/core/SkWriteBuffer.h" |
| #include "src/gpu/ganesh/GrCaps.h" |
| #include "src/gpu/ganesh/GrDirectContextPriv.h" |
| #include "src/gpu/ganesh/GrRecordingContextPriv.h" |
| #include "src/gpu/ganesh/GrShaderCaps.h" |
| #include "src/text/gpu/SDFTControl.h" |
| #include "tests/CtsEnforcement.h" |
| #include "tests/Test.h" |
| #include "tools/Resources.h" |
| #include "tools/ToolUtils.h" |
| #include "tools/fonts/TestEmptyTypeface.h" |
| |
| #include <cmath> |
| #include <cstdint> |
| #include <cstring> |
| #include <initializer_list> |
| #include <memory> |
| #include <optional> |
| #include <vector> |
| |
| using Slug = sktext::gpu::Slug; |
| |
| class DiscardableManager : public SkStrikeServer::DiscardableHandleManager, |
| public SkStrikeClient::DiscardableHandleManager { |
| public: |
| DiscardableManager() { sk_bzero(&fCacheMissCount, sizeof(fCacheMissCount)); } |
| ~DiscardableManager() override = default; |
| |
| // Server implementation. |
| SkDiscardableHandleId createHandle() override { |
| SkAutoMutexExclusive l(fMutex); |
| |
| // Handles starts as locked. |
| fLockedHandles.add(++fNextHandleId); |
| return fNextHandleId; |
| } |
| bool lockHandle(SkDiscardableHandleId id) override { |
| SkAutoMutexExclusive l(fMutex); |
| |
| if (id <= fLastDeletedHandleId) return false; |
| fLockedHandles.add(id); |
| return true; |
| } |
| |
| // Client implementation. |
| bool deleteHandle(SkDiscardableHandleId id) override { |
| SkAutoMutexExclusive l(fMutex); |
| |
| return id <= fLastDeletedHandleId; |
| } |
| |
| void notifyCacheMiss(SkStrikeClient::CacheMissType type, int fontSize) override { |
| SkAutoMutexExclusive l(fMutex); |
| |
| fCacheMissCount[type]++; |
| } |
| bool isHandleDeleted(SkDiscardableHandleId id) override { |
| SkAutoMutexExclusive l(fMutex); |
| |
| return id <= fLastDeletedHandleId; |
| } |
| |
| void unlockAll() { |
| SkAutoMutexExclusive l(fMutex); |
| |
| fLockedHandles.reset(); |
| } |
| void unlockAndDeleteAll() { |
| SkAutoMutexExclusive l(fMutex); |
| |
| fLockedHandles.reset(); |
| fLastDeletedHandleId = fNextHandleId; |
| } |
| const SkTHashSet<SkDiscardableHandleId>& lockedHandles() const { |
| SkAutoMutexExclusive l(fMutex); |
| |
| return fLockedHandles; |
| } |
| SkDiscardableHandleId handleCount() { |
| SkAutoMutexExclusive l(fMutex); |
| |
| return fNextHandleId; |
| } |
| int cacheMissCount(uint32_t type) { |
| SkAutoMutexExclusive l(fMutex); |
| |
| return fCacheMissCount[type]; |
| } |
| bool hasCacheMiss() const { |
| SkAutoMutexExclusive l(fMutex); |
| |
| for (uint32_t i = 0; i <= SkStrikeClient::CacheMissType::kLast; ++i) { |
| if (fCacheMissCount[i] > 0) { return true; } |
| } |
| return false; |
| } |
| void resetCacheMissCounts() { |
| SkAutoMutexExclusive l(fMutex); |
| sk_bzero(&fCacheMissCount, sizeof(fCacheMissCount)); |
| } |
| |
| private: |
| // The tests below run in parallel on multiple threads and use the same |
| // process global SkStrikeCache. So the implementation needs to be |
| // thread-safe. |
| mutable SkMutex fMutex; |
| |
| SkDiscardableHandleId fNextHandleId = 0u; |
| SkDiscardableHandleId fLastDeletedHandleId = 0u; |
| SkTHashSet<SkDiscardableHandleId> fLockedHandles; |
| int fCacheMissCount[SkStrikeClient::CacheMissType::kLast + 1u]; |
| }; |
| |
| sk_sp<SkTextBlob> buildTextBlob(sk_sp<SkTypeface> tf, int glyphCount, int textSize = 1) { |
| SkFont font; |
| font.setTypeface(tf); |
| font.setHinting(SkFontHinting::kNormal); |
| font.setSize(textSize); |
| font.setEdging(SkFont::Edging::kAntiAlias); |
| font.setSubpixel(true); |
| |
| SkTextBlobBuilder builder; |
| SkRect bounds = SkRect::MakeWH(10, 10); |
| const auto& runBuffer = builder.allocRunPosH(font, glyphCount, 0, &bounds); |
| SkASSERT(runBuffer.utf8text == nullptr); |
| SkASSERT(runBuffer.clusters == nullptr); |
| |
| for (int i = 0; i < glyphCount; i++) { |
| runBuffer.glyphs[i] = static_cast<SkGlyphID>(i); |
| runBuffer.pos[i] = SkIntToScalar(i); |
| } |
| return builder.make(); |
| } |
| |
| static void compare_blobs(const SkBitmap& expected, const SkBitmap& actual, |
| skiatest::Reporter* reporter, int tolerance = 0) { |
| SkASSERT(expected.width() == actual.width()); |
| SkASSERT(expected.height() == actual.height()); |
| for (int i = 0; i < expected.width(); ++i) { |
| for (int j = 0; j < expected.height(); ++j) { |
| SkColor expectedColor = expected.getColor(i, j); |
| SkColor actualColor = actual.getColor(i, j); |
| if (0 == tolerance) { |
| REPORTER_ASSERT(reporter, expectedColor == actualColor); |
| } else { |
| for (int k = 0; k < 4; ++k) { |
| int expectedChannel = (expectedColor >> (k*8)) & 0xff; |
| int actualChannel = (actualColor >> (k*8)) & 0xff; |
| REPORTER_ASSERT(reporter, abs(expectedChannel - actualChannel) <= tolerance); |
| } |
| } |
| } |
| } |
| } |
| |
| sk_sp<SkSurface> MakeSurface(int width, int height, GrRecordingContext* rContext) { |
| const SkImageInfo info = |
| SkImageInfo::Make(width, height, kN32_SkColorType, kPremul_SkAlphaType); |
| return SkSurface::MakeRenderTarget(rContext, skgpu::Budgeted::kNo, info); |
| } |
| |
| SkSurfaceProps FindSurfaceProps(GrRecordingContext* rContext) { |
| auto surface = MakeSurface(1, 1, rContext); |
| return surface->props(); |
| } |
| |
| SkBitmap RasterBlob(sk_sp<SkTextBlob> blob, int width, int height, const SkPaint& paint, |
| GrRecordingContext* rContext, const SkMatrix* matrix = nullptr, |
| SkScalar x = 0) { |
| auto surface = MakeSurface(width, height, rContext); |
| if (matrix) { |
| surface->getCanvas()->concat(*matrix); |
| } |
| surface->getCanvas()->drawTextBlob(blob.get(), x, height/2, paint); |
| SkBitmap bitmap; |
| bitmap.allocN32Pixels(width, height); |
| surface->readPixels(bitmap, 0, 0); |
| return bitmap; |
| } |
| |
| SkBitmap RasterBlobThroughSlug(sk_sp<SkTextBlob> blob, int width, int height, const SkPaint& paint, |
| GrRecordingContext* rContext, const SkMatrix* matrix = nullptr, |
| SkScalar x = 0) { |
| auto surface = MakeSurface(width, height, rContext); |
| if (matrix) { |
| surface->getCanvas()->concat(*matrix); |
| } |
| auto canvas = surface->getCanvas(); |
| auto slug = Slug::ConvertBlob(canvas, *blob, {x, height/2.0f}, paint); |
| slug->draw(canvas); |
| SkBitmap bitmap; |
| bitmap.allocN32Pixels(width, height); |
| surface->readPixels(bitmap, 0, 0); |
| return bitmap; |
| } |
| |
| SkBitmap RasterSlug(sk_sp<Slug> slug, int width, int height, const SkPaint& paint, |
| GrRecordingContext* rContext, const SkMatrix* matrix = nullptr, |
| SkScalar x = 0) { |
| auto surface = MakeSurface(width, height, rContext); |
| auto canvas = surface->getCanvas(); |
| if (matrix) { |
| canvas->concat(*matrix); |
| } |
| slug->draw(canvas); |
| SkBitmap bitmap; |
| bitmap.allocN32Pixels(width, height); |
| surface->readPixels(bitmap, 0, 0); |
| return bitmap; |
| } |
| |
| DEF_GANESH_TEST_FOR_RENDERING_CONTEXTS(SkRemoteGlyphCache_StrikeSerialization, |
| reporter, |
| ctxInfo, |
| CtsEnforcement::kNever) { |
| auto dContext = ctxInfo.directContext(); |
| sk_sp<DiscardableManager> discardableManager = sk_make_sp<DiscardableManager>(); |
| SkStrikeServer server(discardableManager.get()); |
| SkStrikeClient client(discardableManager, false); |
| const SkPaint paint; |
| |
| // Server. |
| auto serverTypeface = SkTypeface::MakeFromName("monospace", SkFontStyle()); |
| const SkTypefaceID serverTypefaceID = serverTypeface->uniqueID(); |
| |
| int glyphCount = 10; |
| auto serverBlob = buildTextBlob(serverTypeface, glyphCount); |
| auto props = FindSurfaceProps(dContext); |
| std::unique_ptr<SkCanvas> analysisCanvas = server.makeAnalysisCanvas( |
| 10, 10, props, nullptr, dContext->supportsDistanceFieldText(), |
| !dContext->priv().caps()->disablePerspectiveSDFText()); |
| analysisCanvas->drawTextBlob(serverBlob.get(), 0, 0, paint); |
| |
| std::vector<uint8_t> serverStrikeData; |
| server.writeStrikeData(&serverStrikeData); |
| |
| // Client. |
| REPORTER_ASSERT(reporter, |
| client.readStrikeData(serverStrikeData.data(), serverStrikeData.size())); |
| auto clientTypeface = client.retrieveTypefaceUsingServerIDForTest(serverTypefaceID); |
| |
| // Ensure typeface serialization/deserialization worked. |
| REPORTER_ASSERT(reporter, |
| static_cast<SkTypefaceProxy*>(clientTypeface.get())->remoteTypefaceID() == |
| serverTypefaceID); |
| |
| auto clientBlob = buildTextBlob(clientTypeface, glyphCount); |
| |
| SkBitmap expected = RasterBlob(serverBlob, 10, 10, paint, dContext); |
| SkBitmap actual = RasterBlob(clientBlob, 10, 10, paint, dContext); |
| compare_blobs(expected, actual, reporter); |
| REPORTER_ASSERT(reporter, !discardableManager->hasCacheMiss()); |
| |
| // Must unlock everything on termination, otherwise valgrind complains about memory leaks. |
| discardableManager->unlockAndDeleteAll(); |
| } |
| |
| static void use_padding_options(GrContextOptions* options) { |
| options->fSupportBilerpFromGlyphAtlas = true; |
| } |
| |
| DEF_GANESH_TEST_FOR_CONTEXTS(SkRemoteGlyphCache_StrikeSerializationSlug, |
| sk_gpu_test::GrContextFactory::IsRenderingContext, |
| reporter, |
| ctxInfo, |
| use_padding_options, |
| CtsEnforcement::kNever) { |
| auto dContext = ctxInfo.directContext(); |
| sk_sp<DiscardableManager> discardableManager = sk_make_sp<DiscardableManager>(); |
| SkStrikeServer server(discardableManager.get()); |
| SkStrikeClient client(discardableManager, false); |
| const SkPaint paint; |
| |
| // Server. |
| auto serverTypeface = SkTypeface::MakeFromName("monospace", SkFontStyle()); |
| const SkTypefaceID serverTypefaceID = serverTypeface->uniqueID(); |
| |
| int glyphCount = 10; |
| auto serverBlob = buildTextBlob(serverTypeface, glyphCount); |
| auto props = FindSurfaceProps(dContext); |
| std::unique_ptr<SkCanvas> analysisCanvas = server.makeAnalysisCanvas( |
| 10, 10, props, nullptr, dContext->supportsDistanceFieldText(), |
| !dContext->priv().caps()->disablePerspectiveSDFText()); |
| |
| // Generate strike updates. |
| (void)Slug::ConvertBlob(analysisCanvas.get(), *serverBlob, {0, 0}, paint); |
| |
| std::vector<uint8_t> serverStrikeData; |
| server.writeStrikeData(&serverStrikeData); |
| |
| // Client. |
| REPORTER_ASSERT(reporter, |
| client.readStrikeData(serverStrikeData.data(), serverStrikeData.size())); |
| auto clientTypeface = client.retrieveTypefaceUsingServerIDForTest(serverTypefaceID); |
| auto clientBlob = buildTextBlob(clientTypeface, glyphCount); |
| |
| SkBitmap expected = RasterBlobThroughSlug(serverBlob, 10, 10, paint, dContext); |
| SkBitmap actual = RasterBlobThroughSlug(clientBlob, 10, 10, paint, dContext); |
| compare_blobs(expected, actual, reporter); |
| REPORTER_ASSERT(reporter, !discardableManager->hasCacheMiss()); |
| |
| // Must unlock everything on termination, otherwise valgrind complains about memory leaks. |
| discardableManager->unlockAndDeleteAll(); |
| } |
| |
| DEF_GANESH_TEST_FOR_CONTEXTS(SkRemoteGlyphCache_StrikeSerializationSlugForcePath, |
| sk_gpu_test::GrContextFactory::IsRenderingContext, |
| reporter, |
| ctxInfo, |
| use_padding_options, |
| CtsEnforcement::kNever) { |
| auto dContext = ctxInfo.directContext(); |
| sk_sp<DiscardableManager> discardableManager = sk_make_sp<DiscardableManager>(); |
| SkStrikeServer server(discardableManager.get()); |
| SkStrikeClient client(discardableManager, false); |
| const SkPaint paint; |
| |
| // Server. |
| auto serverTypeface = SkTypeface::MakeFromName("monospace", SkFontStyle()); |
| const SkTypefaceID serverTypefaceID = serverTypeface->uniqueID(); |
| |
| int glyphCount = 10; |
| auto serverBlob = buildTextBlob(serverTypeface, glyphCount, 360); |
| auto props = FindSurfaceProps(dContext); |
| std::unique_ptr<SkCanvas> analysisCanvas = server.makeAnalysisCanvas( |
| 10, 10, props, nullptr, dContext->supportsDistanceFieldText(), |
| !dContext->priv().caps()->disablePerspectiveSDFText()); |
| |
| // Generate strike updates. |
| (void)Slug::ConvertBlob(analysisCanvas.get(), *serverBlob, {0, 0}, paint); |
| |
| std::vector<uint8_t> serverStrikeData; |
| server.writeStrikeData(&serverStrikeData); |
| |
| // Client. |
| REPORTER_ASSERT(reporter, |
| client.readStrikeData(serverStrikeData.data(), serverStrikeData.size())); |
| auto clientTypeface = client.retrieveTypefaceUsingServerIDForTest(serverTypefaceID); |
| auto clientBlob = buildTextBlob(clientTypeface, glyphCount, 360); |
| |
| SkBitmap expected = RasterBlobThroughSlug(serverBlob, 10, 10, paint, dContext); |
| SkBitmap actual = RasterBlobThroughSlug(clientBlob, 10, 10, paint, dContext); |
| compare_blobs(expected, actual, reporter); |
| REPORTER_ASSERT(reporter, !discardableManager->hasCacheMiss()); |
| |
| // Must unlock everything on termination, otherwise valgrind complains about memory leaks. |
| discardableManager->unlockAndDeleteAll(); |
| } |
| |
| DEF_GANESH_TEST_FOR_CONTEXTS(SkRemoteGlyphCache_SlugSerialization, |
| sk_gpu_test::GrContextFactory::IsRenderingContext, |
| reporter, |
| ctxInfo, |
| use_padding_options, |
| CtsEnforcement::kNever) { |
| auto dContext = ctxInfo.directContext(); |
| sk_sp<DiscardableManager> discardableManager = sk_make_sp<DiscardableManager>(); |
| SkStrikeServer server(discardableManager.get()); |
| SkStrikeClient client(discardableManager, false); |
| const SkPaint paint; |
| |
| // Server. |
| auto serverTypeface = SkTypeface::MakeFromName("monospace", SkFontStyle()); |
| |
| int glyphCount = 10; |
| auto serverBlob = buildTextBlob(serverTypeface, glyphCount); |
| auto props = FindSurfaceProps(dContext); |
| std::unique_ptr<SkCanvas> analysisCanvas = server.makeAnalysisCanvas( |
| 10, 10, props, nullptr, dContext->supportsDistanceFieldText(), |
| !dContext->priv().caps()->disablePerspectiveSDFText()); |
| |
| // Generate strike updates. |
| auto srcSlug = Slug::ConvertBlob(analysisCanvas.get(), *serverBlob, {0.3f, 0}, paint); |
| auto dstSlugData = srcSlug->serialize(); |
| |
| std::vector<uint8_t> serverStrikeData; |
| server.writeStrikeData(&serverStrikeData); |
| |
| // Client. |
| REPORTER_ASSERT(reporter, |
| client.readStrikeData(serverStrikeData.data(), serverStrikeData.size())); |
| |
| SkBitmap expected = RasterSlug(srcSlug, 10, 10, paint, dContext); |
| auto dstSlug = client.deserializeSlugForTest(dstSlugData->data(), dstSlugData->size()); |
| REPORTER_ASSERT(reporter, dstSlug != nullptr); |
| SkBitmap actual = RasterSlug(dstSlug, 10, 10, paint, dContext); |
| compare_blobs(expected, actual, reporter); |
| REPORTER_ASSERT(reporter, !discardableManager->hasCacheMiss()); |
| |
| // Must unlock everything on termination, otherwise valgrind complains about memory leaks. |
| discardableManager->unlockAndDeleteAll(); |
| } |
| |
| DEF_GANESH_TEST_FOR_RENDERING_CONTEXTS(SkRemoteGlyphCache_ReleaseTypeFace, |
| reporter, |
| ctxInfo, |
| CtsEnforcement::kNever) { |
| sk_sp<DiscardableManager> discardableManager = sk_make_sp<DiscardableManager>(); |
| SkStrikeServer server(discardableManager.get()); |
| SkStrikeClient client(discardableManager, false); |
| |
| // Server. |
| auto serverTypeface = TestEmptyTypeface::Make(); |
| REPORTER_ASSERT(reporter, serverTypeface->unique()); |
| |
| { |
| const SkPaint paint; |
| int glyphCount = 10; |
| auto serverBlob = buildTextBlob(serverTypeface, glyphCount); |
| const SkSurfaceProps props; |
| std::unique_ptr<SkCanvas> cache_diff_canvas = server.makeAnalysisCanvas( |
| 10, 10, props, nullptr, ctxInfo.directContext()->supportsDistanceFieldText(), |
| !ctxInfo.directContext()->priv().caps()->disablePerspectiveSDFText()); |
| cache_diff_canvas->drawTextBlob(serverBlob.get(), 0, 0, paint); |
| REPORTER_ASSERT(reporter, !serverTypeface->unique()); |
| |
| std::vector<uint8_t> serverStrikeData; |
| server.writeStrikeData(&serverStrikeData); |
| } |
| REPORTER_ASSERT(reporter, serverTypeface->unique()); |
| |
| // Must unlock everything on termination, otherwise valgrind complains about memory leaks. |
| discardableManager->unlockAndDeleteAll(); |
| } |
| |
| DEF_TEST(SkRemoteGlyphCache_StrikeLockingServer, reporter) { |
| sk_sp<DiscardableManager> discardableManager = sk_make_sp<DiscardableManager>(); |
| SkStrikeServer server(discardableManager.get()); |
| SkStrikeClient client(discardableManager, false); |
| |
| auto serverTypeface = SkTypeface::MakeFromName("monospace", SkFontStyle()); |
| int glyphCount = 10; |
| auto serverBlob = buildTextBlob(serverTypeface, glyphCount); |
| |
| const SkSurfaceProps props; |
| std::unique_ptr<SkCanvas> cache_diff_canvas = |
| server.makeAnalysisCanvas(10, 10, props, nullptr, true, true); |
| SkPaint paint; |
| cache_diff_canvas->drawTextBlob(serverBlob.get(), 0, 0, paint); |
| |
| // The strike from the blob should be locked after it has been drawn on the canvas. |
| REPORTER_ASSERT(reporter, discardableManager->handleCount() == 1u); |
| REPORTER_ASSERT(reporter, discardableManager->lockedHandles().count() == 1u); |
| |
| // Write the strike data and unlock everything. Re-analyzing the blob should lock the handle |
| // again. |
| std::vector<uint8_t> fontData; |
| server.writeStrikeData(&fontData); |
| discardableManager->unlockAll(); |
| REPORTER_ASSERT(reporter, discardableManager->lockedHandles().count() == 0u); |
| |
| cache_diff_canvas->drawTextBlob(serverBlob.get(), 0, 0, paint); |
| REPORTER_ASSERT(reporter, discardableManager->handleCount() == 1u); |
| REPORTER_ASSERT(reporter, discardableManager->lockedHandles().count() == 1u); |
| |
| // Must unlock everything on termination, otherwise valgrind complains about memory leaks. |
| discardableManager->unlockAndDeleteAll(); |
| } |
| |
| DEF_TEST(SkRemoteGlyphCache_StrikeDeletionServer, reporter) { |
| sk_sp<DiscardableManager> discardableManager = sk_make_sp<DiscardableManager>(); |
| SkStrikeServer server(discardableManager.get()); |
| SkStrikeClient client(discardableManager, false); |
| |
| auto serverTypeface = SkTypeface::MakeFromName("monospace", SkFontStyle()); |
| int glyphCount = 10; |
| auto serverBlob = buildTextBlob(serverTypeface, glyphCount); |
| |
| const SkSurfaceProps props; |
| std::unique_ptr<SkCanvas> cache_diff_canvas = |
| server.makeAnalysisCanvas(10, 10, props, nullptr, true, true); |
| SkPaint paint; |
| cache_diff_canvas->drawTextBlob(serverBlob.get(), 0, 0, paint); |
| REPORTER_ASSERT(reporter, discardableManager->handleCount() == 1u); |
| |
| // Write the strike data and delete all the handles. Re-analyzing the blob should create new |
| // handles. |
| std::vector<uint8_t> fontData; |
| server.writeStrikeData(&fontData); |
| |
| // Another analysis pass, to ensure that deleting handles after a complete cache hit still |
| // works. This is a regression test for crbug.com/999682. |
| cache_diff_canvas->drawTextBlob(serverBlob.get(), 0, 0, paint); |
| server.writeStrikeData(&fontData); |
| REPORTER_ASSERT(reporter, discardableManager->handleCount() == 1u); |
| |
| discardableManager->unlockAndDeleteAll(); |
| cache_diff_canvas->drawTextBlob(serverBlob.get(), 0, 0, paint); |
| REPORTER_ASSERT(reporter, discardableManager->handleCount() == 2u); |
| |
| // Must unlock everything on termination, otherwise valgrind complains about memory leaks. |
| discardableManager->unlockAndDeleteAll(); |
| } |
| |
| DEF_TEST(SkRemoteGlyphCache_StrikePinningClient, reporter) { |
| sk_sp<DiscardableManager> discardableManager = sk_make_sp<DiscardableManager>(); |
| SkStrikeServer server(discardableManager.get()); |
| SkStrikeClient client(discardableManager, false); |
| |
| // Server. |
| auto serverTypeface = SkTypeface::MakeFromName("monospace", SkFontStyle()); |
| const SkTypefaceID serverTypefaceID = serverTypeface->uniqueID(); |
| |
| int glyphCount = 10; |
| auto serverBlob = buildTextBlob(serverTypeface, glyphCount); |
| |
| const SkSurfaceProps props; |
| std::unique_ptr<SkCanvas> cache_diff_canvas = |
| server.makeAnalysisCanvas(10, 10, props, nullptr, true, true); |
| SkPaint paint; |
| cache_diff_canvas->drawTextBlob(serverBlob.get(), 0, 0, paint); |
| |
| std::vector<uint8_t> serverStrikeData; |
| server.writeStrikeData(&serverStrikeData); |
| |
| // Client. |
| REPORTER_ASSERT(reporter, |
| client.readStrikeData(serverStrikeData.data(), serverStrikeData.size())); |
| auto* clientTypeface = client.retrieveTypefaceUsingServerIDForTest(serverTypefaceID).get(); |
| |
| // The cache remains alive until it is pinned in the discardable manager. |
| SkGraphics::PurgeFontCache(); |
| REPORTER_ASSERT(reporter, !clientTypeface->unique()); |
| |
| // Once the strike is unpinned and purged, SkStrikeClient should be the only owner of the |
| // clientTf. |
| discardableManager->unlockAndDeleteAll(); |
| SkGraphics::PurgeFontCache(); |
| REPORTER_ASSERT(reporter, clientTypeface->unique()); |
| |
| // Must unlock everything on termination, otherwise valgrind complains about memory leaks. |
| discardableManager->unlockAndDeleteAll(); |
| } |
| |
| DEF_TEST(SkRemoteGlyphCache_ClientMemoryAccounting, reporter) { |
| sk_sp<DiscardableManager> discardableManager = sk_make_sp<DiscardableManager>(); |
| SkStrikeServer server(discardableManager.get()); |
| SkStrikeClient client(discardableManager, false); |
| |
| // Server. |
| auto serverTypeface = SkTypeface::MakeFromName("monospace", SkFontStyle()); |
| |
| int glyphCount = 10; |
| auto serverBlob = buildTextBlob(serverTypeface, glyphCount); |
| |
| const SkSurfaceProps props; |
| std::unique_ptr<SkCanvas> cache_diff_canvas = |
| server.makeAnalysisCanvas(10, 10, props, nullptr, true, true); |
| SkPaint paint; |
| cache_diff_canvas->drawTextBlob(serverBlob.get(), 0, 0, paint); |
| |
| std::vector<uint8_t> serverStrikeData; |
| server.writeStrikeData(&serverStrikeData); |
| |
| // Client. |
| REPORTER_ASSERT(reporter, |
| client.readStrikeData(serverStrikeData.data(), serverStrikeData.size())); |
| |
| // Must unlock everything on termination, otherwise valgrind complains about memory leaks. |
| discardableManager->unlockAndDeleteAll(); |
| } |
| |
| DEF_TEST(SkRemoteGlyphCache_PurgesServerEntries, reporter) { |
| sk_sp<DiscardableManager> discardableManager = sk_make_sp<DiscardableManager>(); |
| SkStrikeServer server(discardableManager.get()); |
| server.setMaxEntriesInDescriptorMapForTesting(1u); |
| SkStrikeClient client(discardableManager, false); |
| |
| { |
| auto serverTypeface = SkTypeface::MakeFromName("monospace", SkFontStyle()); |
| int glyphCount = 10; |
| auto serverBlob = buildTextBlob(serverTypeface, glyphCount); |
| |
| const SkSurfaceProps props; |
| std::unique_ptr<SkCanvas> cache_diff_canvas = |
| server.makeAnalysisCanvas(10, 10, props, nullptr, true, true); |
| SkPaint paint; |
| REPORTER_ASSERT(reporter, server.remoteStrikeMapSizeForTesting() == 0u); |
| cache_diff_canvas->drawTextBlob(serverBlob.get(), 0, 0, paint); |
| REPORTER_ASSERT(reporter, server.remoteStrikeMapSizeForTesting() == 1u); |
| } |
| |
| // Serialize to release the lock from the strike server and delete all current |
| // handles. |
| std::vector<uint8_t> fontData; |
| server.writeStrikeData(&fontData); |
| discardableManager->unlockAndDeleteAll(); |
| |
| // Use a different typeface. Creating a new strike should evict the previous |
| // one. |
| { |
| auto serverTypeface = SkTypeface::MakeFromName("Georgia", SkFontStyle()); |
| int glyphCount = 10; |
| auto serverBlob = buildTextBlob(serverTypeface, glyphCount); |
| |
| const SkSurfaceProps props; |
| std::unique_ptr<SkCanvas> cache_diff_canvas = |
| server.makeAnalysisCanvas(10, 10, props, nullptr, true, true); |
| SkPaint paint; |
| REPORTER_ASSERT(reporter, server.remoteStrikeMapSizeForTesting() == 1u); |
| cache_diff_canvas->drawTextBlob(serverBlob.get(), 0, 0, paint); |
| REPORTER_ASSERT(reporter, server.remoteStrikeMapSizeForTesting() == 1u); |
| } |
| |
| // Must unlock everything on termination, otherwise valgrind complains about memory leaks. |
| discardableManager->unlockAndDeleteAll(); |
| } |
| |
| DEF_GANESH_TEST_FOR_RENDERING_CONTEXTS(SkRemoteGlyphCache_DrawTextAsPath, |
| reporter, |
| ctxInfo, |
| CtsEnforcement::kNever) { |
| auto direct = ctxInfo.directContext(); |
| sk_sp<DiscardableManager> discardableManager = sk_make_sp<DiscardableManager>(); |
| SkStrikeServer server(discardableManager.get()); |
| SkStrikeClient client(discardableManager, false); |
| SkPaint paint; |
| paint.setStyle(SkPaint::kStroke_Style); |
| paint.setStrokeWidth(0); |
| REPORTER_ASSERT(reporter, |
| SkStrikeSpec::ShouldDrawAsPath(paint, SkFont(), SkMatrix::I())); |
| |
| // Server. |
| auto serverTypeface = SkTypeface::MakeFromName("monospace", SkFontStyle()); |
| const SkTypefaceID serverTypefaceID = serverTypeface->uniqueID(); |
| |
| int glyphCount = 10; |
| auto serverBlob = buildTextBlob(serverTypeface, glyphCount); |
| auto props = FindSurfaceProps(direct); |
| std::unique_ptr<SkCanvas> cache_diff_canvas = server.makeAnalysisCanvas( |
| 10, 10, props, nullptr, direct->supportsDistanceFieldText(), |
| !direct->priv().caps()->disablePerspectiveSDFText()); |
| cache_diff_canvas->drawTextBlob(serverBlob.get(), 0, 0, paint); |
| |
| std::vector<uint8_t> serverStrikeData; |
| server.writeStrikeData(&serverStrikeData); |
| |
| // Client. |
| REPORTER_ASSERT(reporter, |
| client.readStrikeData(serverStrikeData.data(), serverStrikeData.size())); |
| auto clientTypeface = client.retrieveTypefaceUsingServerIDForTest(serverTypefaceID); |
| auto clientBlob = buildTextBlob(clientTypeface, glyphCount); |
| |
| SkBitmap expected = RasterBlob(serverBlob, 10, 10, paint, direct); |
| SkBitmap actual = RasterBlob(clientBlob, 10, 10, paint, direct); |
| compare_blobs(expected, actual, reporter, 1); |
| REPORTER_ASSERT(reporter, !discardableManager->hasCacheMiss()); |
| |
| // Must unlock everything on termination, otherwise valgrind complains about memory leaks. |
| discardableManager->unlockAndDeleteAll(); |
| } |
| |
| sk_sp<SkTextBlob> make_blob_causing_fallback( |
| sk_sp<SkTypeface> targetTf, const SkTypeface* glyphTf, skiatest::Reporter* reporter) { |
| SkFont font; |
| font.setSubpixel(true); |
| font.setSize(96); |
| font.setHinting(SkFontHinting::kNormal); |
| font.setTypeface(targetTf); |
| |
| REPORTER_ASSERT(reporter, |
| !SkStrikeSpec::ShouldDrawAsPath(SkPaint(), font, SkMatrix::I())); |
| |
| char s[] = "Skia"; |
| int runSize = strlen(s); |
| |
| SkTextBlobBuilder builder; |
| SkRect bounds = SkRect::MakeIWH(100, 100); |
| const auto& runBuffer = builder.allocRunPosH(font, runSize, 10, &bounds); |
| SkASSERT(runBuffer.utf8text == nullptr); |
| SkASSERT(runBuffer.clusters == nullptr); |
| |
| SkFont(sk_ref_sp(glyphTf)).textToGlyphs(s, strlen(s), SkTextEncoding::kUTF8, |
| runBuffer.glyphs, runSize); |
| |
| SkRect glyphBounds; |
| font.getWidths(runBuffer.glyphs, 1, nullptr, &glyphBounds); |
| |
| REPORTER_ASSERT(reporter, glyphBounds.width() > SkGlyphDigest::kSkSideTooBigForAtlas); |
| |
| for (int i = 0; i < runSize; i++) { |
| runBuffer.pos[i] = i * 10; |
| } |
| |
| return builder.make(); |
| } |
| |
| DEF_GANESH_TEST_FOR_RENDERING_CONTEXTS(SkRemoteGlyphCache_DrawTextAsMaskWithPathFallback, |
| reporter, |
| ctxInfo, |
| CtsEnforcement::kNever) { |
| auto direct = ctxInfo.directContext(); |
| sk_sp<DiscardableManager> discardableManager = sk_make_sp<DiscardableManager>(); |
| SkStrikeServer server(discardableManager.get()); |
| SkStrikeClient client(discardableManager, false); |
| |
| SkPaint paint; |
| |
| auto serverTypeface = MakeResourceAsTypeface("fonts/HangingS.ttf"); |
| // TODO: when the cq bots can handle this font remove the check. |
| if (serverTypeface == nullptr) { |
| return; |
| } |
| const SkTypefaceID serverTypefaceID = serverTypeface->uniqueID(); |
| |
| auto serverBlob = make_blob_causing_fallback(serverTypeface, serverTypeface.get(), reporter); |
| |
| auto props = FindSurfaceProps(direct); |
| std::unique_ptr<SkCanvas> cache_diff_canvas = server.makeAnalysisCanvas( |
| 10, 10, props, nullptr, direct->supportsDistanceFieldText(), |
| !direct->priv().caps()->disablePerspectiveSDFText()); |
| cache_diff_canvas->drawTextBlob(serverBlob.get(), 0, 0, paint); |
| |
| std::vector<uint8_t> serverStrikeData; |
| server.writeStrikeData(&serverStrikeData); |
| |
| // Client. |
| REPORTER_ASSERT(reporter, |
| client.readStrikeData(serverStrikeData.data(), serverStrikeData.size())); |
| auto clientTypeface = client.retrieveTypefaceUsingServerIDForTest(serverTypefaceID); |
| auto clientBlob = make_blob_causing_fallback(clientTypeface, serverTypeface.get(), reporter); |
| |
| SkBitmap expected = RasterBlob(serverBlob, 10, 10, paint, direct); |
| SkBitmap actual = RasterBlob(clientBlob, 10, 10, paint, direct); |
| compare_blobs(expected, actual, reporter); |
| REPORTER_ASSERT(reporter, !discardableManager->hasCacheMiss()); |
| |
| // Must unlock everything on termination, otherwise valgrind complains about memory leaks. |
| discardableManager->unlockAndDeleteAll(); |
| } |
| |
| DEF_GANESH_TEST_FOR_RENDERING_CONTEXTS(SkRemoteGlyphCache_DrawTextXY, |
| reporter, |
| ctxInfo, |
| CtsEnforcement::kNever) { |
| auto direct = ctxInfo.directContext(); |
| sk_sp<DiscardableManager> discardableManager = sk_make_sp<DiscardableManager>(); |
| SkStrikeServer server(discardableManager.get()); |
| SkStrikeClient client(discardableManager, false); |
| SkPaint paint; |
| paint.setAntiAlias(true); |
| |
| // Server. |
| auto serverTypeface = SkTypeface::MakeFromName("monospace", SkFontStyle()); |
| const SkTypefaceID serverTypefaceID = serverTypeface->uniqueID(); |
| |
| int glyphCount = 10; |
| auto serverBlob = buildTextBlob(serverTypeface, glyphCount); |
| auto props = FindSurfaceProps(direct); |
| std::unique_ptr<SkCanvas> cache_diff_canvas = server.makeAnalysisCanvas( |
| 10, 10, props, nullptr, direct->supportsDistanceFieldText(), |
| !direct->priv().caps()->disablePerspectiveSDFText()); |
| cache_diff_canvas->drawTextBlob(serverBlob.get(), 0.5, 0, paint); |
| |
| std::vector<uint8_t> serverStrikeData; |
| server.writeStrikeData(&serverStrikeData); |
| |
| // Client. |
| REPORTER_ASSERT(reporter, |
| client.readStrikeData(serverStrikeData.data(), serverStrikeData.size())); |
| auto clientTypeface = client.retrieveTypefaceUsingServerIDForTest(serverTypefaceID); |
| auto clientBlob = buildTextBlob(clientTypeface, glyphCount); |
| |
| SkBitmap expected = RasterBlob(serverBlob, 10, 10, paint, direct, nullptr, 0.5); |
| SkBitmap actual = RasterBlob(clientBlob, 10, 10, paint, direct, nullptr, 0.5); |
| compare_blobs(expected, actual, reporter); |
| REPORTER_ASSERT(reporter, !discardableManager->hasCacheMiss()); |
| |
| // Must unlock everything on termination, otherwise valgrind complains about memory leaks. |
| discardableManager->unlockAndDeleteAll(); |
| } |
| |
| #if !defined(SK_DISABLE_SDF_TEXT) |
| DEF_GANESH_TEST_FOR_RENDERING_CONTEXTS(SkRemoteGlyphCache_DrawTextAsDFT, |
| reporter, |
| ctxInfo, |
| CtsEnforcement::kNever) { |
| auto direct = ctxInfo.directContext(); |
| if (!direct->priv().caps()->shaderCaps()->supportsDistanceFieldText()) { |
| return; |
| } |
| sk_sp<DiscardableManager> discardableManager = sk_make_sp<DiscardableManager>(); |
| SkStrikeServer server(discardableManager.get()); |
| SkStrikeClient client(discardableManager, false); |
| SkPaint paint; |
| SkFont font; |
| |
| // A scale transform forces fallback to dft. |
| SkMatrix matrix = SkMatrix::Scale(16, 16); |
| sktext::gpu::SDFTControl control = |
| direct->priv().asRecordingContext()->priv().getSDFTControl(true); |
| SkScalar approximateDeviceTextSize = SkFontPriv::ApproximateTransformedTextSize(font, matrix, |
| {0, 0}); |
| REPORTER_ASSERT(reporter, control.isSDFT(approximateDeviceTextSize, paint, matrix)); |
| |
| // Server. |
| auto serverTypeface = SkTypeface::MakeFromName("monospace", SkFontStyle()); |
| const SkTypefaceID serverTypefaceID = serverTypeface->uniqueID(); |
| |
| int glyphCount = 10; |
| auto serverBlob = buildTextBlob(serverTypeface, glyphCount); |
| const SkSurfaceProps props; |
| std::unique_ptr<SkCanvas> cache_diff_canvas = server.makeAnalysisCanvas( |
| 10, 10, props, nullptr, direct->supportsDistanceFieldText(), |
| !direct->priv().caps()->disablePerspectiveSDFText()); |
| cache_diff_canvas->concat(matrix); |
| cache_diff_canvas->drawTextBlob(serverBlob.get(), 0, 0, paint); |
| |
| std::vector<uint8_t> serverStrikeData; |
| server.writeStrikeData(&serverStrikeData); |
| |
| // Client. |
| REPORTER_ASSERT(reporter, |
| client.readStrikeData(serverStrikeData.data(), serverStrikeData.size())); |
| auto clientTypeface = client.retrieveTypefaceUsingServerIDForTest(serverTypefaceID); |
| auto clientBlob = buildTextBlob(clientTypeface, glyphCount); |
| |
| SkBitmap expected = RasterBlob(serverBlob, 10, 10, paint, direct, &matrix); |
| SkBitmap actual = RasterBlob(clientBlob, 10, 10, paint, direct, &matrix); |
| compare_blobs(expected, actual, reporter); |
| REPORTER_ASSERT(reporter, !discardableManager->hasCacheMiss()); |
| |
| // Must unlock everything on termination, otherwise valgrind complains about memory leaks. |
| discardableManager->unlockAndDeleteAll(); |
| } |
| #endif // !defined(SK_DISABLE_SDF_TEXT) |
| |
| DEF_GANESH_TEST_FOR_RENDERING_CONTEXTS(SkRemoteGlyphCache_CacheMissReporting, |
| reporter, |
| ctxInfo, |
| CtsEnforcement::kNever) { |
| sk_sp<DiscardableManager> discardableManager = sk_make_sp<DiscardableManager>(); |
| SkStrikeServer server(discardableManager.get()); |
| SkStrikeClient client(discardableManager, false); |
| |
| auto serverTypeface = SkTypeface::MakeFromName("monospace", SkFontStyle()); |
| |
| // Create the clientTypeface proxy directly from the serverTypeface. |
| auto clientTypeface = sk_make_sp<SkTypefaceProxy>( |
| SkTypeface::UniqueID(serverTypeface.get()), |
| serverTypeface->countGlyphs(), |
| serverTypeface->fontStyle(), |
| serverTypeface->isFixedPitch(), |
| /*glyphMaskNeedsCurrentColor=*/ false, |
| discardableManager, |
| /*isLogging=*/ false); |
| |
| REPORTER_ASSERT(reporter, clientTypeface); |
| int glyphCount = 10; |
| auto clientBlob = buildTextBlob(clientTypeface, glyphCount); |
| |
| // Raster the client-side blob without the glyph data, we should get cache miss notifications. |
| SkPaint paint; |
| SkMatrix matrix = SkMatrix::I(); |
| RasterBlob(clientBlob, 10, 10, paint, ctxInfo.directContext(), &matrix); |
| REPORTER_ASSERT(reporter, |
| discardableManager->cacheMissCount(SkStrikeClient::kFontMetrics) == 1); |
| REPORTER_ASSERT(reporter, |
| discardableManager->cacheMissCount(SkStrikeClient::kGlyphMetrics) == 10); |
| |
| // There shouldn't be any image or path requests, since we mark the glyph as empty on a cache |
| // miss. |
| REPORTER_ASSERT(reporter, discardableManager->cacheMissCount(SkStrikeClient::kGlyphImage) == 0); |
| REPORTER_ASSERT(reporter, discardableManager->cacheMissCount(SkStrikeClient::kGlyphPath) == 0); |
| |
| // Must unlock everything on termination, otherwise valgrind complains about memory leaks. |
| discardableManager->unlockAndDeleteAll(); |
| } |
| |
| sk_sp<SkTextBlob> MakeEmojiBlob(sk_sp<SkTypeface> serverTf, SkScalar textSize, |
| sk_sp<SkTypeface> clientTf = nullptr) { |
| SkFont font; |
| font.setTypeface(serverTf); |
| font.setSize(textSize); |
| |
| const char* text = ToolUtils::emoji_sample_text(); |
| auto blob = SkTextBlob::MakeFromText(text, strlen(text), font); |
| if (clientTf == nullptr) return blob; |
| |
| SkSerialProcs s_procs; |
| s_procs.fTypefaceProc = [](SkTypeface*, void* ctx) -> sk_sp<SkData> { |
| return SkData::MakeUninitialized(1u); |
| }; |
| auto serialized = blob->serialize(s_procs); |
| |
| SkDeserialProcs d_procs; |
| d_procs.fTypefaceCtx = &clientTf; |
| d_procs.fTypefaceProc = [](const void* data, size_t length, void* ctx) -> sk_sp<SkTypeface> { |
| return *(static_cast<sk_sp<SkTypeface>*>(ctx)); |
| }; |
| return SkTextBlob::Deserialize(serialized->data(), serialized->size(), d_procs); |
| } |
| |
| DEF_GANESH_TEST_FOR_RENDERING_CONTEXTS(SkRemoteGlyphCache_TypefaceWithNoPaths, |
| reporter, |
| ctxInfo, |
| CtsEnforcement::kNever) { |
| auto direct = ctxInfo.directContext(); |
| sk_sp<DiscardableManager> discardableManager = sk_make_sp<DiscardableManager>(); |
| SkStrikeServer server(discardableManager.get()); |
| SkStrikeClient client(discardableManager, false); |
| |
| auto serverTypeface = ToolUtils::emoji_typeface(); |
| const SkTypefaceID serverTypefaceID = serverTypeface->uniqueID(); |
| |
| auto props = FindSurfaceProps(direct); |
| std::unique_ptr<SkCanvas> cache_diff_canvas = server.makeAnalysisCanvas( |
| 500, 500, props, nullptr, direct->supportsDistanceFieldText(), |
| !direct->priv().caps()->disablePerspectiveSDFText()); |
| for (SkScalar textSize : { 70, 180, 270, 340}) { |
| auto serverBlob = MakeEmojiBlob(serverTypeface, textSize); |
| |
| SkPaint paint; |
| cache_diff_canvas->drawTextBlob(serverBlob.get(), 100, 100, paint); |
| |
| std::vector<uint8_t> serverStrikeData; |
| server.writeStrikeData(&serverStrikeData); |
| if (!serverStrikeData.empty()) { |
| REPORTER_ASSERT(reporter, |
| client.readStrikeData(serverStrikeData.data(), |
| serverStrikeData.size())); |
| } |
| auto clientTypeface = client.retrieveTypefaceUsingServerIDForTest(serverTypefaceID); |
| auto clientBlob = MakeEmojiBlob(serverTypeface, textSize, clientTypeface); |
| REPORTER_ASSERT(reporter, clientBlob); |
| |
| RasterBlob(clientBlob, 500, 500, paint, direct); |
| REPORTER_ASSERT(reporter, !discardableManager->hasCacheMiss()); |
| discardableManager->resetCacheMissCounts(); |
| } |
| |
| // Must unlock everything on termination, otherwise valgrind complains about memory leaks. |
| discardableManager->unlockAndDeleteAll(); |
| } |
| |
| class SkRemoteGlyphCacheTest { |
| public: |
| static sk_sp<SkTextBlob> MakeNormalBlob(SkPaint* paint, |
| sk_sp<SkTypeface> serverTf, bool asPaths, SkScalar textSize, |
| sk_sp<SkTypeface> clientTf = nullptr) { |
| SkFont font; |
| font.setTypeface(serverTf); |
| font.setSize(textSize); |
| |
| const char* text = "Hel lo"; |
| if (asPaths) { |
| font.setupForAsPaths(paint); |
| } else { |
| SkFont font2(font); |
| font2.setupForAsPaths(paint); |
| } |
| auto blob = SkTextBlob::MakeFromText(text, strlen(text), font); |
| if (clientTf == nullptr) return blob; |
| |
| SkSerialProcs s_procs; |
| s_procs.fTypefaceProc = [](SkTypeface*, void* ctx) -> sk_sp<SkData> { |
| return SkData::MakeUninitialized(1u); |
| }; |
| auto serialized = blob->serialize(s_procs); |
| |
| SkDeserialProcs d_procs; |
| d_procs.fTypefaceCtx = &clientTf; |
| d_procs.fTypefaceProc = [](const void* data, size_t length, void* ctx) -> sk_sp<SkTypeface> { |
| return *(static_cast<sk_sp<SkTypeface>*>(ctx)); |
| }; |
| return SkTextBlob::Deserialize(serialized->data(), serialized->size(), d_procs); |
| } |
| }; |
| DEF_GANESH_TEST_FOR_RENDERING_CONTEXTS(SkRemoteGlyphCache_TypefaceWithPaths_MaskThenPath, |
| reporter, |
| ctxInfo, |
| CtsEnforcement::kNever) { |
| auto direct = ctxInfo.directContext(); |
| sk_sp<DiscardableManager> discardableManager = sk_make_sp<DiscardableManager>(); |
| SkStrikeServer server(discardableManager.get()); |
| SkStrikeClient client(discardableManager, true); |
| |
| auto serverTypeface = ToolUtils::create_portable_typeface(); |
| const SkTypefaceID serverTypefaceID = serverTypeface->uniqueID(); |
| |
| auto props = FindSurfaceProps(direct); |
| std::unique_ptr<SkCanvas> cache_diff_canvas = server.makeAnalysisCanvas( |
| 500, 500, props, nullptr, direct->supportsDistanceFieldText(), |
| !direct->priv().caps()->disablePerspectiveSDFText()); |
| SkPaint paint; |
| using Rgct = SkRemoteGlyphCacheTest; |
| |
| // Draw from mask out of the strike which provides paths. |
| { |
| auto serverBlob = Rgct::MakeNormalBlob(&paint, serverTypeface, true, 64); |
| cache_diff_canvas->drawTextBlob(serverBlob.get(), 100, 100, paint); |
| } |
| // Draw from path out of the strike which provides paths. |
| { |
| auto serverBlob = Rgct::MakeNormalBlob(&paint, serverTypeface, false, 440); |
| cache_diff_canvas->drawTextBlob(serverBlob.get(), 100, 100, paint); |
| } |
| std::vector<uint8_t> serverStrikeData; |
| server.writeStrikeData(&serverStrikeData); |
| if (!serverStrikeData.empty()) { |
| REPORTER_ASSERT(reporter, |
| client.readStrikeData(serverStrikeData.data(), |
| serverStrikeData.size())); |
| } |
| |
| auto clientTypeface = client.retrieveTypefaceUsingServerIDForTest(serverTypefaceID); |
| |
| { |
| auto clientBlob = Rgct::MakeNormalBlob(&paint, serverTypeface, true, 64, clientTypeface); |
| REPORTER_ASSERT(reporter, clientBlob); |
| |
| RasterBlob(clientBlob, 100, 100, paint, direct); |
| REPORTER_ASSERT(reporter, !discardableManager->hasCacheMiss()); |
| discardableManager->resetCacheMissCounts(); |
| } |
| { |
| auto clientBlob = Rgct::MakeNormalBlob(&paint, serverTypeface, false, 440, clientTypeface); |
| REPORTER_ASSERT(reporter, clientBlob); |
| |
| RasterBlob(clientBlob, 100, 100, paint, direct); |
| REPORTER_ASSERT(reporter, !discardableManager->hasCacheMiss()); |
| discardableManager->resetCacheMissCounts(); |
| } |
| // Must unlock everything on termination, otherwise valgrind complains about memory leaks. |
| discardableManager->unlockAndDeleteAll(); |
| } |
| |
| DEF_GANESH_TEST_FOR_RENDERING_CONTEXTS(SkRemoteGlyphCache_TypefaceWithPaths_PathThenMask, |
| reporter, |
| ctxInfo, |
| CtsEnforcement::kNever) { |
| auto direct = ctxInfo.directContext(); |
| sk_sp<DiscardableManager> discardableManager = sk_make_sp<DiscardableManager>(); |
| SkStrikeServer server(discardableManager.get()); |
| SkStrikeClient client(discardableManager, true); |
| |
| auto serverTypeface = ToolUtils::create_portable_typeface(); |
| const SkTypefaceID serverTypefaceID = serverTypeface->uniqueID(); |
| |
| auto props = FindSurfaceProps(direct); |
| std::unique_ptr<SkCanvas> cache_diff_canvas = server.makeAnalysisCanvas( |
| 500, 500, props, nullptr, direct->supportsDistanceFieldText(), |
| !direct->priv().caps()->disablePerspectiveSDFText()); |
| SkPaint paint; |
| using Rgct = SkRemoteGlyphCacheTest; |
| |
| // Draw from path out of the strike which provides paths. |
| { |
| auto serverBlob = Rgct::MakeNormalBlob(&paint, serverTypeface, false, 440); |
| cache_diff_canvas->drawTextBlob(serverBlob.get(), 100, 100, paint); |
| } |
| // Draw from mask out of the strike which provides paths. |
| { |
| auto serverBlob = Rgct::MakeNormalBlob(&paint, serverTypeface, true, 64); |
| cache_diff_canvas->drawTextBlob(serverBlob.get(), 100, 100, paint); |
| } |
| std::vector<uint8_t> serverStrikeData; |
| server.writeStrikeData(&serverStrikeData); |
| if (!serverStrikeData.empty()) { |
| REPORTER_ASSERT(reporter, |
| client.readStrikeData(serverStrikeData.data(), |
| serverStrikeData.size())); |
| } |
| |
| auto clientTypeface = client.retrieveTypefaceUsingServerIDForTest(serverTypefaceID); |
| |
| { |
| auto clientBlob = Rgct::MakeNormalBlob(&paint, serverTypeface, true, 64, clientTypeface); |
| REPORTER_ASSERT(reporter, clientBlob); |
| |
| RasterBlob(clientBlob, 100, 100, paint, direct); |
| REPORTER_ASSERT(reporter, !discardableManager->hasCacheMiss()); |
| discardableManager->resetCacheMissCounts(); |
| } |
| { |
| auto clientBlob = Rgct::MakeNormalBlob(&paint, serverTypeface, false, 440, clientTypeface); |
| REPORTER_ASSERT(reporter, clientBlob); |
| |
| RasterBlob(clientBlob, 100, 100, paint, direct); |
| REPORTER_ASSERT(reporter, !discardableManager->hasCacheMiss()); |
| discardableManager->resetCacheMissCounts(); |
| } |
| // Must unlock everything on termination, otherwise valgrind complains about memory leaks. |
| discardableManager->unlockAndDeleteAll(); |
| } |
| |
| DEF_TEST(SkTypefaceProxy_Basic_Serial, reporter) { |
| auto typeface = SkTypeface::MakeFromName("monospace", SkFontStyle()); |
| sk_sp<DiscardableManager> discardableManager = sk_make_sp<DiscardableManager>(); |
| SkTypefaceProxyPrototype srcProto{*typeface}; |
| |
| SkBinaryWriteBuffer writeBuffer; |
| srcProto.flatten(writeBuffer); |
| |
| auto data = writeBuffer.snapshotAsData(); |
| SkReadBuffer readBuffer{data->data(), data->size()}; |
| std::optional<SkTypefaceProxyPrototype> dstProto = |
| SkTypefaceProxyPrototype::MakeFromBuffer(readBuffer); |
| REPORTER_ASSERT(reporter, dstProto.has_value()); |
| auto proxy = sk_make_sp<SkTypefaceProxy>(dstProto.value(), discardableManager, false); |
| REPORTER_ASSERT(reporter, typeface->uniqueID() == proxy->remoteTypefaceID()); |
| REPORTER_ASSERT(reporter, typeface->uniqueID() != proxy->uniqueID()); |
| REPORTER_ASSERT(reporter, typeface->countGlyphs() == proxy->countGlyphs()); |
| REPORTER_ASSERT(reporter, typeface->fontStyle() == proxy->fontStyle()); |
| REPORTER_ASSERT(reporter, typeface->isFixedPitch() == proxy->isFixedPitch()); |
| |
| // Must be multiple of 4 bytes or the buffer will be invalid. |
| uint8_t brokenBytes[] = {1, 2, 3, 4, 5, 6, 7, 8}; |
| SkReadBuffer brokenBuffer{std::data(brokenBytes), std::size(brokenBytes)}; |
| std::optional<SkTypefaceProxyPrototype> brokenProto = |
| SkTypefaceProxyPrototype::MakeFromBuffer(brokenBuffer); |
| REPORTER_ASSERT(reporter, !brokenProto.has_value()); |
| } |