OLD | NEW |
1 // Copyright 2014 The Chromium Authors. All rights reserved. | 1 // Copyright 2014 The Chromium Authors. All rights reserved. |
2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
4 | 4 |
5 #include "platform/graphics/paint/PaintController.h" | 5 #include "platform/graphics/paint/PaintController.h" |
6 | 6 |
7 #include "platform/TraceEvent.h" | 7 #include "platform/TraceEvent.h" |
8 #include "platform/graphics/GraphicsLayer.h" | 8 #include "platform/graphics/GraphicsLayer.h" |
9 #include "platform/graphics/paint/DrawingDisplayItem.h" | 9 #include "platform/graphics/paint/DrawingDisplayItem.h" |
10 #include "third_party/skia/include/core/SkPictureAnalyzer.h" | 10 #include "third_party/skia/include/core/SkPictureAnalyzer.h" |
(...skipping 13 matching lines...) Expand all Loading... |
24 return PaintChunker::DefaultBehavior; | 24 return PaintChunker::DefaultBehavior; |
25 } | 25 } |
26 | 26 |
27 const PaintArtifact& PaintController::paintArtifact() const | 27 const PaintArtifact& PaintController::paintArtifact() const |
28 { | 28 { |
29 DCHECK(m_newDisplayItemList.isEmpty()); | 29 DCHECK(m_newDisplayItemList.isEmpty()); |
30 DCHECK(m_newPaintChunks.isInInitialState()); | 30 DCHECK(m_newPaintChunks.isInInitialState()); |
31 return m_currentPaintArtifact; | 31 return m_currentPaintArtifact; |
32 } | 32 } |
33 | 33 |
| 34 bool PaintController::useCachedDrawingIfPossible(const DisplayItemClient& client
, DisplayItem::Type type) |
| 35 { |
| 36 DCHECK(DisplayItem::isDrawingType(type)); |
| 37 |
| 38 if (displayItemConstructionIsDisabled()) |
| 39 return false; |
| 40 |
| 41 if (!clientCacheIsValid(client)) |
| 42 return false; |
| 43 |
| 44 #if ENABLE(ASSERT) |
| 45 // When under-invalidation checking is enabled, we output CachedDrawing disp
lay item |
| 46 // followed by the display item containing forced painting. |
| 47 if (RuntimeEnabledFeatures::slimmingPaintUnderInvalidationCheckingEnabled()) |
| 48 return false; |
| 49 #endif |
| 50 |
| 51 DisplayItemList::iterator cachedItem = findCachedItem(DisplayItem::Id(client
, type)); |
| 52 if (cachedItem == m_currentPaintArtifact.getDisplayItemList().end()) { |
| 53 NOTREACHED(); |
| 54 return false; |
| 55 } |
| 56 |
| 57 ++m_numCachedNewItems; |
| 58 ensureNewDisplayItemListInitialCapacity(); |
| 59 processNewItem(m_newDisplayItemList.appendByMoving(*cachedItem)); |
| 60 |
| 61 m_nextItemToMatch = cachedItem + 1; |
| 62 // Items before m_nextItemToMatch have been copied so we don't need to index
them. |
| 63 if (m_nextItemToMatch - m_nextItemToIndex > 0) |
| 64 m_nextItemToIndex = m_nextItemToMatch; |
| 65 |
| 66 return true; |
| 67 } |
| 68 |
| 69 bool PaintController::useCachedSubsequenceIfPossible(const DisplayItemClient& cl
ient) |
| 70 { |
| 71 // TODO(crbug.com/596983): Implement subsequence caching for spv2. |
| 72 // The problem is in copyCachedSubsequence() which fails to handle PaintChun
kProperties |
| 73 // of chunks containing cached display items. We need to find the previous |
| 74 // PaintChunkProperties and ensure they are valid in the current paint prope
rty tree. |
| 75 if (RuntimeEnabledFeatures::slimmingPaintV2Enabled()) |
| 76 return false; |
| 77 |
| 78 if (displayItemConstructionIsDisabled() || subsequenceCachingIsDisabled()) |
| 79 return false; |
| 80 |
| 81 if (!clientCacheIsValid(client)) |
| 82 return false; |
| 83 |
| 84 #if ENABLE(ASSERT) |
| 85 // When under-invalidation checking is enabled, we output CachedDrawing disp
lay item |
| 86 // followed by the display item containing forced painting. |
| 87 if (RuntimeEnabledFeatures::slimmingPaintUnderInvalidationCheckingEnabled()) |
| 88 return false; |
| 89 #endif |
| 90 |
| 91 DisplayItemList::iterator cachedItem = findCachedItem(DisplayItem::Id(client
, DisplayItem::Subsequence)); |
| 92 if (cachedItem == m_currentPaintArtifact.getDisplayItemList().end()) { |
| 93 NOTREACHED(); |
| 94 return false; |
| 95 } |
| 96 |
| 97 // |cachedItem| will point to the first item after the subsequence or end of
the current list. |
| 98 ensureNewDisplayItemListInitialCapacity(); |
| 99 copyCachedSubsequence(cachedItem); |
| 100 |
| 101 m_nextItemToMatch = cachedItem; |
| 102 // Items before |cachedItem| have been copied so we don't need to index them
. |
| 103 if (cachedItem - m_nextItemToIndex > 0) |
| 104 m_nextItemToIndex = cachedItem; |
| 105 |
| 106 return true; |
| 107 } |
| 108 |
34 bool PaintController::lastDisplayItemIsNoopBegin() const | 109 bool PaintController::lastDisplayItemIsNoopBegin() const |
35 { | 110 { |
36 if (m_newDisplayItemList.isEmpty()) | 111 if (m_newDisplayItemList.isEmpty()) |
37 return false; | 112 return false; |
38 | 113 |
39 const auto& lastDisplayItem = m_newDisplayItemList.last(); | 114 const auto& lastDisplayItem = m_newDisplayItemList.last(); |
40 return lastDisplayItem.isBegin() && !lastDisplayItem.drawsContent(); | 115 return lastDisplayItem.isBegin() && !lastDisplayItem.drawsContent(); |
41 } | 116 } |
42 | 117 |
43 void PaintController::removeLastDisplayItem() | 118 void PaintController::removeLastDisplayItem() |
(...skipping 12 matching lines...) Expand all Loading... |
56 #endif | 131 #endif |
57 m_newDisplayItemList.removeLast(); | 132 m_newDisplayItemList.removeLast(); |
58 | 133 |
59 if (RuntimeEnabledFeatures::slimmingPaintV2Enabled()) | 134 if (RuntimeEnabledFeatures::slimmingPaintV2Enabled()) |
60 m_newPaintChunks.decrementDisplayItemIndex(); | 135 m_newPaintChunks.decrementDisplayItemIndex(); |
61 } | 136 } |
62 | 137 |
63 void PaintController::processNewItem(DisplayItem& displayItem) | 138 void PaintController::processNewItem(DisplayItem& displayItem) |
64 { | 139 { |
65 DCHECK(!m_constructionDisabled); | 140 DCHECK(!m_constructionDisabled); |
66 DCHECK(!skippingCache() || !displayItem.isCached()); | |
67 | 141 |
68 #if CHECK_DISPLAY_ITEM_CLIENT_ALIVENESS | 142 #if CHECK_DISPLAY_ITEM_CLIENT_ALIVENESS |
69 if (!skippingCache()) { | 143 if (!skippingCache()) { |
70 if (displayItem.isCacheable() || displayItem.isCached()) { | 144 if (displayItem.isCacheable()) { |
71 // Mark the client shouldKeepAlive under this PaintController. | 145 // Mark the client shouldKeepAlive under this PaintController. |
72 // The status will end after the new display items are committed. | 146 // The status will end after the new display items are committed. |
73 displayItem.client().beginShouldKeepAlive(this); | 147 displayItem.client().beginShouldKeepAlive(this); |
74 | 148 |
75 if (!m_currentSubsequenceClients.isEmpty()) { | 149 if (!m_currentSubsequenceClients.isEmpty()) { |
76 // Mark the client shouldKeepAlive under the current subsequence
. | 150 // Mark the client shouldKeepAlive under the current subsequence
. |
77 // The status will end when the subsequence owner is invalidated
or deleted. | 151 // The status will end when the subsequence owner is invalidated
or deleted. |
78 displayItem.client().beginShouldKeepAlive(m_currentSubsequenceCl
ients.last()); | 152 displayItem.client().beginShouldKeepAlive(m_currentSubsequenceCl
ients.last()); |
79 } | 153 } |
80 } | 154 } |
81 | 155 |
82 if (displayItem.getType() == DisplayItem::Subsequence) { | 156 if (displayItem.getType() == DisplayItem::Subsequence) { |
83 m_currentSubsequenceClients.append(&displayItem.client()); | 157 m_currentSubsequenceClients.append(&displayItem.client()); |
84 } else if (displayItem.getType() == DisplayItem::EndSubsequence) { | 158 } else if (displayItem.getType() == DisplayItem::EndSubsequence) { |
85 CHECK(m_currentSubsequenceClients.last() == &displayItem.client()); | 159 CHECK(m_currentSubsequenceClients.last() == &displayItem.client()); |
86 m_currentSubsequenceClients.removeLast(); | 160 m_currentSubsequenceClients.removeLast(); |
87 } | 161 } |
88 } | 162 } |
89 #endif | 163 #endif |
90 | 164 |
91 if (displayItem.isCached()) | |
92 ++m_numCachedNewItems; | |
93 | |
94 #if DCHECK_IS_ON() | 165 #if DCHECK_IS_ON() |
95 // Verify noop begin/end pairs have been removed. | 166 // Verify noop begin/end pairs have been removed. |
96 if (m_newDisplayItemList.size() >= 2 && displayItem.isEnd()) { | 167 if (m_newDisplayItemList.size() >= 2 && displayItem.isEnd()) { |
97 const auto& beginDisplayItem = m_newDisplayItemList[m_newDisplayItemList
.size() - 2]; | 168 const auto& beginDisplayItem = m_newDisplayItemList[m_newDisplayItemList
.size() - 2]; |
98 if (beginDisplayItem.isBegin() && beginDisplayItem.getType() != DisplayI
tem::Subsequence && !beginDisplayItem.drawsContent()) | 169 if (beginDisplayItem.isBegin() && beginDisplayItem.getType() != DisplayI
tem::Subsequence && !beginDisplayItem.drawsContent()) |
99 DCHECK(!displayItem.isEndAndPairedWith(beginDisplayItem.getType())); | 170 DCHECK(!displayItem.isEndAndPairedWith(beginDisplayItem.getType())); |
100 } | 171 } |
101 #endif | 172 #endif |
102 | 173 |
103 if (skippingCache()) | 174 if (skippingCache()) |
104 displayItem.setSkippedCache(); | 175 displayItem.setSkippedCache(); |
105 | 176 |
106 #if DCHECK_IS_ON() | 177 #if DCHECK_IS_ON() |
107 size_t index = findMatchingItemFromIndex(displayItem.nonCachedId(), m_newDis
playItemIndicesByClient, m_newDisplayItemList); | 178 size_t index = findMatchingItemFromIndex(displayItem.getId(), m_newDisplayIt
emIndicesByClient, m_newDisplayItemList); |
108 if (index != kNotFound) { | 179 if (index != kNotFound) { |
109 #ifndef NDEBUG | 180 #ifndef NDEBUG |
110 showDebugData(); | 181 showDebugData(); |
111 WTFLogAlways("DisplayItem %s has duplicated id with previous %s (index=%
d)\n", | 182 WTFLogAlways("DisplayItem %s has duplicated id with previous %s (index=%
d)\n", |
112 displayItem.asDebugString().utf8().data(), m_newDisplayItemList[inde
x].asDebugString().utf8().data(), static_cast<int>(index)); | 183 displayItem.asDebugString().utf8().data(), m_newDisplayItemList[inde
x].asDebugString().utf8().data(), static_cast<int>(index)); |
113 #endif | 184 #endif |
114 NOTREACHED(); | 185 NOTREACHED(); |
115 } | 186 } |
116 addItemToIndexIfNeeded(displayItem, m_newDisplayItemList.size() - 1, m_newDi
splayItemIndicesByClient); | 187 addItemToIndexIfNeeded(displayItem, m_newDisplayItemList.size() - 1, m_newDi
splayItemIndicesByClient); |
117 #endif // DCHECK_IS_ON() | 188 #endif // DCHECK_IS_ON() |
(...skipping 33 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
151 size_t PaintController::findMatchingItemFromIndex(const DisplayItem::Id& id, con
st DisplayItemIndicesByClientMap& displayItemIndicesByClient, const DisplayItemL
ist& list) | 222 size_t PaintController::findMatchingItemFromIndex(const DisplayItem::Id& id, con
st DisplayItemIndicesByClientMap& displayItemIndicesByClient, const DisplayItemL
ist& list) |
152 { | 223 { |
153 DisplayItemIndicesByClientMap::const_iterator it = displayItemIndicesByClien
t.find(&id.client); | 224 DisplayItemIndicesByClientMap::const_iterator it = displayItemIndicesByClien
t.find(&id.client); |
154 if (it == displayItemIndicesByClient.end()) | 225 if (it == displayItemIndicesByClient.end()) |
155 return kNotFound; | 226 return kNotFound; |
156 | 227 |
157 const Vector<size_t>& indices = it->value; | 228 const Vector<size_t>& indices = it->value; |
158 for (size_t index : indices) { | 229 for (size_t index : indices) { |
159 const DisplayItem& existingItem = list[index]; | 230 const DisplayItem& existingItem = list[index]; |
160 DCHECK(!existingItem.hasValidClient() || existingItem.client() == id.cli
ent); | 231 DCHECK(!existingItem.hasValidClient() || existingItem.client() == id.cli
ent); |
161 if (id.matches(existingItem)) | 232 if (id == existingItem.getId()) |
162 return index; | 233 return index; |
163 } | 234 } |
164 | 235 |
165 return kNotFound; | 236 return kNotFound; |
166 } | 237 } |
167 | 238 |
168 void PaintController::addItemToIndexIfNeeded(const DisplayItem& displayItem, siz
e_t index, DisplayItemIndicesByClientMap& displayItemIndicesByClient) | 239 void PaintController::addItemToIndexIfNeeded(const DisplayItem& displayItem, siz
e_t index, DisplayItemIndicesByClientMap& displayItemIndicesByClient) |
169 { | 240 { |
170 if (!displayItem.isCacheable()) | 241 if (!displayItem.isCacheable()) |
171 return; | 242 return; |
172 | 243 |
173 DisplayItemIndicesByClientMap::iterator it = displayItemIndicesByClient.find
(&displayItem.client()); | 244 DisplayItemIndicesByClientMap::iterator it = displayItemIndicesByClient.find
(&displayItem.client()); |
174 Vector<size_t>& indices = it == displayItemIndicesByClient.end() ? | 245 Vector<size_t>& indices = it == displayItemIndicesByClient.end() ? |
175 displayItemIndicesByClient.add(&displayItem.client(), Vector<size_t>()).
storedValue->value : it->value; | 246 displayItemIndicesByClient.add(&displayItem.client(), Vector<size_t>()).
storedValue->value : it->value; |
176 indices.append(index); | 247 indices.append(index); |
177 } | 248 } |
178 | 249 |
179 struct PaintController::OutOfOrderIndexContext { | 250 DisplayItemList::iterator PaintController::findCachedItem(const DisplayItem::Id&
id) |
180 STACK_ALLOCATED(); | |
181 OutOfOrderIndexContext(DisplayItemList::iterator begin) : nextItemToIndex(be
gin) { } | |
182 | |
183 DisplayItemList::iterator nextItemToIndex; | |
184 DisplayItemIndicesByClientMap displayItemIndicesByClient; | |
185 }; | |
186 | |
187 DisplayItemList::iterator PaintController::findOutOfOrderCachedItem(const Displa
yItem::Id& id, OutOfOrderIndexContext& context) | |
188 { | 251 { |
189 DCHECK(clientCacheIsValid(id.client)); | 252 DCHECK(clientCacheIsValid(id.client)); |
190 | 253 |
191 size_t foundIndex = findMatchingItemFromIndex(id, context.displayItemIndices
ByClient, m_currentPaintArtifact.getDisplayItemList()); | 254 // Try to find the item sequentially first. This is fast if the current list
and the new list are in |
192 if (foundIndex != kNotFound) | 255 // the same order around the new item. If found, we don't need to update and
lookup the index. |
| 256 DisplayItemList::iterator end = m_currentPaintArtifact.getDisplayItemList().
end(); |
| 257 DisplayItemList::iterator it = m_nextItemToMatch; |
| 258 for (; it != end; ++it) { |
| 259 // We encounter an item that has already been copied which indicates we
can't do sequential matching. |
| 260 if (!it->hasValidClient()) |
| 261 break; |
| 262 if (id == it->getId()) { |
| 263 #if DCHECK_IS_ON() |
| 264 ++m_numSequentialMatches; |
| 265 #endif |
| 266 return it; |
| 267 } |
| 268 // We encounter a different cacheable item which also indicates we can't
do sequential matching. |
| 269 if (it->isCacheable()) |
| 270 break; |
| 271 } |
| 272 |
| 273 size_t foundIndex = findMatchingItemFromIndex(id, m_outOfOrderItemIndices, m
_currentPaintArtifact.getDisplayItemList()); |
| 274 if (foundIndex != kNotFound) { |
| 275 #if DCHECK_IS_ON() |
| 276 ++m_numOutOfOrderMatches; |
| 277 #endif |
193 return m_currentPaintArtifact.getDisplayItemList().begin() + foundIndex; | 278 return m_currentPaintArtifact.getDisplayItemList().begin() + foundIndex; |
| 279 } |
194 | 280 |
195 return findOutOfOrderCachedItemForward(id, context); | 281 return findOutOfOrderCachedItemForward(id); |
196 } | 282 } |
197 | 283 |
198 // Find forward for the item and index all skipped indexable items. | 284 // Find forward for the item and index all skipped indexable items. |
199 DisplayItemList::iterator PaintController::findOutOfOrderCachedItemForward(const
DisplayItem::Id& id, OutOfOrderIndexContext& context) | 285 DisplayItemList::iterator PaintController::findOutOfOrderCachedItemForward(const
DisplayItem::Id& id) |
200 { | 286 { |
201 DisplayItemList::iterator currentEnd = m_currentPaintArtifact.getDisplayItem
List().end(); | 287 DisplayItemList::iterator end = m_currentPaintArtifact.getDisplayItemList().
end(); |
202 for (; context.nextItemToIndex != currentEnd; ++context.nextItemToIndex) { | 288 for (DisplayItemList::iterator it = m_nextItemToIndex; it != end; ++it) { |
203 const DisplayItem& item = *context.nextItemToIndex; | 289 const DisplayItem& item = *it; |
204 DCHECK(item.hasValidClient()); | 290 DCHECK(item.hasValidClient()); |
205 if (id.matches(item)) | 291 if (id == item.getId()) { |
206 return context.nextItemToIndex++; | 292 #if DCHECK_IS_ON() |
207 if (item.isCacheable()) | 293 ++m_numSequentialMatches; |
208 addItemToIndexIfNeeded(item, context.nextItemToIndex - m_currentPain
tArtifact.getDisplayItemList().begin(), context.displayItemIndicesByClient); | 294 #endif |
| 295 return it; |
| 296 } |
| 297 if (item.isCacheable()) { |
| 298 #if DCHECK_IS_ON() |
| 299 ++m_numIndexedItems; |
| 300 #endif |
| 301 addItemToIndexIfNeeded(item, it - m_currentPaintArtifact.getDisplayI
temList().begin(), m_outOfOrderItemIndices); |
| 302 } |
209 } | 303 } |
210 return currentEnd; | 304 |
| 305 #ifndef NDEBUG |
| 306 showDebugData(); |
| 307 LOG(ERROR) << id.client.debugName() << ":" << DisplayItem::typeAsDebugString
(id.type) << " not found in current display item list"; |
| 308 #endif |
| 309 NOTREACHED(); |
| 310 // We did not find the cached display item. This should be impossible, but m
ay occur if there is a bug |
| 311 // in the system, such as under-invalidation, incorrect cache checking or du
plicate display ids. |
| 312 // In this case, the caller should fall back to repaint the display item. |
| 313 return end; |
211 } | 314 } |
212 | 315 |
213 void PaintController::copyCachedSubsequence(const DisplayItemList& currentList,
DisplayItemList::iterator& currentIt, DisplayItemList& updatedList, SkPictureGpu
Analyzer& gpuAnalyzer) | 316 // On return, |it| points to the item after the EndSubsequence item of the subse
quence. |
| 317 void PaintController::copyCachedSubsequence(DisplayItemList::iterator& it) |
214 { | 318 { |
215 DCHECK(currentIt->getType() == DisplayItem::Subsequence); | 319 DCHECK(it->getType() == DisplayItem::Subsequence); |
216 DisplayItem::Id endSubsequenceId(currentIt->client(), DisplayItem::EndSubseq
uence); | 320 DisplayItem::Id endSubsequenceId(it->client(), DisplayItem::EndSubsequence); |
217 do { | 321 do { |
218 // We should always find the EndSubsequence display item. | 322 // We should always find the EndSubsequence display item. |
219 DCHECK(currentIt != m_currentPaintArtifact.getDisplayItemList().end()); | 323 DCHECK(it != m_currentPaintArtifact.getDisplayItemList().end()); |
220 DCHECK(currentIt->hasValidClient()); | 324 DCHECK(it->hasValidClient()); |
221 #if CHECK_DISPLAY_ITEM_CLIENT_ALIVENESS | 325 #if CHECK_DISPLAY_ITEM_CLIENT_ALIVENESS |
222 CHECK(currentIt->client().isAlive()); | 326 CHECK(it->client().isAlive()); |
223 #endif | 327 #endif |
224 updatedList.appendByMoving(*currentIt, currentList.visualRect(currentIt
- m_currentPaintArtifact.getDisplayItemList().begin()), gpuAnalyzer); | 328 ++m_numCachedNewItems; |
225 ++currentIt; | 329 processNewItem(m_newDisplayItemList.appendByMoving(*it)); |
226 } while (!endSubsequenceId.matches(updatedList.last())); | 330 ++it; |
| 331 } while (endSubsequenceId != m_newDisplayItemList.last().getId()); |
227 } | 332 } |
228 | 333 |
229 static IntRect visualRectForDisplayItem(const DisplayItem& displayItem, const La
youtSize& offsetFromLayoutObject) | 334 static IntRect visualRectForDisplayItem(const DisplayItem& displayItem, const La
youtSize& offsetFromLayoutObject) |
230 { | 335 { |
231 LayoutRect visualRect = displayItem.client().visualRect(); | 336 LayoutRect visualRect = displayItem.client().visualRect(); |
232 visualRect.move(-offsetFromLayoutObject); | 337 visualRect.move(-offsetFromLayoutObject); |
233 return enclosingIntRect(visualRect); | 338 return enclosingIntRect(visualRect); |
234 } | 339 } |
235 | 340 |
236 // Update the existing display items by removing invalidated entries, updating | 341 void PaintController::resetCurrentListIterators() |
237 // repainted ones, and appending new items. | 342 { |
238 // - For cached drawing display item, copy the corresponding cached DrawingDispl
ayItem; | 343 m_nextItemToMatch = m_currentPaintArtifact.getDisplayItemList().begin(); |
239 // - For cached subsequence display item, copy the cached display items between
the | 344 m_nextItemToIndex = m_nextItemToMatch; |
240 // corresponding SubsequenceDisplayItem and EndSubsequenceDisplayItem (incl.); | 345 } |
241 // - Otherwise, copy the new display item. | 346 |
242 // | |
243 // The algorithm is O(|m_currentDisplayItemList| + |m_newDisplayItemList|). | |
244 // Coefficients are related to the ratio of out-of-order CachedDisplayItems | |
245 // and the average number of (Drawing|Subsequence)DisplayItems per client. | |
246 // | |
247 void PaintController::commitNewDisplayItems(const LayoutSize& offsetFromLayoutOb
ject) | 347 void PaintController::commitNewDisplayItems(const LayoutSize& offsetFromLayoutOb
ject) |
248 { | 348 { |
249 TRACE_EVENT2("blink,benchmark", "PaintController::commitNewDisplayItems", | 349 TRACE_EVENT2("blink,benchmark", "PaintController::commitNewDisplayItems", |
250 "current_display_list_size", (int)m_currentPaintArtifact.getDisplayItemL
ist().size(), | 350 "current_display_list_size", (int)m_currentPaintArtifact.getDisplayItemL
ist().size(), |
251 "num_non_cached_new_items", (int)m_newDisplayItemList.size() - m_numCach
edNewItems); | 351 "num_non_cached_new_items", (int)m_newDisplayItemList.size() - m_numCach
edNewItems); |
252 m_numCachedNewItems = 0; | 352 m_numCachedNewItems = 0; |
253 | 353 |
254 // These data structures are used during painting only. | 354 // These data structures are used during painting only. |
255 DCHECK(!skippingCache()); | 355 DCHECK(!skippingCache()); |
256 #if DCHECK_IS_ON() | 356 #if DCHECK_IS_ON() |
257 m_newDisplayItemIndicesByClient.clear(); | 357 m_newDisplayItemIndicesByClient.clear(); |
258 #endif | 358 #endif |
259 | 359 |
260 SkPictureGpuAnalyzer gpuAnalyzer; | 360 SkPictureGpuAnalyzer gpuAnalyzer; |
261 | 361 |
262 if (m_currentPaintArtifact.isEmpty()) { | 362 m_currentCacheGeneration = DisplayItemClient::CacheGenerationOrInvalidationR
eason::next(); |
263 #if DCHECK_IS_ON() | 363 for (const auto& item : m_newDisplayItemList) { |
264 for (const auto& item : m_newDisplayItemList) | 364 // No reason to continue the analysis once we have a veto. |
265 DCHECK(!item.isCached()); | 365 if (gpuAnalyzer.suitableForGpuRasterization()) |
| 366 item.analyzeForGpuRasterization(gpuAnalyzer); |
| 367 |
| 368 m_newDisplayItemList.appendVisualRect(visualRectForDisplayItem(item, off
setFromLayoutObject)); |
| 369 |
| 370 if (item.isCacheable()) |
| 371 item.client().setDisplayItemsCached(m_currentCacheGeneration); |
| 372 } |
| 373 |
| 374 // The new list will not be appended to again so we can release unused memor
y. |
| 375 m_newDisplayItemList.shrinkToFit(); |
| 376 m_currentPaintArtifact = PaintArtifact(std::move(m_newDisplayItemList), m_ne
wPaintChunks.releasePaintChunks(), gpuAnalyzer.suitableForGpuRasterization()); |
| 377 resetCurrentListIterators(); |
| 378 m_outOfOrderItemIndices.clear(); |
| 379 |
| 380 // We'll allocate the initial buffer when we start the next paint. |
| 381 m_newDisplayItemList = DisplayItemList(0); |
| 382 |
| 383 #if CHECK_DISPLAY_ITEM_CLIENT_ALIVENESS |
| 384 CHECK(m_currentSubsequenceClients.isEmpty()); |
| 385 DisplayItemClient::endShouldKeepAliveAllClients(this); |
266 #endif | 386 #endif |
267 | 387 |
268 for (const auto& item : m_newDisplayItemList) { | 388 #if DCHECK_IS_ON() |
269 m_newDisplayItemList.appendVisualRect(visualRectForDisplayItem(item,
offsetFromLayoutObject)); | 389 m_numSequentialMatches = 0; |
270 // No reason to continue the analysis once we have a veto. | 390 m_numOutOfOrderMatches = 0; |
271 if (gpuAnalyzer.suitableForGpuRasterization()) | 391 m_numIndexedItems = 0; |
272 item.analyzeForGpuRasterization(gpuAnalyzer); | |
273 } | |
274 m_currentPaintArtifact = PaintArtifact(std::move(m_newDisplayItemList),
m_newPaintChunks.releasePaintChunks(), gpuAnalyzer.suitableForGpuRasterization()
); | |
275 m_newDisplayItemList = DisplayItemList(kInitialDisplayItemListCapacityBy
tes); | |
276 updateCacheGeneration(); | |
277 return; | |
278 } | |
279 | |
280 // Stores indices to valid DrawingDisplayItems in m_currentDisplayItems that
have not been matched | |
281 // by CachedDisplayItems during synchronized matching. The indexed items wil
l be matched | |
282 // by later out-of-order CachedDisplayItems in m_newDisplayItemList. This en
sures that when | |
283 // out-of-order CachedDisplayItems occur, we only traverse at most once over
m_currentDisplayItems | |
284 // looking for potential matches. Thus we can ensure that the algorithm runs
in linear time. | |
285 OutOfOrderIndexContext outOfOrderIndexContext(m_currentPaintArtifact.getDisp
layItemList().begin()); | |
286 | |
287 // TODO(jbroman): Consider revisiting this heuristic. | |
288 DisplayItemList updatedList(std::max(m_currentPaintArtifact.getDisplayItemLi
st().usedCapacityInBytes(), m_newDisplayItemList.usedCapacityInBytes())); | |
289 Vector<PaintChunk> updatedPaintChunks; | |
290 DisplayItemList::iterator currentIt = m_currentPaintArtifact.getDisplayItemL
ist().begin(); | |
291 DisplayItemList::iterator currentEnd = m_currentPaintArtifact.getDisplayItem
List().end(); | |
292 for (DisplayItemList::iterator newIt = m_newDisplayItemList.begin(); newIt !
= m_newDisplayItemList.end(); ++newIt) { | |
293 const DisplayItem& newDisplayItem = *newIt; | |
294 const DisplayItem::Id newDisplayItemId = newDisplayItem.nonCachedId(); | |
295 bool newDisplayItemHasCachedType = newDisplayItem.getType() != newDispla
yItemId.type; | |
296 | |
297 bool isSynchronized = currentIt != currentEnd && newDisplayItemId.matche
s(*currentIt); | |
298 | |
299 if (newDisplayItemHasCachedType) { | |
300 DCHECK(newDisplayItem.isCached()); | |
301 #if CHECK_DISPLAY_ITEM_CLIENT_ALIVENESS | |
302 CHECK(clientCacheIsValid(newDisplayItem.client())); | |
303 #endif | 392 #endif |
304 if (!isSynchronized) { | |
305 currentIt = findOutOfOrderCachedItem(newDisplayItemId, outOfOrde
rIndexContext); | |
306 | |
307 if (currentIt == currentEnd) { | |
308 #ifndef NDEBUG | |
309 showDebugData(); | |
310 WTFLogAlways("%s not found in m_currentDisplayItemList\n", n
ewDisplayItem.asDebugString().utf8().data()); | |
311 #endif | |
312 NOTREACHED(); | |
313 // We did not find the cached display item. This should be i
mpossible, but may occur if there is a bug | |
314 // in the system, such as under-invalidation, incorrect cach
e checking or duplicate display ids. | |
315 // In this case, attempt to recover rather than crashing or
bailing on display of the rest of the display list. | |
316 continue; | |
317 } | |
318 } | |
319 #if DCHECK_IS_ON() | |
320 if (RuntimeEnabledFeatures::slimmingPaintUnderInvalidationCheckingEn
abled()) { | |
321 DisplayItemList::iterator temp = currentIt; | |
322 checkUnderInvalidation(newIt, temp); | |
323 } | |
324 #endif | |
325 if (newDisplayItem.isCachedDrawing()) { | |
326 updatedList.appendByMoving(*currentIt, m_currentPaintArtifact.ge
tDisplayItemList().visualRect(currentIt - m_currentPaintArtifact.getDisplayItemL
ist().begin()), | |
327 gpuAnalyzer); | |
328 ++currentIt; | |
329 } else { | |
330 DCHECK(newDisplayItem.getType() == DisplayItem::CachedSubsequenc
e); | |
331 copyCachedSubsequence(m_currentPaintArtifact.getDisplayItemList(
), currentIt, updatedList, gpuAnalyzer); | |
332 DCHECK(updatedList.last().getType() == DisplayItem::EndSubsequen
ce); | |
333 } | |
334 } else { | |
335 DCHECK(!newDisplayItem.isDrawing() | |
336 || newDisplayItem.skippedCache() | |
337 || !clientCacheIsValid(newDisplayItem.client())); | |
338 | |
339 updatedList.appendByMoving(*newIt, visualRectForDisplayItem(*newIt,
offsetFromLayoutObject), gpuAnalyzer); | |
340 | |
341 if (isSynchronized) | |
342 ++currentIt; | |
343 } | |
344 // Items before currentIt should have been copied so we don't need to in
dex them. | |
345 if (currentIt - outOfOrderIndexContext.nextItemToIndex > 0) | |
346 outOfOrderIndexContext.nextItemToIndex = currentIt; | |
347 } | |
348 | |
349 // TODO(jbroman): When subsequence caching applies to SPv2, we'll need to | |
350 // merge the paint chunks as well. | |
351 m_currentPaintArtifact = PaintArtifact(std::move(updatedList), m_newPaintChu
nks.releasePaintChunks(), gpuAnalyzer.suitableForGpuRasterization()); | |
352 | |
353 m_newDisplayItemList = DisplayItemList(kInitialDisplayItemListCapacityBytes)
; | |
354 updateCacheGeneration(); | |
355 } | 393 } |
356 | 394 |
357 size_t PaintController::approximateUnsharedMemoryUsage() const | 395 size_t PaintController::approximateUnsharedMemoryUsage() const |
358 { | 396 { |
359 size_t memoryUsage = sizeof(*this); | 397 size_t memoryUsage = sizeof(*this); |
360 | 398 |
361 // Memory outside this class due to m_currentPaintArtifact. | 399 // Memory outside this class due to m_currentPaintArtifact. |
362 memoryUsage += m_currentPaintArtifact.approximateUnsharedMemoryUsage() - siz
eof(m_currentPaintArtifact); | 400 memoryUsage += m_currentPaintArtifact.approximateUnsharedMemoryUsage() - siz
eof(m_currentPaintArtifact); |
363 | 401 |
364 // TODO(jbroman): If display items begin to have significant external memory | 402 // TODO(jbroman): If display items begin to have significant external memory |
365 // usage that's not shared with the embedder, we should account for it here. | 403 // usage that's not shared with the embedder, we should account for it here. |
366 // | 404 // |
367 // External objects, shared with the embedder, such as SkPicture, should be | 405 // External objects, shared with the embedder, such as SkPicture, should be |
368 // excluded to avoid double counting. It is the embedder's responsibility to | 406 // excluded to avoid double counting. It is the embedder's responsibility to |
369 // count such objects. | 407 // count such objects. |
370 // | 408 // |
371 // At time of writing, the only known case of unshared external memory was | 409 // At time of writing, the only known case of unshared external memory was |
372 // the rounded clips vector in ClipDisplayItem, which is not expected to | 410 // the rounded clips vector in ClipDisplayItem, which is not expected to |
373 // contribute significantly to memory usage. | 411 // contribute significantly to memory usage. |
374 | 412 |
375 // Memory outside this class due to m_newDisplayItemList. | 413 // Memory outside this class due to m_newDisplayItemList. |
376 DCHECK(m_newDisplayItemList.isEmpty()); | 414 DCHECK(m_newDisplayItemList.isEmpty()); |
377 memoryUsage += m_newDisplayItemList.memoryUsageInBytes(); | 415 memoryUsage += m_newDisplayItemList.memoryUsageInBytes(); |
378 | 416 |
379 return memoryUsage; | 417 return memoryUsage; |
380 } | 418 } |
381 | 419 |
382 void PaintController::updateCacheGeneration() | |
383 { | |
384 m_currentCacheGeneration = DisplayItemClient::CacheGenerationOrInvalidationR
eason::next(); | |
385 for (const DisplayItem& displayItem : m_currentPaintArtifact.getDisplayItemL
ist()) { | |
386 if (!displayItem.isCacheable()) | |
387 continue; | |
388 displayItem.client().setDisplayItemsCached(m_currentCacheGeneration); | |
389 } | |
390 #if CHECK_DISPLAY_ITEM_CLIENT_ALIVENESS | |
391 CHECK(m_currentSubsequenceClients.isEmpty()); | |
392 DisplayItemClient::endShouldKeepAliveAllClients(this); | |
393 #endif | |
394 } | |
395 | |
396 void PaintController::appendDebugDrawingAfterCommit(const DisplayItemClient& dis
playItemClient, PassRefPtr<SkPicture> picture, const LayoutSize& offsetFromLayou
tObject) | 420 void PaintController::appendDebugDrawingAfterCommit(const DisplayItemClient& dis
playItemClient, PassRefPtr<SkPicture> picture, const LayoutSize& offsetFromLayou
tObject) |
397 { | 421 { |
398 DCHECK(m_newDisplayItemList.isEmpty()); | 422 DCHECK(m_newDisplayItemList.isEmpty()); |
399 DrawingDisplayItem& displayItem = m_currentPaintArtifact.getDisplayItemList(
).allocateAndConstruct<DrawingDisplayItem>(displayItemClient, DisplayItem::Debug
Drawing, picture); | 423 DrawingDisplayItem& displayItem = m_currentPaintArtifact.getDisplayItemList(
).allocateAndConstruct<DrawingDisplayItem>(displayItemClient, DisplayItem::Debug
Drawing, picture); |
400 displayItem.setSkippedCache(); | 424 displayItem.setSkippedCache(); |
401 m_currentPaintArtifact.getDisplayItemList().appendVisualRect(visualRectForDi
splayItem(displayItem, offsetFromLayoutObject)); | 425 m_currentPaintArtifact.getDisplayItemList().appendVisualRect(visualRectForDi
splayItem(displayItem, offsetFromLayoutObject)); |
| 426 |
| 427 // Need to reset the iterators after mutation of the DisplayItemList. |
| 428 resetCurrentListIterators(); |
402 } | 429 } |
403 | 430 |
404 #if DCHECK_IS_ON() | 431 #if 0 // DCHECK_IS_ON() |
| 432 // TODO(wangxianzhu): Fix under-invalidation checking for the new caching method
. |
405 | 433 |
406 void PaintController::checkUnderInvalidation(DisplayItemList::iterator& newIt, D
isplayItemList::iterator& currentIt) | 434 void PaintController::checkUnderInvalidation(DisplayItemList::iterator& newIt, D
isplayItemList::iterator& currentIt) |
407 { | 435 { |
408 DCHECK(RuntimeEnabledFeatures::slimmingPaintUnderInvalidationCheckingEnabled
()); | 436 DCHECK(RuntimeEnabledFeatures::slimmingPaintUnderInvalidationCheckingEnabled
()); |
409 DCHECK(newIt->isCached()); | |
410 | 437 |
411 // When under-invalidation-checking is enabled, the forced painting is follo
wing the cached display item. | 438 // When under-invalidation-checking is enabled, the forced painting is follo
wing the cached display item. |
412 DisplayItem::Type nextItemType = DisplayItem::nonCachedType(newIt->getType()
); | 439 DisplayItem::Type nextItemType = DisplayItem::nonCachedType(newIt->getType()
); |
413 ++newIt; | 440 ++newIt; |
414 DCHECK(newIt->getType() == nextItemType); | 441 DCHECK(newIt->getType() == nextItemType); |
415 | 442 |
416 if (newIt->isDrawing()) { | 443 if (newIt->isDrawing()) { |
417 checkCachedDisplayItemIsUnchanged("", *newIt, *currentIt); | 444 checkCachedDisplayItemIsUnchanged("", *newIt, *currentIt); |
418 return; | 445 return; |
419 } | 446 } |
(...skipping 97 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
517 | 544 |
518 void PaintController::showDebugData() const | 545 void PaintController::showDebugData() const |
519 { | 546 { |
520 WTFLogAlways("current display item list: [%s]\n", displayItemListAsDebugStri
ng(m_currentPaintArtifact.getDisplayItemList()).utf8().data()); | 547 WTFLogAlways("current display item list: [%s]\n", displayItemListAsDebugStri
ng(m_currentPaintArtifact.getDisplayItemList()).utf8().data()); |
521 WTFLogAlways("new display item list: [%s]\n", displayItemListAsDebugString(m
_newDisplayItemList).utf8().data()); | 548 WTFLogAlways("new display item list: [%s]\n", displayItemListAsDebugString(m
_newDisplayItemList).utf8().data()); |
522 } | 549 } |
523 | 550 |
524 #endif // ifndef NDEBUG | 551 #endif // ifndef NDEBUG |
525 | 552 |
526 } // namespace blink | 553 } // namespace blink |
OLD | NEW |