| Index: third_party/WebKit/Source/platform/graphics/paint/PaintController.cpp
|
| diff --git a/third_party/WebKit/Source/platform/graphics/paint/PaintController.cpp b/third_party/WebKit/Source/platform/graphics/paint/PaintController.cpp
|
| index 46b1bdec84ab8531d01d1bf43fc4bfee754bbd95..24e041f2dfa42c8fd87ee2decc2415e96c99bd86 100644
|
| --- a/third_party/WebKit/Source/platform/graphics/paint/PaintController.cpp
|
| +++ b/third_party/WebKit/Source/platform/graphics/paint/PaintController.cpp
|
| @@ -31,6 +31,81 @@ const PaintArtifact& PaintController::paintArtifact() const
|
| return m_currentPaintArtifact;
|
| }
|
|
|
| +bool PaintController::useCachedDrawingIfPossible(const DisplayItemClient& client, DisplayItem::Type type)
|
| +{
|
| + DCHECK(DisplayItem::isDrawingType(type));
|
| +
|
| + if (displayItemConstructionIsDisabled())
|
| + return false;
|
| +
|
| + if (!clientCacheIsValid(client))
|
| + return false;
|
| +
|
| +#if ENABLE(ASSERT)
|
| + // When under-invalidation checking is enabled, we output CachedDrawing display item
|
| + // followed by the display item containing forced painting.
|
| + if (RuntimeEnabledFeatures::slimmingPaintUnderInvalidationCheckingEnabled())
|
| + return false;
|
| +#endif
|
| +
|
| + DisplayItemList::iterator cachedItem = findCachedItem(DisplayItem::Id(client, type));
|
| + if (cachedItem == m_currentPaintArtifact.getDisplayItemList().end()) {
|
| + NOTREACHED();
|
| + return false;
|
| + }
|
| +
|
| + ++m_numCachedNewItems;
|
| + ensureNewDisplayItemListInitialCapacity();
|
| + processNewItem(m_newDisplayItemList.appendByMoving(*cachedItem));
|
| +
|
| + m_nextItemToMatch = cachedItem + 1;
|
| + // Items before m_nextItemToMatch have been copied so we don't need to index them.
|
| + if (m_nextItemToMatch - m_nextItemToIndex > 0)
|
| + m_nextItemToIndex = m_nextItemToMatch;
|
| +
|
| + return true;
|
| +}
|
| +
|
| +bool PaintController::useCachedSubsequenceIfPossible(const DisplayItemClient& client)
|
| +{
|
| + // TODO(crbug.com/596983): Implement subsequence caching for spv2.
|
| + // The problem is in copyCachedSubsequence() which fails to handle PaintChunkProperties
|
| + // of chunks containing cached display items. We need to find the previous
|
| + // PaintChunkProperties and ensure they are valid in the current paint property tree.
|
| + if (RuntimeEnabledFeatures::slimmingPaintV2Enabled())
|
| + return false;
|
| +
|
| + if (displayItemConstructionIsDisabled() || subsequenceCachingIsDisabled())
|
| + return false;
|
| +
|
| + if (!clientCacheIsValid(client))
|
| + return false;
|
| +
|
| +#if ENABLE(ASSERT)
|
| + // When under-invalidation checking is enabled, we output CachedDrawing display item
|
| + // followed by the display item containing forced painting.
|
| + if (RuntimeEnabledFeatures::slimmingPaintUnderInvalidationCheckingEnabled())
|
| + return false;
|
| +#endif
|
| +
|
| + DisplayItemList::iterator cachedItem = findCachedItem(DisplayItem::Id(client, DisplayItem::Subsequence));
|
| + if (cachedItem == m_currentPaintArtifact.getDisplayItemList().end()) {
|
| + NOTREACHED();
|
| + return false;
|
| + }
|
| +
|
| + // |cachedItem| will point to the first item after the subsequence or end of the current list.
|
| + ensureNewDisplayItemListInitialCapacity();
|
| + copyCachedSubsequence(cachedItem);
|
| +
|
| + m_nextItemToMatch = cachedItem;
|
| + // Items before |cachedItem| have been copied so we don't need to index them.
|
| + if (cachedItem - m_nextItemToIndex > 0)
|
| + m_nextItemToIndex = cachedItem;
|
| +
|
| + return true;
|
| +}
|
| +
|
| bool PaintController::lastDisplayItemIsNoopBegin() const
|
| {
|
| if (m_newDisplayItemList.isEmpty())
|
| @@ -63,11 +138,10 @@ void PaintController::removeLastDisplayItem()
|
| void PaintController::processNewItem(DisplayItem& displayItem)
|
| {
|
| DCHECK(!m_constructionDisabled);
|
| - DCHECK(!skippingCache() || !displayItem.isCached());
|
|
|
| #if CHECK_DISPLAY_ITEM_CLIENT_ALIVENESS
|
| if (!skippingCache()) {
|
| - if (displayItem.isCacheable() || displayItem.isCached()) {
|
| + if (displayItem.isCacheable()) {
|
| // Mark the client shouldKeepAlive under this PaintController.
|
| // The status will end after the new display items are committed.
|
| displayItem.client().beginShouldKeepAlive(this);
|
| @@ -88,9 +162,6 @@ void PaintController::processNewItem(DisplayItem& displayItem)
|
| }
|
| #endif
|
|
|
| - if (displayItem.isCached())
|
| - ++m_numCachedNewItems;
|
| -
|
| #if DCHECK_IS_ON()
|
| // Verify noop begin/end pairs have been removed.
|
| if (m_newDisplayItemList.size() >= 2 && displayItem.isEnd()) {
|
| @@ -104,7 +175,7 @@ void PaintController::processNewItem(DisplayItem& displayItem)
|
| displayItem.setSkippedCache();
|
|
|
| #if DCHECK_IS_ON()
|
| - size_t index = findMatchingItemFromIndex(displayItem.nonCachedId(), m_newDisplayItemIndicesByClient, m_newDisplayItemList);
|
| + size_t index = findMatchingItemFromIndex(displayItem.getId(), m_newDisplayItemIndicesByClient, m_newDisplayItemList);
|
| if (index != kNotFound) {
|
| #ifndef NDEBUG
|
| showDebugData();
|
| @@ -158,7 +229,7 @@ size_t PaintController::findMatchingItemFromIndex(const DisplayItem::Id& id, con
|
| for (size_t index : indices) {
|
| const DisplayItem& existingItem = list[index];
|
| DCHECK(!existingItem.hasValidClient() || existingItem.client() == id.client);
|
| - if (id.matches(existingItem))
|
| + if (id == existingItem.getId())
|
| return index;
|
| }
|
|
|
| @@ -176,54 +247,88 @@ void PaintController::addItemToIndexIfNeeded(const DisplayItem& displayItem, siz
|
| indices.append(index);
|
| }
|
|
|
| -struct PaintController::OutOfOrderIndexContext {
|
| - STACK_ALLOCATED();
|
| - OutOfOrderIndexContext(DisplayItemList::iterator begin) : nextItemToIndex(begin) { }
|
| -
|
| - DisplayItemList::iterator nextItemToIndex;
|
| - DisplayItemIndicesByClientMap displayItemIndicesByClient;
|
| -};
|
| -
|
| -DisplayItemList::iterator PaintController::findOutOfOrderCachedItem(const DisplayItem::Id& id, OutOfOrderIndexContext& context)
|
| +DisplayItemList::iterator PaintController::findCachedItem(const DisplayItem::Id& id)
|
| {
|
| DCHECK(clientCacheIsValid(id.client));
|
|
|
| - size_t foundIndex = findMatchingItemFromIndex(id, context.displayItemIndicesByClient, m_currentPaintArtifact.getDisplayItemList());
|
| - if (foundIndex != kNotFound)
|
| + // Try to find the item sequentially first. This is fast if the current list and the new list are in
|
| + // the same order around the new item. If found, we don't need to update and lookup the index.
|
| + DisplayItemList::iterator end = m_currentPaintArtifact.getDisplayItemList().end();
|
| + DisplayItemList::iterator it = m_nextItemToMatch;
|
| + for (; it != end; ++it) {
|
| + // We encounter an item that has already been copied which indicates we can't do sequential matching.
|
| + if (!it->hasValidClient())
|
| + break;
|
| + if (id == it->getId()) {
|
| +#if DCHECK_IS_ON()
|
| + ++m_numSequentialMatches;
|
| +#endif
|
| + return it;
|
| + }
|
| + // We encounter a different cacheable item which also indicates we can't do sequential matching.
|
| + if (it->isCacheable())
|
| + break;
|
| + }
|
| +
|
| + size_t foundIndex = findMatchingItemFromIndex(id, m_outOfOrderItemIndices, m_currentPaintArtifact.getDisplayItemList());
|
| + if (foundIndex != kNotFound) {
|
| +#if DCHECK_IS_ON()
|
| + ++m_numOutOfOrderMatches;
|
| +#endif
|
| return m_currentPaintArtifact.getDisplayItemList().begin() + foundIndex;
|
| + }
|
|
|
| - return findOutOfOrderCachedItemForward(id, context);
|
| + return findOutOfOrderCachedItemForward(id);
|
| }
|
|
|
| // Find forward for the item and index all skipped indexable items.
|
| -DisplayItemList::iterator PaintController::findOutOfOrderCachedItemForward(const DisplayItem::Id& id, OutOfOrderIndexContext& context)
|
| +DisplayItemList::iterator PaintController::findOutOfOrderCachedItemForward(const DisplayItem::Id& id)
|
| {
|
| - DisplayItemList::iterator currentEnd = m_currentPaintArtifact.getDisplayItemList().end();
|
| - for (; context.nextItemToIndex != currentEnd; ++context.nextItemToIndex) {
|
| - const DisplayItem& item = *context.nextItemToIndex;
|
| + DisplayItemList::iterator end = m_currentPaintArtifact.getDisplayItemList().end();
|
| + for (DisplayItemList::iterator it = m_nextItemToIndex; it != end; ++it) {
|
| + const DisplayItem& item = *it;
|
| DCHECK(item.hasValidClient());
|
| - if (id.matches(item))
|
| - return context.nextItemToIndex++;
|
| - if (item.isCacheable())
|
| - addItemToIndexIfNeeded(item, context.nextItemToIndex - m_currentPaintArtifact.getDisplayItemList().begin(), context.displayItemIndicesByClient);
|
| + if (id == item.getId()) {
|
| +#if DCHECK_IS_ON()
|
| + ++m_numSequentialMatches;
|
| +#endif
|
| + return it;
|
| + }
|
| + if (item.isCacheable()) {
|
| +#if DCHECK_IS_ON()
|
| + ++m_numIndexedItems;
|
| +#endif
|
| + addItemToIndexIfNeeded(item, it - m_currentPaintArtifact.getDisplayItemList().begin(), m_outOfOrderItemIndices);
|
| + }
|
| }
|
| - return currentEnd;
|
| +
|
| +#ifndef NDEBUG
|
| + showDebugData();
|
| + LOG(ERROR) << id.client.debugName() << ":" << DisplayItem::typeAsDebugString(id.type) << " not found in current display item list";
|
| +#endif
|
| + NOTREACHED();
|
| + // We did not find the cached display item. This should be impossible, but may occur if there is a bug
|
| + // in the system, such as under-invalidation, incorrect cache checking or duplicate display ids.
|
| + // In this case, the caller should fall back to repaint the display item.
|
| + return end;
|
| }
|
|
|
| -void PaintController::copyCachedSubsequence(const DisplayItemList& currentList, DisplayItemList::iterator& currentIt, DisplayItemList& updatedList, SkPictureGpuAnalyzer& gpuAnalyzer)
|
| +// On return, |it| points to the item after the EndSubsequence item of the subsequence.
|
| +void PaintController::copyCachedSubsequence(DisplayItemList::iterator& it)
|
| {
|
| - DCHECK(currentIt->getType() == DisplayItem::Subsequence);
|
| - DisplayItem::Id endSubsequenceId(currentIt->client(), DisplayItem::EndSubsequence);
|
| + DCHECK(it->getType() == DisplayItem::Subsequence);
|
| + DisplayItem::Id endSubsequenceId(it->client(), DisplayItem::EndSubsequence);
|
| do {
|
| // We should always find the EndSubsequence display item.
|
| - DCHECK(currentIt != m_currentPaintArtifact.getDisplayItemList().end());
|
| - DCHECK(currentIt->hasValidClient());
|
| + DCHECK(it != m_currentPaintArtifact.getDisplayItemList().end());
|
| + DCHECK(it->hasValidClient());
|
| #if CHECK_DISPLAY_ITEM_CLIENT_ALIVENESS
|
| - CHECK(currentIt->client().isAlive());
|
| + CHECK(it->client().isAlive());
|
| #endif
|
| - updatedList.appendByMoving(*currentIt, currentList.visualRect(currentIt - m_currentPaintArtifact.getDisplayItemList().begin()), gpuAnalyzer);
|
| - ++currentIt;
|
| - } while (!endSubsequenceId.matches(updatedList.last()));
|
| + ++m_numCachedNewItems;
|
| + processNewItem(m_newDisplayItemList.appendByMoving(*it));
|
| + ++it;
|
| + } while (endSubsequenceId != m_newDisplayItemList.last().getId());
|
| }
|
|
|
| static IntRect visualRectForDisplayItem(const DisplayItem& displayItem, const LayoutSize& offsetFromLayoutObject)
|
| @@ -233,17 +338,12 @@ static IntRect visualRectForDisplayItem(const DisplayItem& displayItem, const La
|
| return enclosingIntRect(visualRect);
|
| }
|
|
|
| -// Update the existing display items by removing invalidated entries, updating
|
| -// repainted ones, and appending new items.
|
| -// - For cached drawing display item, copy the corresponding cached DrawingDisplayItem;
|
| -// - For cached subsequence display item, copy the cached display items between the
|
| -// corresponding SubsequenceDisplayItem and EndSubsequenceDisplayItem (incl.);
|
| -// - Otherwise, copy the new display item.
|
| -//
|
| -// The algorithm is O(|m_currentDisplayItemList| + |m_newDisplayItemList|).
|
| -// Coefficients are related to the ratio of out-of-order CachedDisplayItems
|
| -// and the average number of (Drawing|Subsequence)DisplayItems per client.
|
| -//
|
| +void PaintController::resetCurrentListIterators()
|
| +{
|
| + m_nextItemToMatch = m_currentPaintArtifact.getDisplayItemList().begin();
|
| + m_nextItemToIndex = m_nextItemToMatch;
|
| +}
|
| +
|
| void PaintController::commitNewDisplayItems(const LayoutSize& offsetFromLayoutObject)
|
| {
|
| TRACE_EVENT2("blink,benchmark", "PaintController::commitNewDisplayItems",
|
| @@ -259,99 +359,37 @@ void PaintController::commitNewDisplayItems(const LayoutSize& offsetFromLayoutOb
|
|
|
| SkPictureGpuAnalyzer gpuAnalyzer;
|
|
|
| - if (m_currentPaintArtifact.isEmpty()) {
|
| -#if DCHECK_IS_ON()
|
| - for (const auto& item : m_newDisplayItemList)
|
| - DCHECK(!item.isCached());
|
| -#endif
|
| + m_currentCacheGeneration = DisplayItemClient::CacheGenerationOrInvalidationReason::next();
|
| + for (const auto& item : m_newDisplayItemList) {
|
| + // No reason to continue the analysis once we have a veto.
|
| + if (gpuAnalyzer.suitableForGpuRasterization())
|
| + item.analyzeForGpuRasterization(gpuAnalyzer);
|
|
|
| - for (const auto& item : m_newDisplayItemList) {
|
| - m_newDisplayItemList.appendVisualRect(visualRectForDisplayItem(item, offsetFromLayoutObject));
|
| - // No reason to continue the analysis once we have a veto.
|
| - if (gpuAnalyzer.suitableForGpuRasterization())
|
| - item.analyzeForGpuRasterization(gpuAnalyzer);
|
| - }
|
| - m_currentPaintArtifact = PaintArtifact(std::move(m_newDisplayItemList), m_newPaintChunks.releasePaintChunks(), gpuAnalyzer.suitableForGpuRasterization());
|
| - m_newDisplayItemList = DisplayItemList(kInitialDisplayItemListCapacityBytes);
|
| - updateCacheGeneration();
|
| - return;
|
| + m_newDisplayItemList.appendVisualRect(visualRectForDisplayItem(item, offsetFromLayoutObject));
|
| +
|
| + if (item.isCacheable())
|
| + item.client().setDisplayItemsCached(m_currentCacheGeneration);
|
| }
|
|
|
| - // Stores indices to valid DrawingDisplayItems in m_currentDisplayItems that have not been matched
|
| - // by CachedDisplayItems during synchronized matching. The indexed items will be matched
|
| - // by later out-of-order CachedDisplayItems in m_newDisplayItemList. This ensures that when
|
| - // out-of-order CachedDisplayItems occur, we only traverse at most once over m_currentDisplayItems
|
| - // looking for potential matches. Thus we can ensure that the algorithm runs in linear time.
|
| - OutOfOrderIndexContext outOfOrderIndexContext(m_currentPaintArtifact.getDisplayItemList().begin());
|
| -
|
| - // TODO(jbroman): Consider revisiting this heuristic.
|
| - DisplayItemList updatedList(std::max(m_currentPaintArtifact.getDisplayItemList().usedCapacityInBytes(), m_newDisplayItemList.usedCapacityInBytes()));
|
| - Vector<PaintChunk> updatedPaintChunks;
|
| - DisplayItemList::iterator currentIt = m_currentPaintArtifact.getDisplayItemList().begin();
|
| - DisplayItemList::iterator currentEnd = m_currentPaintArtifact.getDisplayItemList().end();
|
| - for (DisplayItemList::iterator newIt = m_newDisplayItemList.begin(); newIt != m_newDisplayItemList.end(); ++newIt) {
|
| - const DisplayItem& newDisplayItem = *newIt;
|
| - const DisplayItem::Id newDisplayItemId = newDisplayItem.nonCachedId();
|
| - bool newDisplayItemHasCachedType = newDisplayItem.getType() != newDisplayItemId.type;
|
| -
|
| - bool isSynchronized = currentIt != currentEnd && newDisplayItemId.matches(*currentIt);
|
| -
|
| - if (newDisplayItemHasCachedType) {
|
| - DCHECK(newDisplayItem.isCached());
|
| + // The new list will not be appended to again so we can release unused memory.
|
| + m_newDisplayItemList.shrinkToFit();
|
| + m_currentPaintArtifact = PaintArtifact(std::move(m_newDisplayItemList), m_newPaintChunks.releasePaintChunks(), gpuAnalyzer.suitableForGpuRasterization());
|
| + resetCurrentListIterators();
|
| + m_outOfOrderItemIndices.clear();
|
| +
|
| + // We'll allocate the initial buffer when we start the next paint.
|
| + m_newDisplayItemList = DisplayItemList(0);
|
| +
|
| #if CHECK_DISPLAY_ITEM_CLIENT_ALIVENESS
|
| - CHECK(clientCacheIsValid(newDisplayItem.client()));
|
| + CHECK(m_currentSubsequenceClients.isEmpty());
|
| + DisplayItemClient::endShouldKeepAliveAllClients(this);
|
| #endif
|
| - if (!isSynchronized) {
|
| - currentIt = findOutOfOrderCachedItem(newDisplayItemId, outOfOrderIndexContext);
|
|
|
| - if (currentIt == currentEnd) {
|
| -#ifndef NDEBUG
|
| - showDebugData();
|
| - WTFLogAlways("%s not found in m_currentDisplayItemList\n", newDisplayItem.asDebugString().utf8().data());
|
| -#endif
|
| - NOTREACHED();
|
| - // We did not find the cached display item. This should be impossible, but may occur if there is a bug
|
| - // in the system, such as under-invalidation, incorrect cache checking or duplicate display ids.
|
| - // In this case, attempt to recover rather than crashing or bailing on display of the rest of the display list.
|
| - continue;
|
| - }
|
| - }
|
| #if DCHECK_IS_ON()
|
| - if (RuntimeEnabledFeatures::slimmingPaintUnderInvalidationCheckingEnabled()) {
|
| - DisplayItemList::iterator temp = currentIt;
|
| - checkUnderInvalidation(newIt, temp);
|
| - }
|
| + m_numSequentialMatches = 0;
|
| + m_numOutOfOrderMatches = 0;
|
| + m_numIndexedItems = 0;
|
| #endif
|
| - if (newDisplayItem.isCachedDrawing()) {
|
| - updatedList.appendByMoving(*currentIt, m_currentPaintArtifact.getDisplayItemList().visualRect(currentIt - m_currentPaintArtifact.getDisplayItemList().begin()),
|
| - gpuAnalyzer);
|
| - ++currentIt;
|
| - } else {
|
| - DCHECK(newDisplayItem.getType() == DisplayItem::CachedSubsequence);
|
| - copyCachedSubsequence(m_currentPaintArtifact.getDisplayItemList(), currentIt, updatedList, gpuAnalyzer);
|
| - DCHECK(updatedList.last().getType() == DisplayItem::EndSubsequence);
|
| - }
|
| - } else {
|
| - DCHECK(!newDisplayItem.isDrawing()
|
| - || newDisplayItem.skippedCache()
|
| - || !clientCacheIsValid(newDisplayItem.client()));
|
| -
|
| - updatedList.appendByMoving(*newIt, visualRectForDisplayItem(*newIt, offsetFromLayoutObject), gpuAnalyzer);
|
| -
|
| - if (isSynchronized)
|
| - ++currentIt;
|
| - }
|
| - // Items before currentIt should have been copied so we don't need to index them.
|
| - if (currentIt - outOfOrderIndexContext.nextItemToIndex > 0)
|
| - outOfOrderIndexContext.nextItemToIndex = currentIt;
|
| - }
|
| -
|
| - // TODO(jbroman): When subsequence caching applies to SPv2, we'll need to
|
| - // merge the paint chunks as well.
|
| - m_currentPaintArtifact = PaintArtifact(std::move(updatedList), m_newPaintChunks.releasePaintChunks(), gpuAnalyzer.suitableForGpuRasterization());
|
| -
|
| - m_newDisplayItemList = DisplayItemList(kInitialDisplayItemListCapacityBytes);
|
| - updateCacheGeneration();
|
| }
|
|
|
| size_t PaintController::approximateUnsharedMemoryUsage() const
|
| @@ -379,34 +417,23 @@ size_t PaintController::approximateUnsharedMemoryUsage() const
|
| return memoryUsage;
|
| }
|
|
|
| -void PaintController::updateCacheGeneration()
|
| -{
|
| - m_currentCacheGeneration = DisplayItemClient::CacheGenerationOrInvalidationReason::next();
|
| - for (const DisplayItem& displayItem : m_currentPaintArtifact.getDisplayItemList()) {
|
| - if (!displayItem.isCacheable())
|
| - continue;
|
| - displayItem.client().setDisplayItemsCached(m_currentCacheGeneration);
|
| - }
|
| -#if CHECK_DISPLAY_ITEM_CLIENT_ALIVENESS
|
| - CHECK(m_currentSubsequenceClients.isEmpty());
|
| - DisplayItemClient::endShouldKeepAliveAllClients(this);
|
| -#endif
|
| -}
|
| -
|
| void PaintController::appendDebugDrawingAfterCommit(const DisplayItemClient& displayItemClient, PassRefPtr<SkPicture> picture, const LayoutSize& offsetFromLayoutObject)
|
| {
|
| DCHECK(m_newDisplayItemList.isEmpty());
|
| DrawingDisplayItem& displayItem = m_currentPaintArtifact.getDisplayItemList().allocateAndConstruct<DrawingDisplayItem>(displayItemClient, DisplayItem::DebugDrawing, picture);
|
| displayItem.setSkippedCache();
|
| m_currentPaintArtifact.getDisplayItemList().appendVisualRect(visualRectForDisplayItem(displayItem, offsetFromLayoutObject));
|
| +
|
| + // Need to reset the iterators after mutation of the DisplayItemList.
|
| + resetCurrentListIterators();
|
| }
|
|
|
| -#if DCHECK_IS_ON()
|
| +#if 0 // DCHECK_IS_ON()
|
| +// TODO(wangxianzhu): Fix under-invalidation checking for the new caching method.
|
|
|
| void PaintController::checkUnderInvalidation(DisplayItemList::iterator& newIt, DisplayItemList::iterator& currentIt)
|
| {
|
| DCHECK(RuntimeEnabledFeatures::slimmingPaintUnderInvalidationCheckingEnabled());
|
| - DCHECK(newIt->isCached());
|
|
|
| // When under-invalidation-checking is enabled, the forced painting is following the cached display item.
|
| DisplayItem::Type nextItemType = DisplayItem::nonCachedType(newIt->getType());
|
|
|