Commit 72936ae9 authored by Xianzhu Wang's avatar Xianzhu Wang Committed by Chromium LUCI CQ

Improve raster scale for nested and unknown-scale transform animations

- When calculating the maximum animation to screen scale, for a nested
  or unknown-scale transform animation, use the parent's maximum
  animation scale instead of kInvalidScale which would fallback to
  page_scale * device_scale which ignored ancestor scales.

- When adjusting raster scale for transform animation, use
  max(raster_contents_scale_, maximum_animation_scale).
  In normal cases, if the former is bigger than the latter, it means
  that raster_contents_scale_ calculated when the animation starts is
  more accurate than the maximum_animation_scale calculated by the
  cc/animation subsystem.

- In case that a bigger scale than 1.5x of the previous calculated
  animation scale, force an raster scale. The case should be rare.

Bug: 1165408
Change-Id: Ieccdb9e16fb6f151e74a881855ce3655e857c372
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2633800Reviewed-by: default avatarPhilip Rogers <pdr@chromium.org>
Reviewed-by: default avatarRobert Flack <flackr@chromium.org>
Commit-Queue: Xianzhu Wang <wangxianzhu@chromium.org>
Cr-Commit-Position: refs/heads/master@{#844899}
parent acad8289
......@@ -66,6 +66,15 @@ const float kMaxIdealContentsScale = 10000.f;
// the native scale.
const float kMinScaleRatioForWillChangeTransform = 0.25f;
// Used to avoid raster scale adjustment during a transform animation by
// using the maximum animation scale, but sometimes the maximum animation scale
// can't be accurately calculated (e.g. with nested scale transforms). We'll
// adjust raster scale if it is not affected by invalid scale and is smaller
// than the ideal scale divided by this ratio. The situation is rare.
// See PropertyTrees::MaximumAnimationToScreenScale() and
// AnimationAffectedByInvalidScale().
const float kRatioToAdjustRasterScaleForTransformAnimation = 1.5f;
// Intersect rects which may have right() and bottom() that overflow integer
// boundaries. This code is similar to gfx::Rect::Intersect with the exception
// that the types are promoted to int64_t when there is a chance of overflow.
......@@ -1352,11 +1361,23 @@ bool PictureLayerImpl::ShouldAdjustRasterScale() const {
if (raster_contents_scale_ < MinimumContentsScale())
return true;
// Don't change the raster scale if we have an animating transform, except
// when the device viewport rect has changed because the raster scale may
// depend on the rect.
if (draw_properties().screen_space_transform_is_animating)
return layer_tree_impl()->device_viewport_rect_changed();
// Avoid frequent raster scale changes if we have an animating transform.
if (draw_properties().screen_space_transform_is_animating) {
// Except when the device viewport rect has changed because the raster scale
// may depend on the rect.
if (layer_tree_impl()->device_viewport_rect_changed())
return true;
// Or when the raster scale is not affected by invalid scale and is too
// small compared to the ideal scale.
if (ideal_contents_scale_ >
raster_contents_scale_ *
kRatioToAdjustRasterScaleForTransformAnimation &&
!layer_tree_impl()->property_trees()->AnimationAffectedByInvalidScale(
transform_tree_index())) {
return true;
}
return false;
}
// Don't change the raster scale if the raster scale is already ideal.
if (raster_source_scale_ == ideal_source_scale_)
......@@ -1501,15 +1522,8 @@ void PictureLayerImpl::AdjustRasterScaleForTransformAnimation(
float maximum_animation_scale =
layer_tree_impl()->property_trees()->MaximumAnimationToScreenScale(
transform_tree_index());
if (maximum_animation_scale == kInvalidScale) {
// Use at least the native scale if the animation scale is unknown.
raster_contents_scale_ = std::max(raster_contents_scale_,
ideal_page_scale_ * ideal_device_scale_);
} else {
// We rasterize at the maximum scale that will occur during the animation.
raster_contents_scale_ = maximum_animation_scale;
}
DCHECK_NE(raster_contents_scale_, kInvalidScale);
raster_contents_scale_ =
std::max(raster_contents_scale_, maximum_animation_scale);
// However we want to avoid excessive memory use. Choose a scale at which this
// layer's rastered content is not larger than the viewport.
......
This diff is collapsed.
This diff is collapsed.
......@@ -2045,6 +2045,10 @@ float PropertyTrees::MaximumAnimationToScreenScale(int transform_id) {
return GetAnimationScaleData(transform_id).maximum_to_screen_scale;
}
bool PropertyTrees::AnimationAffectedByInvalidScale(int transform_id) {
return GetAnimationScaleData(transform_id).affected_by_invalid_scale;
}
const AnimationScaleData& PropertyTrees::GetAnimationScaleData(
int transform_id) {
auto& animation_scale = cached_data_.animation_scales[transform_id];
......@@ -2067,11 +2071,13 @@ const AnimationScaleData& PropertyTrees::GetAnimationScaleData(
animation_scale.affected_by_animation_scale =
node_affected_by_animation_scale || ancestor_affected_by_animation_scale;
// Computing maximum animated scale in the presence of non-scale/translation
// transforms isn't supported.
bool failed_for_non_scale_or_translation =
!node->to_parent.IsScaleOrTranslation();
animation_scale.affected_by_invalid_scale =
(parent_node && parent_animation_scale->affected_by_invalid_scale) ||
// Computing maximum animated scale in the presence of
// non-scale/translation transforms isn't supported.
!node->to_parent.IsScaleOrTranslation() ||
(node->has_potential_animation &&
node->maximum_animation_scale == kInvalidScale);
// We don't attempt to accumulate animation scale from multiple nodes with
// scale animations, because of the risk of significant overestimation. For
......@@ -2081,11 +2087,10 @@ const AnimationScaleData& PropertyTrees::GetAnimationScaleData(
bool failed_for_multiple_scale_animations =
ancestor_affected_by_animation_scale && node_affected_by_animation_scale;
if (failed_for_non_scale_or_translation ||
float local_maximum_scale = 1.0f;
if (animation_scale.affected_by_invalid_scale ||
failed_for_multiple_scale_animations) {
// This ensures that descendants know we've failed to compute a maximum
// animated scale.
animation_scale.maximum_to_screen_scale = kInvalidScale;
// Will use the parent's maximum_to_screen_scale.
} else if (!animation_scale.affected_by_animation_scale) {
// No affecting scale animation. Calculate the current to_screen scale.
gfx::Vector2dF to_screen_scales =
......@@ -2093,33 +2098,22 @@ const AnimationScaleData& PropertyTrees::GetAnimationScaleData(
transform_tree.ToScreen(transform_id), kInvalidScale);
animation_scale.maximum_to_screen_scale =
std::max(to_screen_scales.x(), to_screen_scales.y());
} else {
// An ancestor or the current node is affected by scale animation.
// Compute the combination of ancestor and local maximum scales.
DCHECK(ancestor_affected_by_animation_scale ||
node_affected_by_animation_scale);
float ancestor_maximum_scale =
parent_node ? parent_animation_scale->maximum_to_screen_scale : 1.0f;
if (ancestor_maximum_scale == kInvalidScale) {
// Once we've failed to compute a maximum animated scale at an ancestor,
// we continue to fail.
animation_scale.maximum_to_screen_scale = kInvalidScale;
} else {
float local_maximum_scale = kInvalidScale;
if (ancestor_affected_by_animation_scale) {
// An ancestor is animating scale.
return animation_scale;
} else if (ancestor_affected_by_animation_scale) {
DCHECK(!node_affected_by_animation_scale);
gfx::Vector2dF local_scales =
MathUtil::ComputeTransform2dScaleComponents(node->local,
kInvalidScale);
MathUtil::ComputeTransform2dScaleComponents(node->local, 1.0f);
local_maximum_scale = std::max(local_scales.x(), local_scales.y());
} else {
DCHECK(node_affected_by_animation_scale);
DCHECK_NE(node->maximum_animation_scale, kInvalidScale);
local_maximum_scale = node->maximum_animation_scale;
}
animation_scale.maximum_to_screen_scale =
ancestor_maximum_scale * local_maximum_scale;
}
animation_scale.maximum_to_screen_scale = local_maximum_scale;
if (parent_node) {
animation_scale.maximum_to_screen_scale *=
parent_animation_scale->maximum_to_screen_scale;
}
return animation_scale;
......@@ -2127,9 +2121,11 @@ const AnimationScaleData& PropertyTrees::GetAnimationScaleData(
void PropertyTrees::SetMaximumAnimationToScreenScaleForTesting(
int transform_id,
float maximum_scale) {
float maximum_scale,
bool affected_by_invalid_scale) {
auto& animation_scale = cached_data_.animation_scales[transform_id];
animation_scale.maximum_to_screen_scale = maximum_scale;
animation_scale.affected_by_invalid_scale = affected_by_invalid_scale;
animation_scale.update_number = cached_data_.transform_tree_update_number;
}
......
......@@ -553,6 +553,10 @@ struct AnimationScaleData {
// node or its ancestors. A scale animation having maximum scale of 1 is
// treated as not affecting |maximum_to_screen_scale|.
bool affected_by_animation_scale = false;
// Whether |maximum_to_screen_scale| is affected by any non-calculatable
// scale.
bool affected_by_invalid_scale = false;
};
struct DrawTransforms {
......@@ -690,8 +694,12 @@ class CC_EXPORT PropertyTrees final {
std::string ToString() const;
float MaximumAnimationToScreenScale(int transform_id);
void SetMaximumAnimationToScreenScaleForTesting(int transform_id,
float maximum_scale);
bool AnimationAffectedByInvalidScale(int transform_id);
void SetMaximumAnimationToScreenScaleForTesting(
int transform_id,
float maximum_scale,
bool affected_by_invalid_scale);
bool GetToTarget(int transform_id,
int effect_id,
......
......@@ -729,14 +729,21 @@ TEST_F(TreeSynchronizerTest, RefreshPropertyTreesCachedData) {
// This arbitrarily set the animation scale for transform_layer and see if it
// is refreshed when pushing layer trees.
float maximum_animation_scale = 123.f;
bool animation_affected_by_invalid_scale = true;
host_impl->active_tree()
->property_trees()
->SetMaximumAnimationToScreenScaleForTesting(
transform_layer->transform_tree_index(), 10.f);
transform_layer->transform_tree_index(), maximum_animation_scale,
animation_affected_by_invalid_scale);
EXPECT_EQ(
10.f,
maximum_animation_scale,
host_impl->active_tree()->property_trees()->MaximumAnimationToScreenScale(
transform_layer->transform_tree_index()));
EXPECT_TRUE(host_impl->active_tree()
->property_trees()
->AnimationAffectedByInvalidScale(
transform_layer->transform_tree_index()));
host_impl->CreatePendingTree();
host_->CommitAndCreatePendingTree();
......@@ -745,6 +752,10 @@ TEST_F(TreeSynchronizerTest, RefreshPropertyTreesCachedData) {
2.0f,
host_impl->active_tree()->property_trees()->MaximumAnimationToScreenScale(
transform_layer->transform_tree_index()));
EXPECT_FALSE(host_impl->active_tree()
->property_trees()
->AnimationAffectedByInvalidScale(
transform_layer->transform_tree_index()));
}
TEST_F(TreeSynchronizerTest, RoundedScrollDeltasOnCommit) {
......
<!DOCTYPE html>
<div style="width: 200px; height: 100px; background: blue"></div>
<div style="width: 200px; height: 100px; background: green"></div>
<!DOCTYPE html>
<html class="reftest-wait">
<title>Nested scale animations</title>
<link rel="author" title="Xianzhu Wang" href="mailto:wangxianzhu@chromium.org">
<link rel="help" href="https://crbug.com/1165408">
<link rel="match" href="nested-scale-animations-ref.html">
<!-- Allow blurry pixels in 2 rows. -->
<meta name="fuzzy" content="0-255;0-400">
<meta name="assert" content="Contents under nested scale animations should not be too blurry">
<script src="/common/reftest-wait.js"></script>
<style>
@keyframes scale {
0% {transform: scale(1);}
1% {transform: scale(10);}
100% {transform: scale(20);}
}
.animate {
animation: scale 1s infinite;
position: relative;
top: 45%;
left: 45%;
width: 10%;
height: 10%;
}
</style>
<div style="width: 200px; height: 200px; overflow: hidden; position: relative">
<div class="animate">
<div class="animate">
<div style="width: 2px; height: 1px; background: blue"></div>
<div style="width: 2px; height: 1px; background: green"></div>
</div>
</div>
</div>
<script>
takeScreenshotDelayed(200);
</script>
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