Chromium Code Reviews| 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)) ; | |
|
pdr.
2016/07/12 20:09:35
Super nit: "it" -> "cachedItemIterator" or "cached
Xianzhu
2016/07/13 00:28:21
Done.
| |
| 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 // 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 it = findCachedItem(DisplayItem::Id(client, Displa yItem::Subsequence)); | |
|
pdr.
2016/07/12 20:09:35
Super nit: "it" -> "firstCachedItemIterator" or "f
Xianzhu
2016/07/13 00:28:21
Renamed to cachedItem instead of firstCachedItem b
| |
| 92 if (it == m_currentPaintArtifact.getDisplayItemList().end()) { | |
| 93 NOTREACHED(); | |
| 94 return false; | |
| 95 } | |
| 96 | |
| 97 // |it| will point to the first item after the subsequence or end of the cur rent list. | |
|
pdr.
2016/07/12 20:09:35
Before copyCachedSubsequence, won't |it| point to
Xianzhu
2016/07/13 00:28:21
Before copyCachedSubsequence |it| points to the fi
| |
| 98 ensureNewDisplayItemListInitialCapacity(); | |
| 99 copyCachedSubsequence(it); | |
| 100 | |
| 101 m_nextItemToMatch = it; | |
| 102 // Items before |it| have been copied so we don't need to index them. | |
| 103 if (it - m_nextItemToIndex > 0) | |
| 104 m_nextItemToIndex = it; | |
| 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 resetCurrentListIterators(); | |
|
pdr.
2016/07/12 20:09:35
Why is this needed?
Xianzhu
2016/07/13 00:28:21
We need to reset the iterators after mutation of t
| |
| 402 } | 428 } |
| 403 | 429 |
| 404 #if DCHECK_IS_ON() | 430 #if 0 // DCHECK_IS_ON() |
| 431 // TODO(wangxianzhu): Fix under-invalidation checking for the new caching method . | |
| 405 | 432 |
| 406 void PaintController::checkUnderInvalidation(DisplayItemList::iterator& newIt, D isplayItemList::iterator& currentIt) | 433 void PaintController::checkUnderInvalidation(DisplayItemList::iterator& newIt, D isplayItemList::iterator& currentIt) |
| 407 { | 434 { |
| 408 DCHECK(RuntimeEnabledFeatures::slimmingPaintUnderInvalidationCheckingEnabled ()); | 435 DCHECK(RuntimeEnabledFeatures::slimmingPaintUnderInvalidationCheckingEnabled ()); |
| 409 DCHECK(newIt->isCached()); | |
| 410 | 436 |
| 411 // When under-invalidation-checking is enabled, the forced painting is follo wing the cached display item. | 437 // When under-invalidation-checking is enabled, the forced painting is follo wing the cached display item. |
| 412 DisplayItem::Type nextItemType = DisplayItem::nonCachedType(newIt->getType() ); | 438 DisplayItem::Type nextItemType = DisplayItem::nonCachedType(newIt->getType() ); |
| 413 ++newIt; | 439 ++newIt; |
| 414 DCHECK(newIt->getType() == nextItemType); | 440 DCHECK(newIt->getType() == nextItemType); |
| 415 | 441 |
| 416 if (newIt->isDrawing()) { | 442 if (newIt->isDrawing()) { |
| 417 checkCachedDisplayItemIsUnchanged("", *newIt, *currentIt); | 443 checkCachedDisplayItemIsUnchanged("", *newIt, *currentIt); |
| 418 return; | 444 return; |
| 419 } | 445 } |
| (...skipping 97 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 517 | 543 |
| 518 void PaintController::showDebugData() const | 544 void PaintController::showDebugData() const |
| 519 { | 545 { |
| 520 WTFLogAlways("current display item list: [%s]\n", displayItemListAsDebugStri ng(m_currentPaintArtifact.getDisplayItemList()).utf8().data()); | 546 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()); | 547 WTFLogAlways("new display item list: [%s]\n", displayItemListAsDebugString(m _newDisplayItemList).utf8().data()); |
| 522 } | 548 } |
| 523 | 549 |
| 524 #endif // ifndef NDEBUG | 550 #endif // ifndef NDEBUG |
| 525 | 551 |
| 526 } // namespace blink | 552 } // namespace blink |
| OLD | NEW |