summaryrefslogtreecommitdiff
path: root/chromium/third_party/blink/renderer/platform/graphics/paint/cull_rect.cc
diff options
context:
space:
mode:
authorAllan Sandfeld Jensen <allan.jensen@qt.io>2021-05-20 09:47:09 +0200
committerAllan Sandfeld Jensen <allan.jensen@qt.io>2021-06-07 11:15:42 +0000
commit189d4fd8fad9e3c776873be51938cd31a42b6177 (patch)
tree6497caeff5e383937996768766ab3bb2081a40b2 /chromium/third_party/blink/renderer/platform/graphics/paint/cull_rect.cc
parent8bc75099d364490b22f43a7ce366b366c08f4164 (diff)
downloadqtwebengine-chromium-189d4fd8fad9e3c776873be51938cd31a42b6177.tar.gz
BASELINE: Update Chromium to 90.0.4430.221
Change-Id: Iff4d9d18d2fcf1a576f3b1f453010f744a232920 Reviewed-by: Allan Sandfeld Jensen <allan.jensen@qt.io>
Diffstat (limited to 'chromium/third_party/blink/renderer/platform/graphics/paint/cull_rect.cc')
-rw-r--r--chromium/third_party/blink/renderer/platform/graphics/paint/cull_rect.cc300
1 files changed, 254 insertions, 46 deletions
diff --git a/chromium/third_party/blink/renderer/platform/graphics/paint/cull_rect.cc b/chromium/third_party/blink/renderer/platform/graphics/paint/cull_rect.cc
index d2f5981936f..f3b60e9dd99 100644
--- a/chromium/third_party/blink/renderer/platform/graphics/paint/cull_rect.cc
+++ b/chromium/third_party/blink/renderer/platform/graphics/paint/cull_rect.cc
@@ -4,6 +4,7 @@
#include "third_party/blink/renderer/platform/graphics/paint/cull_rect.h"
+#include "base/containers/adapters.h"
#include "third_party/blink/renderer/platform/geometry/float_rect.h"
#include "third_party/blink/renderer/platform/geometry/layout_rect.h"
#include "third_party/blink/renderer/platform/graphics/paint/geometry_mapper.h"
@@ -14,6 +15,30 @@
namespace blink {
+static constexpr int kReasonablePixelLimit =
+ std::numeric_limits<int>::max() / 2;
+
+// Returns the number of pixels to expand the cull rect for composited scroll
+// and transform.
+static int LocalPixelDistanceToExpand(
+ const TransformPaintPropertyNode& root_transform,
+ const TransformPaintPropertyNode& local_transform) {
+ // Number of pixels to expand in root coordinates for cull rect under
+ // composited scroll translation or other composited transform.
+ static constexpr int kPixelDistanceToExpand = 4000;
+
+ FloatRect rect(0, 0, 1, 1);
+ GeometryMapper::SourceToDestinationRect(root_transform, local_transform,
+ rect);
+ // Now rect.Size() is the size of a screen pixel in local coordinates.
+ float scale = std::max(rect.Width(), rect.Height());
+ // A very big scale may be caused by non-invertable near non-invertable
+ // transforms. Fallback to scale 1. The limit is heuristic.
+ if (scale > kReasonablePixelLimit / kPixelDistanceToExpand)
+ return kPixelDistanceToExpand;
+ return scale * kPixelDistanceToExpand;
+}
+
bool CullRect::Intersects(const IntRect& rect) const {
return IsInfinite() || rect.Intersects(rect_);
}
@@ -50,51 +75,57 @@ void CullRect::Move(const FloatSize& offset) {
rect_ = EnclosingIntRect(float_rect);
}
-static void MapRect(const TransformPaintPropertyNode& transform,
- IntRect& rect) {
- if (transform.IsIdentityOr2DTranslation()) {
- FloatRect float_rect(rect);
- float_rect.Move(-transform.Translation2D());
- rect = EnclosingIntRect(float_rect);
+void CullRect::ApplyTransform(const TransformPaintPropertyNode& transform) {
+ if (RuntimeEnabledFeatures::CompositeAfterPaintEnabled() &&
+ transform.ScrollNode()) {
+ // TODO(wangxianzhu): Remove this code path for CullRectUpdate.
+ ApplyScrollTranslation(transform, transform);
} else {
- rect = transform.MatrixWithOriginApplied().Inverse().MapRect(rect);
+ ApplyTransformWithoutExpansion(transform);
}
}
-CullRect::ApplyTransformResult CullRect::ApplyTransformInternal(
+void CullRect::ApplyTransformWithoutExpansion(
const TransformPaintPropertyNode& transform) {
- if (RuntimeEnabledFeatures::CompositeAfterPaintEnabled()) {
- if (const auto* scroll = transform.ScrollNode()) {
- rect_.Intersect(scroll->ContainerRect());
- if (rect_.IsEmpty())
- return kNotExpanded;
-
- MapRect(transform, rect_);
-
- // Don't expand for non-composited scrolling.
- if (!transform.HasDirectCompositingReasons())
- return kNotExpanded;
-
- // We create scroll node for the root scroller even it's not scrollable.
- // Don't expand in the case.
- if (scroll->ContainerRect().Width() >= scroll->ContentsSize().Width() &&
- scroll->ContainerRect().Height() >= scroll->ContentsSize().Height())
- return kNotExpanded;
-
- // Expand the cull rect for scrolling contents for composited scrolling.
- static const int kPixelDistanceToExpand = 4000;
- rect_.Inflate(kPixelDistanceToExpand);
- // Don't clip the cull rect by contents size to let ChangedEnough() work
- // even if the new cull rect exceeds the bounds of contents rect.
- return rect_.Contains(IntRect(IntPoint(), scroll->ContentsSize()))
- ? kExpandedForWholeScrollingContents
- : kExpandedForPartialScrollingContents;
- }
- }
+ if (IsInfinite())
+ return;
- if (!IsInfinite())
- MapRect(transform, rect_);
- return kNotExpanded;
+ DCHECK(transform.Parent());
+ GeometryMapper::SourceToDestinationRect(*transform.Parent(), transform,
+ rect_);
+}
+
+CullRect::ApplyTransformResult CullRect::ApplyScrollTranslation(
+ const TransformPaintPropertyNode& root_transform,
+ const TransformPaintPropertyNode& scroll_translation) {
+ DCHECK(RuntimeEnabledFeatures::CompositeAfterPaintEnabled() ||
+ RuntimeEnabledFeatures::CullRectUpdateEnabled());
+
+ const auto* scroll = scroll_translation.ScrollNode();
+ DCHECK(scroll);
+
+ rect_.Intersect(scroll->ContainerRect());
+ if (rect_.IsEmpty())
+ return kNotExpanded;
+
+ ApplyTransformWithoutExpansion(scroll_translation);
+
+ // Don't expand for non-composited scrolling.
+ if (!scroll_translation.HasDirectCompositingReasons())
+ return kNotExpanded;
+
+ // We create scroll node for the root scroller even it's not scrollable.
+ // Don't expand in the case.
+ if (scroll->ContainerRect().Width() >= scroll->ContentsSize().Width() &&
+ scroll->ContainerRect().Height() >= scroll->ContentsSize().Height())
+ return kNotExpanded;
+
+ // Expand the cull rect for scrolling contents for composited scrolling.
+ rect_.Inflate(LocalPixelDistanceToExpand(root_transform, scroll_translation));
+ IntRect contents_rect(IntPoint(), scroll->ContentsSize());
+ rect_.Intersect(contents_rect);
+ return rect_ == contents_rect ? kExpandedForWholeScrollingContents
+ : kExpandedForPartialScrollingContents;
}
void CullRect::ApplyTransforms(const TransformPaintPropertyNode& source,
@@ -125,7 +156,7 @@ void CullRect::ApplyTransforms(const TransformPaintPropertyNode& source,
*last_transform, *scroll_translation->Parent(), rect_);
}
last_scroll_translation_result =
- ApplyTransformInternal(*scroll_translation);
+ ApplyScrollTranslation(source, *scroll_translation);
last_transform = scroll_translation;
}
@@ -135,25 +166,202 @@ void CullRect::ApplyTransforms(const TransformPaintPropertyNode& source,
}
if (last_scroll_translation_result == kExpandedForPartialScrollingContents &&
- old_cull_rect && !ChangedEnough(*old_cull_rect))
+ old_cull_rect &&
+ !ChangedEnough(*old_cull_rect,
+ &last_transform->ScrollNode()->ContentsSize())) {
rect_ = old_cull_rect->Rect();
+ }
}
-bool CullRect::ChangedEnough(const CullRect& old_cull_rect) const {
- DCHECK(RuntimeEnabledFeatures::CompositeAfterPaintEnabled());
+void CullRect::ApplyPaintPropertiesWithoutExpansion(
+ const PropertyTreeState& source,
+ const PropertyTreeState& destination) {
+ FloatClipRect clip_rect =
+ GeometryMapper::LocalToAncestorClipRect(destination, source);
+ if (!clip_rect.IsInfinite())
+ rect_.Intersect(EnclosingIntRect(clip_rect.Rect()));
+ if (!IsInfinite()) {
+ GeometryMapper::SourceToDestinationRect(source.Transform(),
+ destination.Transform(), rect_);
+ }
+}
+
+void CullRect::ApplyPaintProperties(
+ const PropertyTreeState& root,
+ const PropertyTreeState& source,
+ const PropertyTreeState& destination,
+ const base::Optional<CullRect>& old_cull_rect) {
+ DCHECK(RuntimeEnabledFeatures::CompositeAfterPaintEnabled() ||
+ RuntimeEnabledFeatures::CullRectUpdateEnabled());
+
+ Vector<const TransformPaintPropertyNode*, 4> scroll_translations;
+ Vector<const ClipPaintPropertyNode*, 4> clips;
+ bool abnormal_hierarchy = false;
+
+ for (const auto* t = &destination.Transform(); t != &source.Transform();
+ t = t->UnaliasedParent()) {
+ DCHECK(t);
+ if (t == &root.Transform()) {
+ abnormal_hierarchy = true;
+ break;
+ }
+ if (t->ScrollNode())
+ scroll_translations.push_back(t);
+ }
+
+ if (!abnormal_hierarchy) {
+ for (const auto* c = &destination.Clip(); c != &source.Clip();
+ c = c->UnaliasedParent()) {
+ DCHECK(c);
+ if (c == &root.Clip()) {
+ abnormal_hierarchy = true;
+ break;
+ }
+ clips.push_back(c);
+ }
+ }
+
+ if (abnormal_hierarchy) {
+ // Either the transform or the clip of |source| is not an ancestor of
+ // |destination|. Map infinite rect from the root.
+ *this = Infinite();
+ ApplyPaintProperties(root, root, destination, old_cull_rect);
+ return;
+ }
+
+ // These are either the source transform/clip or the last scroll
+ // translation's transform/clip.
+ const auto* last_transform = &source.Transform();
+ const auto* last_clip = &source.Clip();
+ auto last_scroll_translation_result = kNotExpanded;
+
+ // For now effects (especially pixel-moving filters) are not considered in
+ // this class. The client has to use infinite cull rect in the case.
+ // TODO(wangxianzhu): support clip rect expansion for pixel-moving filters.
+ const auto& effect_root = EffectPaintPropertyNode::Root();
+ auto scroll_translation_it = scroll_translations.rbegin();
+ for (const auto* clip : base::Reversed(clips)) {
+ if (scroll_translation_it == scroll_translations.rend())
+ break;
+
+ const auto* scroll_translation = *scroll_translation_it++;
+ if (&clip->LocalTransformSpace() != scroll_translation->Parent())
+ continue;
+
+ ApplyPaintPropertiesWithoutExpansion(
+ PropertyTreeState(*last_transform, *last_clip, effect_root),
+ PropertyTreeState(*scroll_translation->UnaliasedParent(), *clip,
+ effect_root));
+ last_scroll_translation_result =
+ ApplyScrollTranslation(root.Transform(), *scroll_translation);
+
+ last_transform = scroll_translation;
+ last_clip = clip;
+ }
+
+ ApplyPaintPropertiesWithoutExpansion(
+ PropertyTreeState(*last_transform, *last_clip, effect_root), destination);
+
+ // Since the cull rect mapping above can produce extremely large numbers in
+ // cases of perspective, try our best to "normalize" the result by ensuring
+ // that none of the rect dimensions exceed some large, but reasonable, limit.
+ // Note that by clamping X and Y, we are effectively moving the rect right /
+ // down. However, this will at most make us paint more content, which is
+ // better than erroneously deciding that the rect produced here is far
+ // offscreen.
+ if (rect_.X() < -kReasonablePixelLimit)
+ rect_.SetX(-kReasonablePixelLimit);
+ if (rect_.Y() < -kReasonablePixelLimit)
+ rect_.SetY(-kReasonablePixelLimit);
+ if (rect_.MaxX() > kReasonablePixelLimit)
+ rect_.ShiftMaxXEdgeTo(kReasonablePixelLimit);
+ if (rect_.MaxY() > kReasonablePixelLimit)
+ rect_.ShiftMaxYEdgeTo(kReasonablePixelLimit);
+
+ const IntSize* expansion_bounds = nullptr;
+ bool expanded = false;
+ if (last_scroll_translation_result == kExpandedForPartialScrollingContents &&
+ last_clip == &destination.Clip()) {
+ DCHECK(last_transform->ScrollNode());
+ expansion_bounds = &last_transform->ScrollNode()->ContentsSize();
+ expanded = true;
+ } else if (!IsInfinite() && last_transform != &destination.Transform() &&
+ destination.Transform().HasDirectCompositingReasons()) {
+ // Direct compositing reasons such as will-change transform can cause the
+ // content to move arbitrarily, so there is no exact cull rect. Instead of
+ // using an infinite rect, we use a heuristic of expanding by
+ // |pixel_distance_to_expand|. To avoid extreme expansion in the presence
+ // of nested composited transforms, the heuristic is skipped for rects that
+ // are already very large.
+ int pixel_distance_to_expand =
+ LocalPixelDistanceToExpand(root.Transform(), destination.Transform());
+ if (rect_.Width() < pixel_distance_to_expand) {
+ rect_.InflateX(pixel_distance_to_expand);
+ expanded = true;
+ }
+ if (rect_.Height() < pixel_distance_to_expand) {
+ rect_.InflateY(pixel_distance_to_expand);
+ expanded = true;
+ }
+ }
+
+ if (expanded && old_cull_rect &&
+ !ChangedEnough(*old_cull_rect, expansion_bounds))
+ rect_ = old_cull_rect->Rect();
+}
+
+bool CullRect::ChangedEnough(const CullRect& old_cull_rect,
+ const IntSize* expansion_bounds) const {
+ DCHECK(RuntimeEnabledFeatures::CompositeAfterPaintEnabled() ||
+ RuntimeEnabledFeatures::CullRectUpdateEnabled());
const auto& new_rect = Rect();
const auto& old_rect = old_cull_rect.Rect();
- if (old_rect == new_rect || (old_rect.IsEmpty() && new_rect.IsEmpty()))
+ if (old_rect.Contains(new_rect))
+ return false;
+ if (old_rect.IsEmpty() && new_rect.IsEmpty())
return false;
if (old_rect.IsEmpty())
return true;
+ static constexpr int kChangedEnoughMinimumDistance = 512;
auto expanded_old_rect = old_rect;
- static const int kChangedEnoughMinimumDistance = 512;
expanded_old_rect.Inflate(kChangedEnoughMinimumDistance);
- return !expanded_old_rect.Contains(new_rect);
+ if (!expanded_old_rect.Contains(new_rect))
+ return true;
+
+ // The following edge checking logic applies only when the bounds (which were
+ // used to clip the cull rect) are known.
+ if (!expansion_bounds)
+ return false;
+
+ // The cull rect must have been clipped by *expansion_bounds.
+ DCHECK(IntRect(IntPoint(), *expansion_bounds).Contains(rect_));
+
+ // Even if the new cull rect doesn't include enough new area to satisfy
+ // the condition above, update anyway if it touches the edge of the scrolling
+ // contents that is not touched by the existing cull rect. Because it's
+ // impossible to expose more area in the direction, update cannot be deferred
+ // until the exposed new area satisfies the condition above.
+ // For example,
+ // scroller contents dimensions: 100x1000
+ // old cull rect: 0,100 100x8000
+ // A new rect of 0,0 100x8000 will not be |kChangedEnoughMinimumDistance|
+ // pixels away from the current rect. Without additional logic for this case,
+ // we will continue using the old cull rect.
+ if (rect_.X() == 0 && old_cull_rect.Rect().X() != 0)
+ return true;
+ if (rect_.Y() == 0 && old_cull_rect.Rect().Y() != 0)
+ return true;
+ if (rect_.MaxX() == expansion_bounds->Width() &&
+ old_cull_rect.Rect().MaxX() != expansion_bounds->Width())
+ return true;
+ if (rect_.MaxY() == expansion_bounds->Height() &&
+ old_cull_rect.Rect().MaxY() != expansion_bounds->Height())
+ return true;
+
+ return false;
}
} // namespace blink