<?xml version="1.0" encoding="UTF-8" standalone="yes" ?>
<!DOCTYPE bugzilla SYSTEM "https://bugs.webkit.org/page.cgi?id=bugzilla.dtd">

<bugzilla version="5.0.4.1"
          urlbase="https://bugs.webkit.org/"
          
          maintainer="admin@webkit.org"
>

    <bug>
          <bug_id>61560</bug_id>
          
          <creation_ts>2011-05-26 14:19:20 -0700</creation_ts>
          <short_desc>Canvas performance regression with `clip`</short_desc>
          <delta_ts>2014-04-03 02:38:58 -0700</delta_ts>
          <reporter_accessible>1</reporter_accessible>
          <cclist_accessible>1</cclist_accessible>
          <classification_id>1</classification_id>
          <classification>Unclassified</classification>
          <product>WebKit</product>
          <component>Canvas</component>
          <version>528+ (Nightly build)</version>
          <rep_platform>Mac (Intel)</rep_platform>
          <op_sys>OS X 10.6</op_sys>
          <bug_status>RESOLVED</bug_status>
          <resolution>INVALID</resolution>
          
          
          <bug_file_loc>http://kangax.github.com/jstests/bugs/canvas_clip_perf_test.html</bug_file_loc>
          <status_whiteboard></status_whiteboard>
          <keywords></keywords>
          <priority>P2</priority>
          <bug_severity>Major</bug_severity>
          <target_milestone>---</target_milestone>
          
          
          <everconfirmed>1</everconfirmed>
          <reporter name="kangax">kangax</reporter>
          <assigned_to name="Nobody">webkit-unassigned</assigned_to>
          <cc>jarred</cc>
    
    <cc>krit</cc>
    
    <cc>mathias</cc>
    
    <cc>mdelaney7</cc>
    
    <cc>oliver</cc>
          

      

      

      

          <comment_sort_order>oldest_to_newest</comment_sort_order>  
          <long_desc isprivate="0" >
    <commentid>410821</commentid>
    <comment_count>0</comment_count>
    <who name="kangax">kangax</who>
    <bug_when>2011-05-26 14:19:20 -0700</bug_when>
    <thetext>Canvas&apos; `clip` coupled with one of drawing methods (e.g. `stroke`, `strokeRect`, etc.) results in continuous, reproducible performance loss.

The test page includes the following code:

setTimeout(function animate() {
  
  ctx.beginPath();
  ctx.moveTo(10, 10);
  ctx.lineTo(150, 150);
  ctx.lineTo(220, 20);
  ctx.closePath();
  ctx.stroke();
  ctx.clip();
  
  setTimeout(animate, 10);
}, 10);

On my system (Mac OS X) browser starts consuming ~80% CPU and continuously rises up to 90% and more. The test page also includes an FPS counter; the FPS count keeps dropping as more and more invocations of `stroke` + `clip` occur.

This doesn&apos;t happen in either Firefox (4), Opera (11.11), or IE (9). 

It DOES happen in other webkit-based browsers — Chrome (13), Safari (5).

Here&apos;s a screenshot showing activity monitor (with webkit process consuming ~90% CPU) and dropping FPS counter — http://twitpic.com/52w4ya

This problem seems to be going as far back as Safari 3.0.4</thetext>
  </long_desc><long_desc isprivate="0" >
    <commentid>411431</commentid>
    <comment_count>1</comment_count>
    <who name="Jarred Nicholls">jarred</who>
    <bug_when>2011-05-27 07:42:58 -0700</bug_when>
    <thetext>This is certainly identical symptoms on Chromium and Mac (WebKit Nightly), but interestingly enough, not an issue in Qt port.  Albeit, Qt leaks memory slowly w/ clip() and Chrome/Mac do not.  I&apos;ll check into it this weekend and see what&apos;s up.</thetext>
  </long_desc><long_desc isprivate="0" >
    <commentid>411515</commentid>
    <comment_count>2</comment_count>
    <who name="Oliver Hunt">oliver</who>
    <bug_when>2011-05-27 10:17:06 -0700</bug_when>
    <thetext>Random (non-sample based, entirely hypothetical) guess: we&apos;re applying they same clip over and over again so each frame accumulates another clip region to check against.  logically we should simply be intersecting the clip, but i would have thought that that would happen internally anyway.</thetext>
  </long_desc><long_desc isprivate="0" >
    <commentid>411863</commentid>
    <comment_count>3</comment_count>
    <who name="kangax">kangax</who>
    <bug_when>2011-05-28 10:04:56 -0700</bug_when>
    <thetext>Tried Oliver&apos;s suggestion to add `context.save()` &amp; `context.restore()` around clipping code and performance loss was gone. Using this as a temporary workaround for now, but would love to see this &quot;fixed&quot; without additional &quot;wrapping&quot;.</thetext>
  </long_desc><long_desc isprivate="0" >
    <commentid>415476</commentid>
    <comment_count>4</comment_count>
    <who name="Jarred Nicholls">jarred</who>
    <bug_when>2011-06-04 06:24:55 -0700</bug_when>
    <thetext>The clipping path intersection is not being detected by CGContextClip nor CGContextEOClip it would seem.  Surrounding the CG clip w/ save/restore graphics state certainly does fix the issue.  Bug in CG?

GraphicsContextCG.cpp
@@ -1095,13 +1095,15 @@ void GraphicsContext::clip(const Path&amp; path)
 
     // CGContextClip does nothing if the path is empty, so in this case, we
     // instead clip against a zero rect to reduce the clipping region to
     // nothing - which is the intended behavior of clip() if the path is empty.
     if (path.isEmpty())
         CGContextClipToRect(context, CGRectZero);
     else {
+        CGContextSaveGState(context);
         CGContextBeginPath(context);
         CGContextAddPath(context, path.platformPath());
         CGContextClip(context);
+        CGContextRestoreGState(context);
     }
     m_data-&gt;clip(path);
 }</thetext>
  </long_desc><long_desc isprivate="0" >
    <commentid>997108</commentid>
    <comment_count>5</comment_count>
    <who name="Dirk Schulze">krit</who>
    <bug_when>2014-04-03 02:38:58 -0700</bug_when>
    <thetext>(In reply to comment #4)
&gt; The clipping path intersection is not being detected by CGContextClip nor CGContextEOClip it would seem.  Surrounding the CG clip w/ save/restore graphics state certainly does fix the issue.  Bug in CG?
&gt; 
&gt; GraphicsContextCG.cpp
&gt; @@ -1095,13 +1095,15 @@ void GraphicsContext::clip(const Path&amp; path)
&gt; 
&gt;      // CGContextClip does nothing if the path is empty, so in this case, we
&gt;      // instead clip against a zero rect to reduce the clipping region to
&gt;      // nothing - which is the intended behavior of clip() if the path is empty.
&gt;      if (path.isEmpty())
&gt;          CGContextClipToRect(context, CGRectZero);
&gt;      else {
&gt; +        CGContextSaveGState(context);
&gt;          CGContextBeginPath(context);
&gt;          CGContextAddPath(context, path.platformPath());
&gt;          CGContextClip(context);
&gt; +        CGContextRestoreGState(context);
&gt;      }
&gt;      m_data-&gt;clip(path);
&gt;  }

This is incorrect. The path should not be cleared after clipping.</thetext>
  </long_desc>
      
      

    </bug>

</bugzilla>