diff options
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.cc | 342 |
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 = - ¤t_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 = ¤t_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() ? ¤t_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; |