blob: af35d5df185ee3cfa21abde53bb6688c9beb1c15 [file] [log] [blame]
Julia Lavrova5207f352019-06-21 12:22:32 -04001// Copyright 2019 Google LLC.
Julia Lavrovab7b0b3a2019-07-30 13:32:08 -04002#include "modules/skparagraph/include/ParagraphCache.h"
Mike Klein52337de2019-07-25 09:00:52 -05003#include "modules/skparagraph/src/ParagraphImpl.h"
Julia Lavrova5207f352019-06-21 12:22:32 -04004
5namespace skia {
6namespace textlayout {
7
Julia Lavrovab7b0b3a2019-07-30 13:32:08 -04008class ParagraphCacheKey {
9public:
10 ParagraphCacheKey(const ParagraphImpl* paragraph)
11 : fText(paragraph->fText.c_str(), paragraph->fText.size())
Julia Lavrova4cf18742020-01-14 13:24:45 -050012 , fPlaceholders(paragraph->fPlaceholders)
Julia Lavrova5207f352019-06-21 12:22:32 -040013 , fTextStyles(paragraph->fTextStyles)
14 , fParagraphStyle(paragraph->paragraphStyle()) { }
15
Julia Lavrovab7b0b3a2019-07-30 13:32:08 -040016 SkString fText;
Julia Lavrova4cf18742020-01-14 13:24:45 -050017 SkTArray<Placeholder, true> fPlaceholders;
Julia Lavrovab7b0b3a2019-07-30 13:32:08 -040018 SkTArray<Block, true> fTextStyles;
19 ParagraphStyle fParagraphStyle;
20};
21
22class ParagraphCacheValue {
23public:
24 ParagraphCacheValue(const ParagraphImpl* paragraph)
Julia Lavrova5207f352019-06-21 12:22:32 -040025 : fKey(ParagraphCacheKey(paragraph))
Julia Lavrova3281b962019-12-02 11:32:25 -050026 , fInternalState(paragraph->fState)
Julia Lavrova5207f352019-06-21 12:22:32 -040027 , fRuns(paragraph->fRuns)
Julia Lavrova3281b962019-12-02 11:32:25 -050028 , fClusters(paragraph->fClusters)
29 , fUnresolvedGlyphs(paragraph->fUnresolvedGlyphs){ }
Julia Lavrova5207f352019-06-21 12:22:32 -040030
Julia Lavrovab7b0b3a2019-07-30 13:32:08 -040031 // Input == key
32 ParagraphCacheKey fKey;
Julia Lavrova5207f352019-06-21 12:22:32 -040033
Julia Lavrovab7b0b3a2019-07-30 13:32:08 -040034 // Shaped results:
35 InternalState fInternalState;
Julia Lavrova2e30fde2019-10-09 09:43:02 -040036 SkTArray<Run, false> fRuns;
Julia Lavrovab7b0b3a2019-07-30 13:32:08 -040037 SkTArray<Cluster, true> fClusters;
Julia Lavrova3281b962019-12-02 11:32:25 -050038 size_t fUnresolvedGlyphs;
Julia Lavrovab7b0b3a2019-07-30 13:32:08 -040039};
Julia Lavrova5207f352019-06-21 12:22:32 -040040
Julia Lavrova5207f352019-06-21 12:22:32 -040041
Julia Lavrovab7b0b3a2019-07-30 13:32:08 -040042uint32_t ParagraphCache::KeyHash::mix(uint32_t hash, uint32_t data) const {
43 hash += data;
44 hash += (hash << 10);
45 hash ^= (hash >> 6);
46 return hash;
Julia Lavrova5207f352019-06-21 12:22:32 -040047}
Julia Lavrovab7b0b3a2019-07-30 13:32:08 -040048uint32_t ParagraphCache::KeyHash::operator()(const ParagraphCacheKey& key) const {
49 uint32_t hash = 0;
Julia Lavrova4cf18742020-01-14 13:24:45 -050050 for (auto& ph : key.fPlaceholders) {
51 hash = mix(hash, SkGoodHash()(ph.fRange.start));
52 hash = mix(hash, SkGoodHash()(ph.fRange.end));
53 hash = mix(hash, SkGoodHash()(ph.fStyle.fBaselineOffset));
54 hash = mix(hash, SkGoodHash()(ph.fStyle.fBaseline));
55 hash = mix(hash, SkGoodHash()(ph.fStyle.fAlignment));
56 hash = mix(hash, SkGoodHash()(ph.fStyle.fHeight));
57 hash = mix(hash, SkGoodHash()(ph.fStyle.fWidth));
Julia Lavrova5207f352019-06-21 12:22:32 -040058 }
Julia Lavrovab7b0b3a2019-07-30 13:32:08 -040059 for (auto& ts : key.fTextStyles) {
Julia Lavrova4cf18742020-01-14 13:24:45 -050060 if (ts.fStyle.isPlaceholder()) {
61 continue;
Julia Lavrova916a9042019-08-08 16:51:27 -040062 }
Julia Lavrova4cf18742020-01-14 13:24:45 -050063 hash = mix(hash, SkGoodHash()(ts.fStyle.getLetterSpacing()));
64 hash = mix(hash, SkGoodHash()(ts.fStyle.getWordSpacing()));
65 hash = mix(hash, SkGoodHash()(ts.fRange));
66 for (auto& ff : ts.fStyle.getFontFamilies()) {
67 hash = mix(hash, SkGoodHash()(ff));
68 }
69 hash = mix(hash, SkGoodHash()(ts.fStyle.getFontStyle()));
70 hash = mix(hash, SkGoodHash()(ts.fStyle.getFontSize()));
71 hash = mix(hash, SkGoodHash()(ts.fRange.start));
72 hash = mix(hash, SkGoodHash()(ts.fRange.end));
Julia Lavrovab7b0b3a2019-07-30 13:32:08 -040073 }
74 hash = mix(hash, SkGoodHash()(key.fText));
75 return hash;
Julia Lavrova5207f352019-06-21 12:22:32 -040076}
77
78bool operator==(const ParagraphCacheKey& a, const ParagraphCacheKey& b) {
79 if (a.fText.size() != b.fText.size()) {
80 return false;
81 }
Julia Lavrova4cf18742020-01-14 13:24:45 -050082 if (a.fPlaceholders.count() != b.fPlaceholders.count()) {
Julia Lavrova5207f352019-06-21 12:22:32 -040083 return false;
84 }
85 if (a.fText != b.fText) {
86 return false;
87 }
88 if (a.fTextStyles.size() != b.fTextStyles.size()) {
89 return false;
90 }
91
92 if (a.fParagraphStyle.getMaxLines() != b.fParagraphStyle.getMaxLines()) {
Julia Lavrovab7b0b3a2019-07-30 13:32:08 -040093 // This is too strong, but at least we will not lose lines
Julia Lavrova5207f352019-06-21 12:22:32 -040094 return false;
95 }
Julia Lavrova5207f352019-06-21 12:22:32 -040096 for (size_t i = 0; i < a.fTextStyles.size(); ++i) {
97 auto& tsa = a.fTextStyles[i];
98 auto& tsb = b.fTextStyles[i];
Julia Lavrova4cf18742020-01-14 13:24:45 -050099 if (tsa.fStyle.isPlaceholder()) {
100 continue;
101 }
102 if (!(tsa.fStyle.equalsByFonts(tsb.fStyle))) {
Julia Lavrova5207f352019-06-21 12:22:32 -0400103 return false;
104 }
Julia Lavrova4cf18742020-01-14 13:24:45 -0500105 if (tsa.fRange.width() != tsb.fRange.width()) {
106 return false;
107 }
108 if (tsa.fRange.start != tsb.fRange.start) {
109 return false;
110 }
111 }
112 for (size_t i = 0; i < a.fPlaceholders.size(); ++i) {
113 auto& tsa = a.fPlaceholders[i];
114 auto& tsb = b.fPlaceholders[i];
115 if (!(tsa.fStyle.equals(tsb.fStyle))) {
116 return false;
117 }
118 if (tsa.fRange.width() != tsb.fRange.width()) {
119 return false;
120 }
121 if (tsa.fRange.start != tsb.fRange.start) {
122 return false;
Julia Lavrova5207f352019-06-21 12:22:32 -0400123 }
124 }
125
126 return true;
127}
128
Julia Lavrovab7b0b3a2019-07-30 13:32:08 -0400129struct ParagraphCache::Entry {
Julia Lavrova5207f352019-06-21 12:22:32 -0400130
Julia Lavrovab7b0b3a2019-07-30 13:32:08 -0400131 Entry(ParagraphCacheValue* value) : fValue(value) {}
132 ParagraphCacheValue* fValue;
133};
Julia Lavrova5207f352019-06-21 12:22:32 -0400134
Julia Lavrovab7b0b3a2019-07-30 13:32:08 -0400135ParagraphCache::ParagraphCache()
136 : fChecker([](ParagraphImpl* impl, const char*, bool){ })
137 , fLRUCacheMap(kMaxEntries)
138 , fCacheIsOn(true)
139#ifdef PARAGRAPH_CACHE_STATS
140 , fTotalRequests(0)
141 , fCacheMisses(0)
142 , fHashMisses(0)
143#endif
144{ }
145
146ParagraphCache::~ParagraphCache() { }
147
148void ParagraphCache::updateFrom(const ParagraphImpl* paragraph, Entry* entry) {
149
150 entry->fValue->fInternalState = paragraph->state();
Julia Lavrovab7b0b3a2019-07-30 13:32:08 -0400151 for (size_t i = 0; i < paragraph->fRuns.size(); ++i) {
152 auto& run = paragraph->fRuns[i];
153 if (run.fSpaced) {
154 entry->fValue->fRuns[i] = run;
Julia Lavrova5207f352019-06-21 12:22:32 -0400155 }
156 }
Julia Lavrova5207f352019-06-21 12:22:32 -0400157}
158
Julia Lavrovab7b0b3a2019-07-30 13:32:08 -0400159void ParagraphCache::updateTo(ParagraphImpl* paragraph, const Entry* entry) {
160 paragraph->fRuns.reset();
161 paragraph->fRuns = entry->fValue->fRuns;
162 for (auto& run : paragraph->fRuns) {
163 run.setMaster(paragraph);
164 }
Julia Lavrova5207f352019-06-21 12:22:32 -0400165
Julia Lavrovab7b0b3a2019-07-30 13:32:08 -0400166 paragraph->fClusters.reset();
167 paragraph->fClusters = entry->fValue->fClusters;
168 for (auto& cluster : paragraph->fClusters) {
169 cluster.setMaster(paragraph);
170 }
171
Julia Lavrovab7b0b3a2019-07-30 13:32:08 -0400172 paragraph->fState = entry->fValue->fInternalState;
Julia Lavrova3281b962019-12-02 11:32:25 -0500173 paragraph->fUnresolvedGlyphs = entry->fValue->fUnresolvedGlyphs;
Julia Lavrovab7b0b3a2019-07-30 13:32:08 -0400174}
175
176void ParagraphCache::printStatistics() {
177 SkDebugf("--- Paragraph Cache ---\n");
178 SkDebugf("Total requests: %d\n", fTotalRequests);
179 SkDebugf("Cache misses: %d\n", fCacheMisses);
180 SkDebugf("Cache miss %%: %f\n", (fTotalRequests > 0) ? 100.f * fCacheMisses / fTotalRequests : 0.f);
181 int cacheHits = fTotalRequests - fCacheMisses;
182 SkDebugf("Hash miss %%: %f\n", (cacheHits > 0) ? 100.f * fHashMisses / cacheHits : 0.f);
183 SkDebugf("---------------------\n");
184}
185
186void ParagraphCache::abandon() {
187 SkAutoMutexExclusive lock(fParagraphMutex);
188 fLRUCacheMap.foreach([](std::unique_ptr<Entry>* e) {
189 });
190
191 this->reset();
192}
193
194void ParagraphCache::reset() {
195 SkAutoMutexExclusive lock(fParagraphMutex);
196#ifdef PARAGRAPH_CACHE_STATS
197 fTotalRequests = 0;
198 fCacheMisses = 0;
199 fHashMisses = 0;
200#endif
201 fLRUCacheMap.reset();
202}
203
204bool ParagraphCache::findParagraph(ParagraphImpl* paragraph) {
205 if (!fCacheIsOn) {
206 return false;
207 }
208#ifdef PARAGRAPH_CACHE_STATS
209 ++fTotalRequests;
210#endif
211 SkAutoMutexExclusive lock(fParagraphMutex);
212 ParagraphCacheKey key(paragraph);
213 std::unique_ptr<Entry>* entry = fLRUCacheMap.find(key);
214 if (!entry) {
215 // We have a cache miss
216#ifdef PARAGRAPH_CACHE_STATS
217 ++fCacheMisses;
218#endif
219 fChecker(paragraph, "missingParagraph", true);
220 return false;
221 }
222 updateTo(paragraph, entry->get());
223 fChecker(paragraph, "foundParagraph", true);
224 return true;
225}
226
227bool ParagraphCache::updateParagraph(ParagraphImpl* paragraph) {
228 if (!fCacheIsOn) {
229 return false;
230 }
231#ifdef PARAGRAPH_CACHE_STATS
232 ++fTotalRequests;
233#endif
234 SkAutoMutexExclusive lock(fParagraphMutex);
235 ParagraphCacheKey key(paragraph);
236 std::unique_ptr<Entry>* entry = fLRUCacheMap.find(key);
237 if (!entry) {
238 ParagraphCacheValue* value = new ParagraphCacheValue(paragraph);
239 fLRUCacheMap.insert(key, std::unique_ptr<Entry>(new Entry(value)));
240 fChecker(paragraph, "addedParagraph", true);
241 return true;
242 } else {
243 updateFrom(paragraph, entry->get());
244 fChecker(paragraph, "updatedParagraph", true);
245 return false;
Julia Lavrova5207f352019-06-21 12:22:32 -0400246 }
247}
Julia Lavrova5207f352019-06-21 12:22:32 -0400248}
249}