summaryrefslogtreecommitdiff
path: root/chromium/third_party/blink/renderer/platform/graphics/paint/paint_controller.cc
diff options
context:
space:
mode:
Diffstat (limited to 'chromium/third_party/blink/renderer/platform/graphics/paint/paint_controller.cc')
-rw-r--r--chromium/third_party/blink/renderer/platform/graphics/paint/paint_controller.cc342
1 files changed, 220 insertions, 122 deletions
diff --git a/chromium/third_party/blink/renderer/platform/graphics/paint/paint_controller.cc b/chromium/third_party/blink/renderer/platform/graphics/paint/paint_controller.cc
index ff52743090b..fa69422b443 100644
--- a/chromium/third_party/blink/renderer/platform/graphics/paint/paint_controller.cc
+++ b/chromium/third_party/blink/renderer/platform/graphics/paint/paint_controller.cc
@@ -31,15 +31,57 @@ PaintController::~PaintController() {
DCHECK(usage_ == kTransient || new_display_item_list_.IsEmpty());
}
+// For micro benchmarks of record time.
+static bool g_subsequence_caching_disabled = false;
+static bool g_partial_invalidation = false;
+static int g_partial_invalidation_display_item_count = 0;
+static int g_partial_invalidation_subsequence_count = 0;
+
+// This is used to invalidate one out of every |kInvalidateDisplayItemInterval|
+// display items for the micro benchmark of record time with partial
+// invalidation.
+static bool ShouldInvalidateDisplayItemForBenchmark() {
+ constexpr int kInvalidateDisplayItemInterval = 8;
+ return g_partial_invalidation &&
+ !(g_partial_invalidation_display_item_count++ %
+ kInvalidateDisplayItemInterval);
+}
+// Similar to the above, but for subsequences.
+static bool ShouldInvalidateSubsequenceForBenchmark() {
+ constexpr int kInvalidateSubsequenceInterval = 2;
+ return g_partial_invalidation &&
+ !(g_partial_invalidation_subsequence_count++ %
+ kInvalidateSubsequenceInterval);
+}
+
+void PaintController::SetSubsequenceCachingDisabledForBenchmark() {
+ g_subsequence_caching_disabled = true;
+}
+
+void PaintController::SetPartialInvalidationForBenchmark() {
+ g_partial_invalidation = true;
+ g_partial_invalidation_display_item_count = 0;
+ g_partial_invalidation_subsequence_count = 0;
+}
+
+bool PaintController::ShouldForcePaintForBenchmark() {
+ return g_subsequence_caching_disabled || g_partial_invalidation;
+}
+
+void PaintController::ClearFlagsForBenchmark() {
+ g_subsequence_caching_disabled = false;
+ g_partial_invalidation = false;
+}
+
bool PaintController::UseCachedItemIfPossible(const DisplayItemClient& client,
DisplayItem::Type type) {
if (usage_ == kTransient)
return false;
- if (DisplayItemConstructionIsDisabled())
+ if (!ClientCacheIsValid(client))
return false;
- if (!ClientCacheIsValid(client))
+ if (ShouldInvalidateDisplayItemForBenchmark())
return false;
if (RuntimeEnabledFeatures::PaintUnderInvalidationCheckingEnabled() &&
@@ -49,7 +91,7 @@ bool PaintController::UseCachedItemIfPossible(const DisplayItemClient& client,
return false;
}
- size_t cached_item =
+ auto cached_item =
FindCachedItem(DisplayItem::Id(client, type, current_fragment_));
if (cached_item == kNotFound) {
// See FindOutOfOrderCachedItemForward() for explanation of the situation.
@@ -89,7 +131,10 @@ bool PaintController::UseCachedSubsequenceIfPossible(
if (usage_ == kTransient)
return false;
- if (DisplayItemConstructionIsDisabled() || SubsequenceCachingIsDisabled())
+ if (g_subsequence_caching_disabled)
+ return false;
+
+ if (ShouldInvalidateSubsequenceForBenchmark())
return false;
if (!ClientCacheIsValid(client))
@@ -109,7 +154,14 @@ bool PaintController::UseCachedSubsequenceIfPossible(
return false;
}
- if (current_paint_artifact_->GetDisplayItemList()[markers->start]
+ wtf_size_t start_item_index =
+ current_paint_artifact_->PaintChunks()[markers->start_chunk_index]
+ .begin_index;
+ wtf_size_t end_item_index =
+ current_paint_artifact_->PaintChunks()[markers->end_chunk_index - 1]
+ .end_index;
+ if (end_item_index > start_item_index &&
+ current_paint_artifact_->GetDisplayItemList()[start_item_index]
.IsTombstone()) {
// The subsequence has already been copied, indicating that the same client
// created multiple subsequences. If DCHECK_IS_ON(), then we should have
@@ -121,23 +173,23 @@ bool PaintController::UseCachedSubsequenceIfPossible(
EnsureNewDisplayItemListInitialCapacity();
- if (next_item_to_match_ == markers->start) {
+ if (next_item_to_match_ == start_item_index) {
// We are matching new and cached display items sequentially. Skip the
// subsequence for later sequential matching of individual display items.
- next_item_to_match_ = markers->end;
+ next_item_to_match_ = end_item_index;
// Items before |next_item_to_match_| have been copied so we don't need to
// index them.
if (next_item_to_match_ > next_item_to_index_)
next_item_to_index_ = next_item_to_match_;
}
- num_cached_new_items_ += markers->end - markers->start;
+ num_cached_new_items_ += end_item_index - start_item_index;
++num_cached_new_subsequences_;
if (RuntimeEnabledFeatures::PaintUnderInvalidationCheckingEnabled()) {
DCHECK(!IsCheckingUnderInvalidation());
- under_invalidation_checking_begin_ = markers->start;
- under_invalidation_checking_end_ = markers->end;
+ under_invalidation_checking_begin_ = start_item_index;
+ under_invalidation_checking_end_ = end_item_index;
under_invalidation_message_prefix_ =
"(In cached subsequence for " + client.DebugName() + ")";
// Return false to let the painter actually paint. We will check if the new
@@ -145,9 +197,9 @@ bool PaintController::UseCachedSubsequenceIfPossible(
return false;
}
- size_t start = BeginSubsequence();
- CopyCachedSubsequence(markers->start, markers->end);
- EndSubsequence(client, start);
+ auto new_start_chunk_index = BeginSubsequence();
+ CopyCachedSubsequence(markers->start_chunk_index, markers->end_chunk_index);
+ EndSubsequence(client, new_start_chunk_index);
return true;
}
@@ -159,33 +211,50 @@ PaintController::SubsequenceMarkers* PaintController::GetSubsequenceMarkers(
return &result->value;
}
-size_t PaintController::BeginSubsequence() {
+wtf_size_t PaintController::BeginSubsequence() {
// Force new paint chunk which is required for subsequence caching.
- new_paint_chunks_.ForceNewChunk();
- return new_display_item_list_.size();
+ SetForceNewChunk(true);
+ return new_paint_chunks_.size();
}
void PaintController::EndSubsequence(const DisplayItemClient& client,
- size_t start) {
- size_t end = new_display_item_list_.size();
+ wtf_size_t start_chunk_index) {
+ auto end_chunk_index = new_paint_chunks_.size();
if (RuntimeEnabledFeatures::PaintUnderInvalidationCheckingEnabled() &&
IsCheckingUnderInvalidation()) {
SubsequenceMarkers* markers = GetSubsequenceMarkers(client);
- if (!markers && start != end) {
- ShowSequenceUnderInvalidationError(
- "under-invalidation : unexpected subsequence", client, start, end);
- CHECK(false);
- }
- if (markers && markers->end - markers->start != end - start) {
- ShowSequenceUnderInvalidationError(
- "under-invalidation: new subsequence wrong length", client, start,
- end);
- CHECK(false);
+ if (!markers) {
+ if (start_chunk_index != end_chunk_index) {
+ ShowSequenceUnderInvalidationError(
+ "under-invalidation : unexpected subsequence", client);
+ CHECK(false);
+ }
+ } else {
+ if (markers->end_chunk_index - markers->start_chunk_index !=
+ end_chunk_index - start_chunk_index) {
+ ShowSequenceUnderInvalidationError(
+ "under-invalidation: new subsequence wrong length", client);
+ CHECK(false);
+ }
+ auto old_chunk_index = markers->start_chunk_index;
+ for (auto new_chunk_index = start_chunk_index;
+ new_chunk_index < end_chunk_index;
+ ++new_chunk_index, ++old_chunk_index) {
+ const auto& old_chunk =
+ current_paint_artifact_->PaintChunks()[old_chunk_index];
+ const auto& new_chunk =
+ new_paint_chunks_.PaintChunks()[new_chunk_index];
+ if (!old_chunk.EqualsForUnderInvalidationChecking(new_chunk)) {
+ ShowSequenceUnderInvalidationError(
+ "under-invalidation: chunk changed", client);
+ CHECK(false) << "Changed chunk: " << new_chunk;
+ }
+ }
}
}
- if (start == end) {
+ if (start_chunk_index == end_chunk_index) {
// Omit the empty subsequence. The forcing-new-chunk flag set by
// BeginSubsequence() still applies, but this not a big deal because empty
// subsequences are not common. Also we should not clear the flag because
@@ -194,35 +263,24 @@ void PaintController::EndSubsequence(const DisplayItemClient& client,
}
// Force new paint chunk which is required for subsequence caching.
- new_paint_chunks_.ForceNewChunk();
+ SetForceNewChunk(true);
DCHECK(!new_cached_subsequences_.Contains(&client))
<< "Multiple subsequences for client: " << client.DebugName();
- new_cached_subsequences_.insert(&client, SubsequenceMarkers(start, end));
+ new_cached_subsequences_.insert(
+ &client, SubsequenceMarkers{start_chunk_index, end_chunk_index});
}
-void PaintController::ProcessNewItem(DisplayItem& display_item) {
- DCHECK(!construction_disabled_);
-
- if (IsSkippingCache() && usage_ == kMultiplePaints) {
- display_item.Client().Invalidate(PaintInvalidationReason::kUncacheable);
- display_item.SetUncacheable();
- }
-
- bool chunk_added = new_paint_chunks_.IncrementDisplayItemIndex(display_item);
+void PaintController::DidAppendItem(DisplayItem& display_item) {
+ if (usage_ == kTransient)
+ return;
#if DCHECK_IS_ON()
- if (chunk_added && CurrentPaintChunk().is_cacheable) {
- AddToIndicesByClientMap(CurrentPaintChunk().id.client,
- new_paint_chunks_.LastChunkIndex(),
- new_paint_chunk_indices_by_client_);
- }
-
- if (usage_ == kMultiplePaints && display_item.IsCacheable()) {
- size_t index = FindMatchingItemFromIndex(
- display_item.GetId(), new_display_item_indices_by_client_,
- new_display_item_list_);
+ if (display_item.IsCacheable()) {
+ auto index = FindMatchingItemFromIndex(display_item.GetId(),
+ new_display_item_indices_by_client_,
+ new_display_item_list_);
if (index != kNotFound) {
ShowDebugData();
NOTREACHED() << "DisplayItem " << display_item.AsDebugString().Utf8()
@@ -234,13 +292,20 @@ void PaintController::ProcessNewItem(DisplayItem& display_item) {
new_display_item_list_.size() - 1,
new_display_item_indices_by_client_);
}
-#else // DCHECK_IS_ON()
- std::ignore = chunk_added;
#endif
- if (usage_ == kMultiplePaints &&
- RuntimeEnabledFeatures::PaintUnderInvalidationCheckingEnabled())
+ if (RuntimeEnabledFeatures::PaintUnderInvalidationCheckingEnabled())
CheckUnderInvalidation();
+}
+
+void PaintController::ProcessNewItem(DisplayItem& display_item) {
+ if (IsSkippingCache() && usage_ == kMultiplePaints) {
+ display_item.Client().Invalidate(PaintInvalidationReason::kUncacheable);
+ display_item.SetUncacheable();
+ }
+
+ if (new_paint_chunks_.IncrementDisplayItemIndex(display_item))
+ DidAppendChunk();
if (!frame_first_paints_.back().first_painted && display_item.IsDrawing() &&
// Here we ignore all document-background paintings because we don't
@@ -251,13 +316,26 @@ void PaintController::ProcessNewItem(DisplayItem& display_item) {
display_item.DrawsContent()) {
SetFirstPainted();
}
+
+ DidAppendItem(display_item);
}
-DisplayItem& PaintController::MoveItemFromCurrentListToNewList(size_t index) {
+DisplayItem& PaintController::MoveItemFromCurrentListToNewList(
+ wtf_size_t index) {
return new_display_item_list_.AppendByMoving(
current_paint_artifact_->GetDisplayItemList()[index]);
}
+void PaintController::DidAppendChunk() {
+#if DCHECK_IS_ON()
+ if (new_paint_chunks_.LastChunk().is_cacheable) {
+ AddToIndicesByClientMap(new_paint_chunks_.LastChunk().id.client,
+ new_paint_chunks_.size() - 1,
+ new_paint_chunk_indices_by_client_);
+ }
+#endif
+}
+
void PaintController::InvalidateAll() {
DCHECK(!RuntimeEnabledFeatures::CompositeAfterPaintEnabled());
InvalidateAllInternal();
@@ -278,6 +356,25 @@ bool PaintController::CacheIsAllInvalid() const {
return cache_is_all_invalid_;
}
+void PaintController::UpdateCurrentPaintChunkProperties(
+ const PaintChunk::Id* id,
+ const PropertyTreeState& properties) {
+ if (id) {
+ PaintChunk::Id id_with_fragment(*id, current_fragment_);
+ new_paint_chunks_.UpdateCurrentPaintChunkProperties(&id_with_fragment,
+ properties);
+ CheckDuplicatePaintChunkId(id_with_fragment);
+ } else {
+ new_paint_chunks_.UpdateCurrentPaintChunkProperties(nullptr, properties);
+ }
+}
+
+void PaintController::AppendChunkByMoving(PaintChunk&& chunk) {
+ CheckDuplicatePaintChunkId(chunk.id);
+ new_paint_chunks_.AppendByMoving(std::move(chunk));
+ DidAppendChunk();
+}
+
bool PaintController::ClientCacheIsValid(
const DisplayItemClient& client) const {
#if DCHECK_IS_ON()
@@ -288,7 +385,7 @@ bool PaintController::ClientCacheIsValid(
return client.IsValid();
}
-size_t PaintController::FindMatchingItemFromIndex(
+wtf_size_t PaintController::FindMatchingItemFromIndex(
const DisplayItem::Id& id,
const IndicesByClientMap& display_item_indices_by_client,
const DisplayItemList& list) {
@@ -297,8 +394,7 @@ size_t PaintController::FindMatchingItemFromIndex(
if (it == display_item_indices_by_client.end())
return kNotFound;
- const Vector<size_t>& indices = it->value;
- for (size_t index : indices) {
+ for (auto index : it->value) {
const DisplayItem& existing_item = list[index];
if (existing_item.IsTombstone())
continue;
@@ -311,17 +407,17 @@ size_t PaintController::FindMatchingItemFromIndex(
}
void PaintController::AddToIndicesByClientMap(const DisplayItemClient& client,
- size_t index,
+ wtf_size_t index,
IndicesByClientMap& map) {
auto it = map.find(&client);
auto& indices =
it == map.end()
- ? map.insert(&client, Vector<size_t>()).stored_value->value
+ ? map.insert(&client, Vector<wtf_size_t>()).stored_value->value
: it->value;
indices.push_back(index);
}
-size_t PaintController::FindCachedItem(const DisplayItem::Id& id) {
+wtf_size_t PaintController::FindCachedItem(const DisplayItem::Id& id) {
DCHECK(ClientCacheIsValid(id.client));
if (next_item_to_match_ <
@@ -342,7 +438,7 @@ size_t PaintController::FindCachedItem(const DisplayItem::Id& id) {
}
}
- size_t found_index =
+ wtf_size_t found_index =
FindMatchingItemFromIndex(id, out_of_order_item_indices_,
current_paint_artifact_->GetDisplayItemList());
if (found_index != kNotFound) {
@@ -356,9 +452,9 @@ size_t PaintController::FindCachedItem(const DisplayItem::Id& id) {
}
// Find forward for the item and index all skipped indexable items.
-size_t PaintController::FindOutOfOrderCachedItemForward(
+wtf_size_t PaintController::FindOutOfOrderCachedItemForward(
const DisplayItem::Id& id) {
- for (size_t i = next_item_to_index_;
+ for (auto i = next_item_to_index_;
i < current_paint_artifact_->GetDisplayItemList().size(); ++i) {
const DisplayItem& item = current_paint_artifact_->GetDisplayItemList()[i];
if (item.IsTombstone())
@@ -398,64 +494,48 @@ size_t PaintController::FindOutOfOrderCachedItemForward(
// When paintUnderInvaldiationCheckingEnabled() we'll not actually
// copy the subsequence, but mark the begin and end of the subsequence for
// under-invalidation checking.
-void PaintController::CopyCachedSubsequence(size_t begin_index,
- size_t end_index) {
+void PaintController::CopyCachedSubsequence(wtf_size_t start_chunk_index,
+ wtf_size_t end_chunk_index) {
+#if DCHECK_IS_ON()
DCHECK(!RuntimeEnabledFeatures::PaintUnderInvalidationCheckingEnabled());
-
- const DisplayItem* cached_item =
- &current_paint_artifact_->GetDisplayItemList()[begin_index];
-
- auto* cached_chunk =
- current_paint_artifact_->FindChunkByDisplayItemIndex(begin_index);
- DCHECK(cached_chunk != current_paint_artifact_->PaintChunks().end());
auto properties_before_subsequence =
new_paint_chunks_.CurrentPaintChunkProperties();
- UpdateCurrentPaintChunkPropertiesUsingIdWithFragment(
- cached_chunk->id, cached_chunk->properties.GetPropertyTreeState());
-
- for (size_t current_index = begin_index; current_index < end_index;
- ++current_index) {
- cached_item = &current_paint_artifact_->GetDisplayItemList()[current_index];
- SECURITY_CHECK(!cached_item->IsTombstone());
-#if DCHECK_IS_ON()
- DCHECK(cached_item->Client().IsAlive());
#endif
- if (current_index == cached_chunk->end_index) {
- ++cached_chunk;
- DCHECK(cached_chunk != current_paint_artifact_->PaintChunks().end());
- new_paint_chunks_.ForceNewChunk();
- UpdateCurrentPaintChunkPropertiesUsingIdWithFragment(
- cached_chunk->id, cached_chunk->properties.GetPropertyTreeState());
- }
-
+ for (auto chunk_index = start_chunk_index; chunk_index < end_chunk_index;
+ ++chunk_index) {
+ auto& cached_chunk = current_paint_artifact_->PaintChunks()[chunk_index];
+ auto cached_item_index = cached_chunk.begin_index;
+ for (auto& cached_item :
+ current_paint_artifact_->GetDisplayItemList().ItemsInPaintChunk(
+ cached_chunk)) {
+ SECURITY_CHECK(!cached_item.IsTombstone());
#if DCHECK_IS_ON()
- // Visual rect change should not happen in a cached subsequence.
- // However, because of different method of pixel snapping in different
- // paths, there are false positives. Just log an error.
- if (cached_item->VisualRect() != cached_item->Client().VisualRect()) {
- DLOG(ERROR) << "Visual rect changed in a cached subsequence: "
- << cached_item->Client().DebugName()
- << " old=" << cached_item->VisualRect()
- << " new=" << cached_item->Client().VisualRect();
- }
+ DCHECK(cached_item.Client().IsAlive());
+ // Visual rect change should not happen in a cached subsequence.
+ // However, because of different method of pixel snapping in different
+ // paths, there are false positives. Just log an error.
+ if (cached_item.VisualRect() != cached_item.Client().VisualRect()) {
+ DLOG(ERROR) << "Visual rect changed in a cached subsequence: "
+ << cached_item.Client().DebugName()
+ << " old=" << cached_item.VisualRect()
+ << " new=" << cached_item.Client().VisualRect();
+ }
#endif
+ auto& item = MoveItemFromCurrentListToNewList(cached_item_index++);
+ item.SetMovedFromCachedSubsequence(true);
+ DidAppendItem(item);
+ }
- ProcessNewItem(MoveItemFromCurrentListToNewList(current_index));
- DCHECK((!CurrentPaintChunk().is_cacheable && !cached_chunk->is_cacheable) ||
- CurrentPaintChunk().Matches(*cached_chunk));
+ DCHECK_EQ(cached_item_index, cached_chunk.end_index);
+ AppendChunkByMoving(std::move(cached_chunk));
}
- if (RuntimeEnabledFeatures::PaintUnderInvalidationCheckingEnabled()) {
- under_invalidation_checking_end_ = end_index;
- DCHECK(IsCheckingUnderInvalidation());
- } else {
- // Restore properties and force new chunk for any trailing display items
- // after the cached subsequence without new properties.
- new_paint_chunks_.ForceNewChunk();
- UpdateCurrentPaintChunkProperties(base::nullopt,
- properties_before_subsequence);
- }
+ SetForceNewChunk(true);
+
+#if DCHECK_IS_ON()
+ DCHECK_EQ(properties_before_subsequence, CurrentPaintChunkProperties());
+#endif
}
void PaintController::ResetCurrentListIndices() {
@@ -527,13 +607,31 @@ void PaintController::FinishCycle() {
}
for (const auto& item : current_paint_artifact_->GetDisplayItemList()) {
const auto& client = item.Client();
+ if (item.IsMovedFromCachedSubsequence()) {
+ // We don't need to validate the clients of a display item that is
+ // copied from a cached subsequence, because it should be already
+ // valid. See http://crbug.com/1050090 for more details.
+#if DCHECK_IS_ON()
+ DCHECK(client.IsAlive());
+ DCHECK(client.IsValid() || !client.IsCacheable());
+#endif
+ continue;
+ }
client.ClearPartialInvalidationVisualRect();
if (client.IsCacheable())
client.Validate();
}
for (const auto& chunk : current_paint_artifact_->PaintChunks()) {
- if (chunk.id.client.IsCacheable())
- chunk.id.client.Validate();
+ const auto& client = chunk.id.client;
+ if (chunk.is_moved_from_cached_subsequence) {
+#if DCHECK_IS_ON()
+ DCHECK(client.IsAlive());
+ DCHECK(client.IsValid() || !client.IsCacheable());
+#endif
+ continue;
+ }
+ if (client.IsCacheable())
+ client.Validate();
}
}
@@ -638,9 +736,7 @@ void PaintController::ShowUnderInvalidationError(
void PaintController::ShowSequenceUnderInvalidationError(
const char* reason,
- const DisplayItemClient& client,
- int start,
- int end) {
+ const DisplayItemClient& client) {
LOG(ERROR) << under_invalidation_message_prefix_ << " " << reason;
LOG(ERROR) << "Subsequence client: " << client.DebugName();
#if DCHECK_IS_ON()
@@ -669,7 +765,7 @@ void PaintController::CheckUnderInvalidation() {
}
const DisplayItem& new_item = new_display_item_list_.Last();
- size_t old_item_index = under_invalidation_checking_begin_;
+ auto old_item_index = under_invalidation_checking_begin_;
DisplayItem* old_item =
old_item_index < current_paint_artifact_->GetDisplayItemList().size()
? &current_paint_artifact_->GetDisplayItemList()[old_item_index]
@@ -720,19 +816,21 @@ FrameFirstPaint PaintController::EndFrame(const void* frame) {
return result;
}
-#if DCHECK_IS_ON()
void PaintController::CheckDuplicatePaintChunkId(const PaintChunk::Id& id) {
+#if DCHECK_IS_ON()
if (IsSkippingCache())
return;
- if (DisplayItem::IsForeignLayerType(id.type))
+ if (DisplayItem::IsGraphicsLayerWrapperType(id.type) ||
+ DisplayItem::IsForeignLayerType(id.type)) {
return;
+ }
auto it = new_paint_chunk_indices_by_client_.find(&id.client);
if (it != new_paint_chunk_indices_by_client_.end()) {
const auto& indices = it->value;
for (auto index : indices) {
- const auto& chunk = new_paint_chunks_.PaintChunkAt(index);
+ const auto& chunk = new_paint_chunks_.PaintChunks()[index];
if (chunk.id == id) {
ShowDebugData();
NOTREACHED() << "New paint chunk id " << id
@@ -740,8 +838,8 @@ void PaintController::CheckDuplicatePaintChunkId(const PaintChunk::Id& id) {
}
}
}
-}
#endif
+}
size_t PaintController::sum_num_items_ = 0;
size_t PaintController::sum_num_cached_items_ = 0;