-
Notifications
You must be signed in to change notification settings - Fork 6k
Set SkPath::setIsVolatile based on whether the path survives at least two frames #22620
Changes from 5 commits
bc78eea
f2867c8
34e2523
100b2ec
6a84f9e
24df8c7
280b3c9
07d2d99
88d4125
62c238f
9fc3504
c8171ad
17eefed
9cd0ac9
e087e3d
50dadd3
38281c2
7767698
b1d122e
847a998
cfaa5b0
71153fe
1eac95f
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -67,43 +67,92 @@ void CanvasPath::RegisterNatives(tonic::DartLibraryNatives* natives) { | |
| FOR_EACH_BINDING(DART_REGISTER_NATIVE)}); | ||
| } | ||
|
|
||
| CanvasPath::CanvasPath() {} | ||
| CanvasPath::CanvasPath() | ||
| : tracked_path_(std::make_shared<TrackedVolatilePath>()) { | ||
| resetVolatility(); | ||
| } | ||
|
|
||
| CanvasPath::~CanvasPath() = default; | ||
|
|
||
| void CanvasPath::resetVolatility() { | ||
| if (!tracked_path_->tracking_volatility) { | ||
| tracked_path_->path_.setIsVolatile(true); | ||
| tracked_path_->tracking_volatility = true; | ||
| { | ||
| std::scoped_lock guard(volatile_paths_mutex_); | ||
| volatile_paths_.insert(tracked_path_); | ||
| } | ||
| } | ||
| } | ||
|
|
||
| CanvasPath::~CanvasPath() {} | ||
| // static | ||
| std::mutex CanvasPath::volatile_paths_mutex_; | ||
| // static | ||
| std::unordered_set<std::shared_ptr<CanvasPath::TrackedVolatilePath>> | ||
| CanvasPath::volatile_paths_; | ||
| // static | ||
| void CanvasPath::updatePathVolatility() { | ||
| TRACE_EVENT0("flutter", "updatePathVolatility"); | ||
| std::scoped_lock guard(volatile_paths_mutex_); | ||
| for (auto it = volatile_paths_.begin(), last = volatile_paths_.end(); | ||
| it != last;) { | ||
| auto path = *it; | ||
| path->frame_count++; | ||
| if (path->frame_count >= number_of_frames_until_non_volatile) { | ||
| path->path_.setIsVolatile(false); | ||
| path->tracking_volatility = false; | ||
| it = volatile_paths_.erase(it); | ||
| } else { | ||
| ++it; | ||
| } | ||
| } | ||
| } | ||
|
|
||
| void CanvasPath::ReleaseDartWrappableReference() const { | ||
| std::scoped_lock guard(volatile_paths_mutex_); | ||
| volatile_paths_.erase(tracked_path_); | ||
| } | ||
|
|
||
| int CanvasPath::getFillType() { | ||
| return static_cast<int>(path_.getFillType()); | ||
| return static_cast<int>(tracked_path_->path_.getFillType()); | ||
|
||
| } | ||
|
|
||
| void CanvasPath::setFillType(int fill_type) { | ||
| path_.setFillType(static_cast<SkPathFillType>(fill_type)); | ||
| tracked_path_->path_.setFillType(static_cast<SkPathFillType>(fill_type)); | ||
| resetVolatility(); | ||
| } | ||
|
|
||
| void CanvasPath::moveTo(float x, float y) { | ||
| path_.moveTo(x, y); | ||
| tracked_path_->path_.moveTo(x, y); | ||
| resetVolatility(); | ||
| } | ||
|
|
||
| void CanvasPath::relativeMoveTo(float x, float y) { | ||
| path_.rMoveTo(x, y); | ||
| tracked_path_->path_.rMoveTo(x, y); | ||
| resetVolatility(); | ||
| } | ||
|
|
||
| void CanvasPath::lineTo(float x, float y) { | ||
| path_.lineTo(x, y); | ||
| tracked_path_->path_.lineTo(x, y); | ||
| resetVolatility(); | ||
| } | ||
|
|
||
| void CanvasPath::relativeLineTo(float x, float y) { | ||
| path_.rLineTo(x, y); | ||
| tracked_path_->path_.rLineTo(x, y); | ||
| resetVolatility(); | ||
| } | ||
|
|
||
| void CanvasPath::quadraticBezierTo(float x1, float y1, float x2, float y2) { | ||
| path_.quadTo(x1, y1, x2, y2); | ||
| tracked_path_->path_.quadTo(x1, y1, x2, y2); | ||
| resetVolatility(); | ||
| } | ||
|
|
||
| void CanvasPath::relativeQuadraticBezierTo(float x1, | ||
| float y1, | ||
| float x2, | ||
| float y2) { | ||
| path_.rQuadTo(x1, y1, x2, y2); | ||
| tracked_path_->path_.rQuadTo(x1, y1, x2, y2); | ||
| resetVolatility(); | ||
| } | ||
|
|
||
| void CanvasPath::cubicTo(float x1, | ||
|
|
@@ -112,7 +161,8 @@ void CanvasPath::cubicTo(float x1, | |
| float y2, | ||
| float x3, | ||
| float y3) { | ||
| path_.cubicTo(x1, y1, x2, y2, x3, y3); | ||
| tracked_path_->path_.cubicTo(x1, y1, x2, y2, x3, y3); | ||
| resetVolatility(); | ||
| } | ||
|
|
||
| void CanvasPath::relativeCubicTo(float x1, | ||
|
|
@@ -121,19 +171,22 @@ void CanvasPath::relativeCubicTo(float x1, | |
| float y2, | ||
| float x3, | ||
| float y3) { | ||
| path_.rCubicTo(x1, y1, x2, y2, x3, y3); | ||
| tracked_path_->path_.rCubicTo(x1, y1, x2, y2, x3, y3); | ||
| resetVolatility(); | ||
| } | ||
|
|
||
| void CanvasPath::conicTo(float x1, float y1, float x2, float y2, float w) { | ||
| path_.conicTo(x1, y1, x2, y2, w); | ||
| tracked_path_->path_.conicTo(x1, y1, x2, y2, w); | ||
| resetVolatility(); | ||
| } | ||
|
|
||
| void CanvasPath::relativeConicTo(float x1, | ||
| float y1, | ||
| float x2, | ||
| float y2, | ||
| float w) { | ||
| path_.rConicTo(x1, y1, x2, y2, w); | ||
| tracked_path_->path_.rConicTo(x1, y1, x2, y2, w); | ||
| resetVolatility(); | ||
| } | ||
|
|
||
| void CanvasPath::arcTo(float left, | ||
|
|
@@ -143,9 +196,10 @@ void CanvasPath::arcTo(float left, | |
| float startAngle, | ||
| float sweepAngle, | ||
| bool forceMoveTo) { | ||
| path_.arcTo(SkRect::MakeLTRB(left, top, right, bottom), | ||
| startAngle * 180.0 / M_PI, sweepAngle * 180.0 / M_PI, | ||
| forceMoveTo); | ||
| tracked_path_->path_.arcTo(SkRect::MakeLTRB(left, top, right, bottom), | ||
| startAngle * 180.0 / M_PI, | ||
| sweepAngle * 180.0 / M_PI, forceMoveTo); | ||
| resetVolatility(); | ||
| } | ||
|
|
||
| void CanvasPath::arcToPoint(float arcEndX, | ||
|
|
@@ -160,8 +214,9 @@ void CanvasPath::arcToPoint(float arcEndX, | |
| const auto direction = | ||
| isClockwiseDirection ? SkPathDirection::kCW : SkPathDirection::kCCW; | ||
|
|
||
| path_.arcTo(radiusX, radiusY, xAxisRotation, arcSize, direction, arcEndX, | ||
| arcEndY); | ||
| tracked_path_->path_.arcTo(radiusX, radiusY, xAxisRotation, arcSize, | ||
| direction, arcEndX, arcEndY); | ||
| resetVolatility(); | ||
| } | ||
|
|
||
| void CanvasPath::relativeArcToPoint(float arcEndDeltaX, | ||
|
|
@@ -175,16 +230,19 @@ void CanvasPath::relativeArcToPoint(float arcEndDeltaX, | |
| : SkPath::ArcSize::kSmall_ArcSize; | ||
| const auto direction = | ||
| isClockwiseDirection ? SkPathDirection::kCW : SkPathDirection::kCCW; | ||
| path_.rArcTo(radiusX, radiusY, xAxisRotation, arcSize, direction, | ||
| arcEndDeltaX, arcEndDeltaY); | ||
| tracked_path_->path_.rArcTo(radiusX, radiusY, xAxisRotation, arcSize, | ||
| direction, arcEndDeltaX, arcEndDeltaY); | ||
| resetVolatility(); | ||
| } | ||
|
|
||
| void CanvasPath::addRect(float left, float top, float right, float bottom) { | ||
| path_.addRect(SkRect::MakeLTRB(left, top, right, bottom)); | ||
| tracked_path_->path_.addRect(SkRect::MakeLTRB(left, top, right, bottom)); | ||
| resetVolatility(); | ||
| } | ||
|
|
||
| void CanvasPath::addOval(float left, float top, float right, float bottom) { | ||
| path_.addOval(SkRect::MakeLTRB(left, top, right, bottom)); | ||
| tracked_path_->path_.addOval(SkRect::MakeLTRB(left, top, right, bottom)); | ||
| resetVolatility(); | ||
| } | ||
|
|
||
| void CanvasPath::addArc(float left, | ||
|
|
@@ -193,25 +251,31 @@ void CanvasPath::addArc(float left, | |
| float bottom, | ||
| float startAngle, | ||
| float sweepAngle) { | ||
| path_.addArc(SkRect::MakeLTRB(left, top, right, bottom), | ||
| startAngle * 180.0 / M_PI, sweepAngle * 180.0 / M_PI); | ||
| tracked_path_->path_.addArc(SkRect::MakeLTRB(left, top, right, bottom), | ||
| startAngle * 180.0 / M_PI, | ||
| sweepAngle * 180.0 / M_PI); | ||
| resetVolatility(); | ||
| } | ||
|
|
||
| void CanvasPath::addPolygon(const tonic::Float32List& points, bool close) { | ||
| path_.addPoly(reinterpret_cast<const SkPoint*>(points.data()), | ||
| points.num_elements() / 2, close); | ||
| tracked_path_->path_.addPoly(reinterpret_cast<const SkPoint*>(points.data()), | ||
| points.num_elements() / 2, close); | ||
| resetVolatility(); | ||
| } | ||
|
|
||
| void CanvasPath::addRRect(const RRect& rrect) { | ||
| path_.addRRect(rrect.sk_rrect); | ||
| tracked_path_->path_.addRRect(rrect.sk_rrect); | ||
| resetVolatility(); | ||
| } | ||
|
|
||
| void CanvasPath::addPath(CanvasPath* path, double dx, double dy) { | ||
| if (!path) { | ||
| Dart_ThrowException(ToDart("Path.addPath called with non-genuine Path.")); | ||
| return; | ||
| } | ||
| path_.addPath(path->path(), dx, dy, SkPath::kAppend_AddPathMode); | ||
| tracked_path_->path_.addPath(path->path(), dx, dy, | ||
| SkPath::kAppend_AddPathMode); | ||
| resetVolatility(); | ||
| } | ||
|
|
||
| void CanvasPath::addPathWithMatrix(CanvasPath* path, | ||
|
|
@@ -227,8 +291,10 @@ void CanvasPath::addPathWithMatrix(CanvasPath* path, | |
| SkMatrix matrix = ToSkMatrix(matrix4); | ||
| matrix.setTranslateX(matrix.getTranslateX() + dx); | ||
| matrix.setTranslateY(matrix.getTranslateY() + dy); | ||
| path_.addPath(path->path(), matrix, SkPath::kAppend_AddPathMode); | ||
| tracked_path_->path_.addPath(path->path(), matrix, | ||
| SkPath::kAppend_AddPathMode); | ||
| matrix4.Release(); | ||
| resetVolatility(); | ||
| } | ||
|
|
||
| void CanvasPath::extendWithPath(CanvasPath* path, double dx, double dy) { | ||
|
|
@@ -237,7 +303,9 @@ void CanvasPath::extendWithPath(CanvasPath* path, double dx, double dy) { | |
| ToDart("Path.extendWithPath called with non-genuine Path.")); | ||
| return; | ||
| } | ||
| path_.addPath(path->path(), dx, dy, SkPath::kExtend_AddPathMode); | ||
| path->tracked_path_->path_.addPath(path->path(), dx, dy, | ||
| SkPath::kExtend_AddPathMode); | ||
| resetVolatility(); | ||
| } | ||
|
|
||
| void CanvasPath::extendWithPathAndMatrix(CanvasPath* path, | ||
|
|
@@ -253,37 +321,44 @@ void CanvasPath::extendWithPathAndMatrix(CanvasPath* path, | |
| SkMatrix matrix = ToSkMatrix(matrix4); | ||
| matrix.setTranslateX(matrix.getTranslateX() + dx); | ||
| matrix.setTranslateY(matrix.getTranslateY() + dy); | ||
| path_.addPath(path->path(), matrix, SkPath::kExtend_AddPathMode); | ||
| path->tracked_path_->path_.addPath(path->path(), matrix, | ||
| SkPath::kExtend_AddPathMode); | ||
| matrix4.Release(); | ||
| resetVolatility(); | ||
| } | ||
|
|
||
| void CanvasPath::close() { | ||
| path_.close(); | ||
| tracked_path_->path_.close(); | ||
| resetVolatility(); | ||
| } | ||
|
|
||
| void CanvasPath::reset() { | ||
| path_.reset(); | ||
| tracked_path_->path_.reset(); | ||
| resetVolatility(); | ||
| } | ||
|
|
||
| bool CanvasPath::contains(double x, double y) { | ||
| return path_.contains(x, y); | ||
| return tracked_path_->path_.contains(x, y); | ||
| } | ||
|
|
||
| void CanvasPath::shift(Dart_Handle path_handle, double dx, double dy) { | ||
| fml::RefPtr<CanvasPath> path = CanvasPath::Create(path_handle); | ||
| path_.offset(dx, dy, &path->path_); | ||
| tracked_path_->path_.offset(dx, dy, &path->tracked_path_->path_); | ||
| resetVolatility(); | ||
| } | ||
|
|
||
| void CanvasPath::transform(Dart_Handle path_handle, | ||
| tonic::Float64List& matrix4) { | ||
| fml::RefPtr<CanvasPath> path = CanvasPath::Create(path_handle); | ||
| path_.transform(ToSkMatrix(matrix4), &path->path_); | ||
| tracked_path_->path_.transform(ToSkMatrix(matrix4), | ||
| &path->tracked_path_->path_); | ||
| matrix4.Release(); | ||
| resetVolatility(); | ||
|
||
| } | ||
|
|
||
| tonic::Float32List CanvasPath::getBounds() { | ||
| tonic::Float32List rect(Dart_NewTypedData(Dart_TypedData_kFloat32, 4)); | ||
| const SkRect& bounds = path_.getBounds(); | ||
| const SkRect& bounds = tracked_path_->path_.getBounds(); | ||
| rect[0] = bounds.left(); | ||
| rect[1] = bounds.top(); | ||
| rect[2] = bounds.right(); | ||
|
|
@@ -293,21 +368,22 @@ tonic::Float32List CanvasPath::getBounds() { | |
|
|
||
| bool CanvasPath::op(CanvasPath* path1, CanvasPath* path2, int operation) { | ||
| return Op(path1->path(), path2->path(), static_cast<SkPathOp>(operation), | ||
| &path_); | ||
| &tracked_path_->path_); | ||
| resetVolatility(); | ||
| } | ||
|
|
||
| void CanvasPath::clone(Dart_Handle path_handle) { | ||
| fml::RefPtr<CanvasPath> path = CanvasPath::Create(path_handle); | ||
| // per Skia docs, this will create a fast copy | ||
| // data is shared until the source path or dest path are mutated | ||
| path->path_ = path_; | ||
| path->tracked_path_->path_ = tracked_path_->path_; | ||
| } | ||
|
|
||
| // This is doomed to be called too early, since Paths are mutable. | ||
| // However, it can help for some of the clone/shift/transform type methods | ||
| // where the resultant path will initially have a meaningful size. | ||
| size_t CanvasPath::GetAllocationSize() const { | ||
| return sizeof(CanvasPath) + path_.approximateBytesUsed(); | ||
| return sizeof(CanvasPath) + tracked_path_->path_.approximateBytesUsed(); | ||
| } | ||
|
|
||
| } // namespace flutter | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -5,6 +5,9 @@ | |
| #ifndef FLUTTER_LIB_UI_PAINTING_PATH_H_ | ||
| #define FLUTTER_LIB_UI_PAINTING_PATH_H_ | ||
|
|
||
| #include <mutex> | ||
| #include <unordered_set> | ||
|
|
||
| #include "flutter/lib/ui/dart_wrapper.h" | ||
| #include "flutter/lib/ui/painting/rrect.h" | ||
| #include "third_party/skia/include/core/SkPath.h" | ||
|
|
@@ -36,7 +39,7 @@ class CanvasPath : public RefCountedDartWrappable<CanvasPath> { | |
| static fml::RefPtr<CanvasPath> CreateFrom(Dart_Handle path_handle, | ||
| const SkPath& src) { | ||
| fml::RefPtr<CanvasPath> path = CanvasPath::Create(path_handle); | ||
| path->path_ = src; | ||
| path->tracked_path_->path_ = src; | ||
|
||
| return path; | ||
| } | ||
|
|
||
|
|
@@ -108,16 +111,39 @@ class CanvasPath : public RefCountedDartWrappable<CanvasPath> { | |
| bool op(CanvasPath* path1, CanvasPath* path2, int operation); | ||
| void clone(Dart_Handle path_handle); | ||
|
|
||
| const SkPath& path() const { return path_; } | ||
| const SkPath& path() const { return tracked_path_->path_; } | ||
|
|
||
| size_t GetAllocationSize() const override; | ||
|
|
||
| static void RegisterNatives(tonic::DartLibraryNatives* natives); | ||
|
|
||
| // Called by the shell at the end of a frame after notifying Dart about idle | ||
| // time. | ||
| // | ||
| // This method will flip the volatility bit to false for any paths that have | ||
| // survived the |number_of_frames_until_non_volatile|. | ||
| static void updatePathVolatility(); | ||
|
|
||
| virtual void ReleaseDartWrappableReference() const override; | ||
|
|
||
| private: | ||
| struct TrackedVolatilePath { | ||
| bool tracking_volatility = false; | ||
| int frame_count = 0; | ||
| SkPath path_; | ||
| }; | ||
|
|
||
| CanvasPath(); | ||
|
|
||
| SkPath path_; | ||
| static constexpr int number_of_frames_until_non_volatile = 2; | ||
| static std::mutex volatile_paths_mutex_; | ||
| static std::unordered_set<std::shared_ptr<TrackedVolatilePath>> | ||
|
||
| volatile_paths_; | ||
|
|
||
| std::shared_ptr<TrackedVolatilePath> tracked_path_; | ||
|
|
||
| // Must be called whenever the path is created or mutated. | ||
| void resetVolatility(); | ||
| }; | ||
|
|
||
| } // namespace flutter | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The name
resetVolatilitysounds like it will also reset the volatility frame count but that does not seem to be the case. I believe resetting the count is the intended behavior as it's called in those methods that mutate the path.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done