| /* | 
 |  * Copyright 2010 Google Inc. | 
 |  * | 
 |  * Use of this source code is governed by a BSD-style license that can be | 
 |  * found in the LICENSE file. | 
 |  */ | 
 |  | 
 | #ifndef SkStrikeCache_DEFINED | 
 | #define SkStrikeCache_DEFINED | 
 |  | 
 | #include <unordered_map> | 
 | #include <unordered_set> | 
 |  | 
 | #include "include/core/SkDrawable.h" | 
 | #include "include/private/SkSpinlock.h" | 
 | #include "include/private/SkTemplates.h" | 
 | #include "src/core/SkDescriptor.h" | 
 | #include "src/core/SkScalerCache.h" | 
 | #include "src/core/SkStrikeSpec.h" | 
 | #include "src/text/StrikeForGPU.h" | 
 |  | 
 | class SkTraceMemoryDump; | 
 | class SkStrikeCache; | 
 |  | 
 | #ifndef SK_DEFAULT_FONT_CACHE_COUNT_LIMIT | 
 |     #define SK_DEFAULT_FONT_CACHE_COUNT_LIMIT   2048 | 
 | #endif | 
 |  | 
 | #ifndef SK_DEFAULT_FONT_CACHE_LIMIT | 
 |     #define SK_DEFAULT_FONT_CACHE_LIMIT     (2 * 1024 * 1024) | 
 | #endif | 
 |  | 
 | /////////////////////////////////////////////////////////////////////////////// | 
 |  | 
 | class SkStrikePinner { | 
 | public: | 
 |     virtual ~SkStrikePinner() = default; | 
 |     virtual bool canDelete() = 0; | 
 |     virtual void assertValid() {} | 
 | }; | 
 |  | 
 | class SkStrike final : public SkRefCnt, public sktext::StrikeForGPU { | 
 | public: | 
 |     SkStrike(SkStrikeCache* strikeCache, | 
 |              const SkStrikeSpec& strikeSpec, | 
 |              std::unique_ptr<SkScalerContext> scaler, | 
 |              const SkFontMetrics* metrics, | 
 |              std::unique_ptr<SkStrikePinner> pinner) | 
 |         : fStrikeSpec(strikeSpec) | 
 |         , fStrikeCache{strikeCache} | 
 |         , fScalerCache{std::move(scaler), metrics} | 
 |         , fPinner{std::move(pinner)} {} | 
 |  | 
 |     SkGlyph* mergeGlyphAndImage(SkPackedGlyphID toID, const SkGlyph& from) { | 
 |         auto [glyph, increase] = fScalerCache.mergeGlyphAndImage(toID, from); | 
 |         this->updateDelta(increase); | 
 |         return glyph; | 
 |     } | 
 |  | 
 |     const SkPath* mergePath(SkGlyph* glyph, const SkPath* path, bool hairline) { | 
 |         auto [glyphPath, increase] = fScalerCache.mergePath(glyph, path, hairline); | 
 |         this->updateDelta(increase); | 
 |         return glyphPath; | 
 |     } | 
 |  | 
 |     const SkDrawable* mergeDrawable(SkGlyph* glyph, sk_sp<SkDrawable> drawable) { | 
 |         auto [glyphDrawable, increase] = fScalerCache.mergeDrawable(glyph, std::move(drawable)); | 
 |         this->updateDelta(increase); | 
 |         return glyphDrawable; | 
 |     } | 
 |  | 
 |     // [[deprecated]] | 
 |     SkScalerContext* getScalerContext() const { | 
 |         return fScalerCache.getScalerContext(); | 
 |     } | 
 |  | 
 |     void findIntercepts(const SkScalar bounds[2], SkScalar scale, SkScalar xPos, | 
 |                         SkGlyph* glyph, SkScalar* array, int* count) { | 
 |         fScalerCache.findIntercepts(bounds, scale, xPos, glyph, array, count); | 
 |     } | 
 |  | 
 |     const SkFontMetrics& getFontMetrics() const { | 
 |         return fScalerCache.getFontMetrics(); | 
 |     } | 
 |  | 
 |     SkSpan<const SkGlyph*> metrics(SkSpan<const SkGlyphID> glyphIDs, | 
 |                                    const SkGlyph* results[]) { | 
 |         auto [glyphs, increase] = fScalerCache.metrics(glyphIDs, results); | 
 |         this->updateDelta(increase); | 
 |         return glyphs; | 
 |     } | 
 |  | 
 |     SkSpan<const SkGlyph*> preparePaths(SkSpan<const SkGlyphID> glyphIDs, | 
 |                                         const SkGlyph* results[]) { | 
 |         auto [glyphs, increase] = fScalerCache.preparePaths(glyphIDs, results); | 
 |         this->updateDelta(increase); | 
 |         return glyphs; | 
 |     } | 
 |  | 
 |     SkSpan<const SkGlyph*> prepareImages(SkSpan<const SkPackedGlyphID> glyphIDs, | 
 |                                          const SkGlyph* results[]) { | 
 |         auto [glyphs, increase] = fScalerCache.prepareImages(glyphIDs, results); | 
 |         this->updateDelta(increase); | 
 |         return glyphs; | 
 |     } | 
 |  | 
 |     SkSpan<const SkGlyph*> prepareDrawables(SkSpan<const SkGlyphID> glyphIDs, | 
 |                                             const SkGlyph* results[]) { | 
 |         auto [glyphs, increase] = fScalerCache.prepareDrawables(glyphIDs, results); | 
 |         this->updateDelta(increase); | 
 |         return glyphs; | 
 |     } | 
 |  | 
 |     void prepareForDrawingMasksCPU(SkDrawableGlyphBuffer* accepted) { | 
 |         size_t increase = fScalerCache.prepareForDrawingMasksCPU(accepted); | 
 |         this->updateDelta(increase); | 
 |     } | 
 |  | 
 |     const SkGlyphPositionRoundingSpec& roundingSpec() const override { | 
 |         return fScalerCache.roundingSpec(); | 
 |     } | 
 |  | 
 |     const SkDescriptor& getDescriptor() const override { | 
 |         return fStrikeSpec.descriptor(); | 
 |     } | 
 |  | 
 |     const SkStrikeSpec& strikeSpec() const { | 
 |         return fStrikeSpec; | 
 |     } | 
 |  | 
 |     void verifyPinnedStrike() const { | 
 |         if (fPinner != nullptr) { | 
 |             fPinner->assertValid(); | 
 |         } | 
 |     } | 
 |  | 
 | #if SK_SUPPORT_GPU | 
 |     sk_sp<sktext::gpu::TextStrike> findOrCreateTextStrike( | 
 |             sktext::gpu::StrikeCache* gpuStrikeCache) const; | 
 | #endif | 
 |  | 
 |     SkRect prepareForMaskDrawing(SkDrawableGlyphBuffer* accepted, | 
 |                                  SkSourceGlyphBuffer* rejected) override { | 
 |         auto [rect, increase] = fScalerCache.prepareForMaskDrawing(accepted, rejected); | 
 |         this->updateDelta(increase); | 
 |         return rect; | 
 |     } | 
 |  | 
 |     SkRect prepareForSDFTDrawing(SkScalar strikeToSourceScale, | 
 |                                  SkDrawableGlyphBuffer* accepted, | 
 |                                  SkSourceGlyphBuffer* rejected) override { | 
 |         auto [rect, increase] = fScalerCache.prepareForSDFTDrawing( | 
 |                 strikeToSourceScale, accepted, rejected); | 
 |         this->updateDelta(increase); | 
 |         return rect; | 
 |     } | 
 |  | 
 |     void prepareForPathDrawing( | 
 |             SkDrawableGlyphBuffer* accepted, SkSourceGlyphBuffer* rejected) override { | 
 |         size_t increase = fScalerCache.prepareForPathDrawing(accepted, rejected); | 
 |         this->updateDelta(increase); | 
 |     } | 
 |  | 
 |     void glyphIDsToPaths(SkSpan<sktext::IDOrPath> idsOrPaths) { | 
 |         size_t increase = fScalerCache.glyphIDsToPaths(idsOrPaths); | 
 |         this->updateDelta(increase); | 
 |     } | 
 |  | 
 |     void prepareForDrawableDrawing( | 
 |             SkDrawableGlyphBuffer* accepted, SkSourceGlyphBuffer* rejected) override { | 
 |         size_t increase = fScalerCache.prepareForDrawableDrawing(accepted, rejected); | 
 |         this->updateDelta(increase); | 
 |     } | 
 |  | 
 |     void glyphIDsToDrawables(SkSpan<sktext::IDOrDrawable> idsOrDrawables) { | 
 |         size_t increase = fScalerCache.glyphIDsToDrawables(idsOrDrawables); | 
 |         this->updateDelta(increase); | 
 |     } | 
 |  | 
 |     sktext::SkStrikePromise strikePromise() override { | 
 |         return sktext::SkStrikePromise(sk_ref_sp<SkStrike>(this)); | 
 |     } | 
 |  | 
 |     SkScalar findMaximumGlyphDimension(SkSpan<const SkGlyphID> glyphs) override { | 
 |         auto [maxDimension, increase] = fScalerCache.findMaximumGlyphDimension(glyphs); | 
 |         this->updateDelta(increase); | 
 |         return maxDimension; | 
 |     } | 
 |  | 
 |     void onAboutToExitScope() override { | 
 |         this->unref(); | 
 |     } | 
 |  | 
 |     sk_sp<SkStrike> getUnderlyingStrike() const override { | 
 |         return sk_ref_sp(this); | 
 |     } | 
 |  | 
 |     void updateDelta(size_t increase); | 
 |  | 
 |     const SkStrikeSpec              fStrikeSpec; | 
 |     SkStrikeCache* const            fStrikeCache; | 
 |     SkStrike*                       fNext{nullptr}; | 
 |     SkStrike*                       fPrev{nullptr}; | 
 |     SkScalerCache                   fScalerCache; | 
 |     std::unique_ptr<SkStrikePinner> fPinner; | 
 |     size_t                          fMemoryUsed{sizeof(SkScalerCache)}; | 
 |     bool                            fRemoved{false}; | 
 | };  // SkStrike | 
 |  | 
 | class SkStrikeCache final : public sktext::StrikeForGPUCacheInterface { | 
 | public: | 
 |     SkStrikeCache() = default; | 
 |  | 
 |     static SkStrikeCache* GlobalStrikeCache(); | 
 |  | 
 |     sk_sp<SkStrike> findStrike(const SkDescriptor& desc) SK_EXCLUDES(fLock); | 
 |  | 
 |     sk_sp<SkStrike> createStrike( | 
 |             const SkStrikeSpec& strikeSpec, | 
 |             SkFontMetrics* maybeMetrics = nullptr, | 
 |             std::unique_ptr<SkStrikePinner> = nullptr) SK_EXCLUDES(fLock); | 
 |  | 
 |     sk_sp<SkStrike> findOrCreateStrike(const SkStrikeSpec& strikeSpec) SK_EXCLUDES(fLock); | 
 |  | 
 |     sktext::ScopedStrikeForGPU findOrCreateScopedStrike( | 
 |             const SkStrikeSpec& strikeSpec) override SK_EXCLUDES(fLock); | 
 |  | 
 |     static void PurgeAll(); | 
 |     static void Dump(); | 
 |  | 
 |     // Dump memory usage statistics of all the attaches caches in the process using the | 
 |     // SkTraceMemoryDump interface. | 
 |     static void DumpMemoryStatistics(SkTraceMemoryDump* dump); | 
 |  | 
 |     void purgeAll() SK_EXCLUDES(fLock); // does not change budget | 
 |  | 
 |     int getCacheCountLimit() const SK_EXCLUDES(fLock); | 
 |     int setCacheCountLimit(int limit) SK_EXCLUDES(fLock); | 
 |     int getCacheCountUsed() const SK_EXCLUDES(fLock); | 
 |  | 
 |     size_t getCacheSizeLimit() const SK_EXCLUDES(fLock); | 
 |     size_t setCacheSizeLimit(size_t limit) SK_EXCLUDES(fLock); | 
 |     size_t getTotalMemoryUsed() const SK_EXCLUDES(fLock); | 
 |  | 
 | private: | 
 |     friend class SkStrike;  // for SkStrike::updateDelta | 
 |     sk_sp<SkStrike> internalFindStrikeOrNull(const SkDescriptor& desc) SK_REQUIRES(fLock); | 
 |     sk_sp<SkStrike> internalCreateStrike( | 
 |             const SkStrikeSpec& strikeSpec, | 
 |             SkFontMetrics* maybeMetrics = nullptr, | 
 |             std::unique_ptr<SkStrikePinner> = nullptr) SK_REQUIRES(fLock); | 
 |  | 
 |     // The following methods can only be called when mutex is already held. | 
 |     void internalRemoveStrike(SkStrike* strike) SK_REQUIRES(fLock); | 
 |     void internalAttachToHead(sk_sp<SkStrike> strike) SK_REQUIRES(fLock); | 
 |  | 
 |     // Checkout budgets, modulated by the specified min-bytes-needed-to-purge, | 
 |     // and attempt to purge caches to match. | 
 |     // Returns number of bytes freed. | 
 |     size_t internalPurge(size_t minBytesNeeded = 0) SK_REQUIRES(fLock); | 
 |  | 
 |     // A simple accounting of what each glyph cache reports and the strike cache total. | 
 |     void validate() const SK_REQUIRES(fLock); | 
 |  | 
 |     void forEachStrike(std::function<void(const SkStrike&)> visitor) const SK_EXCLUDES(fLock); | 
 |  | 
 |     mutable SkMutex fLock; | 
 |     SkStrike* fHead SK_GUARDED_BY(fLock) {nullptr}; | 
 |     SkStrike* fTail SK_GUARDED_BY(fLock) {nullptr}; | 
 |     struct StrikeTraits { | 
 |         static const SkDescriptor& GetKey(const sk_sp<SkStrike>& strike) { | 
 |             return strike->getDescriptor(); | 
 |         } | 
 |         static uint32_t Hash(const SkDescriptor& descriptor) { | 
 |             return descriptor.getChecksum(); | 
 |         } | 
 |     }; | 
 |     SkTHashTable<sk_sp<SkStrike>, SkDescriptor, StrikeTraits> fStrikeLookup SK_GUARDED_BY(fLock); | 
 |  | 
 |     size_t  fCacheSizeLimit{SK_DEFAULT_FONT_CACHE_LIMIT}; | 
 |     size_t  fTotalMemoryUsed SK_GUARDED_BY(fLock) {0}; | 
 |     int32_t fCacheCountLimit{SK_DEFAULT_FONT_CACHE_COUNT_LIMIT}; | 
 |     int32_t fCacheCount SK_GUARDED_BY(fLock) {0}; | 
 | }; | 
 |  | 
 | #endif  // SkStrikeCache_DEFINED |