JDK-8103438 : Canvas should clear its buffered commands on certain clearRect, fillRect calls
  • Type: Enhancement
  • Component: javafx
  • Sub-Component: scenegraph
  • Affected Version: 8
  • Priority: P3
  • Status: Resolved
  • Resolution: Fixed
  • Submitted: 2013-08-09
  • Updated: 2015-06-16
  • Resolved: 2013-10-25
The Version table provides details related to the release that this issue/RFE will be addressed.

Unresolved : Release in which this issue/RFE will be addressed.
Resolved: Release in which this issue/RFE has been resolved.
Fixed : Release in which this issue/RFE has been fixed. The release containing this fix may be available for download as an Early Access Release or a General Availability Release.

To download the current JDK release, click here.
JDK 8
8Fixed
Related Reports
Blocks :  
Duplicate :  
Relates :  
Relates :  
Description
If you have a canvas and you scribble on it, and then scribble on it some more, and then scribble on it some more, then in order for us to get the right result in the end, we need to replay all those scribbles in order. If pulses are not happening, we still need to remember these scribbles so we can draw the right result. The problem is that this can lead to OOM errors (see RT-23312).

BUT, if you issue a command to the canvas which will cause it to "clear" all its contents, then we could throw away any previously buffered data. Right now the only way to do that would be a fillRect with a solid fill where the fillRect encompasses the entire canvas area, or a clearRect where the clearRect encompasses the entire canvas area.

This seems like a very simple fix. GraphicsContext.clearRect and GraphicsContext.fillRect should both (under the right conditions) throw away the previously buffered commands. Then all you have to do is be sure to make one of these calls (likely just a clearRect) before each frame, and we'll never buffer more than a single frame's worth of data. We could also add a "clear" method which is "clearRect(0, 0, w, h)" to make this more foolproof, and then document it as a best practice to clear the canvas before each rendering if you intend to redraw the entire thing on each frame.

If you're making use of manually operated "dirty rects" so that you only clear the damaged area to repaint, then we couldn't employ this technique and we'd have to buffer 'till kingdom come (or cause a pulse / render step in cases where we wouldn't have otherwise). So we still need a mechanism exposed in the scene graph of "liveness" and associated events so that when the scene is no longer live (for example, when minimized) you could stop your animation timer, but for the specific use case of John Hendrikx and others where they are using canvas to draw pixels from VLC, this would work quite well and is a decent optimization on our part anyway.

Comments
http://hg.openjdk.java.net/openjfx/8/graphics/rt/rev/b7d847549c94
25-10-2013

Fixed in the 8.0 graphics scrum with the following changesets: rt workspace: changeset: 5547:b7d847549c94 date: Fri Oct 25 15:24:32 2013 -0700 summary: Fix RT-32242: Canvas should clear buffer on certain clearRect, fillRect calls rt-closed workspace: changeset: 18759:716e69f777d4 date: Fri Oct 25 15:25:37 2013 -0700 summary: Fix RT-32242: Canvas should clear buffer on certain clearRect, fillRect calls
25-10-2013

Attaching a second test case, identical to the first, but it uses a transparent canvas over a gradient showing whether the optimization works for clearRect()...
01-10-2013

Adding a test case to test the performance issues that can arise due to not clearing the buffer. It shows a canvas with a blob of 500 or so lines on it. If you click and drag the mouse, the blob will (appear to) follow the mouse. But internally, by default, what it actually does is clear the canvas and render the blob on every pixel between the old and new mouse locations. Only the last blob will show, since all of the intermediate blobs are erased, but they will fill and grow the buffer and take time to process unless they are dumped when the new clear operation happens. If you run it with a command line argument, it will skip the intermediate renderings and only render it at the final mouse coordinates in the events. Running it with this argument, the blob keeps up with the mouse very well as rendering a single blob is not all that time consuming. But, if you run it without any command line arguments (the default render-at-every-pixel mode), and if you move the mouse slowly then it can keep up. But if you move the mouse quickly then on my Macbook Pro it gets seriously and unusably behind, rendering hundreds of versions of the scene that will never actually be shown. With a simple patch to clear the buffer on a full background fill, the performance is always snappy on my Macbook Pro - it is virtually impossible to tell the difference between running it with or without the command-line argument.
01-10-2013

One thing to be careful about is to make sure that all of the intervening state-changing calls are preserved. The GC has the concept of "stale" attributes so it could simply mark all of the basic attributes as "stale" so that they are synched on the next render operation, but unfortunately the interaction with save/restore and the clip stack will have to be carefully thought out...
09-08-2013

Another option is to also add a "empty()" method to the GraphicsContext (or some other name) which would empty the buffer of previous drawing commands. This way if people are not actually doing a full clear / fillRect, but just want to empty the buffer, they can do so without incurring the overhead of an actual clear / fill operation
09-08-2013

I think this is both easy to do, and would have a positive impact on real customers
09-08-2013