[OpenJDK 2D-Dev] sun.java2D.Pisces renderer Performance and Memory enhancements

Laurent Bourgès bourges.laurent at gmail.com
Tue Apr 30 16:29:11 UTC 2013


Jim,

here is the latest webrev:
http://jmmc.fr/~bourgesl/share/java2d-pisces/webrev-3/

I tried to revert some syntax mistakes (indentation, moved constants +
cleanup) : code cleanup is still in progress;
so please don't have a frawny face while looking at the code!

I fixed also Ductus and Jules rendering engines (TileState added in
interface)

In this patch, I modified the rowAARLE[][] array to only contain a single
tile line [32][4096] (512K). Each line is quite big already because I want
to store there the alpha data instead of RLE values:
RLE values are very interesting for horizontal lines but for dashed lines
it becomes bigger than alpha data. For example a dashed line [2-1] will
produce many segments that will be encoding like [01][x2]...

I have few questions regarding renderer edge handling and float ceil calls:
    private void addLine(float x1, float y1, float x2, float y2) {
...
        // LBO: why ceil ie upper integer ?
        final int firstCrossing = Math.max(ceil(y1), boundsMinY); // upper
integer
        final int lastCrossing  = Math.min(ceil(y2), boundsMaxY); // upper
integer

=> firstCrossing / lastCrossing should use lower and upper integer values
(rounding issues) ?

    boolean endRendering() {
        // TODO: perform shape clipping to avoid dealing with segments out
of bounding box

        // Ensure shape edges are within bbox:
        if (edgeMinX > edgeMaxX || edgeMaxX < 0f) {
            return false; // undefined X bounds or negative Xmax
        }
        if (edgeMinY > edgeMaxY || edgeMaxY < 0f) {
            return false; // undefined Y bounds or negative Ymax
        }

        // why use upper integer for edge min values ?
=> Here is the question: why use ceil instead of floor ?

        final int eMinX = ceil(edgeMinX); // upper positive int
        if (eMinX > boundsMaxX) {
            return false; // Xmin > bbox maxX
        }

        final int eMinY = ceil(edgeMinY); // upper positive int
        if (eMinY > boundsMaxY) {
            return false; // Ymin > bbox maxY
        }

        int spminX = Math.max(eMinX, boundsMinX);
        int spmaxX = Math.min(ceil(edgeMaxX), boundsMaxX); // upper
positive int
        int spminY = Math.max(eMinY, boundsMinY);
        int spmaxY = Math.min(ceil(edgeMaxY), boundsMaxY); // upper
positive int

        int pminX = spminX >> SUBPIXEL_LG_POSITIONS_X;
        int pmaxX = (spmaxX + SUBPIXEL_MASK_X) >> SUBPIXEL_LG_POSITIONS_X;
        int pminY = spminY >> SUBPIXEL_LG_POSITIONS_Y;
        int pmaxY = (spmaxY + SUBPIXEL_MASK_Y) >> SUBPIXEL_LG_POSITIONS_Y;

        // store BBox to answer ptg.getBBox():
        this.cache.init(pminX, pminY, pmaxX, pmaxY);

In PiscesCache:
    void init(int minx, int miny, int maxx, int maxy) {
...
        // LBO: why add +1 ??
        bboxX1 = maxx /* + 1 */;
        bboxY1 = maxy /* + 1 */;

=> piscesCache previously added +1 to maximum (upper loop condition y < y1)
but the pmaxX already uses ceil (+1) and (spmaxY + SUBPIXEL_MASK_Y) ensures
the last pixel is reached.

I fixed these limits to have correct rendering due to dirty rowAARLE reuse.

I think that the thread local's RendererContext is now small enough (< 1
Mb) to be used by web containers but if the number of threads is very high,
concurrent queue could help minimizing wasted memory.

Few comments:

2013/4/24 Jim Graham <james.graham at oracle.com>

> On 4/24/13 1:59 AM, Laurent Bourgčs wrote:
>
>> Jim,
>>
>> First, here are both updated webrev and benchmark results:
>> - results: http://jmmc.fr/~bourgesl/**share/java2d-pisces/patch_opt_**
>> night.log<http://jmmc.fr/%7Ebourgesl/share/java2d-pisces/patch_opt_night.log>
>> - webrev: http://jmmc.fr/~bourgesl/**share/java2d-pisces/webrev-2/<http://jmmc.fr/%7Ebourgesl/share/java2d-pisces/webrev-2/>
>>
>
> Some comments on the webrev:
>
> - This caching of pipeline data in SG2D is a new precedent and I'm wary of
> it for a couple of reasons:
>     - It gets tricky to satisfy all pipelines using that kind of technique
>     - It breaks encapsulation, but at least it is isolated to internal code
>     - SG2D will need to deal with the new field in clone().
>     - Rendering a hierarchy of Swing objects uses clone() a lot so caching
> storage in SG2D may not help much in that case and thread local may be more
> of a win
> - Thread local storage doesn't really have those issues
>     - It's only used if that pipeline is used
>     - No encapsulation issues
>     - Don't need to modify clone() and it will work across clones
>

Ok nevermind, I thought first it was possible but I agree it is not a
proper solution.


> - Curve iterator uses auto-boxing, is that causing any churn?
>
No, integers are in the Integer cache.


> - RenderingEngine may want to provide "forwarding methods" for Ductus as a
> temporary solution until we fix Ductus to be aware of the new signatures
>
Done in the new patch.


> - new constants in Dasher were moved out of the class they are used
> from...?
>
To be done (TBD)


> - Why get rid of the final modifiers in Dasher?  Was it not as effective
> as copying to local variables?  Note that the manual copying to local
> variables is prone to maintenance issues if someone inserts a method call
> in a block where the fields are stale.
>
I have to fix it.


> - PRE.pathTo() could still be static, no?  Also, it would be nice to make
> this change to the existing RE helper method directly (and possibly provide
> a backwards compatibility method with the old argument list that simply
> forwards with a "new float[6]").
>
Agreed: TBD


> - Perhaps it is time to get rid of the try/catch pairs in PTG.getAlpha()
> entirely?
>
Done.


> - When you have a field cached in a local variable and you compute a new
> value for it, then "field = var = ..." is probably better than "var = field
> = ..." since the latter implies that the answer gets stored in the field
> and then read back which is an extra memory operation.  I noticed this in a
> couple of places in Renderer, but I know I saw the local variable caching
> in other files as well.
>
Fixed.

>
> - A lot of undoing of some very carefully planned indentation and code
> alignment issues.  Auto-formatting is evil... ;)
>
Sorry, I may tune netbeans formatting settings.


> - A personal rant - I'm a big fan of the { on a line by itself if it
> follows an indented line-continued method declaration or control statement.
>  It helps the eye quickly find the start of the body because it stands out.
>  Your autoformatting got rid of a bunch of those and I made a frowny
> face... :(  (It may not be true to the JDK code style guidelines, but we've
> been using that style throughout the 2D code for years...)
>
Sorry again; however I like autoformating to let me work more on the code
not on syntax / indentation.

>
> - We switched to a new "within" technique in the JavaFX version of Pisces
> based upon this paper:
>
> http://www.cygnus-software.**com/papers/comparingfloats/**
> comparingfloats.htm<http://www.cygnus-software.com/papers/comparingfloats/comparingfloats.htm>
>
> Good idea, but I think there is many place where float <-> int conversion
/ tests should be improved ...

Do you have any faster implementation for Math.ceil/floor ? I now use
casting 1 + (int) / (int) but I know it is only correct for positive
numbers (> 0).

I have found few bugs:
- rendering a infinite line [vertical] draws nothing: I have to do a
diagnostic ...
- clipping issues:
for very large dashed rectangle, millions of segments are emited from
dasher (x1) to stroker (x4 segments) to renderer (x4 segments).

However, I would like to add a minimal clipping algorithm but the rendering
pipeline seems to be handling affine transforms between stages:
        /*
         * Pipeline seems to be:
         *    shape.getPathIterator
         * -> inverseDeltaTransformConsumer
         * -> Dasher
         * -> Stroker
         * -> deltaTransformConsumer OR transformConsumer
         * -> pc2d = Renderer (bounding box)
         */

It is complicated to perform clipping in the Renderer and maybe to late as
a complete stroker's segment must be considered as a whole (4 segments
without caps ...).

Could you give me advices how to hack / add clipping algorithm in the
rendering pipeline (dasher / stroker / renderer) ?

Should I try to derive a bounding box for the dasher / stroker depending on
the Affine transforms ?

Laurent
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://mail.openjdk.java.net/pipermail/2d-dev/attachments/20130430/a1ff768c/attachment.html>


More information about the 2d-dev mailing list