Title: [257732] trunk/Source/WebCore
Revision
257732
Author
[email protected]
Date
2020-03-02 13:25:57 -0800 (Mon, 02 Mar 2020)

Log Message

Make Path::Path(const Path&) and Path::operator=(const Path&) cheap
https://bugs.webkit.org/show_bug.cgi?id=208265
<rdar://problem/59922452>

Reviewed by Darin Adler.

Make WebCore::Path's copy constructor and copy assignment operator cheaper. See below for more details; there
should be no observable change in behavior. This gives us approximately 6% back on the canvas lines subtest on
MotionMark, when the GPU process is used to render canvases (tested against macOS 10.15.3, on a 10-core iMac).

* platform/graphics/Path.h:
* platform/graphics/cg/PathCG.cpp:
(WebCore::Path::~Path):
(WebCore::Path::ensurePlatformPath):
(WebCore::Path::Path):

Update move constructors to set m_copyPathBeforeMutation to false on the given (moved) path.

(WebCore::Path::swap):
(WebCore::Path::initializeOrCopyPlatformPathIfNeeded):

Whenever we need to ensure the platform CGPath (i.e. prior to appending to it), check the new flag to see
whether it's necessary to apply the subsequent mutation on a copy of m_path, rather than m_path itself. The
exception to this is when m_path only has a retain count of 1, which implies that all other copies of this path
have been mutated (and have copied their platform CGPath as a result); this means it's safe to apply changes to
m_path, since this path is now the sole owner of the CGPath.

(WebCore::Path::operator=):

The copy constructor and copy assignment operators currently call CGPathCreateMutableCopy on the given path's
platform CGPath; this is to ensure that if either path is mutated in the future, the mutation won't also affect
the other copy of the WebCore::Path.

However, this call to CGPathCreateMutableCopy isn't needed in the case where a path is copied, but neither of
the paths are mutated. To avoid this extra copy, we can instead introduce a dirty bit (m_copyPathBeforeMutation)
that is set when a path is copied (either via the constructor, or through assignment) and proceed to simply copy
the path pointer to the new path. Only if either path is mutated do we then call CGPathCreateMutableCopy.

(WebCore::Path::transform):
(WebCore::Path::closeSubpath):
(WebCore::Path::addArc):
(WebCore::Path::addPath):
(WebCore::Path::clear):

Modified Paths

Diff

Modified: trunk/Source/WebCore/ChangeLog (257731 => 257732)


--- trunk/Source/WebCore/ChangeLog	2020-03-02 21:21:58 UTC (rev 257731)
+++ trunk/Source/WebCore/ChangeLog	2020-03-02 21:25:57 UTC (rev 257732)
@@ -1,3 +1,49 @@
+2020-03-02  Wenson Hsieh  <[email protected]>
+
+        Make Path::Path(const Path&) and Path::operator=(const Path&) cheap
+        https://bugs.webkit.org/show_bug.cgi?id=208265
+        <rdar://problem/59922452>
+
+        Reviewed by Darin Adler.
+
+        Make WebCore::Path's copy constructor and copy assignment operator cheaper. See below for more details; there
+        should be no observable change in behavior. This gives us approximately 6% back on the canvas lines subtest on
+        MotionMark, when the GPU process is used to render canvases (tested against macOS 10.15.3, on a 10-core iMac).
+
+        * platform/graphics/Path.h:
+        * platform/graphics/cg/PathCG.cpp:
+        (WebCore::Path::~Path):
+        (WebCore::Path::ensurePlatformPath):
+        (WebCore::Path::Path):
+
+        Update move constructors to set m_copyPathBeforeMutation to false on the given (moved) path.
+
+        (WebCore::Path::swap):
+        (WebCore::Path::initializeOrCopyPlatformPathIfNeeded):
+
+        Whenever we need to ensure the platform CGPath (i.e. prior to appending to it), check the new flag to see
+        whether it's necessary to apply the subsequent mutation on a copy of m_path, rather than m_path itself. The
+        exception to this is when m_path only has a retain count of 1, which implies that all other copies of this path
+        have been mutated (and have copied their platform CGPath as a result); this means it's safe to apply changes to
+        m_path, since this path is now the sole owner of the CGPath.
+
+        (WebCore::Path::operator=):
+
+        The copy constructor and copy assignment operators currently call CGPathCreateMutableCopy on the given path's
+        platform CGPath; this is to ensure that if either path is mutated in the future, the mutation won't also affect
+        the other copy of the WebCore::Path.
+
+        However, this call to CGPathCreateMutableCopy isn't needed in the case where a path is copied, but neither of
+        the paths are mutated. To avoid this extra copy, we can instead introduce a dirty bit (m_copyPathBeforeMutation)
+        that is set when a path is copied (either via the constructor, or through assignment) and proceed to simply copy
+        the path pointer to the new path. Only if either path is mutated do we then call CGPathCreateMutableCopy.
+
+        (WebCore::Path::transform):
+        (WebCore::Path::closeSubpath):
+        (WebCore::Path::addArc):
+        (WebCore::Path::addPath):
+        (WebCore::Path::clear):
+
 2020-03-02  Said Abou-Hallawa  <[email protected]>
 
         Implement the remote ImageBuffer

Modified: trunk/Source/WebCore/platform/graphics/Path.h (257731 => 257732)


--- trunk/Source/WebCore/platform/graphics/Path.h	2020-03-02 21:21:58 UTC (rev 257731)
+++ trunk/Source/WebCore/platform/graphics/Path.h	2020-03-02 21:25:57 UTC (rev 257732)
@@ -214,6 +214,11 @@
     template<class Decoder> static Optional<Path> decode(Decoder&);
 
 private:
