Commit e6c9e2ce authored by Ian Prest's avatar Ian Prest Committed by Commit Bot

SVG: Cache strokes for hit-testing

This change caches the strokes (converting them to separate paths) used
for hit-testing in SVG, so they don't need to be recomputed on every
hit-test / mouse-move.

This change is motivated by a subsequent change that will increase
hit-testing precision while significantly worsening performance.  By
caching the stroke path, the aggregate result of the two changes is
expected to perform better than the current status quo.

Bug: 964614
Change-Id: I48de70a22c2f8137604f214a699bae258ff741d8
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2140819Reviewed-by: default avatarFredrik Söderquist <fs@opera.com>
Reviewed-by: default avatarPhilip Rogers <pdr@chromium.org>
Commit-Queue: Ian Prest <iapres@microsoft.com>
Cr-Commit-Position: refs/heads/master@{#758897}
parent d77e9337
...@@ -74,6 +74,9 @@ void LayoutSVGRect::UpdateShapeFromElement() { ...@@ -74,6 +74,9 @@ void LayoutSVGRect::UpdateShapeFromElement() {
} }
} }
if (!use_path_fallback_)
ClearPath();
fill_bounding_box_ = FloatRect( fill_bounding_box_ = FloatRect(
length_context.ResolveLengthPair(svg_style.X(), svg_style.Y(), style), length_context.ResolveLengthPair(svg_style.X(), svg_style.Y(), style),
bounding_box_size); bounding_box_size);
......
...@@ -69,6 +69,19 @@ void LayoutSVGShape::StyleDidChange(StyleDifference diff, ...@@ -69,6 +69,19 @@ void LayoutSVGShape::StyleDidChange(StyleDifference diff,
TransformHelper::DependsOnReferenceBox(StyleRef()); TransformHelper::DependsOnReferenceBox(StyleRef());
LayoutSVGModelObject::StyleDidChange(diff, old_style); LayoutSVGModelObject::StyleDidChange(diff, old_style);
SVGResources::UpdatePaints(*GetElement(), old_style, StyleRef()); SVGResources::UpdatePaints(*GetElement(), old_style, StyleRef());
// Most of the stroke attributes (caps, joins, miters, width, etc.) will cause
// a re-layout which will clear the stroke-path cache; however, there are a
// couple of additional properties that *won't* cause a layout, but are
// significant enough to require invalidating the cache.
if (!diff.NeedsFullLayout() && old_style && stroke_path_cache_) {
const auto& old_svg_style = old_style->SvgStyle();
const auto& svg_style = StyleRef().SvgStyle();
if (old_svg_style.StrokeDashOffset() != svg_style.StrokeDashOffset() ||
*old_svg_style.StrokeDashArray() != *svg_style.StrokeDashArray()) {
stroke_path_cache_.reset();
}
}
} }
void LayoutSVGShape::WillBeDestroyed() { void LayoutSVGShape::WillBeDestroyed() {
...@@ -76,10 +89,20 @@ void LayoutSVGShape::WillBeDestroyed() { ...@@ -76,10 +89,20 @@ void LayoutSVGShape::WillBeDestroyed() {
LayoutSVGModelObject::WillBeDestroyed(); LayoutSVGModelObject::WillBeDestroyed();
} }
void LayoutSVGShape::ClearPath() {
path_.reset();
stroke_path_cache_.reset();
}
void LayoutSVGShape::CreatePath() { void LayoutSVGShape::CreatePath() {
if (!path_) if (!path_)
path_ = std::make_unique<Path>(); path_ = std::make_unique<Path>();
*path_ = To<SVGGeometryElement>(GetElement())->AsPath(); *path_ = To<SVGGeometryElement>(GetElement())->AsPath();
// When the path changes, we need to ensure the stale stroke path cache is
// cleared. Because this is done in all callsites, we can just DCHECK that it
// has been cleared here.
DCHECK(!stroke_path_cache_);
} }
float LayoutSVGShape::DashScaleFactor() const { float LayoutSVGShape::DashScaleFactor() const {
...@@ -150,24 +173,33 @@ FloatRect LayoutSVGShape::HitTestStrokeBoundingBox() const { ...@@ -150,24 +173,33 @@ FloatRect LayoutSVGShape::HitTestStrokeBoundingBox() const {
bool LayoutSVGShape::ShapeDependentStrokeContains( bool LayoutSVGShape::ShapeDependentStrokeContains(
const HitTestLocation& location) { const HitTestLocation& location) {
// In case the subclass didn't create path during UpdateShapeFromElement() if (!stroke_path_cache_) {
// for optimization but still calls this method. // In case the subclass didn't create path during UpdateShapeFromElement()
if (!HasPath()) // for optimization but still calls this method.
CreatePath(); if (!HasPath())
CreatePath();
StrokeData stroke_data;
SVGLayoutSupport::ApplyStrokeStyleToStrokeData(stroke_data, StyleRef(), *this, StrokeData stroke_data;
DashScaleFactor()); SVGLayoutSupport::ApplyStrokeStyleToStrokeData(stroke_data, StyleRef(),
*this, DashScaleFactor());
if (HasNonScalingStroke()) {
// The reason is similar to the above code about HasPath(). if (HasNonScalingStroke()) {
if (!rare_data_) // The reason is similar to the above code about HasPath().
UpdateNonScalingStrokeData(); if (!rare_data_)
return NonScalingStrokePath().StrokeContains( UpdateNonScalingStrokeData();
NonScalingStrokeTransform().MapPoint(location.TransformedPoint()), stroke_path_cache_ = std::make_unique<Path>(
stroke_data); NonScalingStrokePath().StrokePath(stroke_data));
} else {
stroke_path_cache_ =
std::make_unique<Path>(path_->StrokePath(stroke_data));
}
} }
return path_->StrokeContains(location.TransformedPoint(), stroke_data);
DCHECK(stroke_path_cache_);
auto point = location.TransformedPoint();
if (HasNonScalingStroke())
point = NonScalingStrokeTransform().MapPoint(point);
return stroke_path_cache_->Contains(point);
} }
bool LayoutSVGShape::ShapeDependentFillContains( bool LayoutSVGShape::ShapeDependentFillContains(
...@@ -217,6 +249,10 @@ void LayoutSVGShape::UpdateLayout() { ...@@ -217,6 +249,10 @@ void LayoutSVGShape::UpdateLayout() {
if (EverHadLayout() && SelfNeedsLayout()) if (EverHadLayout() && SelfNeedsLayout())
SVGResourcesCache::ClientLayoutChanged(*this); SVGResourcesCache::ClientLayoutChanged(*this);
// The cached stroke may be affected by the ancestor transform, and so needs
// to be cleared regardless of whether the shape or bounds have changed.
stroke_path_cache_.reset();
bool update_parent_boundaries = false; bool update_parent_boundaries = false;
bool bbox_changed = false; bool bbox_changed = false;
// UpdateShapeFromElement() also updates the object & stroke bounds - which // UpdateShapeFromElement() also updates the object & stroke bounds - which
......
...@@ -131,7 +131,7 @@ class LayoutSVGShape : public LayoutSVGModelObject { ...@@ -131,7 +131,7 @@ class LayoutSVGShape : public LayoutSVGModelObject {
float VisualRectOutsetForRasterEffects() const override; float VisualRectOutsetForRasterEffects() const override;
void ClearPath() { path_.reset(); } void ClearPath();
void CreatePath(); void CreatePath();
// Update (cached) shape data and the (object) bounding box. // Update (cached) shape data and the (object) bounding box.
...@@ -189,6 +189,7 @@ class LayoutSVGShape : public LayoutSVGModelObject { ...@@ -189,6 +189,7 @@ class LayoutSVGShape : public LayoutSVGModelObject {
// into removing it. // into removing it.
std::unique_ptr<Path> path_; std::unique_ptr<Path> path_;
mutable std::unique_ptr<LayoutSVGShapeRareData> rare_data_; mutable std::unique_ptr<LayoutSVGShapeRareData> rare_data_;
std::unique_ptr<Path> stroke_path_cache_;
StrokeGeometryClass geometry_class_; StrokeGeometryClass geometry_class_;
bool needs_boundaries_update_ : 1; bool needs_boundaries_update_ : 1;
......
...@@ -82,6 +82,7 @@ class PLATFORM_EXPORT Path { ...@@ -82,6 +82,7 @@ class PLATFORM_EXPORT Path {
bool Contains(const FloatPoint&) const; bool Contains(const FloatPoint&) const;
bool Contains(const FloatPoint&, WindRule) const; bool Contains(const FloatPoint&, WindRule) const;
bool StrokeContains(const FloatPoint&, const StrokeData&) const; bool StrokeContains(const FloatPoint&, const StrokeData&) const;
SkPath StrokePath(const StrokeData&) const;
FloatRect BoundingRect() const; FloatRect BoundingRect() const;
FloatRect StrokeBoundingRect(const StrokeData&) const; FloatRect StrokeBoundingRect(const StrokeData&) const;
...@@ -192,7 +193,6 @@ class PLATFORM_EXPORT Path { ...@@ -192,7 +193,6 @@ class PLATFORM_EXPORT Path {
float radius_y, float radius_y,
float start_angle, float start_angle,
float end_angle); float end_angle);
SkPath StrokePath(const StrokeData&) const;
SkPath path_; SkPath path_;
}; };
......
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