Bad performance with Canvas and extensive clipping

Jim Graham james.graham at oracle.com
Tue May 27 21:47:50 UTC 2014


Canvas is, essentially, a "draw pixels" mechanism.  We have to bundle 
the requests into a command stream due to threading issues, but when the 
requests get to the render thread then they get turned into pixels so 
the command stream is a temporary intermediary.  Some of the hw J2D 
pipelines also have a temporary command stream due to platform threading 
issues as well.  It all depends on which pipeline you use and on which 
platform in the case of J2D.  FX simply normalized the threading on all 
pipelines/platforms so that we have a separate UI and render thread in 
all cases, but that concept is not foreign to J2D either.

I'm fairly certain that the lack of simple rectangular clipping is 
probably the biggest cause of your performance problems.  We do AA on 
everything in FX, though, whereas rendering to a BufferedImage by 
default will be non-AA unless you requested AA using the graphics hints. 
  But on the up-side, we hw accelerate just about every operation in FX 
so it should be on par with performance there, modulo the lack of 
rectangular clipping...

			...jim

On 5/23/14 5:46 PM, Tom Schindl wrote:
> Hi,
>
> As an experiment I've now written a SWT-GC implementation using a
> BufferedImage & Graphics2D and transfering the pixels over to JavaFX and
> the performance is as it is with native SWT.
>
> I always thought Canvas works similar to Image and one only draws pixels
> - looks like that is not the case, having a dep in my application
> java.awt is not what I'm aiming at but without acceptable performance in
> conjunction with clipping it looks like i have to go this route :-(
>
> Tom
>
> On 23.05.14 23:57, Tom Schindl wrote:
>> In the current usecase it is a rect all time but that's just in this special use case.
>>
>> I guess that rect clipping is the most common one so having an optimization for rects and a slow path for none rects might help.
>>
>> Tom
>>
>> Von meinem iPhone gesendet
>>
>>> Am 23.05.2014 um 23:35 schrieb Jim Graham <james.graham at oracle.com>:
>>>
>>> Are you clipping to an arbitrary path in all cases or just a rectangle?  Unfortunately we only offer the arbitrary clip-to-current-path method that isn't optimized for basic rectangular clipping and it implements soft clipping.
>>>
>>> There is an outstanding tweak that we added faster clipping support for WebNode and we need to start using it for Node.setClipNode(non-rectangle) and Canvas, but we haven't implemented that yet.  (https://javafx-jira.kenai.com/browse/RT-30107)  It basically is a direct "render this texture through that other texture as a clip" operation instead of the current code that runs it through some Blend effect filters.  It would definitely improve your run times, but I'm not sure how much.
>>>
>>> Even more savings could be had for rectangular clips if we provided some way to communicate them to the GC...
>>>
>>>             ...jim
>>>
>>>> On 5/23/14 11:47 AM, Tom Schindl wrote:
>>>> Hi,
>>>>
>>>> Maybe as some of you might know I've been working since sometime on SWT
>>>> on JavaFX and to implement direct drawing operations we use JavaFX-Canvas.
>>>>
>>>> I've today tried to run a heavy direct drawing grid implementation and
>>>> it performed very bad because it makes heavy use of clipping.
>>>>
>>>> For a grid I've counted ~1500 clipping operations the library works
>>>> something like this:
>>>>
>>>> boolean activeClip;
>>>> Canvas canvas = new Canvas();
>>>>
>>>> public void setClipping(PathIterator pathIterator) {
>>>>    GraphicsContext gc = canvas.getGraphicsContext2D();
>>>>    if(activeClip) {
>>>>      gc.restore();
>>>>      activeClip= false;
>>>>    }
>>>>
>>>>    if( pathIterator == null ) {
>>>>      return;
>>>>    }
>>>>
>>>>    activeClip = true;
>>>>    float coords[] = new float[6];
>>>>    gc.save();
>>>>         gc.beginPath();
>>>>
>>>>         float x = 0;
>>>>         float y = 0;
>>>>
>>>>
>>>>         gc.moveTo(0, 0);
>>>>
>>>>         while( ! pathIterator.isDone() ) {
>>>>             switch (pathIterator.currentSegment(coords)) {
>>>>             case PathIterator.SEG_CLOSE:
>>>>                 gc.lineTo(x, y);
>>>>                 break;
>>>>             case PathIterator.SEG_CUBICTO:
>>>>                 gc.bezierCurveTo(coords[0], coords[1], coords[2], coords[3],
>>>> coords[4], coords[5]);
>>>>                 break;
>>>>             case PathIterator.SEG_LINETO:
>>>>                 gc.lineTo(coords[0], coords[1]);
>>>>                 break;
>>>>             case PathIterator.SEG_MOVETO:
>>>>                 gc.moveTo(coords[0], coords[1]);
>>>>                 x = coords[0];
>>>>                 y = coords[1];
>>>>                 break;
>>>>             case PathIterator.SEG_QUADTO:
>>>>                 gc.quadraticCurveTo(coords[0], coords[1], coords[2], coords[3]);
>>>>                 break;
>>>>             default:
>>>>                 break;
>>>>             }
>>>>             pathIterator.next();
>>>>         }
>>>>
>>>>         gc.clip();
>>>>         gc.closePath();
>>>> }
>>>>
>>>> Am I doing something ultimately wrong, totally wrong? Has anyone an idea
>>>> how I would work around the problem?
>>>>
>>>> Tom
>>>>
>


More information about the openjfx-dev mailing list