diff options
-rw-r--r-- | gn/tests.gni | 1 | ||||
-rw-r--r-- | src/core/SkRemoteGlyphCache.cpp | 797 | ||||
-rw-r--r-- | src/core/SkRemoteGlyphCache.h | 288 | ||||
-rw-r--r-- | src/core/SkScalerContext.h | 4 | ||||
-rw-r--r-- | src/core/SkStrikeCache.h | 19 | ||||
-rw-r--r-- | src/core/SkTypeface_remote.cpp | 17 | ||||
-rw-r--r-- | src/core/SkTypeface_remote.h | 27 | ||||
-rw-r--r-- | tests/SkRemoteGlyphCacheTest.cpp | 220 | ||||
-rw-r--r-- | tools/remote_demo.cpp | 130 |
9 files changed, 861 insertions, 642 deletions
diff --git a/gn/tests.gni b/gn/tests.gni index d18758c05e..ebf83bbd63 100644 --- a/gn/tests.gni +++ b/gn/tests.gni @@ -226,6 +226,7 @@ tests_sources = [ "$_tests/SkNxTest.cpp", "$_tests/SkPEGTest.cpp", "$_tests/SkRasterPipelineTest.cpp", + "$_tests/SkRemoteGlyphCacheTest.cpp", "$_tests/SkResourceCacheTest.cpp", "$_tests/SkSharedMutexTest.cpp", "$_tests/SkSLErrorTest.cpp", diff --git a/src/core/SkRemoteGlyphCache.cpp b/src/core/SkRemoteGlyphCache.cpp index ed484dd6fd..e45421aa05 100644 --- a/src/core/SkRemoteGlyphCache.cpp +++ b/src/core/SkRemoteGlyphCache.cpp @@ -9,13 +9,51 @@ #include <iterator> #include <memory> +#include <string> #include <tuple> #include "SkDevice.h" #include "SkFindAndPlaceGlyph.h" #include "SkStrikeCache.h" +#include "SkTextBlobRunIterator.h" +#include "SkTraceEvent.h" #include "SkTypeface_remote.h" +static SkDescriptor* auto_descriptor_from_desc(const SkDescriptor* source_desc, + SkFontID font_id, + SkAutoDescriptor* ad) { + ad->reset(source_desc->getLength()); + auto* desc = ad->getDesc(); + desc->init(); + + // Rec. + { + uint32_t size; + auto ptr = source_desc->findEntry(kRec_SkDescriptorTag, &size); + SkScalerContextRec rec; + std::memcpy(&rec, ptr, size); + rec.fFontID = font_id; + desc->addEntry(kRec_SkDescriptorTag, sizeof(rec), &rec); + } + + // Path effect. + { + uint32_t size; + auto ptr = source_desc->findEntry(kPathEffect_SkDescriptorTag, &size); + if (ptr) desc->addEntry(kPathEffect_SkDescriptorTag, size, ptr); + } + + // Mask filter. + { + uint32_t size; + auto ptr = source_desc->findEntry(kMaskFilter_SkDescriptorTag, &size); + if (ptr) desc->addEntry(kMaskFilter_SkDescriptorTag, size, ptr); + } + + desc->computeChecksum(); + return desc; +} + template <typename T> class ArraySlice final : public std::tuple<const T*, size_t> { public: @@ -26,7 +64,6 @@ public: const T* begin() { return this->data(); } - const T* end() { return &this->data()[this->size()]; } @@ -46,28 +83,32 @@ private: // -- Serializer ---------------------------------------------------------------------------------- -static size_t pad(size_t size, size_t alignment) { - return (size + (alignment - 1)) & ~(alignment - 1); -} +size_t pad(size_t size, size_t alignment) { return (size + (alignment - 1)) & ~(alignment - 1); } -// N.B. pointers are only valid until the next call. class Serializer { public: Serializer(std::vector<uint8_t>* buffer) : fBuffer{buffer} { } - template <typename T> - T* push_back(const T& data) { - auto result = allocate(sizeof(T), alignof(T)); - return new (result) T(data); - } - template <typename T, typename... Args> - T* emplace_back(Args&& ... args) { + T* emplace(Args&&... args) { auto result = allocate(sizeof(T), alignof(T)); return new (result) T{std::forward<Args>(args)...}; } + template <typename T> + void write(const T& data) { + T* result = (T*)allocate(sizeof(T), alignof(T)); + memcpy(result, &data, sizeof(T)); + } + + template <typename T> + T* allocate() { + T* result = (T*)allocate(sizeof(T), alignof(T)); + return result; + } + void writeDescriptor(const SkDescriptor& desc) { + write(desc.getLength()); auto result = allocate(desc.getLength(), alignof(SkDescriptor)); memcpy(result, &desc, desc.getLength()); } @@ -89,86 +130,69 @@ private: }; // -- Deserializer ------------------------------------------------------------------------------- - +// Note that the Deserializer is reading untrusted data, we need to guard against invalid data. class Deserializer { public: - Deserializer(const SkData& buffer) : fBuffer{buffer} { } + Deserializer(const volatile char* memory, size_t memorySize) + : fMemory(memory), fMemorySize(memorySize) {} template <typename T> - T* read() { - size_t padded = pad(fCursor, alignof(T)); - fCursor = padded + sizeof(T); - auto data = (uint8_t*)fBuffer.data(); - return (T*)&data[padded]; - } - - SkDescriptor* readDescriptor() { - size_t padded = pad(fCursor, alignof(SkDescriptor)); - auto data = (uint8_t*)fBuffer.data(); - SkDescriptor* result = (SkDescriptor*)&data[padded]; - fCursor = padded + result->getLength(); - return result; + bool read(T* val) { + auto* result = this->ensureAtLeast(sizeof(T), alignof(T)); + if (!result) return false; + + memcpy(val, const_cast<const char*>(result), sizeof(T)); + return true; + } + + bool readDescriptor(SkAutoDescriptor* ad) { + uint32_t desc_length = 0u; + if (!read<uint32_t>(&desc_length)) return false; + + auto* result = this->ensureAtLeast(desc_length, alignof(SkDescriptor)); + if (!result) return false; + + ad->reset(desc_length); + memcpy(ad->getDesc(), const_cast<const char*>(result), desc_length); + return true; } template <typename T> ArraySlice<T> readArray(int count) { - size_t padded = pad(fCursor, alignof(T)); size_t size = count * sizeof(T); - auto data = (uint8_t*)fBuffer.data(); - const T* base = (const T*)&data[padded]; + const T* base = (const T*)this->ensureAtLeast(size, alignof(T)); + if (!base) return ArraySlice<T>(); + ArraySlice<T> result = ArraySlice<T>{base, (uint32_t)count}; - fCursor = padded + size; return result; } - size_t size() {return fCursor;} - private: - const SkData& fBuffer; - size_t fCursor{0}; -}; - + const volatile char* ensureAtLeast(size_t size, size_t alignment) { + size_t padded = pad(fBytesRead, alignment); -// -- SkStrikeCacheDifferenceSpec ------------------------------------------------------------------ + // Not enough data + if (padded + size > fMemorySize) return nullptr; -SkStrikeDifferences::SkStrikeDifferences( - SkFontID typefaceID, std::unique_ptr<SkDescriptor> desc) - : fTypefaceID{typefaceID} - , fDesc{std::move(desc)} { } - -void SkStrikeDifferences::add(uint16_t glyphID, SkIPoint pos) { - SkPackedGlyphID packedGlyphID{glyphID, pos.x(), pos.y()}; - fGlyphIDs->add(packedGlyphID); -} + auto* result = fMemory + padded; + fBytesRead = padded + size; + return result; + } -SkStrikeDifferences& SkStrikeCacheDifferenceSpec::findStrikeDifferences( - const SkDescriptor& desc, SkFontID typefaceID) -{ - auto mapIter = fDescriptorToDifferencesMap.find(&desc); - if (mapIter == fDescriptorToDifferencesMap.end()) { - auto newDesc = desc.copy(); - auto newDescPtr = newDesc.get(); - SkStrikeDifferences strikeDiffs{typefaceID, std::move(newDesc)}; + // Note that we read each piece of memory only once to guard against TOCTOU violations. + const volatile char* fMemory; + size_t fMemorySize; + size_t fBytesRead = 0u; +}; - mapIter = fDescriptorToDifferencesMap.emplace_hint( - mapIter, newDescPtr, std::move(strikeDiffs)); +size_t SkDescriptorMapOperators::operator()(const SkDescriptor* key) const { + return key->getChecksum(); } - return mapIter->second; -} - -template <typename PerStrike, typename PerGlyph> -void SkStrikeCacheDifferenceSpec::iterateDifferences(PerStrike perStrike, PerGlyph perGlyph) const { - for (auto& i : fDescriptorToDifferencesMap) { - auto strikeDiff = &i.second; - perStrike(strikeDiff->fTypefaceID, - *strikeDiff->fDesc, - strikeDiff->fGlyphIDs->count()); - strikeDiff->fGlyphIDs->foreach([&](SkPackedGlyphID id) { - perGlyph(id); - }); + bool SkDescriptorMapOperators::operator()(const SkDescriptor* lhs, + const SkDescriptor* rhs) const { + return *lhs == *rhs; } -} // -- TrackLayerDevice ----------------------------------------------------------------------------- class TrackLayerDevice : public SkNoPixelsDevice { @@ -182,17 +206,18 @@ public: }; // -- SkTextBlobCacheDiffCanvas ------------------------------------------------------------------- -SkTextBlobCacheDiffCanvas::SkTextBlobCacheDiffCanvas( - int width, int height, - const SkMatrix& deviceMatrix, - const SkSurfaceProps& props, - SkScalerContextFlags flags, - SkStrikeCacheDifferenceSpec* strikeDiffs) +SkTextBlobCacheDiffCanvas::SkTextBlobCacheDiffCanvas(int width, int height, + const SkMatrix& deviceMatrix, + const SkSurfaceProps& props, + SkStrikeServer* strikeSever) : SkNoDrawCanvas{new TrackLayerDevice{SkIRect::MakeWH(width, height), props}} , fDeviceMatrix{deviceMatrix} , fSurfaceProps{props} - , fScalerContextFlags{flags} - , fStrikeCacheDiff{strikeDiffs} { } + , fStrikeServer{strikeSever} { + SkASSERT(fStrikeServer); +} + +SkTextBlobCacheDiffCanvas::~SkTextBlobCacheDiffCanvas() = default; SkCanvas::SaveLayerStrategy SkTextBlobCacheDiffCanvas::getSaveLayerStrategy( const SaveLayerRec&rec) @@ -235,6 +260,10 @@ void SkTextBlobCacheDiffCanvas::processLooper( } } +#define FAIL_AND_RETURN \ + SkDEBUGFAIL("Failed to process glyph run"); \ + return; + void SkTextBlobCacheDiffCanvas::processGlyphRun( const SkPoint& position, const SkTextBlobRunIterator& it, @@ -242,20 +271,24 @@ void SkTextBlobCacheDiffCanvas::processGlyphRun( { if (runPaint.getTextEncoding() != SkPaint::TextEncoding::kGlyphID_TextEncoding) { - return; + TRACE_EVENT0("skia", "kGlyphID_TextEncoding"); + FAIL_AND_RETURN } // All other alignment modes need the glyph advances. Use the slow drawing mode. if (runPaint.getTextAlign() != SkPaint::kLeft_Align) { - return; + TRACE_EVENT0("skia", "kLeft_Align"); + FAIL_AND_RETURN } using PosFn = SkPoint(*)(int index, const SkScalar* pos); PosFn posFn; switch (it.positioning()) { - case SkTextBlob::kDefault_Positioning: + case SkTextBlob::kDefault_Positioning: { // Default positioning needs advances. Can't do that. - return; + TRACE_EVENT0("skia", "kDefault_Positioning"); + FAIL_AND_RETURN + } case SkTextBlob::kHorizontal_Positioning: posFn = [](int index, const SkScalar* pos) { @@ -278,7 +311,8 @@ void SkTextBlobCacheDiffCanvas::processGlyphRun( SkMatrix blobMatrix{fDeviceMatrix}; blobMatrix.preConcat(this->getTotalMatrix()); if (blobMatrix.hasPerspective()) { - return; + TRACE_EVENT0("skia", "hasPerspective"); + FAIL_AND_RETURN } blobMatrix.preTranslate(position.x(), position.y()); @@ -315,19 +349,21 @@ void SkTextBlobCacheDiffCanvas::processGlyphRun( SK_ABORT("Bad matrix."); } - SkAutoDescriptor ad; SkScalerContextRec rec; SkScalerContextEffects effects; + // TODO(crbug.com/831354): The typeface proxy on the client does not replicate the + // filtering done by the typeface on the server. SkScalerContext::MakeRecAndEffects(runPaint, &fSurfaceProps, &runMatrix, - fScalerContextFlags, &rec, &effects); - - auto desc = SkScalerContext::AutoDescriptorGivenRecAndEffects(rec, effects, &ad); + SkScalerContextFlags::kFakeGammaAndBoostContrast, &rec, + &effects); - auto typefaceID = SkTypefaceProxy::DownCast(runPaint.getTypeface())->remoteTypefaceID(); - auto& diffs = fStrikeCacheDiff->findStrikeDifferences(*desc, typefaceID); + TRACE_EVENT1("skia", "RecForDesc", "rec", TRACE_STR_COPY(rec.dump().c_str())); + auto desc = SkScalerContext::DescriptorGivenRecAndEffects(rec, effects); + auto* glyphCacheState = static_cast<SkStrikeServer*>(fStrikeServer) + ->getOrCreateCache(runPaint.getTypeface(), std::move(desc)); + SkASSERT(glyphCacheState); - auto cache = SkStrikeCache::FindStrikeExclusive(*desc); bool isSubpixel = SkToBool(rec.fFlags & SkScalerContext::kSubpixelPositioning_Flag); SkAxisAlignment axisAlignment = SkAxisAlignment::kNone_SkAxisAlignment; if (it.positioning() == SkTextBlob::kHorizontal_Positioning) { @@ -342,392 +378,323 @@ void SkTextBlobCacheDiffCanvas::processGlyphRun( subPixelPos = SkFindAndPlaceGlyph::SubpixelAlignment(axisAlignment, glyphPos); } - if (cache && - cache->isGlyphCached(glyphs[index], subPixelPos.x(), subPixelPos.y())) { - continue; - } - - diffs.add(glyphs[index], subPixelPos); + glyphCacheState->addGlyph(runPaint.getTypeface(), + effects, + SkPackedGlyphID(glyphs[index], subPixelPos.x(), subPixelPos.y())); } } -// Op code semantics: -// * FontMetrics - (SkFontID, SkDescriptor) -> SkPaint::FontMetrics -// * GlyphPath - (SkFontID, SkDescriptor, SkPackedGlyphID) -> SkPath -// * GlyphMetricsAndImage - (SkFontID, SkDescriptor, SkPackedGlyphID) -> (SkGlyph, <image bits>) -// * PrepopulateCache - StrikeCacheDifferenceSpec -> StrikeCacheDifferenceData - -enum class OpCode : int32_t { - kFontMetrics = 0, - kGlyphPath = 1, - kGlyphMetricsAndImage = 2, - kPrepopulateCache = 3, -}; - -struct StrikeDiffHeader { - StrikeDiffHeader() {} - StrikeDiffHeader(int strikeCount_) : strikeCount{strikeCount_} {} - int strikeCount; -}; - struct StrikeSpec { - StrikeSpec(SkFontID typefaceID_, uint32_t descLength_, int glyphCount_) + StrikeSpec() {} + StrikeSpec(SkFontID typefaceID_, size_t glyphCount_, SkDiscardableHandleId discardableHandleId_) : typefaceID{typefaceID_} - , descLength{descLength_} - , glyphCount{glyphCount_} { } - SkFontID typefaceID; - uint32_t descLength; - int glyphCount; + , glyphCount{glyphCount_} + , discardableHandleId(discardableHandleId_) {} + SkFontID typefaceID = 0u; + size_t glyphCount = 0u; + SkDiscardableHandleId discardableHandleId = 0u; /* desc */ /* n X (glyphs ids) */ }; struct WireTypeface { + WireTypeface() = default; + WireTypeface(SkFontID typeface_id, int glyph_count, SkFontStyle style, bool is_fixed) + : typefaceID(typeface_id), glyphCount(glyph_count), style(style), isFixed(is_fixed) {} + + // std::thread::id thread_id; // TODO:need to figure a good solution SkFontID typefaceID; int glyphCount; SkFontStyle style; bool isFixed; }; -class Op { -public: - Op(OpCode opCode, SkFontID typefaceId, const SkScalerContextRec& rec) - : opCode{opCode} - , typefaceId{typefaceId} - , descriptor{rec} { } - const OpCode opCode; - const SkFontID typefaceId; - const SkScalerContextRecDescriptor descriptor; - union { - // kGlyphPath and kGlyphMetricsAndImage - SkPackedGlyphID glyphID; - // kPrepopulateCache - StrikeDiffHeader strikeSpecHeader; - }; -}; +// SkStrikeServer ----------------------------------------- -size_t SkStrikeCacheDifferenceSpec::sizeBytes() const { - size_t sum = sizeof(Op) + sizeof(StrikeDiffHeader); - for (auto& pair : fDescriptorToDifferencesMap) { - const auto& strike = pair.second; - sum += sizeof(StrikeSpec) - + strike.fDesc->getLength() - + strike.fGlyphIDs->count() * sizeof(SkPackedGlyphID); - } - return sum; +SkStrikeServer::SkStrikeServer(DiscardableHandleManager* discardableHandleManager) + : fDiscardableHandleManager(discardableHandleManager) { + SkASSERT(fDiscardableHandleManager); } -static void write_strikes_spec(const SkStrikeCacheDifferenceSpec &spec, - Serializer* serializer) { - serializer->emplace_back<Op>(OpCode::kPrepopulateCache, SkFontID{0}, SkScalerContextRec{}); +SkStrikeServer::~SkStrikeServer() = default; - serializer->emplace_back<StrikeDiffHeader>(spec.strikeCount()); +sk_sp<SkData> SkStrikeServer::serializeTypeface(SkTypeface* tf) { + WireTypeface wire(SkTypeface::UniqueID(tf), tf->countGlyphs(), tf->fontStyle(), + tf->isFixedPitch()); + return SkData::MakeWithCopy(&wire, sizeof(wire)); +} + +void SkStrikeServer::writeStrikeData(std::vector<uint8_t>* memory) { + if (fLockedDescs.empty() && fTypefacesToSend.empty()) return; - auto perStrike = [serializer](SkFontID typefaceID, const SkDescriptor& desc, int glyphCount) { - serializer->emplace_back<StrikeSpec>(typefaceID, desc.getLength(), glyphCount); - serializer->writeDescriptor(desc); - }; + Serializer serializer(memory); + serializer.emplace<size_t>(fTypefacesToSend.size()); + for (const auto& tf : fTypefacesToSend) serializer.write<WireTypeface>(tf); + fTypefacesToSend.clear(); - auto perGlyph = [serializer](SkPackedGlyphID glyphID) { - serializer->push_back<SkPackedGlyphID>(glyphID); - }; + serializer.emplace<size_t>(fLockedDescs.size()); + for (const auto* desc : fLockedDescs) { + auto it = fRemoteGlyphStateMap.find(desc); + SkASSERT(it != fRemoteGlyphStateMap.end()); - spec.iterateDifferences(perStrike, perGlyph); + // TODO: This is unnecessary, write only the descs which has any glyphs + // to send. It was getting awkward to write the size after writing the + // descs because the vector reallocs. + serializer.emplace<bool>(it->second->has_pending_glyphs()); + if (!it->second->has_pending_glyphs()) continue; + + it->second->writePendingGlyphs(&serializer); + } + fLockedDescs.clear(); } -static void read_strikes_spec_write_strikes_data( - Deserializer* deserializer, Serializer* serializer, SkStrikeServer* server) -{ - // Don't start because the op started this deserialization. - auto header = deserializer->read<StrikeDiffHeader>(); - serializer->push_back<StrikeDiffHeader>(*header); - for (int i = 0; i < header->strikeCount; i++) { - auto spec = deserializer->read<StrikeSpec>(); - auto desc = deserializer->readDescriptor(); - serializer->push_back<StrikeSpec>(*spec); - serializer->writeDescriptor(*desc); - SkScalerContextRecDescriptor recDesc{*desc}; - auto scaler = server->generateScalerContext(recDesc, spec->typefaceID); - SkPaint::FontMetrics fontMetrics; - scaler->getFontMetrics(&fontMetrics); - serializer->push_back<SkPaint::FontMetrics>(fontMetrics); - auto glyphIDs = deserializer->readArray<SkPackedGlyphID>(spec->glyphCount); - for (auto glyphID : glyphIDs) { - auto glyph = serializer->emplace_back<SkGlyph>(); - glyph->initWithGlyphID(glyphID); - scaler->getMetrics(glyph); - auto imageSize = glyph->computeImageSize(); - glyph->fPathData = nullptr; - glyph->fImage = nullptr; - - if (imageSize > 0) { - // Since the allocateArray can move glyph, make one that stays in one place. - SkGlyph stationaryGlyph = *glyph; - stationaryGlyph.fImage = serializer->allocateArray<uint8_t>(imageSize); - scaler->getImage(stationaryGlyph); - } +SkStrikeServer::SkGlyphCacheState* SkStrikeServer::getOrCreateCache( + SkTypeface* tf, std::unique_ptr<SkDescriptor> desc) { + SkASSERT(desc); + + // Already locked. + if (fLockedDescs.find(desc.get()) != fLockedDescs.end()) { + auto it = fRemoteGlyphStateMap.find(desc.get()); + SkASSERT(it != fRemoteGlyphStateMap.end()); + return it->second.get(); + } + + // Try to lock. + auto it = fRemoteGlyphStateMap.find(desc.get()); + if (it != fRemoteGlyphStateMap.end()) { + bool locked = fDiscardableHandleManager->lockHandle(it->second->discardable_handle_id()); + if (locked) { + fLockedDescs.insert(it->first); + return it->second.get(); } + + // If the lock failed, the entry was deleted on the client. Remove our + // tracking. + fRemoteGlyphStateMap.erase(it); } + + const SkFontID typeface_id = tf->uniqueID(); + if (!fCachedTypefaces.contains(typeface_id)) { + fCachedTypefaces.add(typeface_id); + fTypefacesToSend.emplace_back(typeface_id, tf->countGlyphs(), tf->fontStyle(), + tf->isFixedPitch()); + } + + auto* desc_ptr = desc.get(); + auto new_handle = fDiscardableHandleManager->createHandle(); + auto cache_state = skstd::make_unique<SkGlyphCacheState>(std::move(desc), new_handle); + auto* cache_state_ptr = cache_state.get(); + + fLockedDescs.insert(desc_ptr); + fRemoteGlyphStateMap[desc_ptr] = std::move(cache_state); + return cache_state_ptr; } -static void update_caches_from_strikes_data(SkStrikeClient *client, - Deserializer *deserializer) { - auto header = deserializer->read<StrikeDiffHeader>(); - for (int i = 0; i < header->strikeCount; i++) { - auto spec = deserializer->read<StrikeSpec>(); - auto desc = deserializer->readDescriptor(); - auto fontMetrics = deserializer->read<SkPaint::FontMetrics>(); - auto tf = client->lookupTypeface(spec->typefaceID); - - // TODO: implement effects handling. - SkScalerContextEffects effects; - auto strike = SkStrikeCache::FindStrikeExclusive(*desc); - if (strike == nullptr) { - auto scaler = SkStrikeCache::CreateScalerContext(*desc, effects, *tf); - strike = SkStrikeCache::CreateStrikeExclusive(*desc, std::move(scaler), fontMetrics); - } - for (int j = 0; j < spec->glyphCount; j++) { - auto glyph = deserializer->read<SkGlyph>(); - ArraySlice<uint8_t> image; - auto imageSize = glyph->computeImageSize(); - if (imageSize != 0) { - image = deserializer->readArray<uint8_t>(imageSize); - } - SkGlyph* allocatedGlyph = strike->getRawGlyphByID(glyph->getPackedID()); - *allocatedGlyph = *glyph; - allocatedGlyph->allocImage(strike->getAlloc()); - memcpy(allocatedGlyph->fImage, image.data(), image.size()); +SkStrikeServer::SkGlyphCacheState::SkGlyphCacheState(std::unique_ptr<SkDescriptor> desc, + uint32_t discardable_handle_id) + : fDesc(std::move(desc)), fDiscardableHandleId(discardable_handle_id) { + SkASSERT(fDesc); +} + +SkStrikeServer::SkGlyphCacheState::~SkGlyphCacheState() = default; + +void SkStrikeServer::SkGlyphCacheState::addGlyph(SkTypeface* typeface, + const SkScalerContextEffects& effects, + SkPackedGlyphID glyph) { + // Already cached. + if (fCachedGlyphs.contains(glyph)) return; + + // Serialize and cache. Also create the scalar context to use when serializing + // this glyph. + fCachedGlyphs.add(glyph); + fPendingGlyphs.push_back(glyph); + if (!fContext) fContext = typeface->createScalerContext(effects, fDesc.get(), false); +} + +void SkStrikeServer::SkGlyphCacheState::writePendingGlyphs(Serializer* serializer) { + // Write the desc. + serializer->emplace<StrikeSpec>(fContext->getTypeface()->uniqueID(), fPendingGlyphs.size(), + fDiscardableHandleId); + serializer->writeDescriptor(*fDesc.get()); + + // Write FontMetrics. + SkPaint::FontMetrics fontMetrics; + fContext->getFontMetrics(&fontMetrics); + serializer->write<SkPaint::FontMetrics>(fontMetrics); + + // Write Glyphs. + for (const auto& glyphID : fPendingGlyphs) { + auto glyph = serializer->emplace<SkGlyph>(); + glyph->initWithGlyphID(glyphID); + fContext->getMetrics(glyph); + auto imageSize = glyph->computeImageSize(); + glyph->fPathData = nullptr; + glyph->fImage = nullptr; + + if (imageSize > 0) { + // Since the allocateArray can move glyph, make one that stays in one place. + SkGlyph stationaryGlyph = *glyph; + stationaryGlyph.fImage = serializer->allocateArray<uint8_t>(imageSize); + fContext->getImage(stationaryGlyph); } } + + // Note that we reset the context after serializing pending glyphs since we + // don't want to extend the lifetime of the typeface. + fPendingGlyphs.clear(); + fContext.reset(); } -// -- SkStrikeServer ------------------------------------------------------------------------------- -SkStrikeServer::SkStrikeServer() { } +// SkStrikeClient ----------------------------------------- -SkStrikeServer::~SkStrikeServer() { - printf("Strike server - ops: %d\n", fOpCount); -} +class SkStrikeClient::DiscardableStrikePinner : public SkStrikePinner { +public: + DiscardableStrikePinner(SkDiscardableHandleId discardableHandleId, + sk_sp<DiscardableHandleManager> manager) + : fDiscardableHandleId(discardableHandleId), fManager(std::move(manager)) {} -void SkStrikeServer::serve(const SkData& inBuffer, std::vector<uint8_t>* outBuffer) { + ~DiscardableStrikePinner() override = default; + bool canDelete() override { return fManager->deleteHandle(fDiscardableHandleId); } - fOpCount += 1; +private: + const SkDiscardableHandleId fDiscardableHandleId; + sk_sp<DiscardableHandleManager> fManager; +}; - Serializer serializer{outBuffer}; - Deserializer deserializer{inBuffer}; - Op* op = deserializer.read<Op>(); +SkStrikeClient::SkStrikeClient(sk_sp<DiscardableHandleManager> discardableManager) + : fDiscardableHandleManager(std::move(discardableManager)) {} - switch (op->opCode) { - case OpCode::kFontMetrics : { - auto scaler = this->generateScalerContext(op->descriptor, op->typefaceId); - SkPaint::FontMetrics metrics; - scaler->getFontMetrics(&metrics); - serializer.push_back<SkPaint::FontMetrics>(metrics); - break; - } - case OpCode::kGlyphPath : { - auto sc = this->generateScalerContext(op->descriptor, op->typefaceId); - // TODO: check for buffer overflow. - SkPath path; - if (sc->getPath(op->glyphID, &path)) { - size_t pathSize = path.writeToMemory(nullptr); - serializer.push_back<size_t>(pathSize); - auto pathData = serializer.allocateArray<uint8_t>(pathSize); - path.writeToMemory(pathData); - } - break; - } - case OpCode::kGlyphMetricsAndImage : { - auto scaler = this->generateScalerContext(op->descriptor, op->typefaceId); - - auto glyph = serializer.emplace_back<SkGlyph>(); - // TODO: check for buffer overflow. - glyph->initWithGlyphID(op->glyphID); - scaler->getMetrics(glyph); - auto imageSize = glyph->computeImageSize(); - glyph->fPathData = nullptr; - glyph->fImage = nullptr; - if (imageSize > 0) { - // Since the allocateArray can move glyph, make one that stays in one place. - SkGlyph stationaryGlyph = *glyph; - stationaryGlyph.fImage = serializer.allocateArray<uint8_t>(imageSize); - scaler->getImage(stationaryGlyph); - } - break; - } - case OpCode::kPrepopulateCache : { - read_strikes_spec_write_strikes_data( - &deserializer, &serializer, this); - break; - } +SkStrikeClient::~SkStrikeClient() = default; - default: - SK_ABORT("Bad op"); +#define READ_FAILURE \ + { \ + SkDEBUGFAIL("Bad serialization"); \ + return false; \ } -} -void SkStrikeServer::prepareSerializeProcs(SkSerialProcs* procs) { - auto encode = [](SkTypeface* tf, void* ctx) { - return reinterpret_cast<SkStrikeServer*>(ctx)->encodeTypeface(tf); - }; - procs->fTypefaceProc = encode; - procs->fTypefaceCtx = this; -} +bool SkStrikeClient::readStrikeData(const volatile void* memory, size_t memorySize) { + SkASSERT(memorySize != 0u); + Deserializer deserializer(static_cast<const volatile char*>(memory), memorySize); -SkScalerContext* SkStrikeServer::generateScalerContext( - const SkScalerContextRecDescriptor& desc, SkFontID typefaceId) -{ + size_t typefaceSize = 0u; + if (!deserializer.read<size_t>(&typefaceSize)) READ_FAILURE - auto scaler = fScalerContextMap.find(desc); - if (scaler == nullptr) { - auto typefaceIter = fTypefaceMap.find(typefaceId); - if (typefaceIter == nullptr) { - // TODO: handle this with some future fallback strategy. - SK_ABORT("unknown type face"); - // Should never happen - return nullptr; - } - auto tf = typefaceIter->get(); - // TODO: make effects really work. - SkScalerContextEffects effects; - auto mapSc = tf->createScalerContext(effects, &desc.desc(), false); - scaler = fScalerContextMap.set(desc, std::move(mapSc)); + for (size_t i = 0; i < typefaceSize; ++i) { + WireTypeface wire; + if (!deserializer.read<WireTypeface>(&wire)) READ_FAILURE + + // TODO(khushalsagar): The typeface no longer needs a reference to the + // SkStrikeClient, since all needed glyphs must have been pushed before + // raster. + addTypeface(wire); } - return scaler->get(); -} -sk_sp<SkData> SkStrikeServer::encodeTypeface(SkTypeface* tf) { - WireTypeface wire = { - SkTypeface::UniqueID(tf), - tf->countGlyphs(), - tf->fontStyle(), - tf->isFixedPitch() - }; - auto typeFace = fTypefaceMap.find(SkTypeface::UniqueID(tf)); - if (typeFace == nullptr) { - fTypefaceMap.set(SkTypeface::UniqueID(tf), sk_ref_sp(tf)); - } - // Can this be done with no copy? - return SkData::MakeWithCopy(&wire, sizeof(wire)); -} + size_t strikeCount = 0u; + if (!deserializer.read<size_t>(&strikeCount)) READ_FAILURE -// -- SkStrikeClient ------------------------------------------------------------------------------- -SkStrikeClient::SkStrikeClient(SkStrikeCacheClientRPC clientRPC) - : fClientRPC{clientRPC} { } + for (size_t i = 0; i < strikeCount; ++i) { + bool has_glyphs = false; + if (!deserializer.read<bool>(&has_glyphs)) READ_FAILURE -void SkStrikeClient::generateFontMetrics( - const SkTypefaceProxy& typefaceProxy, - const SkScalerContextRec& rec, - SkPaint::FontMetrics* metrics) -{ - fBuffer.clear(); + if (!has_glyphs) continue; - Serializer serializer{&fBuffer}; - serializer.emplace_back<Op>(OpCode::kFontMetrics, typefaceProxy.remoteTypefaceID(), rec); + StrikeSpec spec; + if (!deserializer.read<StrikeSpec>(&spec)) READ_FAILURE - auto outBuffer = SkData::MakeWithoutCopy(fBuffer.data(), fBuffer.size()); - auto inbuffer = fClientRPC(*outBuffer); - Deserializer deserializer(*inbuffer); - *metrics = *deserializer.read<SkPaint::FontMetrics>(); -} + SkAutoDescriptor sourceAd; + if (!deserializer.readDescriptor(&sourceAd)) READ_FAILURE -void SkStrikeClient::generateMetricsAndImage( - const SkTypefaceProxy& typefaceProxy, - const SkScalerContextRec& rec, - SkArenaAlloc* alloc, - SkGlyph* glyph) -{ - fBuffer.clear(); - Serializer serializer(&fBuffer); - Op *op = serializer.emplace_back<Op>( - OpCode::kGlyphMetricsAndImage, typefaceProxy.remoteTypefaceID(), rec); - op->glyphID = glyph->getPackedID(); - - auto outBuffer = SkData::MakeWithoutCopy(fBuffer.data(), fBuffer.size()); - auto inbuffer = fClientRPC(*outBuffer); - Deserializer deserializer(*inbuffer); - *glyph = *deserializer.read<SkGlyph>(); - auto imageSize = glyph->computeImageSize(); - glyph->fPathData = nullptr; - glyph->fImage = nullptr; - if (imageSize > 0) { - auto image = deserializer.readArray<uint8_t>(imageSize); - SkASSERT(imageSize == image.size()); - glyph->allocImage(alloc); - memcpy(glyph->fImage, image.data(), imageSize); + SkPaint::FontMetrics fontMetrics; + if (!deserializer.read<SkPaint::FontMetrics>(&fontMetrics)) READ_FAILURE + + // Get the local typeface from remote fontID. + auto* tf = fRemoteFontIdToTypeface.find(spec.typefaceID)->get(); + // Received strikes for a typeface which doesn't exist. + if (!tf) READ_FAILURE + + // Replace the ContextRec in the desc from the server to create the client + // side descriptor. + // TODO: Can we do this in-place and re-compute checksum? Instead of a complete copy. + SkAutoDescriptor ad; + auto* client_desc = auto_descriptor_from_desc(sourceAd.getDesc(), tf->uniqueID(), &ad); + + auto strike = SkStrikeCache::FindStrikeExclusive(*client_desc); + if (strike == nullptr) { + // Note that we don't need to deserialize the effects since we won't be generating any + // glyphs here anyway, and the desc is still correct since it includes the serialized + // effects. + SkScalerContextEffects effects; + auto scaler = SkStrikeCache::CreateScalerContext(*client_desc, effects, *tf); + strike = SkStrikeCache::CreateStrikeExclusive( + *client_desc, std::move(scaler), &fontMetrics, + skstd::make_unique<DiscardableStrikePinner>(spec.discardableHandleId, + fDiscardableHandleManager)); + } + + for (size_t j = 0; j < spec.glyphCount; j++) { + SkGlyph glyph; + if (!deserializer.read<SkGlyph>(&glyph)) READ_FAILURE + + ArraySlice<uint8_t> image; + auto imageSize = glyph.computeImageSize(); + if (imageSize != 0) { + image = deserializer.readArray<uint8_t>(imageSize); + if (!image.data()) READ_FAILURE + } + + SkGlyph* allocatedGlyph = strike->getRawGlyphByID(glyph.getPackedID()); + *allocatedGlyph = glyph; + allocatedGlyph->allocImage(strike->getAlloc()); + memcpy(allocatedGlyph->fImage, image.data(), image.size()); + } } -} -bool SkStrikeClient::generatePath( - const SkTypefaceProxy& typefaceProxy, - const SkScalerContextRec& rec, - SkGlyphID glyphID, - SkPath* path) -{ - fBuffer.clear(); - - Serializer serializer{&fBuffer}; - Op *op = serializer.emplace_back<Op>( - OpCode::kGlyphPath, typefaceProxy.remoteTypefaceID(), rec); - op->glyphID = glyphID; - - auto outBuffer = SkData::MakeWithoutCopy(fBuffer.data(), fBuffer.size()); - auto inbuffer = fClientRPC(*outBuffer); - Deserializer deserializer(*inbuffer); - size_t pathSize = *deserializer.read<size_t>(); - if (pathSize == 0) { - return false; - } - auto rawPath = deserializer.readArray<uint8_t>(pathSize); - path->readFromMemory(rawPath.data(), rawPath.size()); return true; } -void SkStrikeClient::primeStrikeCache(const SkStrikeCacheDifferenceSpec& strikeDifferences) { - fBuffer.clear(); - fBuffer.reserve(strikeDifferences.sizeBytes()); - - Serializer serializer{&fBuffer}; - write_strikes_spec(strikeDifferences, &serializer); +sk_sp<SkTypeface> SkStrikeClient::deserializeTypeface(const void* buf, size_t len) { + WireTypeface wire; + if (len != sizeof(wire)) return nullptr; - auto outBuffer = SkData::MakeWithoutCopy(fBuffer.data(), fBuffer.size()); - auto inbuffer = fClientRPC(*outBuffer); - Deserializer deserializer(*inbuffer); - update_caches_from_strikes_data(this, &deserializer); + memcpy(&wire, buf, sizeof(wire)); + return addTypeface(wire); } -void SkStrikeClient::prepareDeserializeProcs(SkDeserialProcs* procs) { - auto decode = [](const void* buf, size_t len, void* ctx) { - return reinterpret_cast<SkStrikeClient*>(ctx)->decodeTypeface(buf, len); - }; - procs->fTypefaceProc = decode; - procs->fTypefaceCtx = this; +sk_sp<SkTypeface> SkStrikeClient::addTypeface(const WireTypeface& wire) { + auto* typeface = fRemoteFontIdToTypeface.find(wire.typefaceID); + if (typeface) return *typeface; + auto newTypeface = sk_make_sp<SkTypefaceProxy>(wire.typefaceID, wire.glyphCount, wire.style, + wire.isFixed, this); + fRemoteFontIdToTypeface.set(wire.typefaceID, newTypeface); + return newTypeface; } -SkTypeface* SkStrikeClient::lookupTypeface(SkFontID id) { - auto typeface = fMapIdToTypeface.find(id); - SkASSERT(typeface != nullptr); - return typeface->get(); +void SkStrikeClient::generateFontMetrics(const SkTypefaceProxy& typefaceProxy, + const SkScalerContextRec& rec, + SkPaint::FontMetrics* metrics) { + TRACE_EVENT1("skia", "generateFontMetrics", "rec", TRACE_STR_COPY(rec.dump().c_str())); + SkDebugf("generateFontMetrics: %s\n", rec.dump().c_str()); + SkStrikeCache::Dump(); + SkDEBUGFAIL("GlyphCacheMiss"); } -sk_sp<SkTypeface> SkStrikeClient::decodeTypeface(const void* buf, size_t len) { - WireTypeface wire; - if (len < sizeof(wire)) { - SK_ABORT("Incomplete transfer"); - return nullptr; - } - memcpy(&wire, buf, sizeof(wire)); - - auto typeFace = fMapIdToTypeface.find(wire.typefaceID); - if (typeFace == nullptr) { - auto newTypeface = sk_make_sp<SkTypefaceProxy>( - wire.typefaceID, - wire.glyphCount, - wire.style, - wire.isFixed, - this); +void SkStrikeClient::generateMetricsAndImage(const SkTypefaceProxy& typefaceProxy, + const SkScalerContextRec& rec, + SkArenaAlloc* alloc, + SkGlyph* glyph) { + TRACE_EVENT1("skia", "generateMetricsAndImage", "rec", TRACE_STR_COPY(rec.dump().c_str())); + SkDebugf("generateMetricsAndImage: %s\n", rec.dump().c_str()); + SkStrikeCache::Dump(); + SkDEBUGFAIL("GlyphCacheMiss"); +} - typeFace = fMapIdToTypeface.set(wire.typefaceID, newTypeface); - } - return *typeFace; +void SkStrikeClient::generatePath(const SkTypefaceProxy& typefaceProxy, + const SkScalerContextRec& rec, + SkGlyphID glyphID, + SkPath* path) { + TRACE_EVENT1("skia", "generateMetricsAndImage", "rec", TRACE_STR_COPY(rec.dump().c_str())); + SkDebugf("generatePath: %s\n", rec.dump().c_str()); + SkStrikeCache::Dump(); + SkDEBUGFAIL("GlyphCacheMiss"); } diff --git a/src/core/SkRemoteGlyphCache.h b/src/core/SkRemoteGlyphCache.h index ff905862fa..b210f1c6d7 100644 --- a/src/core/SkRemoteGlyphCache.h +++ b/src/core/SkRemoteGlyphCache.h @@ -5,192 +5,196 @@ * found in the LICENSE file. */ -#ifndef SkRemoteGlyphCache_DEFINED -#define SkRemoteGlyphCache_DEFINED +#ifndef SkRemoteGlyphCachePriv_DEFINED +#define SkRemoteGlyphCachePriv_DEFINED #include <memory> #include <tuple> #include <unordered_map> +#include <unordered_set> #include <vector> +#include "../private/SkTHash.h" #include "SkData.h" -#include "SkDescriptor.h" #include "SkDrawLooper.h" -#include "SkGlyphCache.h" #include "SkMakeUnique.h" #include "SkNoDrawCanvas.h" #include "SkRefCnt.h" +#include "SkRemoteGlyphCache.h" #include "SkSerialProcs.h" -#include "SkStrikeCache.h" -#include "SkTextBlobRunIterator.h" -#include "SkTHash.h" #include "SkTypeface.h" -#include "SkTypeface_remote.h" -// The client uses a SkStrikeCacheClientRPC to send and receive data. -using SkStrikeCacheClientRPC = std::function<sk_sp<SkData>(const SkData&)>; +class Serializer; +class SkDescriptor; +class SkGlyphCache; +struct SkPackedGlyphID; +class SkScalerContextRecDescriptor; +class SkTextBlobRunIterator; +class SkTypefaceProxy; +struct WireTypeface; -class SkScalerContextRecDescriptor { -public: - SkScalerContextRecDescriptor() {} - explicit SkScalerContextRecDescriptor(const SkScalerContextRec& rec) { - auto desc = reinterpret_cast<SkDescriptor*>(&fDescriptor); - desc->init(); - desc->addEntry(kRec_SkDescriptorTag, sizeof(rec), &rec); - desc->computeChecksum(); - SkASSERT(sizeof(fDescriptor) == desc->getLength()); - } - - explicit SkScalerContextRecDescriptor(const SkDescriptor& desc) - : SkScalerContextRecDescriptor(ExtractRec(desc)) { } - - SkScalerContextRecDescriptor& operator=(const SkScalerContextRecDescriptor& rhs) { - std::memcpy(&fDescriptor, &rhs.fDescriptor, rhs.desc().getLength()); - return *this; - } - - const SkDescriptor& desc() const { - return *reinterpret_cast<const SkDescriptor*>(&fDescriptor); - } - - struct Hash { - uint32_t operator()(SkScalerContextRecDescriptor const& s) const { - return s.desc().getChecksum(); - } - }; - - friend bool operator==(const SkScalerContextRecDescriptor& lhs, - const SkScalerContextRecDescriptor& rhs ) { - return lhs.desc() == rhs.desc(); - } +class SkStrikeServer; -private: - static SkScalerContextRec ExtractRec(const SkDescriptor& desc) { - uint32_t size; - auto recPtr = desc.findEntry(kRec_SkDescriptorTag, &size); - - SkScalerContextRec result; - std::memcpy(&result, recPtr, size); - return result; - } - // The system only passes descriptors without effects. That is why it uses a fixed size - // descriptor. storageFor is needed because some of the constructors below are private. - template <typename T> - using storageFor = typename std::aligned_storage<sizeof(T), alignof(T)>::type; - struct { - storageFor<SkDescriptor> dummy1; - storageFor<SkDescriptor::Entry> dummy2; - storageFor<SkScalerContextRec> dummy3; - } fDescriptor; +struct SkDescriptorMapOperators { + size_t operator()(const SkDescriptor* key) const; + bool operator()(const SkDescriptor* lhs, const SkDescriptor* rhs) const; }; -class SkStrikeDifferences { -public: - SkStrikeDifferences(SkFontID typefaceID, std::unique_ptr<SkDescriptor> desc); - void add(uint16_t glyphID, SkIPoint pos); - SkFontID fTypefaceID; - std::unique_ptr<SkDescriptor> fDesc; - std::unique_ptr<SkTHashSet<SkPackedGlyphID>> fGlyphIDs = - skstd::make_unique<SkTHashSet<SkPackedGlyphID>>(); -}; +template <typename T> +using SkDescriptorMap = std::unordered_map<const SkDescriptor*, T, SkDescriptorMapOperators, + SkDescriptorMapOperators>; -class SkStrikeCacheDifferenceSpec { -public: - SkStrikeDifferences& findStrikeDifferences(const SkDescriptor& desc, SkFontID typefaceID); - int strikeCount() const { return fDescriptorToDifferencesMap.size(); } - size_t sizeBytes() const; - template <typename PerStrike, typename PerGlyph> - void iterateDifferences(PerStrike perStrike, PerGlyph perGlyph) const; +using SkDescriptorSet = + std::unordered_set<const SkDescriptor*, SkDescriptorMapOperators, SkDescriptorMapOperators>; -private: - SkDescriptorMap<SkStrikeDifferences> fDescriptorToDifferencesMap{16}; -}; - -class SkTextBlobCacheDiffCanvas : public SkNoDrawCanvas { +// A SkTextBlobCacheDiffCanvas is used to populate the SkStrikeServer with ops +// which will be serialized and renderered using the SkStrikeClient. +class SK_API SkTextBlobCacheDiffCanvas : public SkNoDrawCanvas { public: - SkTextBlobCacheDiffCanvas(int width, int height, - const SkMatrix& deviceMatrix, - const SkSurfaceProps& props, - SkScalerContextFlags flags, - SkStrikeCacheDifferenceSpec* strikeDiffs); + SkTextBlobCacheDiffCanvas(int width, int height, const SkMatrix& deviceMatrix, + const SkSurfaceProps& props, SkStrikeServer* strikeserver); + ~SkTextBlobCacheDiffCanvas() override; protected: - SaveLayerStrategy getSaveLayerStrategy(const SaveLayerRec& rec) override; + SkCanvas::SaveLayerStrategy getSaveLayerStrategy(const SaveLayerRec& rec) override; - void onDrawTextBlob( - const SkTextBlob* blob, SkScalar x, SkScalar y, const SkPaint& paint) override; + void onDrawTextBlob(const SkTextBlob* blob, SkScalar x, SkScalar y, + const SkPaint& paint) override; private: - void processLooper( - const SkPoint& position, - const SkTextBlobRunIterator& it, - const SkPaint& origPaint, - SkDrawLooper* looper); - - void processGlyphRun( - const SkPoint& position, - const SkTextBlobRunIterator& it, - const SkPaint& runPaint); + void processLooper(const SkPoint& position, + const SkTextBlobRunIterator& it, + const SkPaint& origPaint, + SkDrawLooper* looper); + void processGlyphRun(const SkPoint& position, + const SkTextBlobRunIterator& it, + const SkPaint& runPaint); const SkMatrix fDeviceMatrix; const SkSurfaceProps fSurfaceProps; - const SkScalerContextFlags fScalerContextFlags; - - SkStrikeCacheDifferenceSpec* const fStrikeCacheDiff; + SkStrikeServer* const fStrikeServer; }; -class SkStrikeServer { -public: - SkStrikeServer(); - ~SkStrikeServer(); +using SkDiscardableHandleId = uint32_t; - // embedding clients call these methods - void serve(const SkData&, std::vector<uint8_t>*); +// This class is not thread-safe. +class SK_API SkStrikeServer { +public: + // An interface used by the server to create handles for pinning SkGlyphCache + // entries on the remote client. + class SK_API DiscardableHandleManager { + public: + virtual ~DiscardableHandleManager() {} + + // Creates a new *locked* handle and returns a unique ID that can be used to identify + // it on the remote client. + virtual SkDiscardableHandleId createHandle() = 0; + + // Returns true if the handle could be successfully locked. The server can + // assume it will remain locked until the next set of serialized entries is + // pulled from the SkStrikeServer. + // If returns false, the cache entry mapped to the handle has been deleted + // on the client. Any subsequent attempts to lock the same handle are not + // allowed. + virtual bool lockHandle(SkDiscardableHandleId) = 0; + + // TODO(khushalsagar): Add an API which checks whether a handle is still + // valid without locking, so we can avoid tracking stale handles once they + // have been purged on the remote side. + }; - void prepareSerializeProcs(SkSerialProcs* procs); + SkStrikeServer(DiscardableHandleManager* discardableHandleManager); + ~SkStrikeServer(); - // mostly called internally by Skia - SkScalerContext* generateScalerContext( - const SkScalerContextRecDescriptor& desc, SkFontID typefaceId); + // Serializes the typeface to be remoted using this server. + sk_sp<SkData> serializeTypeface(SkTypeface*); + + // Serializes the strike data captured using a SkTextBlobCacheDiffCanvas. Any + // handles locked using the DiscardableHandleManager will be assumed to be + // unlocked after this call. + void writeStrikeData(std::vector<uint8_t>* memory); + + // Methods used internally in skia ------------------------------------------ + class SkGlyphCacheState { + public: + SkGlyphCacheState(std::unique_ptr<SkDescriptor> desc, + SkDiscardableHandleId discardableHandleId); + ~SkGlyphCacheState(); + + void addGlyph(SkTypeface*, const SkScalerContextEffects&, SkPackedGlyphID); + void writePendingGlyphs(Serializer* serializer); + bool has_pending_glyphs() const { return !fPendingGlyphs.empty(); } + SkDiscardableHandleId discardable_handle_id() const { return fDiscardableHandleId; } + + private: + // The set of glyphs cached on the remote client. + SkTHashSet<SkPackedGlyphID> fCachedGlyphs; + + // The set of glyphs which has not yet been serialized and sent to the + // remote client. + std::vector<SkPackedGlyphID> fPendingGlyphs; + + std::unique_ptr<SkDescriptor> fDesc; + const SkDiscardableHandleId fDiscardableHandleId = -1; + std::unique_ptr<SkScalerContext> fContext; + }; + SkGlyphCacheState* getOrCreateCache(SkTypeface*, std::unique_ptr<SkDescriptor>); private: - using DescriptorToContextMap = SkTHashMap<SkScalerContextRecDescriptor, - std::unique_ptr<SkScalerContext>, - SkScalerContextRecDescriptor::Hash>; - - sk_sp<SkData> encodeTypeface(SkTypeface* tf); + SkDescriptorMap<std::unique_ptr<SkGlyphCacheState>> fRemoteGlyphStateMap; + DiscardableHandleManager* const fDiscardableHandleManager; + SkTHashSet<SkFontID> fCachedTypefaces; - int fOpCount = 0; - SkTHashMap<SkFontID, sk_sp<SkTypeface>> fTypefaceMap; - DescriptorToContextMap fScalerContextMap; + // State cached until the next serialization. + SkDescriptorSet fLockedDescs; + std::vector<WireTypeface> fTypefacesToSend; }; -class SkStrikeClient { +class SK_API SkStrikeClient { public: - SkStrikeClient(SkStrikeCacheClientRPC); - - // embedding clients call these methods - void primeStrikeCache(const SkStrikeCacheDifferenceSpec&); - void prepareDeserializeProcs(SkDeserialProcs* procs); + // An interface to delete handles that may be pinned by the remote server. + class DiscardableHandleManager : public SkRefCnt { + public: + virtual ~DiscardableHandleManager() {} + + // Returns true if the handle was unlocked and can be safely deleted. Once + // successful, subsequent attempts to delete the same handle are invalid. + virtual bool deleteHandle(SkDiscardableHandleId) = 0; + }; - // mostly called internally by Skia - void generateFontMetrics( - const SkTypefaceProxy&, const SkScalerContextRec&, SkPaint::FontMetrics*); - void generateMetricsAndImage( - const SkTypefaceProxy&, const SkScalerContextRec&, SkArenaAlloc*, SkGlyph*); - bool generatePath( - const SkTypefaceProxy&, const SkScalerContextRec&, SkGlyphID glyph, SkPath* path); - SkTypeface* lookupTypeface(SkFontID id); + SkStrikeClient(sk_sp<DiscardableHandleManager>); + ~SkStrikeClient(); + + // Deserializes the typeface previously serialized using the SkStrikeServer. Returns null if the + // data is invalid. + sk_sp<SkTypeface> deserializeTypeface(const void* data, size_t length); + + // Deserializes the strike data from a SkStrikeServer. All messages generated + // from a server when serializing the ops must be deserialized before the op + // is rasterized. + // Returns false if the data is invalid. + bool readStrikeData(const volatile void* memory, size_t memorySize); + + // TODO: Remove these since we don't support pulling this data on-demand. + void generateFontMetrics(const SkTypefaceProxy& typefaceProxy, + const SkScalerContextRec& rec, + SkPaint::FontMetrics* metrics); + void generateMetricsAndImage(const SkTypefaceProxy& typefaceProxy, + const SkScalerContextRec& rec, + SkArenaAlloc* alloc, + SkGlyph* glyph); + void generatePath(const SkTypefaceProxy& typefaceProxy, + const SkScalerContextRec& rec, + SkGlyphID glyphID, + SkPath* path); private: - sk_sp<SkTypeface> decodeTypeface(const void* buf, size_t len); - - // TODO: Figure out how to manage the entries for the following maps. - SkTHashMap<SkFontID, sk_sp<SkTypefaceProxy>> fMapIdToTypeface; + class DiscardableStrikePinner; - SkStrikeCacheClientRPC fClientRPC; + sk_sp<SkTypeface> addTypeface(const WireTypeface& wire); - std::vector<uint8_t> fBuffer; + SkTHashMap<SkFontID, sk_sp<SkTypeface>> fRemoteFontIdToTypeface; + sk_sp<DiscardableHandleManager> fDiscardableHandleManager; }; -#endif // SkRemoteGlyphCache_DEFINED +#endif // SkRemoteGlyphCachePriv_DEFINED diff --git a/src/core/SkScalerContext.h b/src/core/SkScalerContext.h index 84abdcc1a4..675e9a0524 100644 --- a/src/core/SkScalerContext.h +++ b/src/core/SkScalerContext.h @@ -135,8 +135,8 @@ public: fPost2x2[0][1], fPost2x2[1][0], fPost2x2[1][1]); msg.appendf(" frame %g miter %g format %d join %d cap %d flags %#hx\n", fFrameWidth, fMiterLimit, fMaskFormat, fStrokeJoin, fStrokeCap, fFlags); - msg.appendf(" lum bits %x, device gamma %d, paint gamma %d contrast %d\n", - fLumBits, fDeviceGamma, fPaintGamma, fContrast); + msg.appendf(" lum bits %x, device gamma %d, paint gamma %d contrast %d\n", fLumBits, + fDeviceGamma, fPaintGamma, fContrast); return msg; } diff --git a/src/core/SkStrikeCache.h b/src/core/SkStrikeCache.h index 3e199da1e9..df048e33be 100644 --- a/src/core/SkStrikeCache.h +++ b/src/core/SkStrikeCache.h @@ -9,6 +9,7 @@ #define SkStrikeCache_DEFINED #include <unordered_map> +#include <unordered_set> #include "SkDescriptor.h" #include "SkSpinlock.h" @@ -31,24 +32,6 @@ class SkTraceMemoryDump; /////////////////////////////////////////////////////////////////////////////// -struct SkDescriptorMapOperators { - size_t operator()(const SkDescriptor* key) const { - return key->getChecksum(); - } - - bool operator()(const SkDescriptor* lhs, const SkDescriptor* rhs) const { - return *lhs == *rhs; - } -}; - -template <typename T> -using SkDescriptorMap = - std::unordered_map< - const SkDescriptor*, - T, - SkDescriptorMapOperators, - SkDescriptorMapOperators>; - class SkStrikePinner { public: virtual ~SkStrikePinner() = default; diff --git a/src/core/SkTypeface_remote.cpp b/src/core/SkTypeface_remote.cpp index bc0d04bc2a..3b58af9366 100644 --- a/src/core/SkTypeface_remote.cpp +++ b/src/core/SkTypeface_remote.cpp @@ -6,17 +6,15 @@ */ #include "SkTypeface_remote.h" +#include "SkRemoteGlyphCache.h" #include "SkPaint.h" -#include "SkRemoteGlyphCache.h" -SkScalerContextProxy::SkScalerContextProxy( - sk_sp<SkTypeface> tf, - const SkScalerContextEffects& effects, - const SkDescriptor* desc, - SkStrikeClient* rsc) - : SkScalerContext{std::move(tf), effects, desc} - , fClient{rsc} {} +SkScalerContextProxy::SkScalerContextProxy(sk_sp<SkTypeface> tf, + const SkScalerContextEffects& effects, + const SkDescriptor* desc, + SkStrikeClient* rsc) + : SkScalerContext{std::move(tf), effects, desc}, fClient{rsc} {} unsigned SkScalerContextProxy::generateGlyphCount() { SK_ABORT("Should never be called."); @@ -40,7 +38,8 @@ void SkScalerContextProxy::generateImage(const SkGlyph& glyph) { } bool SkScalerContextProxy::generatePath(SkGlyphID glyphID, SkPath* path) { - return fClient->generatePath(*this->typefaceProxy(), this->getRec(), glyphID, path); + fClient->generatePath(*this->typefaceProxy(), this->getRec(), glyphID, path); + return true; } void SkScalerContextProxy::generateFontMetrics(SkPaint::FontMetrics* metrics) { diff --git a/src/core/SkTypeface_remote.h b/src/core/SkTypeface_remote.h index 85a8fc5617..691b124049 100644 --- a/src/core/SkTypeface_remote.h +++ b/src/core/SkTypeface_remote.h @@ -21,11 +21,10 @@ class SkTypefaceProxy; class SkScalerContextProxy : public SkScalerContext { public: - SkScalerContextProxy( - sk_sp<SkTypeface> tf, - const SkScalerContextEffects& effects, - const SkDescriptor* desc, - SkStrikeClient* rsc); + SkScalerContextProxy(sk_sp<SkTypeface> tf, + const SkScalerContextEffects& effects, + const SkDescriptor* desc, + SkStrikeClient* rsc); protected: unsigned generateGlyphCount() override; @@ -52,16 +51,12 @@ private: class SkTypefaceProxy : public SkTypeface { public: - SkTypefaceProxy( - SkFontID fontId, - int glyphCount, - const SkFontStyle& style, - bool isFixed, - SkStrikeClient* rsc) - : INHERITED{style, false} - , fFontId{fontId} - , fGlyphCount{glyphCount} - , fRsc{rsc} { } + SkTypefaceProxy(SkFontID fontId, + int glyphCount, + const SkFontStyle& style, + bool isFixed, + SkStrikeClient* rsc) + : INHERITED{style, false}, fFontId{fontId}, fGlyphCount{glyphCount}, fRsc{rsc} {} SkFontID remoteTypefaceID() const {return fFontId;} int glyphCount() const {return fGlyphCount;} static SkTypefaceProxy* DownCast(SkTypeface* typeface) { @@ -138,6 +133,8 @@ protected: private: const SkFontID fFontId; const int fGlyphCount; + + // TODO: Does this need a ref to the strike client? If yes, make it a weak ref. SkStrikeClient* const fRsc; typedef SkTypeface INHERITED; diff --git a/tests/SkRemoteGlyphCacheTest.cpp b/tests/SkRemoteGlyphCacheTest.cpp new file mode 100644 index 0000000000..7369acec15 --- /dev/null +++ b/tests/SkRemoteGlyphCacheTest.cpp @@ -0,0 +1,220 @@ +/* + * 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 "SkGraphics.h" +#include "SkMutex.h" +#include "SkRemoteGlyphCache.h" +#include "SkStrikeCache.h" +#include "SkSurface.h" +#include "SkTextBlob.h" +#include "SkTypeface_remote.h" +#include "Test.h" + +class DiscardableManager : public SkStrikeServer::DiscardableHandleManager, + public SkStrikeClient::DiscardableHandleManager { +public: + DiscardableManager() = default; + ~DiscardableManager() override = default; + + // Server implementation. + SkDiscardableHandleId createHandle() override { + // Handles starts as locked. + fLockedHandles.add(++fNextHandleId); + return fNextHandleId; + } + bool lockHandle(SkDiscardableHandleId id) override { + if (id <= fLastDeletedHandleId) return false; + fLockedHandles.add(id); + return true; + } + + // Client implementation. + bool deleteHandle(SkDiscardableHandleId id) override { return id <= fLastDeletedHandleId; } + + void unlockAll() { fLockedHandles.reset(); } + void unlockAndDeleteAll() { + unlockAll(); + fLastDeletedHandleId = fNextHandleId; + } + const SkTHashSet<SkDiscardableHandleId>& lockedHandles() const { return fLockedHandles; } + SkDiscardableHandleId handleCount() { return fNextHandleId; } + +private: + SkDiscardableHandleId fNextHandleId = 0u; + SkDiscardableHandleId fLastDeletedHandleId = 0u; + SkTHashSet<SkDiscardableHandleId> fLockedHandles; +}; + +sk_sp<SkTextBlob> buildTextBlob(sk_sp<SkTypeface> tf, int glyphCount) { + SkPaint font; + font.setTypeface(tf); + font.setTextEncoding(SkPaint::kGlyphID_TextEncoding); + font.setTextAlign(SkPaint::kLeft_Align); + font.setStyle(SkPaint::kFill_Style); + font.setHinting(SkPaint::kNormal_Hinting); + font.setTextSize(1u); + + SkTextBlobBuilder builder; + SkRect bounds = SkRect::MakeWH(10, 10); + const auto& runBuffer = builder.allocRunPosH(font, glyphCount, 0, &bounds); + for (int i = 0; i < glyphCount; i++) runBuffer.glyphs[i] = static_cast<SkGlyphID>(i); + return builder.make(); +} + +SkBitmap RasterBlob(sk_sp<SkTextBlob> blob, int width, int height) { + auto surface = SkSurface::MakeRasterN32Premul(width, height); + SkPaint paint; + surface->getCanvas()->drawTextBlob(blob.get(), 0u, 0u, paint); + SkBitmap bitmap; + bitmap.allocN32Pixels(width, height); + surface->readPixels(bitmap, 0, 0); + return bitmap; +} + +DEF_TEST(SkRemoteGlyphCache_TypefaceSerialization, reporter) { + sk_sp<DiscardableManager> discardableManager = sk_make_sp<DiscardableManager>(); + SkStrikeServer server(discardableManager.get()); + SkStrikeClient client(discardableManager); + + auto server_tf = SkTypeface::MakeDefault(); + auto tf_data = server.serializeTypeface(server_tf.get()); + + auto client_tf = client.deserializeTypeface(tf_data->data(), tf_data->size()); + REPORTER_ASSERT(reporter, client_tf); + REPORTER_ASSERT(reporter, SkTypefaceProxy::DownCast(client_tf.get())->remoteTypefaceID() == + server_tf->uniqueID()); +} + +#if 0 +TODO(khushalsagar): Re-enable once crbug.com/831354 is fixed. +DEF_TEST(SkRemoteGlyphCache_StrikeSerialization, reporter) { + sk_sp<DiscardableManager> discardableManager = sk_make_sp<DiscardableManager>(); + SkStrikeServer server(discardableManager.get()); + SkStrikeClient client(discardableManager); + + // Server. + auto serverTf = SkTypeface::MakeFromName("monospace", SkFontStyle()); + auto serverTfData = server.serializeTypeface(serverTf.get()); + + int glyphCount = 10; + auto serverBlob = buildTextBlob(serverTf, glyphCount); + const SkSurfaceProps props(SkSurfaceProps::kLegacyFontHost_InitType); + SkTextBlobCacheDiffCanvas cache_diff_canvas(10, 10, SkMatrix::I(), props, &server); + SkPaint paint; + cache_diff_canvas.drawTextBlob(serverBlob.get(), 0, 0, paint); + + std::vector<uint8_t> serverStrikeData; + server.writeStrikeData(&serverStrikeData); + + // Client. + auto clientTf = client.deserializeTypeface(serverTfData->data(), serverTfData->size()); + REPORTER_ASSERT(reporter, + client.readStrikeData(serverStrikeData.data(), serverStrikeData.size())); + auto clientBlob = buildTextBlob(clientTf, glyphCount); + + SkBitmap expected = RasterBlob(serverBlob, 10, 10); + SkBitmap actual = RasterBlob(clientBlob, 10, 10); + for (int i = 0; i < expected.width(); ++i) { + for (int j = 0; j < expected.height(); ++j) { + REPORTER_ASSERT(reporter, expected.getColor(i, j) == actual.getColor(i, j)); + } + } +} +#endif + +DEF_TEST(SkRemoteGlyphCache_StrikeLockingServer, reporter) { + sk_sp<DiscardableManager> discardableManager = sk_make_sp<DiscardableManager>(); + SkStrikeServer server(discardableManager.get()); + SkStrikeClient client(discardableManager); + + auto serverTf = SkTypeface::MakeFromName("monospace", SkFontStyle()); + server.serializeTypeface(serverTf.get()); + int glyphCount = 10; + auto serverBlob = buildTextBlob(serverTf, glyphCount); + + const SkSurfaceProps props(SkSurfaceProps::kLegacyFontHost_InitType); + SkTextBlobCacheDiffCanvas cache_diff_canvas(10, 10, SkMatrix::I(), props, &server); + 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); +} + +DEF_TEST(SkRemoteGlyphCache_StrikeDeletionServer, reporter) { + sk_sp<DiscardableManager> discardableManager = sk_make_sp<DiscardableManager>(); + SkStrikeServer server(discardableManager.get()); + SkStrikeClient client(discardableManager); + + auto serverTf = SkTypeface::MakeFromName("monospace", SkFontStyle()); + server.serializeTypeface(serverTf.get()); + int glyphCount = 10; + auto serverBlob = buildTextBlob(serverTf, glyphCount); + + const SkSurfaceProps props(SkSurfaceProps::kLegacyFontHost_InitType); + SkTextBlobCacheDiffCanvas cache_diff_canvas(10, 10, SkMatrix::I(), props, &server); + 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); + discardableManager->unlockAndDeleteAll(); + cache_diff_canvas.drawTextBlob(serverBlob.get(), 0, 0, paint); + printf("HandleCount: %d\n ", discardableManager->handleCount()); + REPORTER_ASSERT(reporter, discardableManager->handleCount() == 2u); +} + +DEF_TEST(SkRemoteGlyphCache_StrikePinningClient, reporter) { + sk_sp<DiscardableManager> discardableManager = sk_make_sp<DiscardableManager>(); + SkStrikeServer server(discardableManager.get()); + SkStrikeClient client(discardableManager); + + // Server. + auto serverTf = SkTypeface::MakeFromName("monospace", SkFontStyle()); + auto serverTfData = server.serializeTypeface(serverTf.get()); + + int glyphCount = 10; + auto serverBlob = buildTextBlob(serverTf, glyphCount); + + const SkSurfaceProps props(SkSurfaceProps::kLegacyFontHost_InitType); + SkTextBlobCacheDiffCanvas cache_diff_canvas(10, 10, SkMatrix::I(), props, &server); + 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* clientTf = client.deserializeTypeface(serverTfData->data(), serverTfData->size()).get(); + + // The cache remains alive until it is pinned in the discardable manager. + SkGraphics::PurgeFontCache(); + REPORTER_ASSERT(reporter, !clientTf->unique()); + + // Once the strike is unpinned and purged, SkStrikeClient should be the only owner of the + // clientTf. + discardableManager->unlockAndDeleteAll(); + SkGraphics::PurgeFontCache(); + REPORTER_ASSERT(reporter, clientTf->unique()); +} diff --git a/tools/remote_demo.cpp b/tools/remote_demo.cpp index 8fc96cadb8..105a9b3ea7 100644 --- a/tools/remote_demo.cpp +++ b/tools/remote_demo.cpp @@ -16,8 +16,9 @@ #include <thread> #include <unistd.h> -#include "SkRemoteGlyphCache.h" #include "SkGraphics.h" +#include "SkRemoteGlyphCache.h" +#include "SkScalerContext.h" #include "SkSurface.h" static std::string gSkpName; @@ -25,6 +26,46 @@ static bool gUseGpu = true; static bool gPurgeFontCaches = true; static bool gUseProcess = true; +class ServerDiscardableManager : public SkStrikeServer::DiscardableHandleManager { +public: + ServerDiscardableManager() = default; + ~ServerDiscardableManager() override = default; + + SkDiscardableHandleId createHandle() override { return ++nextHandleId; } + bool lockHandle(SkDiscardableHandleId handleId) override { + return handleId > lastPurgedHandleId; + } + void purgeAll() { lastPurgedHandleId = nextHandleId; } + +private: + SkDiscardableHandleId nextHandleId = 0u; + SkDiscardableHandleId lastPurgedHandleId = 0u; +}; + +class ClientDiscardableManager : public SkStrikeClient::DiscardableHandleManager { +public: + class ScopedPurgeCache { + public: + ScopedPurgeCache(ClientDiscardableManager* manager) : fManager(manager) { + if (fManager) fManager->allowPurging = true; + } + ~ScopedPurgeCache() { + if (fManager) fManager->allowPurging = false; + } + + private: + ClientDiscardableManager* fManager; + }; + + ClientDiscardableManager() = default; + ~ClientDiscardableManager() override = default; + + bool deleteHandle(SkDiscardableHandleId) override { return allowPurging; } + +private: + bool allowPurging = false; +}; + static bool write_SkData(int fd, const SkData& data) { size_t size = data.size(); ssize_t bytesWritten = ::write(fd, &size, sizeof(size)); @@ -43,7 +84,6 @@ static bool write_SkData(int fd, const SkData& data) { } static sk_sp<SkData> read_SkData(int fd) { - size_t size; ssize_t readSize = ::read(fd, &size, sizeof(size)); if (readSize <= 0) { @@ -92,44 +132,56 @@ private: std::chrono::duration<double> fElapsedSeconds{0.0}; }; -static void build_prime_cache_spec(const SkIRect &bounds, - const SkSurfaceProps &props, - const SkPicture &pic, - SkStrikeCacheDifferenceSpec *strikeDifference) { +static bool push_font_data(const SkPicture& pic, SkStrikeServer* strikeServer, int writeFd) { SkMatrix deviceMatrix = SkMatrix::I(); - - SkTextBlobCacheDiffCanvas filter( - bounds.width(), bounds.height(), deviceMatrix, props, - SkScalerContextFlags::kFakeGammaAndBoostContrast, - strikeDifference); - + const SkIRect bounds = pic.cullRect().round(); + const SkSurfaceProps props(SkSurfaceProps::kLegacyFontHost_InitType); + SkTextBlobCacheDiffCanvas filter(bounds.width(), bounds.height(), deviceMatrix, props, + strikeServer); pic.playback(&filter); + + std::vector<uint8_t> fontData; + strikeServer->writeStrikeData(&fontData); + auto data = SkData::MakeWithoutCopy(fontData.data(), fontData.size()); + return write_SkData(writeFd, *data); } -static void final_draw(std::string outFilename, - SkDeserialProcs* procs, - SkData* picData, - SkStrikeClient* client) { +static void final_draw(std::string outFilename, SkData* picData, SkStrikeClient* client, + ClientDiscardableManager* discardableManager, int readFd, int writeFd) { + SkDeserialProcs procs; + auto decode = [](const void* data, size_t length, void* ctx) -> sk_sp<SkTypeface> { + return reinterpret_cast<SkStrikeClient*>(ctx)->deserializeTypeface(data, length); + }; + procs.fTypefaceProc = decode; + procs.fTypefaceCtx = client; - auto pic = SkPicture::MakeFromData(picData, procs); + auto pic = SkPicture::MakeFromData(picData, &procs); auto cullRect = pic->cullRect(); auto r = cullRect.round(); auto s = SkSurface::MakeRasterN32Premul(r.width(), r.height()); auto c = s->getCanvas(); - auto picUnderTest = SkPicture::MakeFromData(picData, procs); + auto picUnderTest = SkPicture::MakeFromData(picData, &procs); Timer drawTime; + auto randomData = SkData::MakeUninitialized(1u); for (int i = 0; i < 100; i++) { if (gPurgeFontCaches) { + ClientDiscardableManager::ScopedPurgeCache purge(discardableManager); SkGraphics::PurgeFontCache(); + SkASSERT(SkGraphics::GetFontCacheUsed() == 0u); } + drawTime.start(); if (client != nullptr) { - SkStrikeCacheDifferenceSpec strikeDifference; - build_prime_cache_spec(r, s->props(), *picUnderTest, &strikeDifference); - client->primeStrikeCache(strikeDifference); + // Kick the renderer to send us the fonts. + write_SkData(writeFd, *randomData); + auto fontData = read_SkData(readFd); + if (fontData && !fontData->isEmpty()) { + if (!client->readStrikeData(fontData->data(), fontData->size())) + SK_ABORT("Bad serialization"); + } } c->drawPicture(picUnderTest); drawTime.stop(); @@ -150,22 +202,16 @@ static void final_draw(std::string outFilename, static void gpu(int readFd, int writeFd) { if (gUseGpu) { - auto clientRPC = [readFd, writeFd](const SkData& inBuffer) { - write_SkData(writeFd, inBuffer); - return read_SkData(readFd); - }; - auto picData = read_SkData(readFd); if (picData == nullptr) { return; } - SkStrikeClient client{clientRPC}; + sk_sp<ClientDiscardableManager> discardableManager = sk_make_sp<ClientDiscardableManager>(); + SkStrikeClient strikeClient(discardableManager); - SkDeserialProcs procs; - client.prepareDeserializeProcs(&procs); - - final_draw("test.png", &procs, picData.get(), &client); + final_draw("test.png", picData.get(), &strikeClient, discardableManager.get(), readFd, + writeFd); } ::close(writeFd); @@ -177,7 +223,8 @@ static void gpu(int readFd, int writeFd) { static int renderer( const std::string& skpName, int readFd, int writeFd) { - SkStrikeServer server{}; + ServerDiscardableManager discardableManager; + SkStrikeServer server(&discardableManager); auto closeAll = [readFd, writeFd]() { ::close(writeFd); ::close(readFd); @@ -186,11 +233,16 @@ static int renderer( auto skpData = SkData::MakeFromFileName(skpName.c_str()); std::cout << "skp stream is " << skpData->size() << " bytes long " << std::endl; - SkSerialProcs procs; sk_sp<SkData> stream; if (gUseGpu) { auto pic = SkPicture::MakeFromData(skpData.get()); - server.prepareSerializeProcs(&procs); + SkSerialProcs procs; + auto encode = [](SkTypeface* tf, void* ctx) -> sk_sp<SkData> { + return reinterpret_cast<SkStrikeServer*>(ctx)->serializeTypeface(tf); + }; + procs.fTypefaceProc = encode; + procs.fTypefaceCtx = &server; + stream = pic->serialize(&procs); if (!write_SkData(writeFd, *stream)) { @@ -198,22 +250,18 @@ static int renderer( return 1; } - std::vector<uint8_t> tmpBuffer; while (true) { auto inBuffer = read_SkData(readFd); if (inBuffer == nullptr) { closeAll(); return 0; } - - tmpBuffer.clear(); - server.serve(*inBuffer, &tmpBuffer); - auto outBuffer = SkData::MakeWithoutCopy(tmpBuffer.data(), tmpBuffer.size()); - write_SkData(writeFd, *outBuffer); + if (gPurgeFontCaches) discardableManager.purgeAll(); + push_font_data(*pic.get(), &server, writeFd); } } else { stream = skpData; - final_draw("test-correct.png", nullptr, stream.get(), nullptr); + final_draw("test-correct.png", stream.get(), nullptr, nullptr, -1, -1); closeAll(); return 0; } |