#include "cc/trees/damage_tracker.h"
#include <stddef.h>
#include <algorithm>
#include "base/containers/contains.h"
#include "base/memory/ptr_util.h"
#include "cc/base/math_util.h"
#include "cc/layers/heads_up_display_layer_impl.h"
#include "cc/layers/layer_impl.h"
#include "cc/layers/render_surface_impl.h"
#include "cc/paint/filter_operations.h"
#include "cc/trees/effect_node.h"
#include "cc/trees/layer_tree_impl.h"
#include "components/viz/common/viz_utils.h"
#include "ui/gfx/geometry/rect_conversions.h"
#include "ui/gfx/geometry/skia_conversions.h"
namespace cc {
std::unique_ptr<DamageTracker> DamageTracker::Create() {
return base::WrapUnique(new DamageTracker());
}
DamageTracker::DamageTracker() = default;
DamageTracker::~DamageTracker() = default;
void DamageTracker::UpdateDamageTracking(LayerTreeImpl* layer_tree_impl) {
ViewTransitionElementResourceIdToRenderSurfaceMap id_to_render_surface_map;
InitializeUpdateDamageTracking(layer_tree_impl, id_to_render_surface_map);
EffectTree& effect_tree =
layer_tree_impl->property_trees()->effect_tree_mutable();
int current_target_effect_id = kContentsRootPropertyNodeId;
DCHECK(effect_tree.GetRenderSurface(current_target_effect_id));
for (LayerImpl* layer : *layer_tree_impl) {
if (!layer->contributes_to_drawn_render_surface())
continue;
int next_target_effect_id = layer->render_target_effect_tree_index();
if (next_target_effect_id != current_target_effect_id) {
int lowest_common_ancestor_id =
effect_tree.LowestCommonAncestorWithRenderSurface(
current_target_effect_id, next_target_effect_id);
while (current_target_effect_id != lowest_common_ancestor_id) {
RenderSurfaceImpl* current_target =
effect_tree.GetRenderSurface(current_target_effect_id);
current_target->damage_tracker()->ComputeSurfaceDamage(current_target);
RenderSurfaceImpl* parent_target = current_target->render_target();
parent_target->damage_tracker()->AccumulateDamageFromRenderSurface(
current_target);
current_target_effect_id =
effect_tree.Node(current_target_effect_id)->target_id;
}
current_target_effect_id = next_target_effect_id;
}
RenderSurfaceImpl* target_surface = layer->render_target();
if (layer != layer_tree_impl->hud_layer()) {
target_surface->damage_tracker()->AccumulateDamageFromLayer(
layer, id_to_render_surface_map);
}
}
DCHECK_GE(current_target_effect_id, kContentsRootPropertyNodeId);
RenderSurfaceImpl* current_target =
effect_tree.GetRenderSurface(current_target_effect_id);
while (true) {
current_target->damage_tracker()->ComputeSurfaceDamage(current_target);
if (current_target->EffectTreeIndex() == kContentsRootPropertyNodeId)
break;
RenderSurfaceImpl* next_target = current_target->render_target();
next_target->damage_tracker()->AccumulateDamageFromRenderSurface(
current_target);
current_target = next_target;
}
}
void DamageTracker::InitializeUpdateDamageTracking(
LayerTreeImpl* layer_tree_impl,
ViewTransitionElementResourceIdToRenderSurfaceMap&
id_to_render_surface_map) {
for (RenderSurfaceImpl* render_surface :
layer_tree_impl->GetRenderSurfaceList()) {
render_surface->damage_tracker()->PrepareForUpdate();
auto resource_id =
render_surface->OwningEffectNode()->view_transition_element_resource_id;
if (resource_id.IsValid()) {
DCHECK(!base::Contains(id_to_render_surface_map, resource_id));
id_to_render_surface_map.emplace(resource_id, render_surface);
}
}
}
void DamageTracker::ComputeSurfaceDamage(RenderSurfaceImpl* render_surface) {
DamageAccumulator damage_from_leftover_rects = TrackDamageFromLeftoverRects();
has_damage_from_contributing_content_ |=
!damage_from_leftover_rects.IsEmpty();
gfx::Rect expanded_damage_rect;
bool valid = damage_from_leftover_rects.GetAsRect(&expanded_damage_rect);
bool expanded = false;
for (auto& contributing_surface : contributing_surfaces_) {
RenderSurfaceImpl* surface = contributing_surface.render_surface;
bool has_pixel_moving_backdrop_filters =
surface->BackdropFilters().HasFilterThatMovesPixels();
if (!surface->intersects_damage_under() ||
has_pixel_moving_backdrop_filters) {
if (!valid || contributing_surface.rect_in_target_space.Intersects(
expanded_damage_rect)) {
surface->set_intersects_damage_under(true);
if (has_pixel_moving_backdrop_filters) {
expanded_damage_rect.Union(contributing_surface.rect_in_target_space);
expanded = true;
}
}
}
}
if (expanded) {
damage_for_this_update_.Union(expanded_damage_rect,
damage_from_leftover_rects.reasons());
}
contributing_surfaces_.clear();
DamageReasonSet reasons = damage_for_this_update_.reasons();
if (render_surface->SurfacePropertyChanged()) {
reasons.Put(DamageReason::kUntracked);
}
if (!damage_from_leftover_rects.IsEmpty()) {
reasons.PutAll(damage_from_leftover_rects.reasons());
}
{
damage_for_this_update_.Union(damage_from_leftover_rects, {});
gfx::Rect damage_rect;
bool is_rect_valid = damage_for_this_update_.GetAsRect(&damage_rect);
if (is_rect_valid && !damage_rect.IsEmpty()) {
damage_rect = render_surface->Filters().MapRect(
damage_rect,
gfx::TransformToFlattenedSkMatrix(render_surface->SurfaceScale()));
damage_for_this_update_ = DamageAccumulator();
damage_for_this_update_.Union(damage_rect, {});
}
}
damage_for_this_update_.UnionReasons(reasons);
if (render_surface->SurfacePropertyChanged()) {
damage_for_this_update_.Union(render_surface->content_rect(), {});
has_damage_from_contributing_content_ |= !damage_for_this_update_.IsEmpty();
}
current_damage_.Union(damage_for_this_update_,
damage_for_this_update_.reasons());
}
bool DamageTracker::GetDamageRectIfValid(gfx::Rect* rect) {
return current_damage_.GetAsRect(rect);
}
DamageReasonSet DamageTracker::GetDamageReasons() {
return current_damage_.reasons();
}
DamageTracker::LayerRectMapData& DamageTracker::RectDataForLayer(
int layer_id,
bool* layer_is_new) {
LayerRectMapData data(layer_id);
auto it = std::lower_bound(rect_history_for_layers_.begin(),
rect_history_for_layers_.end(), data);
if (it == rect_history_for_layers_.end() || it->layer_id_ != layer_id) {
*layer_is_new = true;
it = rect_history_for_layers_.insert(it, data);
}
return *it;
}
DamageTracker::SurfaceRectMapData& DamageTracker::RectDataForSurface(
ElementId surface_id,
bool* surface_is_new) {
SurfaceRectMapData data(surface_id);
auto it = std::lower_bound(rect_history_for_surfaces_.begin(),
rect_history_for_surfaces_.end(), data);
if (it == rect_history_for_surfaces_.end() || it->surface_id_ != surface_id) {
*surface_is_new = true;
it = rect_history_for_surfaces_.insert(it, data);
}
return *it;
}
void DamageTracker::PrepareForUpdate() {
mailbox_id_++;
damage_for_this_update_ = DamageAccumulator();
has_damage_from_contributing_content_ = false;
contributing_surfaces_.clear();
current_view_transition_content_surfaces_by_id_.swap(
previous_view_transition_content_surfaces_by_id_);
current_view_transition_content_surfaces_by_id_.clear();
}
DamageTracker::DamageAccumulator DamageTracker::TrackDamageFromLeftoverRects() {
DamageAccumulator damage;
auto layer_cur_pos = rect_history_for_layers_.begin();
auto layer_copy_pos = layer_cur_pos;
auto surface_cur_pos = rect_history_for_surfaces_.begin();
auto surface_copy_pos = surface_cur_pos;
while (layer_cur_pos < rect_history_for_layers_.end()) {
if (layer_cur_pos->mailbox_id_ == mailbox_id_) {
if (layer_cur_pos != layer_copy_pos)
*layer_copy_pos = *layer_cur_pos;
++layer_copy_pos;
} else {
damage.Union(layer_cur_pos->rect_, {DamageReason::kUntracked});
}
++layer_cur_pos;
}
while (surface_cur_pos < rect_history_for_surfaces_.end()) {
if (surface_cur_pos->mailbox_id_ == mailbox_id_) {
if (surface_cur_pos != surface_copy_pos)
*surface_copy_pos = *surface_cur_pos;
++surface_copy_pos;
} else {
damage.Union(surface_cur_pos->rect_, {DamageReason::kUntracked});
}
++surface_cur_pos;
}
if (layer_copy_pos != rect_history_for_layers_.end())
rect_history_for_layers_.erase(layer_copy_pos,
rect_history_for_layers_.end());
if (surface_copy_pos != rect_history_for_surfaces_.end())
rect_history_for_surfaces_.erase(surface_copy_pos,
rect_history_for_surfaces_.end());
if (rect_history_for_layers_.capacity() > rect_history_for_layers_.size() * 4)
SortedRectMapForLayers(rect_history_for_layers_)
.swap(rect_history_for_layers_);
if (rect_history_for_surfaces_.capacity() >
rect_history_for_surfaces_.size() * 4)
SortedRectMapForSurfaces(rect_history_for_surfaces_)
.swap(rect_history_for_surfaces_);
return damage;
}
void DamageTracker::AccumulateDamageFromLayer(
LayerImpl* layer,
ViewTransitionElementResourceIdToRenderSurfaceMap&
id_to_render_surface_map) {
bool layer_is_new = false;
LayerRectMapData& data = RectDataForLayer(layer->id(), &layer_is_new);
gfx::Rect old_visible_rect_in_target_space = data.rect_;
gfx::Rect visible_rect_in_target_space =
layer->GetEnclosingVisibleRectInTargetSpace();
data.Update(visible_rect_in_target_space, mailbox_id_);
gfx::Rect view_transition_content_surface_damage_rect;
if (layer->ViewTransitionResourceId().IsValid()) {
view_transition_content_surface_damage_rect =
GetViewTransitionContentSurfaceDamageInSharedElementLayerSpace(
layer, id_to_render_surface_map);
}
if (layer_is_new || layer->LayerPropertyChanged()) {
DamageReasonSet reasons = layer->GetDamageReasons();
if (layer_is_new) {
reasons.Put(DamageReason::kUntracked);
}
damage_for_this_update_.Union(visible_rect_in_target_space, reasons);
damage_for_this_update_.Union(old_visible_rect_in_target_space, {});
} else {
DamageReasonSet reasons = layer->GetDamageReasons();
gfx::Rect damage_rect =
gfx::UnionRects(layer->update_rect(), layer->GetDamageRect());
damage_rect.Union(view_transition_content_surface_damage_rect);
if (view_transition_content_surface_damage_rect.Intersects(
gfx::Rect(layer->bounds()))) {
reasons.Put(DamageReason::kUntracked);
}
damage_rect.Intersect(gfx::Rect(layer->bounds()));
if (!damage_rect.IsEmpty()) {
gfx::Rect damage_visible_rect_in_target_space =
MathUtil::MapEnclosingClippedRect(layer->DrawTransform(),
damage_rect);
damage_for_this_update_.Union(damage_visible_rect_in_target_space, {});
}
damage_for_this_update_.UnionReasons(reasons);
}
bool property_change_on_non_target_node = false;
if (layer->LayerPropertyChangedFromPropertyTrees()) {
auto effect_id = layer->render_target()->EffectTreeIndex();
const auto* effect_node =
layer->layer_tree_impl()->property_trees()->effect_tree().Node(
effect_id);
auto transform_id = effect_node->transform_id;
property_change_on_non_target_node =
layer->effect_tree_index() != effect_id ||
layer->transform_tree_index() != transform_id;
}
if (layer_is_new || !layer->update_rect().IsEmpty() ||
layer->LayerPropertyChangedNotFromPropertyTrees() ||
!layer->GetDamageRect().IsEmpty() || property_change_on_non_target_node ||
!view_transition_content_surface_damage_rect.IsEmpty()) {
has_damage_from_contributing_content_ |= !damage_for_this_update_.IsEmpty();
}
}
void DamageTracker::AccumulateDamageFromRenderSurface(
RenderSurfaceImpl* render_surface) {
bool surface_is_new = false;
SurfaceRectMapData& data =
RectDataForSurface(render_surface->id(), &surface_is_new);
gfx::Rect old_surface_rect = data.rect_;
gfx::Rect surface_rect_in_target_space =
gfx::ToEnclosingRect(render_surface->DrawableContentRect());
data.Update(surface_rect_in_target_space, mailbox_id_);
contributing_surfaces_.emplace_back(render_surface,
surface_rect_in_target_space);
gfx::Rect damage_on_target;
bool valid = damage_for_this_update_.GetAsRect(&damage_on_target);
bool intersects_damage_under =
!valid || damage_on_target.Intersects(surface_rect_in_target_space);
if (render_surface->BackdropFilters().HasFilterThatMovesPixels() &&
intersects_damage_under) {
damage_for_this_update_.Union(surface_rect_in_target_space,
{DamageReason::kUntracked});
}
if (surface_is_new || render_surface->SurfacePropertyChanged() ||
render_surface->AncestorPropertyChanged()) {
DamageReasonSet reasons =
render_surface->damage_tracker()->GetDamageReasons();
if (surface_is_new) {
reasons.Put(DamageReason::kUntracked);
}
damage_for_this_update_.Union(surface_rect_in_target_space, reasons);
damage_for_this_update_.Union(old_surface_rect, {});
intersects_damage_under = true;
} else {
gfx::Rect damage_rect_in_local_space;
bool is_valid_rect = render_surface->damage_tracker()->GetDamageRectIfValid(
&damage_rect_in_local_space);
if (is_valid_rect && !damage_rect_in_local_space.IsEmpty()) {
const gfx::Transform& draw_transform = render_surface->draw_transform();
gfx::Rect damage_rect_in_target_space = MathUtil::MapEnclosingClippedRect(
draw_transform, damage_rect_in_local_space);
damage_rect_in_target_space.Intersect(surface_rect_in_target_space);
damage_for_this_update_.Union(
damage_rect_in_target_space,
render_surface->damage_tracker()->GetDamageReasons());
} else if (!is_valid_rect) {
damage_for_this_update_.Union(
surface_rect_in_target_space,
render_surface->damage_tracker()->GetDamageReasons());
}
}
render_surface->set_intersects_damage_under(intersects_damage_under);
has_damage_from_contributing_content_ |= !damage_for_this_update_.IsEmpty();
}
bool DamageTracker::DamageAccumulator::GetAsRect(gfx::Rect* rect) {
if (!is_valid_rect_)
return false;
base::CheckedNumeric<int> width = right_;
width -= x_;
base::CheckedNumeric<int> height = bottom_;
height -= y_;
if (!width.IsValid() || !height.IsValid()) {
is_valid_rect_ = false;
return false;
}
rect->set_x(x_);
rect->set_y(y_);
rect->set_width(width.ValueOrDie());
rect->set_height(height.ValueOrDie());
return true;
}
gfx::Rect
DamageTracker::GetViewTransitionContentSurfaceDamageInSharedElementLayerSpace(
LayerImpl* layer,
ViewTransitionElementResourceIdToRenderSurfaceMap&
id_to_render_surface_map) {
DCHECK(layer->ViewTransitionResourceId().IsValid());
auto vt_resource_id = layer->ViewTransitionResourceId();
RenderSurfaceImpl* view_transition_content_surface = nullptr;
auto shared_surface_it = id_to_render_surface_map.find(vt_resource_id);
if (shared_surface_it != id_to_render_surface_map.end()) {
view_transition_content_surface = shared_surface_it->second;
DCHECK(!base::Contains(current_view_transition_content_surfaces_by_id_,
vt_resource_id));
current_view_transition_content_surfaces_by_id_.push_back(vt_resource_id);
}
gfx::Rect layer_drawable_bounds = gfx::Rect(layer->bounds());
if (view_transition_content_surface) {
bool surface_is_new = false;
auto& data = RectDataForSurface(view_transition_content_surface->id(),
&surface_is_new);
data.Update(layer_drawable_bounds, mailbox_id_);
if (surface_is_new ||
view_transition_content_surface->SurfacePropertyChanged() ||
view_transition_content_surface->AncestorPropertyChanged()) {
return layer_drawable_bounds;
}
}
if (base::Contains(previous_view_transition_content_surfaces_by_id_,
vt_resource_id) !=
base::Contains(current_view_transition_content_surfaces_by_id_,
vt_resource_id)) {
return layer_drawable_bounds;
}
if (!view_transition_content_surface) {
return gfx::Rect();
}
gfx::Rect damage_rect_in_local_space;
bool is_valid_rect =
view_transition_content_surface->damage_tracker()->GetDamageRectIfValid(
&damage_rect_in_local_space);
if (!is_valid_rect) {
return layer_drawable_bounds;
} else if (damage_rect_in_local_space.IsEmpty()) {
return gfx::Rect();
} else {
gfx::Rect render_surface_content_rect =
view_transition_content_surface->content_rect();
gfx::Transform view_transition_transform = viz::GetViewTransitionTransform(
layer_drawable_bounds, render_surface_content_rect);
return MathUtil::MapEnclosingClippedRect(view_transition_transform,
damage_rect_in_local_space);
}
}
}