+#if USE(CG)
+    void swap(Path&);
+    void initializeOrCopyPlatformPathIfNeeded();
+#endif
+
 #if USE(DIRECT2D)
     Vector<ID2D1Geometry*> m_geometries;
     COMPtr<ID2D1GeometryGroup> m_path;
@@ -220,8 +225,12 @@
     mutable COMPtr<ID2D1GeometrySink> m_activePath;
     mutable bool m_figureIsOpened { false };
 #else
-    PlatformPathPtr m_path { nullptr };
+    mutable PlatformPathPtr m_path { nullptr };
 #endif
+
+#if USE(CG)
+    mutable bool m_copyPathBeforeMutation { false };
+#endif
 };
 
 WTF::TextStream& operator<<(WTF::TextStream&, const Path&);

Modified: trunk/Source/WebCore/platform/graphics/cg/PathCG.cpp (257731 => 257732)


--- trunk/Source/WebCore/platform/graphics/cg/PathCG.cpp	2020-03-02 21:21:58 UTC (rev 257731)
+++ trunk/Source/WebCore/platform/graphics/cg/PathCG.cpp	2020-03-02 21:25:57 UTC (rev 257732)
@@ -95,45 +95,65 @@
 Path::~Path()
 {
     if (m_path)
-        CGPathRelease(m_path);
+        CFRelease(m_path);
 }
 
 PlatformPathPtr Path::ensurePlatformPath()
 {
-    if (!m_path)
-        m_path = CGPathCreateMutable();
+    initializeOrCopyPlatformPathIfNeeded();
     return m_path;
 }
 
 Path::Path(const Path& other)
 {
-    m_path = other.m_path ? CGPathCreateMutableCopy(other.m_path) : 0;
+    m_path = { other.m_path };
+    if (m_path) {
+        m_copyPathBeforeMutation = true;
+        other.m_copyPathBeforeMutation = true;
+        CFRetain(m_path);
+    }
 }
 
 Path::Path(Path&& other)
+    : m_path(std::exchange(other.m_path, nullptr))
+    , m_copyPathBeforeMutation(std::exchange(other.m_copyPathBeforeMutation, false))
 {
-    m_path = other.m_path;
-    other.m_path = nullptr;
 }
-    
+
+void Path::swap(Path& otherPath)
+{
+    std::swap(m_path, otherPath.m_path);
+    std::swap(m_copyPathBeforeMutation, otherPath.m_copyPathBeforeMutation);
+}
+
+void Path::initializeOrCopyPlatformPathIfNeeded()
+{
+    if (!m_path) {
+        m_path = CGPathCreateMutable();
+        return;
+    }
+
+    if (m_copyPathBeforeMutation) {
+        if (CFGetRetainCount(m_path) > 1) {
+            auto pathToCopy = m_path;
+            m_path = CGPathCreateMutableCopy(pathToCopy);
+            CFRelease(pathToCopy);
+        }
+        m_copyPathBeforeMutation = false;
+    }
+}
+
 Path& Path::operator=(const Path& other)
 {
-    if (this == &other)
-        return *this;
-    if (m_path)
-        CGPathRelease(m_path);
-    m_path = other.m_path ? CGPathCreateMutableCopy(other.m_path) : nullptr;
+    Path copy { other };
+    swap(copy);
     return *this;
 }
 
 Path& Path::operator=(Path&& other)
 {
-    if (this == &other)
-        return *this;
-    if (m_path)
-        CGPathRelease(m_path);
-    m_path = other.m_path;
-    other.m_path = nullptr;
+    Path copy { WTFMove(other) };
+    swap(copy);
     return *this;
 }
 
@@ -222,8 +242,9 @@
 #else
     CGMutablePathRef path = CGPathCreateMutableCopyByTransformingPath(m_path, &transformCG);
 #endif
-    CGPathRelease(m_path);
+    CFRelease(m_path);
     m_path = path;
+    m_copyPathBeforeMutation = false;
 }
 
 FloatRect Path::boundingRect() const
@@ -348,14 +369,16 @@
     if (isNull())
         return;
 
-    CGPathCloseSubpath(m_path);
+    CGPathCloseSubpath(ensurePlatformPath());
 }
 
 void Path::addArc(const FloatPoint& p, float radius, float startAngle, float endAngle, bool clockwise)
 {
     // Workaround for <rdar://problem/5189233> CGPathAddArc hangs or crashes when passed inf as start or end angle
-    if (std::isfinite(startAngle) && std::isfinite(endAngle))
-        CGPathAddArc(ensurePlatformPath(), nullptr, p.x(), p.y(), radius, startAngle, endAngle, clockwise);
+    if (!std::isfinite(startAngle) || !std::isfinite(endAngle))
+        return;
+
+    CGPathAddArc(ensurePlatformPath(), nullptr, p.x(), p.y(), radius, startAngle, endAngle, clockwise);
 }
 
 void Path::addRect(const FloatRect& r)
@@ -394,17 +417,17 @@
     }
     CGPathRef pathCopy = CGPathCreateCopy(path.platformPath());
     CGPathAddPath(ensurePlatformPath(), &transformCG, pathCopy);
-    CGPathRelease(pathCopy);
+    CFRelease(pathCopy);
 }
 
-
 void Path::clear()
 {
     if (isNull())
         return;
 
-    CGPathRelease(m_path);
+    CFRelease(m_path);
     m_path = CGPathCreateMutable();
+    m_copyPathBeforeMutation = false;
 }
 
 bool Path::isEmpty() const
_______________________________________________
webkit-changes mailing list
[email protected]
https://lists.webkit.org/mailman/listinfo/webkit-changes

Reply via email to