| 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 it = findCachedItem(DisplayItem::Id(client, type))
; |
| 52 if (it == m_currentPaintArtifact.getDisplayItemList().end()) { |
| 53 NOTREACHED(); |
| 54 return false; |
| 55 } |
| 56 |
| 57 ++m_numCachedNewItems; |
| 58 ensureNewDisplayItemListInitialCapacity(); |
| 59 processNewItem(m_newDisplayItemList.appendByMoving(*it)); |
| 60 |
| 61 m_nextItemToMatch = it + 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 if (displayItemConstructionIsDisabled() || subsequenceCachingIsDisabled()) |
| 72 return false; |
| 73 |
| 74 if (!clientCacheIsValid(client)) |
| 75 return false; |
| 76 |
| 77 #if ENABLE(ASSERT) |
| 78 // When under-invalidation checking is enabled, we output CachedDrawing disp
lay item |
| 79 // followed by the display item containing forced painting. |
| 80 if (RuntimeEnabledFeatures::slimmingPaintUnderInvalidationCheckingEnabled()) |
| 81 return false; |
| 82 #endif |
| 83 |
| 84 DisplayItemList::iterator it = findCachedItem(DisplayItem::Id(client, Displa
yItem::Subsequence)); |
| 85 if (it == m_currentPaintArtifact.getDisplayItemList().end()) { |
| 86 NOTREACHED(); |
| 87 return false; |
| 88 } |
| 89 |
| 90 // |it| will point to the first item after the subsequence or end of the cur
rent list. |
| 91 ensureNewDisplayItemListInitialCapacity(); |
| 92 copyCachedSubsequence(it); |
| 93 |
| 94 m_nextItemToMatch = it; |
| 95 // Items before |it| have been copied so we don't need to index them. |
| 96 if (it - m_nextItemToIndex > 0) |
| 97 m_nextItemToIndex = it; |
| 98 |
| 99 return true; |
| 100 } |
| 101 |
| 34 bool PaintController::lastDisplayItemIsNoopBegin() const | 102 bool PaintController::lastDisplayItemIsNoopBegin() const |
| 35 { | 103 { |
| 36 if (m_newDisplayItemList.isEmpty()) | 104 if (m_newDisplayItemList.isEmpty()) |
| 37 return false; | 105 return false; |
| 38 | 106 |
| 39 const auto& lastDisplayItem = m_newDisplayItemList.last(); | 107 const auto& lastDisplayItem = m_newDisplayItemList.last(); |
| 40 return lastDisplayItem.isBegin() && !lastDisplayItem.drawsContent(); | 108 return lastDisplayItem.isBegin() && !lastDisplayItem.drawsContent(); |
| 41 } | 109 } |
| 42 | 110 |
| 43 void PaintController::removeLastDisplayItem() | 111 void PaintController::removeLastDisplayItem() |
| (...skipping 12 matching lines...) Expand all Loading... |
| 56 #endif | 124 #endif |
| 57 m_newDisplayItemList.removeLast(); | 125 m_newDisplayItemList.removeLast(); |
| 58 | 126 |
| 59 if (RuntimeEnabledFeatures::slimmingPaintV2Enabled()) | 127 if (RuntimeEnabledFeatures::slimmingPaintV2Enabled()) |
| 60 m_newPaintChunks.decrementDisplayItemIndex(); | 128 m_newPaintChunks.decrementDisplayItemIndex(); |
| 61 } | 129 } |
| 62 | 130 |
| 63 void PaintController::processNewItem(DisplayItem& displayItem) | 131 void PaintController::processNewItem(DisplayItem& displayItem) |
| 64 { | 132 { |
| 65 DCHECK(!m_constructionDisabled); | 133 DCHECK(!m_constructionDisabled); |
| 66 DCHECK(!skippingCache() || !displayItem.isCached()); | |
| 67 | 134 |
| 68 #if CHECK_DISPLAY_ITEM_CLIENT_ALIVENESS | 135 #if CHECK_DISPLAY_ITEM_CLIENT_ALIVENESS |
| 69 if (!skippingCache()) { | 136 if (!skippingCache()) { |
| 70 if (displayItem.isCacheable() || displayItem.isCached()) { | 137 if (displayItem.isCacheable()) { |
| 71 // Mark the client shouldKeepAlive under this PaintController. | 138 // Mark the client shouldKeepAlive under this PaintController. |
| 72 // The status will end after the new display items are committed. | 139 // The status will end after the new display items are committed. |
| 73 displayItem.client().beginShouldKeepAlive(this); | 140 displayItem.client().beginShouldKeepAlive(this); |
| 74 | 141 |
| 75 if (!m_currentSubsequenceClients.isEmpty()) { | 142 if (!m_currentSubsequenceClients.isEmpty()) { |
| 76 // Mark the client shouldKeepAlive under the current subsequence
. | 143 // Mark the client shouldKeepAlive under the current subsequence
. |
| 77 // The status will end when the subsequence owner is invalidated
or deleted. | 144 // The status will end when the subsequence owner is invalidated
or deleted. |
| 78 displayItem.client().beginShouldKeepAlive(m_currentSubsequenceCl
ients.last()); | 145 displayItem.client().beginShouldKeepAlive(m_currentSubsequenceCl
ients.last()); |
| 79 } | 146 } |
| 80 } | 147 } |
| 81 | 148 |
| 82 if (displayItem.getType() == DisplayItem::Subsequence) { | 149 if (displayItem.getType() == DisplayItem::Subsequence) { |
| 83 m_currentSubsequenceClients.append(&displayItem.client()); | 150 m_currentSubsequenceClients.append(&displayItem.client()); |
| 84 } else if (displayItem.getType() == DisplayItem::EndSubsequence) { | 151 } else if (displayItem.getType() == DisplayItem::EndSubsequence) { |
| 85 CHECK(m_currentSubsequenceClients.last() == &displayItem.client()); | 152 CHECK(m_currentSubsequenceClients.last() == &displayItem.client()); |
| 86 m_currentSubsequenceClients.removeLast(); | 153 m_currentSubsequenceClients.removeLast(); |
| 87 } | 154 } |
| 88 } | 155 } |
| 89 #endif | 156 #endif |
| 90 | 157 |
| 91 if (displayItem.isCached()) | |
| 92 ++m_numCachedNewItems; | |
| 93 | |
| 94 #if DCHECK_IS_ON() | 158 #if DCHECK_IS_ON() |
| 95 // Verify noop begin/end pairs have been removed. | 159 // Verify noop begin/end pairs have been removed. |
| 96 if (m_newDisplayItemList.size() >= 2 && displayItem.isEnd()) { | 160 if (m_newDisplayItemList.size() >= 2 && displayItem.isEnd()) { |
| 97 const auto& beginDisplayItem = m_newDisplayItemList[m_newDisplayItemList
.size() - 2]; | 161 const auto& beginDisplayItem = m_newDisplayItemList[m_newDisplayItemList
.size() - 2]; |
| 98 if (beginDisplayItem.isBegin() && beginDisplayItem.getType() != DisplayI
tem::Subsequence && !beginDisplayItem.drawsContent()) | 162 if (beginDisplayItem.isBegin() && beginDisplayItem.getType() != DisplayI
tem::Subsequence && !beginDisplayItem.drawsContent()) |
| 99 DCHECK(!displayItem.isEndAndPairedWith(beginDisplayItem.getType())); | 163 DCHECK(!displayItem.isEndAndPairedWith(beginDisplayItem.getType())); |
| 100 } | 164 } |
| 101 #endif | 165 #endif |
| 102 | 166 |
| 103 if (skippingCache()) | 167 if (skippingCache()) |
| 104 displayItem.setSkippedCache(); | 168 displayItem.setSkippedCache(); |
| 105 | 169 |
| 106 #if DCHECK_IS_ON() | 170 #if DCHECK_IS_ON() |
| 107 size_t index = findMatchingItemFromIndex(displayItem.nonCachedId(), m_newDis
playItemIndicesByClient, m_newDisplayItemList); | 171 size_t index = findMatchingItemFromIndex(displayItem.getId(), m_newDisplayIt
emIndicesByClient, m_newDisplayItemList); |
| 108 if (index != kNotFound) { | 172 if (index != kNotFound) { |
| 109 #ifndef NDEBUG | 173 #ifndef NDEBUG |
| 110 showDebugData(); | 174 showDebugData(); |
| 111 WTFLogAlways("DisplayItem %s has duplicated id with previous %s (index=%
d)\n", | 175 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)); | 176 displayItem.asDebugString().utf8().data(), m_newDisplayItemList[inde
x].asDebugString().utf8().data(), static_cast<int>(index)); |
| 113 #endif | 177 #endif |
| 114 NOTREACHED(); | 178 NOTREACHED(); |
| 115 } | 179 } |
| 116 addItemToIndexIfNeeded(displayItem, m_newDisplayItemList.size() - 1, m_newDi
splayItemIndicesByClient); | 180 addItemToIndexIfNeeded(displayItem, m_newDisplayItemList.size() - 1, m_newDi
splayItemIndicesByClient); |
| 117 #endif // DCHECK_IS_ON() | 181 #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) | 215 size_t PaintController::findMatchingItemFromIndex(const DisplayItem::Id& id, con
st DisplayItemIndicesByClientMap& displayItemIndicesByClient, const DisplayItemL
ist& list) |
| 152 { | 216 { |
| 153 DisplayItemIndicesByClientMap::const_iterator it = displayItemIndicesByClien
t.find(&id.client); | 217 DisplayItemIndicesByClientMap::const_iterator it = displayItemIndicesByClien
t.find(&id.client); |
| 154 if (it == displayItemIndicesByClient.end()) | 218 if (it == displayItemIndicesByClient.end()) |
| 155 return kNotFound; | 219 return kNotFound; |
| 156 | 220 |
| 157 const Vector<size_t>& indices = it->value; | 221 const Vector<size_t>& indices = it->value; |
| 158 for (size_t index : indices) { | 222 for (size_t index : indices) { |
| 159 const DisplayItem& existingItem = list[index]; | 223 const DisplayItem& existingItem = list[index]; |
| 160 DCHECK(!existingItem.hasValidClient() || existingItem.client() == id.cli
ent); | 224 DCHECK(!existingItem.hasValidClient() || existingItem.client() == id.cli
ent); |
| 161 if (id.matches(existingItem)) | 225 if (id == existingItem.getId()) |
| 162 return index; | 226 return index; |
| 163 } | 227 } |
| 164 | 228 |
| 165 return kNotFound; | 229 return kNotFound; |
| 166 } | 230 } |
| 167 | 231 |
| 168 void PaintController::addItemToIndexIfNeeded(const DisplayItem& displayItem, siz
e_t index, DisplayItemIndicesByClientMap& displayItemIndicesByClient) | 232 void PaintController::addItemToIndexIfNeeded(const DisplayItem& displayItem, siz
e_t index, DisplayItemIndicesByClientMap& displayItemIndicesByClient) |
| 169 { | 233 { |
| 170 if (!displayItem.isCacheable()) | 234 if (!displayItem.isCacheable()) |
| 171 return; | 235 return; |
| 172 | 236 |
| 173 DisplayItemIndicesByClientMap::iterator it = displayItemIndicesByClient.find
(&displayItem.client()); | 237 DisplayItemIndicesByClientMap::iterator it = displayItemIndicesByClient.find
(&displayItem.client()); |
| 174 Vector<size_t>& indices = it == displayItemIndicesByClient.end() ? | 238 Vector<size_t>& indices = it == displayItemIndicesByClient.end() ? |
| 175 displayItemIndicesByClient.add(&displayItem.client(), Vector<size_t>()).
storedValue->value : it->value; | 239 displayItemIndicesByClient.add(&displayItem.client(), Vector<size_t>()).
storedValue->value : it->value; |
| 176 indices.append(index); | 240 indices.append(index); |
| 177 } | 241 } |
| 178 | 242 |
| 179 struct PaintController::OutOfOrderIndexContext { | 243 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 { | 244 { |
| 189 DCHECK(clientCacheIsValid(id.client)); | 245 DCHECK(clientCacheIsValid(id.client)); |
| 190 | 246 |
| 191 size_t foundIndex = findMatchingItemFromIndex(id, context.displayItemIndices
ByClient, m_currentPaintArtifact.getDisplayItemList()); | 247 // Try to find the item sequentially first. This is fast if the current list
and the new list are in |
| 192 if (foundIndex != kNotFound) | 248 // the same order around the new item. If found, we don't need to update and
lookup the index. |
| 249 DisplayItemList::iterator end = m_currentPaintArtifact.getDisplayItemList().
end(); |
| 250 DisplayItemList::iterator it = m_nextItemToMatch; |
| 251 for (; it != end; ++it) { |
| 252 // We encounter an item that has already been copied which indicates we
can't do sequential matching. |
| 253 if (!it->hasValidClient()) |
| 254 break; |
| 255 if (id == it->getId()) { |
| 256 #if DCHECK_IS_ON() |
| 257 ++m_numSequentialMatches; |
| 258 #endif |
| 259 return it; |
| 260 } |
| 261 // We encounter a different cacheable item which also indicates we can't
do sequential matching. |
| 262 if (it->isCacheable()) |
| 263 break; |
| 264 } |
| 265 |
| 266 size_t foundIndex = findMatchingItemFromIndex(id, m_outOfOrderItemIndices, m
_currentPaintArtifact.getDisplayItemList()); |
| 267 if (foundIndex != kNotFound) { |
| 268 #if DCHECK_IS_ON() |
| 269 ++m_numOutOfOrderMatches; |
| 270 #endif |
| 193 return m_currentPaintArtifact.getDisplayItemList().begin() + foundIndex; | 271 return m_currentPaintArtifact.getDisplayItemList().begin() + foundIndex; |
| 272 } |
| 194 | 273 |
| 195 return findOutOfOrderCachedItemForward(id, context); | 274 return findOutOfOrderCachedItemForward(id); |
| 196 } | 275 } |
| 197 | 276 |
| 198 // Find forward for the item and index all skipped indexable items. | 277 // Find forward for the item and index all skipped indexable items. |
| 199 DisplayItemList::iterator PaintController::findOutOfOrderCachedItemForward(const
DisplayItem::Id& id, OutOfOrderIndexContext& context) | 278 DisplayItemList::iterator PaintController::findOutOfOrderCachedItemForward(const
DisplayItem::Id& id) |
| 200 { | 279 { |
| 201 DisplayItemList::iterator currentEnd = m_currentPaintArtifact.getDisplayItem
List().end(); | 280 DisplayItemList::iterator end = m_currentPaintArtifact.getDisplayItemList().
end(); |
| 202 for (; context.nextItemToIndex != currentEnd; ++context.nextItemToIndex) { | 281 for (DisplayItemList::iterator it = m_nextItemToIndex; it != end; ++it) { |
| 203 const DisplayItem& item = *context.nextItemToIndex; | 282 const DisplayItem& item = *it; |
| 204 DCHECK(item.hasValidClient()); | 283 DCHECK(item.hasValidClient()); |
| 205 if (id.matches(item)) | 284 if (id == item.getId()) { |
| 206 return context.nextItemToIndex++; | 285 #if DCHECK_IS_ON() |
| 207 if (item.isCacheable()) | 286 ++m_numSequentialMatches; |
| 208 addItemToIndexIfNeeded(item, context.nextItemToIndex - m_currentPain
tArtifact.getDisplayItemList().begin(), context.displayItemIndicesByClient); | 287 #endif |
| 288 return it; |
| 289 } |
| 290 if (item.isCacheable()) { |
| 291 #if DCHECK_IS_ON() |
| 292 ++m_numIndexedItems; |
| 293 #endif |
| 294 addItemToIndexIfNeeded(item, it - m_currentPaintArtifact.getDisplayI
temList().begin(), m_outOfOrderItemIndices); |
| 295 } |
| 209 } | 296 } |
| 210 return currentEnd; | 297 |
| 298 #ifndef NDEBUG |
| 299 showDebugData(); |
| 300 LOG(ERROR) << id.client.debugName() << ":" << DisplayItem::typeAsDebugString
(id.type) << " not found in current display item list"; |
| 301 #endif |
| 302 NOTREACHED(); |
| 303 // We did not find the cached display item. This should be impossible, but m
ay occur if there is a bug |
| 304 // in the system, such as under-invalidation, incorrect cache checking or du
plicate display ids. |
| 305 // In this case, the caller should fall back to repaint the display item. |
| 306 return end; |
| 211 } | 307 } |
| 212 | 308 |
| 213 void PaintController::copyCachedSubsequence(const DisplayItemList& currentList,
DisplayItemList::iterator& currentIt, DisplayItemList& updatedList, SkPictureGpu
Analyzer& gpuAnalyzer) | 309 // On return, |it| points to the item after the EndSubsequence item of the subse
quence. |
| 310 void PaintController::copyCachedSubsequence(DisplayItemList::iterator& it) |
| 214 { | 311 { |
| 215 DCHECK(currentIt->getType() == DisplayItem::Subsequence); | 312 DCHECK(it->getType() == DisplayItem::Subsequence); |
| 216 DisplayItem::Id endSubsequenceId(currentIt->client(), DisplayItem::EndSubseq
uence); | 313 DisplayItem::Id endSubsequenceId(it->client(), DisplayItem::EndSubsequence); |
| 217 do { | 314 do { |
| 218 // We should always find the EndSubsequence display item. | 315 // We should always find the EndSubsequence display item. |
| 219 DCHECK(currentIt != m_currentPaintArtifact.getDisplayItemList().end()); | 316 DCHECK(it != m_currentPaintArtifact.getDisplayItemList().end()); |
| 220 DCHECK(currentIt->hasValidClient()); | 317 DCHECK(it->hasValidClient()); |
| 221 #if CHECK_DISPLAY_ITEM_CLIENT_ALIVENESS | 318 #if CHECK_DISPLAY_ITEM_CLIENT_ALIVENESS |
| 222 CHECK(currentIt->client().isAlive()); | 319 CHECK(it->client().isAlive()); |
| 223 #endif | 320 #endif |
| 224 updatedList.appendByMoving(*currentIt, currentList.visualRect(currentIt
- m_currentPaintArtifact.getDisplayItemList().begin()), gpuAnalyzer); | 321 ++m_numCachedNewItems; |
| 225 ++currentIt; | 322 processNewItem(m_newDisplayItemList.appendByMoving(*it)); |
| 226 } while (!endSubsequenceId.matches(updatedList.last())); | 323 ++it; |
| 324 } while (endSubsequenceId != m_newDisplayItemList.last().getId()); |
| 227 } | 325 } |
| 228 | 326 |
| 229 static IntRect visualRectForDisplayItem(const DisplayItem& displayItem, const La
youtSize& offsetFromLayoutObject) | 327 static IntRect visualRectForDisplayItem(const DisplayItem& displayItem, const La
youtSize& offsetFromLayoutObject) |
| 230 { | 328 { |
| 231 LayoutRect visualRect = displayItem.client().visualRect(); | 329 LayoutRect visualRect = displayItem.client().visualRect(); |
| 232 visualRect.move(-offsetFromLayoutObject); | 330 visualRect.move(-offsetFromLayoutObject); |
| 233 return enclosingIntRect(visualRect); | 331 return enclosingIntRect(visualRect); |
| 234 } | 332 } |
| 235 | 333 |
| 236 // Update the existing display items by removing invalidated entries, updating | 334 void PaintController::resetCurrentListIterators() |
| 237 // repainted ones, and appending new items. | 335 { |
| 238 // - For cached drawing display item, copy the corresponding cached DrawingDispl
ayItem; | 336 m_nextItemToMatch = m_currentPaintArtifact.getDisplayItemList().begin(); |
| 239 // - For cached subsequence display item, copy the cached display items between
the | 337 m_nextItemToIndex = m_nextItemToMatch; |
| 240 // corresponding SubsequenceDisplayItem and EndSubsequenceDisplayItem (incl.); | 338 } |
| 241 // - Otherwise, copy the new display item. | 339 |
| 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) | 340 void PaintController::commitNewDisplayItems(const LayoutSize& offsetFromLayoutOb
ject) |
| 248 { | 341 { |
| 249 TRACE_EVENT2("blink,benchmark", "PaintController::commitNewDisplayItems", | 342 TRACE_EVENT2("blink,benchmark", "PaintController::commitNewDisplayItems", |
| 250 "current_display_list_size", (int)m_currentPaintArtifact.getDisplayItemL
ist().size(), | 343 "current_display_list_size", (int)m_currentPaintArtifact.getDisplayItemL
ist().size(), |
| 251 "num_non_cached_new_items", (int)m_newDisplayItemList.size() - m_numCach
edNewItems); | 344 "num_non_cached_new_items", (int)m_newDisplayItemList.size() - m_numCach
edNewItems); |
| 252 m_numCachedNewItems = 0; | 345 m_numCachedNewItems = 0; |
| 253 | 346 |
| 254 // These data structures are used during painting only. | 347 // These data structures are used during painting only. |
| 255 DCHECK(!skippingCache()); | 348 DCHECK(!skippingCache()); |
| 256 #if DCHECK_IS_ON() | 349 #if DCHECK_IS_ON() |
| 257 m_newDisplayItemIndicesByClient.clear(); | 350 m_newDisplayItemIndicesByClient.clear(); |
| 258 #endif | 351 #endif |
| 259 | 352 |
| 260 SkPictureGpuAnalyzer gpuAnalyzer; | 353 SkPictureGpuAnalyzer gpuAnalyzer; |
| 261 | 354 |
| 262 if (m_currentPaintArtifact.isEmpty()) { | 355 m_currentCacheGeneration = DisplayItemClient::CacheGenerationOrInvalidationR
eason::next(); |
| 263 #if DCHECK_IS_ON() | 356 for (const auto& item : m_newDisplayItemList) { |
| 264 for (const auto& item : m_newDisplayItemList) | 357 // No reason to continue the analysis once we have a veto. |
| 265 DCHECK(!item.isCached()); | 358 if (gpuAnalyzer.suitableForGpuRasterization()) |
| 359 item.analyzeForGpuRasterization(gpuAnalyzer); |
| 360 |
| 361 m_newDisplayItemList.appendVisualRect(visualRectForDisplayItem(item, off
setFromLayoutObject)); |
| 362 |
| 363 if (item.isCacheable()) |
| 364 item.client().setDisplayItemsCached(m_currentCacheGeneration); |
| 365 } |
| 366 |
| 367 m_newDisplayItemList.removeEmptyBuffers(); |
| 368 m_currentPaintArtifact = PaintArtifact(std::move(m_newDisplayItemList), m_ne
wPaintChunks.releasePaintChunks(), gpuAnalyzer.suitableForGpuRasterization()); |
| 369 resetCurrentListIterators(); |
| 370 m_outOfOrderItemIndices.clear(); |
| 371 |
| 372 // We'll allocate the initial buffer when we start the next paint. |
| 373 m_newDisplayItemList = DisplayItemList(0); |
| 374 |
| 375 #if CHECK_DISPLAY_ITEM_CLIENT_ALIVENESS |
| 376 CHECK(m_currentSubsequenceClients.isEmpty()); |
| 377 DisplayItemClient::endShouldKeepAliveAllClients(this); |
| 266 #endif | 378 #endif |
| 267 | 379 |
| 268 for (const auto& item : m_newDisplayItemList) { | 380 #if DCHECK_IS_ON() |
| 269 m_newDisplayItemList.appendVisualRect(visualRectForDisplayItem(item,
offsetFromLayoutObject)); | 381 m_numSequentialMatches = 0; |
| 270 // No reason to continue the analysis once we have a veto. | 382 m_numOutOfOrderMatches = 0; |
| 271 if (gpuAnalyzer.suitableForGpuRasterization()) | 383 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 | 384 #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 } | 385 } |
| 356 | 386 |
| 357 size_t PaintController::approximateUnsharedMemoryUsage() const | 387 size_t PaintController::approximateUnsharedMemoryUsage() const |
| 358 { | 388 { |
| 359 size_t memoryUsage = sizeof(*this); | 389 size_t memoryUsage = sizeof(*this); |
| 360 | 390 |
| 361 // Memory outside this class due to m_currentPaintArtifact. | 391 // Memory outside this class due to m_currentPaintArtifact. |
| 362 memoryUsage += m_currentPaintArtifact.approximateUnsharedMemoryUsage() - siz
eof(m_currentPaintArtifact); | 392 memoryUsage += m_currentPaintArtifact.approximateUnsharedMemoryUsage() - siz
eof(m_currentPaintArtifact); |
| 363 | 393 |
| 364 // TODO(jbroman): If display items begin to have significant external memory | 394 // 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. | 395 // usage that's not shared with the embedder, we should account for it here. |
| 366 // | 396 // |
| 367 // External objects, shared with the embedder, such as SkPicture, should be | 397 // External objects, shared with the embedder, such as SkPicture, should be |
| 368 // excluded to avoid double counting. It is the embedder's responsibility to | 398 // excluded to avoid double counting. It is the embedder's responsibility to |
| 369 // count such objects. | 399 // count such objects. |
| 370 // | 400 // |
| 371 // At time of writing, the only known case of unshared external memory was | 401 // 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 | 402 // the rounded clips vector in ClipDisplayItem, which is not expected to |
| 373 // contribute significantly to memory usage. | 403 // contribute significantly to memory usage. |
| 374 | 404 |
| 375 // Memory outside this class due to m_newDisplayItemList. | 405 // Memory outside this class due to m_newDisplayItemList. |
| 376 DCHECK(m_newDisplayItemList.isEmpty()); | 406 DCHECK(m_newDisplayItemList.isEmpty()); |
| 377 memoryUsage += m_newDisplayItemList.memoryUsageInBytes(); | 407 memoryUsage += m_newDisplayItemList.memoryUsageInBytes(); |
| 378 | 408 |
| 379 return memoryUsage; | 409 return memoryUsage; |
| 380 } | 410 } |
| 381 | 411 |
| 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) | 412 void PaintController::appendDebugDrawingAfterCommit(const DisplayItemClient& dis
playItemClient, PassRefPtr<SkPicture> picture, const LayoutSize& offsetFromLayou
tObject) |
| 397 { | 413 { |
| 398 DCHECK(m_newDisplayItemList.isEmpty()); | 414 DCHECK(m_newDisplayItemList.isEmpty()); |
| 399 DrawingDisplayItem& displayItem = m_currentPaintArtifact.getDisplayItemList(
).allocateAndConstruct<DrawingDisplayItem>(displayItemClient, DisplayItem::Debug
Drawing, picture); | 415 DrawingDisplayItem& displayItem = m_currentPaintArtifact.getDisplayItemList(
).allocateAndConstruct<DrawingDisplayItem>(displayItemClient, DisplayItem::Debug
Drawing, picture); |
| 400 displayItem.setSkippedCache(); | 416 displayItem.setSkippedCache(); |
| 401 m_currentPaintArtifact.getDisplayItemList().appendVisualRect(visualRectForDi
splayItem(displayItem, offsetFromLayoutObject)); | 417 m_currentPaintArtifact.getDisplayItemList().appendVisualRect(visualRectForDi
splayItem(displayItem, offsetFromLayoutObject)); |
| 418 |
| 419 resetCurrentListIterators(); |
| 402 } | 420 } |
| 403 | 421 |
| 404 #if DCHECK_IS_ON() | 422 #if 0 // DCHECK_IS_ON() |
| 423 // TODO(wangxianzhu): Fix under-invalidation checking for the new caching method
. |
| 405 | 424 |
| 406 void PaintController::checkUnderInvalidation(DisplayItemList::iterator& newIt, D
isplayItemList::iterator& currentIt) | 425 void PaintController::checkUnderInvalidation(DisplayItemList::iterator& newIt, D
isplayItemList::iterator& currentIt) |
| 407 { | 426 { |
| 408 DCHECK(RuntimeEnabledFeatures::slimmingPaintUnderInvalidationCheckingEnabled
()); | 427 DCHECK(RuntimeEnabledFeatures::slimmingPaintUnderInvalidationCheckingEnabled
()); |
| 409 DCHECK(newIt->isCached()); | |
| 410 | 428 |
| 411 // When under-invalidation-checking is enabled, the forced painting is follo
wing the cached display item. | 429 // When under-invalidation-checking is enabled, the forced painting is follo
wing the cached display item. |
| 412 DisplayItem::Type nextItemType = DisplayItem::nonCachedType(newIt->getType()
); | 430 DisplayItem::Type nextItemType = DisplayItem::nonCachedType(newIt->getType()
); |
| 413 ++newIt; | 431 ++newIt; |
| 414 DCHECK(newIt->getType() == nextItemType); | 432 DCHECK(newIt->getType() == nextItemType); |
| 415 | 433 |
| 416 if (newIt->isDrawing()) { | 434 if (newIt->isDrawing()) { |
| 417 checkCachedDisplayItemIsUnchanged("", *newIt, *currentIt); | 435 checkCachedDisplayItemIsUnchanged("", *newIt, *currentIt); |
| 418 return; | 436 return; |
| 419 } | 437 } |
| (...skipping 97 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 517 | 535 |
| 518 void PaintController::showDebugData() const | 536 void PaintController::showDebugData() const |
| 519 { | 537 { |
| 520 WTFLogAlways("current display item list: [%s]\n", displayItemListAsDebugStri
ng(m_currentPaintArtifact.getDisplayItemList()).utf8().data()); | 538 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()); | 539 WTFLogAlways("new display item list: [%s]\n", displayItemListAsDebugString(m
_newDisplayItemList).utf8().data()); |
| 522 } | 540 } |
| 523 | 541 |
| 524 #endif // ifndef NDEBUG | 542 #endif // ifndef NDEBUG |
| 525 | 543 |
| 526 } // namespace blink | 544 } // namespace blink |
| OLD | NEW |