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