Simplify subtree (now subsequence) caching

We no longer need paint-phase-based subtree display items because we'll
use paint layers as units of subtrees. This greatly simplifies subtree
caching.

Renamed subtree to subsequence.

BUG=410087

Review URL: https://codereview.chromium.org/1313223002

git-svn-id: svn://svn.chromium.org/blink/trunk@201282 bbb929c8-8fbe-4397-9dbb-9b2b20218538
parent d7eed001
......@@ -23,28 +23,12 @@
#include "core/paint/ScrollRecorder.h"
#include "core/paint/ScrollableAreaPainter.h"
#include "platform/graphics/paint/ClipRecorder.h"
#include "platform/graphics/paint/SubtreeRecorder.h"
#include "wtf/Optional.h"
namespace blink {
// We need to balance the benefit of subtree optimization and the cost of subtree display items.
// Only output subtree information if the block has multiple children or multiple line boxes.
static bool needsSubtreeRecorder(const LayoutBlock& layoutBlock)
{
return (layoutBlock.firstChild() && layoutBlock.firstChild()->nextSibling())
|| (layoutBlock.isLayoutBlockFlow() && toLayoutBlockFlow(layoutBlock).firstLineBox() && toLayoutBlockFlow(layoutBlock).firstLineBox()->nextLineBox());
}
void BlockPainter::paint(const PaintInfo& paintInfo, const LayoutPoint& paintOffset)
{
Optional<SubtreeRecorder> subtreeRecorder;
if (needsSubtreeRecorder(m_layoutBlock)) {
subtreeRecorder.emplace(*paintInfo.context, m_layoutBlock, paintInfo.phase);
if (subtreeRecorder->canUseCache())
return;
}
PaintInfo localPaintInfo(paintInfo);
LayoutPoint adjustedPaintOffset = paintOffset + m_layoutBlock.location();
......
......@@ -247,12 +247,10 @@ TEST_F(DisplayItemListPaintTestForSlimmingPaintV2, InlineRelayout)
InlineTextBox& newFirstTextBox = *newText.firstTextBox();
InlineTextBox& secondTextBox = *newText.firstTextBox()->nextTextBox();
EXPECT_DISPLAY_LIST_WITH_RED_FILL_IN_DEBUG(rootDisplayItemList().displayItems(), 5,
EXPECT_DISPLAY_LIST_WITH_RED_FILL_IN_DEBUG(rootDisplayItemList().displayItems(), 3,
TestDisplayItem(layoutView, DisplayItem::BoxDecorationBackground),
TestDisplayItem(divBlock, DisplayItem::paintPhaseToBeginSubtreeType(PaintPhaseForeground)),
TestDisplayItem(newFirstTextBox, DisplayItem::paintPhaseToDrawingType(PaintPhaseForeground)),
TestDisplayItem(secondTextBox, DisplayItem::paintPhaseToDrawingType(PaintPhaseForeground)),
TestDisplayItem(divBlock, DisplayItem::paintPhaseToEndSubtreeType(PaintPhaseForeground)));
TestDisplayItem(secondTextBox, DisplayItem::paintPhaseToDrawingType(PaintPhaseForeground)));
}
} // namespace blink
......@@ -680,9 +680,9 @@
'graphics/paint/ScrollDisplayItem.cpp',
'graphics/paint/ScrollDisplayItem.h',
'graphics/paint/SkPictureBuilder.h',
'graphics/paint/SubtreeDisplayItem.h',
'graphics/paint/SubtreeRecorder.cpp',
'graphics/paint/SubtreeRecorder.h',
'graphics/paint/SubsequenceDisplayItem.h',
'graphics/paint/SubsequenceRecorder.cpp',
'graphics/paint/SubsequenceRecorder.h',
'graphics/paint/Transform3DDisplayItem.cpp',
'graphics/paint/Transform3DDisplayItem.h',
'graphics/paint/TransformDisplayItem.cpp',
......
......@@ -180,13 +180,6 @@ WTF::String DisplayItem::typeAsDebugString(Type type)
if (isEndTransform3DType(type))
return "End" + transform3DTypeAsDebugString(endTransform3DTypeToTransform3DType(type));
PAINT_PHASE_BASED_DEBUG_STRINGS(CachedSubtree);
PAINT_PHASE_BASED_DEBUG_STRINGS(BeginSubtree);
PAINT_PHASE_BASED_DEBUG_STRINGS(EndSubtree);
if (type == UninitializedType)
return "UninitializedType";
switch (type) {
DEBUG_STRING_CASE(BeginFilter);
DEBUG_STRING_CASE(EndFilter);
......@@ -200,6 +193,10 @@ WTF::String DisplayItem::typeAsDebugString(Type type)
DEBUG_STRING_CASE(EndFixedPosition);
DEBUG_STRING_CASE(BeginFixedPositionContainer);
DEBUG_STRING_CASE(EndFixedPositionContainer);
DEBUG_STRING_CASE(BeginSubsequence);
DEBUG_STRING_CASE(EndSubsequence);
DEBUG_STRING_CASE(CachedSubsequence);
DEBUG_STRING_CASE(UninitializedType);
DEFAULT_CASE;
}
}
......
......@@ -164,20 +164,9 @@ public:
BeginFixedPositionContainer,
EndFixedPositionContainer,
CachedSubtreeFirst,
CachedSubtreePaintPhaseFirst = CachedSubtreeFirst,
CachedSubtreePaintPhaseLast = CachedSubtreePaintPhaseFirst + PaintPhaseMax,
CachedSubtreeLast = CachedSubtreePaintPhaseLast,
BeginSubtreeFirst,
BeginSubtreePaintPhaseFirst = BeginSubtreeFirst,
BeginSubtreePaintPhaseLast = BeginSubtreePaintPhaseFirst + PaintPhaseMax,
BeginSubtreeLast = BeginSubtreePaintPhaseLast,
EndSubtreeFirst,
EndSubtreePaintPhaseFirst = EndSubtreeFirst,
EndSubtreePaintPhaseLast = EndSubtreePaintPhaseFirst + PaintPhaseMax,
EndSubtreeLast = EndSubtreePaintPhaseLast,
BeginSubsequence,
EndSubsequence,
CachedSubsequence,
UninitializedType,
TypeLast = UninitializedType
......@@ -238,8 +227,8 @@ public:
{
if (isCachedDrawingType(type))
return cachedDrawingTypeToDrawingType(type);
if (isCachedSubtreeType(type))
return cachedSubtreeTypeToBeginSubtreeType(type);
if (type == CachedSubsequence)
return BeginSubsequence;
return type;
}
......@@ -319,19 +308,9 @@ public:
DEFINE_PAIRED_CATEGORY_METHODS(Transform3D, transform3D);
DEFINE_CATEGORY_METHODS(CachedSubtree)
DEFINE_PAINT_PHASE_CONVERSION_METHOD(CachedSubtree)
DEFINE_CATEGORY_METHODS(BeginSubtree)
DEFINE_PAINT_PHASE_CONVERSION_METHOD(BeginSubtree)
DEFINE_CATEGORY_METHODS(EndSubtree)
DEFINE_PAINT_PHASE_CONVERSION_METHOD(EndSubtree)
DEFINE_CONVERSION_METHODS(CachedSubtree, cachedSubtree, BeginSubtree, beginSubtree)
DEFINE_CONVERSION_METHODS(CachedSubtree, cachedSubtree, EndSubtree, endSubtree)
DEFINE_CONVERSION_METHODS(BeginSubtree, beginSubtree, EndSubtree, endSubtree)
static bool isCachedType(Type type) { return isCachedDrawingType(type) || isCachedSubtreeType(type); }
static bool isCachedType(Type type) { return isCachedDrawingType(type) || type == CachedSubsequence; }
bool isCached() const { return isCachedType(m_type); }
static bool isCacheableType(Type type) { return isDrawingType(type) || isBeginSubtreeType(type); }
static bool isCacheableType(Type type) { return isDrawingType(type) || type == BeginSubsequence; }
bool isCacheable() const { return !skippedCache() && isCacheableType(m_type); }
virtual bool isBegin() const { return false; }
......
......@@ -73,7 +73,7 @@ void DisplayItemList::processNewItem(DisplayItem* displayItem)
// Verify noop begin/end pairs have been removed.
if (m_newDisplayItems.size() >= 2 && displayItem->isEnd()) {
const auto& beginDisplayItem = m_newDisplayItems[m_newDisplayItems.size() - 2];
if (beginDisplayItem.isBegin() && !beginDisplayItem.drawsContent())
if (beginDisplayItem.isBegin() && beginDisplayItem.type() != DisplayItem::BeginSubsequence && !beginDisplayItem.drawsContent())
ASSERT(!displayItem->isEndAndPairedWith(beginDisplayItem.type()));
}
#endif
......@@ -178,20 +178,20 @@ DisplayItems::iterator DisplayItemList::findOutOfOrderCachedItem(DisplayItems::i
{
ASSERT(clientCacheIsValid(id.client));
// Skip indexing of copied items.
if (currentIt - context.nextItemToIndex > 0)
context.nextItemToIndex = currentIt;
size_t foundIndex = findMatchingItemFromIndex(id, context.displayItemIndicesByClient, m_currentDisplayItems);
if (foundIndex != kNotFound)
return m_currentDisplayItems.begin() + foundIndex;
return findOutOfOrderCachedItemForward(id, context);
return findOutOfOrderCachedItemForward(currentIt, id, context);
}
// Find forward for the item and index all skipped indexable items.
DisplayItems::iterator DisplayItemList::findOutOfOrderCachedItemForward(const DisplayItem::Id& id, OutOfOrderIndexContext& context)
DisplayItems::iterator DisplayItemList::findOutOfOrderCachedItemForward(DisplayItems::iterator currentIt, const DisplayItem::Id& id, OutOfOrderIndexContext& context)
{
// Items before currentIt should have been copied. Skip indexing of them.
if (currentIt - context.nextItemToIndex > 0)
context.nextItemToIndex = currentIt;
DisplayItems::iterator currentEnd = m_currentDisplayItems.end();
for (; context.nextItemToIndex != currentEnd; ++context.nextItemToIndex) {
const DisplayItem& item = *context.nextItemToIndex;
......@@ -206,30 +206,30 @@ DisplayItems::iterator DisplayItemList::findOutOfOrderCachedItemForward(const Di
return currentEnd;
}
void DisplayItemList::copyCachedSubtree(DisplayItems::iterator& currentIt, DisplayItems& updatedList)
void DisplayItemList::copyCachedSubsequence(DisplayItems::iterator& currentIt, DisplayItems& updatedList)
{
ASSERT(RuntimeEnabledFeatures::slimmingPaintV2Enabled());
ASSERT(currentIt->isBeginSubtree());
ASSERT(currentIt->type() == DisplayItem::BeginSubsequence);
ASSERT(!currentIt->scope());
DisplayItem::Id endSubtreeId(currentIt->client(), DisplayItem::beginSubtreeTypeToEndSubtreeType(currentIt->type()), 0);
DisplayItem::Id endSubsequenceId(currentIt->client(), DisplayItem::EndSubsequence, 0);
do {
// We should always find the EndSubtree display item.
// We should always find the EndSubsequence display item.
ASSERT(currentIt != m_currentDisplayItems.end());
updatedList.appendByMoving(*currentIt, currentIt->derivedSize());
++currentIt;
} while (!endSubtreeId.matches(updatedList.last()));
} while (!endSubsequenceId.matches(updatedList.last()));
}
// Update the existing display items by removing invalidated entries, updating
// repainted ones, and appending new items.
// - For CachedDisplayItem, copy the corresponding cached DrawingDisplayItem;
// - For SubtreeCachedDisplayItem, copy the cached display items between the
// corresponding BeginSubtreeDisplayItem and EndSubtreeDisplayItem (incl.);
// - For SubsequenceCachedDisplayItem, copy the cached display items between the
// corresponding BeginSubsequenceDisplayItem and EndSubsequenceDisplayItem (incl.);
// - Otherwise, copy the new display item.
//
// The algorithm is O(|m_currentDisplayItems| + |m_newDisplayItems|).
// Coefficients are related to the ratio of out-of-order [Subtree]CachedDisplayItems
// and the average number of (Drawing|BeginSubtree)DisplayItems per client.
// Coefficients are related to the ratio of out-of-order [Subsequence]CachedDisplayItems
// and the average number of (Drawing|BeginSubsequence)DisplayItems per client.
//
// TODO(pdr): Implement the DisplayListDiff algorithm for SlimmingPaintV2.
void DisplayItemList::commitNewDisplayItems(DisplayListDiff*)
......@@ -295,32 +295,28 @@ void DisplayItemList::commitNewDisplayItems(DisplayListDiff*)
ASSERT(newDisplayItem.isCached());
ASSERT(clientCacheIsValid(newDisplayItem.client()));
if (!isSynchronized) {
DisplayItems::iterator foundIt = findOutOfOrderCachedItem(currentIt, newDisplayItemId, outOfOrderIndexContext);
currentIt = findOutOfOrderCachedItem(currentIt, newDisplayItemId, outOfOrderIndexContext);
if (foundIt == currentEnd) {
if (currentIt == currentEnd) {
#ifndef NDEBUG
showDebugData();
WTFLogAlways("%s not found in m_currentDisplayItems\n", newDisplayItem.asDebugString().utf8().data());
#endif
ASSERT_NOT_REACHED();
// If foundIt == currentEnd, it means that 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.
// 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;
}
ASSERT(foundIt != currentIt); // because we are in 'if (!isSynchronized)'
currentIt = foundIt;
}
if (newDisplayItem.isCachedDrawing()) {
updatedList.appendByMoving(*currentIt, currentIt->derivedSize());
++currentIt;
} else {
ASSERT(newDisplayItem.isCachedSubtree());
copyCachedSubtree(currentIt, updatedList);
ASSERT(updatedList.last().isEndSubtree());
ASSERT(newDisplayItem.type() == DisplayItem::CachedSubsequence);
copyCachedSubsequence(currentIt, updatedList);
ASSERT(updatedList.last().type() == DisplayItem::EndSubsequence);
}
} else {
#if ENABLE(ASSERT)
......
......@@ -135,7 +135,7 @@ private:
WTF::String displayItemsAsDebugString(const DisplayItems&) const;
#endif
// Indices into PaintList of all DrawingDisplayItems and BeginSubtreeDisplayItems of each client.
// Indices into PaintList of all DrawingDisplayItems and BeginSubsequenceDisplayItems of each client.
// Temporarily used during merge to find out-of-order display items.
using DisplayItemIndicesByClientMap = HashMap<DisplayItemClient, Vector<size_t>>;
......@@ -144,8 +144,8 @@ private:
struct OutOfOrderIndexContext;
DisplayItems::iterator findOutOfOrderCachedItem(DisplayItems::iterator currentIt, const DisplayItem::Id&, OutOfOrderIndexContext&);
DisplayItems::iterator findOutOfOrderCachedItemForward(const DisplayItem::Id&, OutOfOrderIndexContext&);
void copyCachedSubtree(DisplayItems::iterator& currentIt, DisplayItems& updatedList);
DisplayItems::iterator findOutOfOrderCachedItemForward(DisplayItems::iterator currentIt, const DisplayItem::Id&, OutOfOrderIndexContext&);
void copyCachedSubsequence(DisplayItems::iterator& currentIt, DisplayItems& updatedList);
#if ENABLE(ASSERT)
// The following two methods are for checking under-invalidations
......
......@@ -2,8 +2,8 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef SubtreeDisplayItem_h
#define SubtreeDisplayItem_h
#ifndef SubsequenceDisplayItem_h
#define SubsequenceDisplayItem_h
#include "platform/geometry/FloatRect.h"
#include "platform/graphics/paint/DisplayItem.h"
......@@ -11,28 +11,24 @@
namespace blink {
class BeginSubtreeDisplayItem final : public PairedBeginDisplayItem {
class BeginSubsequenceDisplayItem final : public PairedBeginDisplayItem {
public:
BeginSubtreeDisplayItem(const DisplayItemClientWrapper& client, Type type)
: PairedBeginDisplayItem(client, type, sizeof(*this))
{
ASSERT(isBeginSubtreeType(type));
}
BeginSubsequenceDisplayItem(const DisplayItemClientWrapper& client)
: PairedBeginDisplayItem(client, BeginSubsequence, sizeof(*this))
{ }
};
class EndSubtreeDisplayItem final : public PairedEndDisplayItem {
class EndSubsequenceDisplayItem final : public PairedEndDisplayItem {
public:
EndSubtreeDisplayItem(const DisplayItemClientWrapper& client, Type type)
: PairedEndDisplayItem(client, type, sizeof(*this))
{
ASSERT(isEndSubtreeType(type));
}
EndSubsequenceDisplayItem(const DisplayItemClientWrapper& client)
: PairedEndDisplayItem(client, EndSubsequence, sizeof(*this))
{ }
#if ENABLE(ASSERT)
bool isEndAndPairedWith(DisplayItem::Type otherType) const final { return DisplayItem::isBeginSubtreeType(otherType); }
bool isEndAndPairedWith(DisplayItem::Type otherType) const final { return otherType == BeginSubsequence; }
#endif
};
} // namespace blink
#endif // SubtreeDisplayItem_h
#endif // SubsequenceDisplayItem_h
......@@ -3,56 +3,52 @@
// found in the LICENSE file.
#include "config.h"
#include "platform/graphics/paint/SubtreeRecorder.h"
#include "platform/graphics/paint/SubsequenceRecorder.h"
#include "platform/RuntimeEnabledFeatures.h"
#include "platform/graphics/GraphicsContext.h"
#include "platform/graphics/paint/CachedDisplayItem.h"
#include "platform/graphics/paint/DisplayItemList.h"
#include "platform/graphics/paint/SubtreeDisplayItem.h"
#include "platform/graphics/paint/SubsequenceDisplayItem.h"
namespace blink {
SubtreeRecorder::SubtreeRecorder(GraphicsContext& context, const DisplayItemClientWrapper& client, int paintPhase)
: m_displayItemList(context.displayItemList())
, m_client(client)
, m_paintPhase(paintPhase)
, m_canUseCache(false)
#if ENABLE(ASSERT)
, m_checkedCanUseCache(false)
#endif
bool SubsequenceRecorder::useCachedSubsequenceIfPossible(GraphicsContext& context, const DisplayItemClientWrapper& client)
{
if (!RuntimeEnabledFeatures::slimmingPaintV2Enabled())
return;
return false;
ASSERT(m_displayItemList);
ASSERT(context.displayItemList());
if (context.displayItemList()->displayItemConstructionIsDisabled() || RuntimeEnabledFeatures::slimmingPaintUnderInvalidationCheckingEnabled())
return false;
// TODO(wangxianzhu): Implement subtree caching.
if (!context.displayItemList()->clientCacheIsValid(client.displayItemClient()))
return false;
if (!m_canUseCache)
m_displayItemList->createAndAppend<BeginSubtreeDisplayItem>(m_client, DisplayItem::paintPhaseToBeginSubtreeType(paintPhase));
context.displayItemList()->createAndAppend<CachedDisplayItem>(client, DisplayItem::CachedSubsequence);
return true;
}
SubtreeRecorder::~SubtreeRecorder()
SubsequenceRecorder::SubsequenceRecorder(GraphicsContext& context, const DisplayItemClientWrapper& client)
: m_displayItemList(context.displayItemList())
, m_client(client)
{
if (!RuntimeEnabledFeatures::slimmingPaintV2Enabled())
return;
ASSERT(m_checkedCanUseCache);
if (m_canUseCache)
m_displayItemList->createAndAppend<CachedDisplayItem>(m_client, DisplayItem::paintPhaseToCachedSubtreeType(m_paintPhase));
else if (m_displayItemList->lastDisplayItemIsNoopBegin())
m_displayItemList->removeLastDisplayItem();
else
m_displayItemList->createAndAppend<EndSubtreeDisplayItem>(m_client, DisplayItem::paintPhaseToEndSubtreeType(m_paintPhase));
ASSERT(m_displayItemList);
m_displayItemList->createAndAppend<BeginSubsequenceDisplayItem>(m_client);
}
bool SubtreeRecorder::canUseCache() const
SubsequenceRecorder::~SubsequenceRecorder()
{
#if ENABLE(ASSERT)
m_checkedCanUseCache = true;
#endif
return m_canUseCache;
if (!RuntimeEnabledFeatures::slimmingPaintV2Enabled())
return;
// Don't remove no-op BeginSubsequence/EndSubsequence pairs because we need to
// match them later with CachedSubsequences.
m_displayItemList->createAndAppend<EndSubsequenceDisplayItem>(m_client);
}
} // namespace blink
......@@ -2,8 +2,8 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef SubtreeRecorder_h
#define SubtreeRecorder_h
#ifndef SubsequenceRecorder_h
#define SubsequenceRecorder_h
#include "platform/graphics/paint/DisplayItemClient.h"
......@@ -12,26 +12,26 @@ namespace blink {
class DisplayItemList;
class GraphicsContext;
// Records subtree information during painting. The recorder's life span covers
// all painting operations executed during the root of the subtree's paint method
// for the paint phase.
class PLATFORM_EXPORT SubtreeRecorder {
// SubsequenceRecorder records BeginSubsequenceDisplayItem and EndSubsequenceDisplayItem
// sentinels at either end of a continguous sequence of DisplayItems, and supports
// caching via a CachedDisplayItem with the CachedSubsequence DisplayItem type.
//
// Also note that useCachedSubsequenceIfPossible is not sufficient to determine whether a
// CachedSubsequence can be used. In particular, the client is responsible for checking that
// none of the DisplayItemClients that contribute to the subsequence have been invalidated.
//
class PLATFORM_EXPORT SubsequenceRecorder {
public:
SubtreeRecorder(GraphicsContext&, const DisplayItemClientWrapper&, int paintPhase);
~SubtreeRecorder();
static bool useCachedSubsequenceIfPossible(GraphicsContext&, const DisplayItemClientWrapper&);
bool canUseCache() const;
SubsequenceRecorder(GraphicsContext&, const DisplayItemClientWrapper&);
~SubsequenceRecorder();
private:
DisplayItemList* m_displayItemList;
DisplayItemClientWrapper m_client;
const int m_paintPhase;
bool m_canUseCache;
#if ENABLE(ASSERT)
mutable bool m_checkedCanUseCache;
#endif
};
} // namespace blink
#endif // SubtreeRecorder_h
#endif // SubsequenceRecorder_h
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment