diff options
Diffstat (limited to 'chromium/third_party/blink/renderer/platform/graphics/compositing/composited_layer_raster_invalidator.cc')
-rw-r--r-- | chromium/third_party/blink/renderer/platform/graphics/compositing/composited_layer_raster_invalidator.cc | 337 |
1 files changed, 337 insertions, 0 deletions
diff --git a/chromium/third_party/blink/renderer/platform/graphics/compositing/composited_layer_raster_invalidator.cc b/chromium/third_party/blink/renderer/platform/graphics/compositing/composited_layer_raster_invalidator.cc new file mode 100644 index 00000000000..33942364fc2 --- /dev/null +++ b/chromium/third_party/blink/renderer/platform/graphics/compositing/composited_layer_raster_invalidator.cc @@ -0,0 +1,337 @@ +// Copyright 2015 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "third_party/blink/renderer/platform/graphics/compositing/composited_layer_raster_invalidator.h" + +#include <algorithm> +#include <memory> +#include <utility> + +#include "third_party/blink/renderer/platform/graphics/compositing/paint_chunks_to_cc_layer.h" +#include "third_party/blink/renderer/platform/graphics/paint/geometry_mapper.h" +#include "third_party/blink/renderer/platform/graphics/paint/paint_chunk_subset.h" + +namespace blink { + +void CompositedLayerRasterInvalidator::SetTracksRasterInvalidations( + bool should_track) { + if (should_track) { + if (!tracking_info_) + tracking_info_ = std::make_unique<RasterInvalidationTrackingInfo>(); + tracking_info_->tracking.ClearInvalidations(); + for (const auto& info : paint_chunks_info_) { + tracking_info_->old_client_debug_names.Set(&info.id.client, + info.id.client.DebugName()); + } + } else if (!RuntimeEnabledFeatures::PaintUnderInvalidationCheckingEnabled()) { + tracking_info_ = nullptr; + } else if (tracking_info_) { + tracking_info_->tracking.ClearInvalidations(); + } +} + +size_t CompositedLayerRasterInvalidator::MatchNewChunkToOldChunk( + const PaintChunk& new_chunk, + size_t old_index) { + for (size_t i = old_index; i < paint_chunks_info_.size(); i++) { + if (paint_chunks_info_[i].Matches(new_chunk)) + return i; + } + for (size_t i = 0; i < old_index; i++) { + if (paint_chunks_info_[i].Matches(new_chunk)) + return i; + } + return kNotFound; +} + +static bool ApproximatelyEqual(const SkMatrix& a, const SkMatrix& b) { + static constexpr float kTolerance = 1e-5f; + for (int i = 0; i < 9; i++) { + if (std::abs(a[i] - b[i]) > kTolerance) + return false; + } + return true; +} + +PaintInvalidationReason +CompositedLayerRasterInvalidator::ChunkPropertiesChanged( + const PaintChunkInfo& new_chunk, + const PaintChunkInfo& old_chunk, + const PropertyTreeState& layer_state) const { + if (new_chunk.properties.backface_hidden != + old_chunk.properties.backface_hidden) + return PaintInvalidationReason::kPaintProperty; + + // Special case for transform changes because we may create or delete some + // transform nodes when no raster invalidation is needed. For example, when + // a composited layer previously not transformed now gets transformed. + // Check for real accumulated transform change instead. + if (!ApproximatelyEqual(new_chunk.chunk_to_layer_transform, + old_chunk.chunk_to_layer_transform)) + return PaintInvalidationReason::kPaintProperty; + + // Treat the chunk property as changed if the effect node pointer is + // different, or the effect node's value changed between the layer state and + // the chunk state. + const auto& new_chunk_state = new_chunk.properties.property_tree_state; + const auto& old_chunk_state = old_chunk.properties.property_tree_state; + if (new_chunk_state.Effect() != old_chunk_state.Effect() || + new_chunk_state.Effect()->Changed(*layer_state.Effect())) + return PaintInvalidationReason::kPaintProperty; + + // Check for accumulated clip rect change, if the clip rects are tight. + if (new_chunk.chunk_to_layer_clip.IsTight() && + old_chunk.chunk_to_layer_clip.IsTight()) { + if (new_chunk.chunk_to_layer_clip == old_chunk.chunk_to_layer_clip) + return PaintInvalidationReason::kNone; + // Ignore differences out of the current layer bounds. + if (ClipByLayerBounds(new_chunk.chunk_to_layer_clip.Rect()) == + ClipByLayerBounds(old_chunk.chunk_to_layer_clip.Rect())) + return PaintInvalidationReason::kNone; + return PaintInvalidationReason::kIncremental; + } + + // Otherwise treat the chunk property as changed if the clip node pointer is + // different, or the clip node's value changed between the layer state and the + // chunk state. + if (new_chunk_state.Clip() != old_chunk_state.Clip() || + new_chunk_state.Clip()->Changed(*layer_state.Clip())) + return PaintInvalidationReason::kPaintProperty; + + return PaintInvalidationReason::kNone; +} + +// Generates raster invalidations by checking changes (appearing, disappearing, +// reordering, property changes) of chunks. The logic is similar to +// PaintController::GenerateRasterInvalidations(). The complexity is between +// O(n) and O(m*n) where m and n are the numbers of old and new chunks, +// respectively. Normally both m and n are small numbers. The best caseis that +// all old chunks have matching new chunks in the same order. The worst case is +// that no matching chunks except the first one (which always matches otherwise +// we won't reuse the CompositedLayerRasterInvalidator), which is rare. In +// common cases that most of the chunks can be matched in-order, the complexity +// is slightly larger than O(n). +void CompositedLayerRasterInvalidator::GenerateRasterInvalidations( + const PaintChunkSubset& new_chunks, + const PropertyTreeState& layer_state, + const FloatSize& visual_rect_subpixel_offset, + Vector<PaintChunkInfo>& new_chunks_info) { + ChunkToLayerMapper mapper(layer_state, layer_bounds_.OffsetFromOrigin(), + visual_rect_subpixel_offset); + Vector<bool> old_chunks_matched; + old_chunks_matched.resize(paint_chunks_info_.size()); + size_t old_index = 0; + size_t max_matched_old_index = 0; + for (const auto& new_chunk : new_chunks) { + mapper.SwitchToChunk(new_chunk); + auto& new_chunk_info = + new_chunks_info.emplace_back(*this, mapper, new_chunk); + + if (!new_chunk.is_cacheable) { + FullyInvalidateNewChunk(new_chunk_info, + PaintInvalidationReason::kChunkUncacheable); + continue; + } + + size_t matched_old_index = MatchNewChunkToOldChunk(new_chunk, old_index); + if (matched_old_index == kNotFound) { + // The new chunk doesn't match any old chunk. + FullyInvalidateNewChunk(new_chunk_info, + PaintInvalidationReason::kAppeared); + continue; + } + + DCHECK(!old_chunks_matched[matched_old_index]); + old_chunks_matched[matched_old_index] = true; + + auto& old_chunk_info = paint_chunks_info_[matched_old_index]; + // Clip the old chunk bounds by the new layer bounds. + old_chunk_info.bounds_in_layer = + ClipByLayerBounds(old_chunk_info.bounds_in_layer); + + PaintInvalidationReason reason = + matched_old_index < max_matched_old_index + ? PaintInvalidationReason::kChunkReordered + : ChunkPropertiesChanged(new_chunk_info, old_chunk_info, + layer_state); + + if (IsFullPaintInvalidationReason(reason)) { + // Invalidate both old and new bounds of the chunk if the chunk's paint + // properties changed, or is moved backward and may expose area that was + // previously covered by it. + FullyInvalidateChunk(old_chunk_info, new_chunk_info, reason); + // Ignore the display item raster invalidations because we have fully + // invalidated the chunk. + } else { + // We may have ignored tiny changes of transform, in which case we should + // use the old chunk_to_layer_transform for later comparison to correctly + // invalidate animating transform in tiny increments when the accumulated + // change exceeds the tolerance. + new_chunk_info.chunk_to_layer_transform = + old_chunk_info.chunk_to_layer_transform; + + if (reason == PaintInvalidationReason::kIncremental) + IncrementallyInvalidateChunk(old_chunk_info, new_chunk_info); + + // Add the raster invalidations found by PaintController within the chunk. + AddDisplayItemRasterInvalidations(new_chunk, mapper); + } + + old_index = matched_old_index + 1; + if (old_index == paint_chunks_info_.size()) + old_index = 0; + max_matched_old_index = std::max(max_matched_old_index, matched_old_index); + } + + // Invalidate remaining unmatched (disappeared or uncacheable) old chunks. + for (size_t i = 0; i < paint_chunks_info_.size(); ++i) { + if (old_chunks_matched[i]) + continue; + FullyInvalidateOldChunk(paint_chunks_info_[i], + paint_chunks_info_[i].is_cacheable + ? PaintInvalidationReason::kDisappeared + : PaintInvalidationReason::kChunkUncacheable); + } +} + +void CompositedLayerRasterInvalidator::AddDisplayItemRasterInvalidations( + const PaintChunk& chunk, + const ChunkToLayerMapper& mapper) { + DCHECK(chunk.raster_invalidation_tracking.IsEmpty() || + chunk.raster_invalidation_rects.size() == + chunk.raster_invalidation_tracking.size()); + + if (chunk.raster_invalidation_rects.IsEmpty()) + return; + + for (size_t i = 0; i < chunk.raster_invalidation_rects.size(); ++i) { + auto rect = ClipByLayerBounds( + mapper.MapVisualRect(chunk.raster_invalidation_rects[i])); + if (rect.IsEmpty()) + continue; + raster_invalidation_function_(rect); + + if (!chunk.raster_invalidation_tracking.IsEmpty()) { + const auto& info = chunk.raster_invalidation_tracking[i]; + tracking_info_->tracking.AddInvalidation( + info.client, info.client_debug_name, rect, info.reason); + } + } +} + +void CompositedLayerRasterInvalidator::IncrementallyInvalidateChunk( + const PaintChunkInfo& old_chunk, + const PaintChunkInfo& new_chunk) { + SkRegion diff(old_chunk.bounds_in_layer); + diff.op(new_chunk.bounds_in_layer, SkRegion::kXOR_Op); + for (SkRegion::Iterator it(diff); !it.done(); it.next()) { + const SkIRect& r = it.rect(); + AddRasterInvalidation(IntRect(r.x(), r.y(), r.width(), r.height()), + &new_chunk.id.client, + PaintInvalidationReason::kIncremental); + } +} + +void CompositedLayerRasterInvalidator::FullyInvalidateChunk( + const PaintChunkInfo& old_chunk, + const PaintChunkInfo& new_chunk, + PaintInvalidationReason reason) { + FullyInvalidateOldChunk(old_chunk, reason); + if (old_chunk.bounds_in_layer != new_chunk.bounds_in_layer) + FullyInvalidateNewChunk(new_chunk, reason); +} + +void CompositedLayerRasterInvalidator::FullyInvalidateNewChunk( + const PaintChunkInfo& info, + PaintInvalidationReason reason) { + AddRasterInvalidation(info.bounds_in_layer, &info.id.client, reason); +} + +void CompositedLayerRasterInvalidator::FullyInvalidateOldChunk( + const PaintChunkInfo& info, + PaintInvalidationReason reason) { + String debug_name; + if (tracking_info_) + debug_name = tracking_info_->old_client_debug_names.at(&info.id.client); + AddRasterInvalidation(info.bounds_in_layer, &info.id.client, reason, + &debug_name); +} + +void CompositedLayerRasterInvalidator::AddRasterInvalidation( + const IntRect& rect, + const DisplayItemClient* client, + PaintInvalidationReason reason, + const String* debug_name) { + raster_invalidation_function_(rect); + if (tracking_info_) { + tracking_info_->tracking.AddInvalidation( + client, debug_name ? *debug_name : client->DebugName(), rect, reason); + } +} + +RasterInvalidationTracking& CompositedLayerRasterInvalidator::EnsureTracking() { + if (!tracking_info_) + tracking_info_ = std::make_unique<RasterInvalidationTrackingInfo>(); + return tracking_info_->tracking; +} + +void CompositedLayerRasterInvalidator::Generate( + const gfx::Rect& layer_bounds, + const PaintChunkSubset& paint_chunks, + const PropertyTreeState& layer_state, + const FloatSize& visual_rect_subpixel_offset) { + if (RuntimeEnabledFeatures::DisableRasterInvalidationEnabled()) + return; + + if (RuntimeEnabledFeatures::PaintUnderInvalidationCheckingEnabled()) + EnsureTracking(); + + if (tracking_info_) { + for (const auto& chunk : paint_chunks) { + tracking_info_->new_client_debug_names.insert( + &chunk.id.client, chunk.id.client.DebugName()); + } + } + + bool layer_bounds_was_empty = layer_bounds_.IsEmpty(); + layer_bounds_ = layer_bounds; + + Vector<PaintChunkInfo> new_chunks_info; + new_chunks_info.ReserveCapacity(paint_chunks.size()); + + if (layer_bounds_was_empty || layer_bounds_.IsEmpty()) { + // No raster invalidation is needed if either the old bounds or the new + // bounds is empty, but we still need to update new_chunks_info for the + // next cycle. + ChunkToLayerMapper mapper(layer_state, layer_bounds.OffsetFromOrigin(), + visual_rect_subpixel_offset); + for (const auto& chunk : paint_chunks) { + mapper.SwitchToChunk(chunk); + new_chunks_info.emplace_back(*this, mapper, chunk); + } + } else { + GenerateRasterInvalidations(paint_chunks, layer_state, + visual_rect_subpixel_offset, new_chunks_info); + } + + paint_chunks_info_ = std::move(new_chunks_info); + + if (tracking_info_) { + tracking_info_->old_client_debug_names = + std::move(tracking_info_->new_client_debug_names); + } + + for (const auto& chunk : paint_chunks) { + chunk.client_is_just_created = false; + chunk.raster_invalidation_rects.clear(); + chunk.raster_invalidation_tracking.clear(); + } +} + +size_t CompositedLayerRasterInvalidator::ApproximateUnsharedMemoryUsage() + const { + return sizeof(*this) + paint_chunks_info_.capacity() * sizeof(PaintChunkInfo); +} + +} // namespace blink |