[OpenJDK Rasterizer] Marlin renderer contribution for review

Laurent Bourgès bourges.laurent at gmail.com
Fri Mar 27 09:19:37 UTC 2015


Hi Phil & Joe,

Thanks for your opinions, but let me explain the impact of ceil(float) in
the Marlin renderer:
- for every line, 2 ceil calls are performed => depends on the shape
complexity
- for every rendering pass, typically every 32 pixel stride, 4 ceil calls
are performed => depends on the shape's vertical coverage

=> In my benchmarks, I experienced that the Math.ceil(float) method is
called million times (I can gather numbers if you want).

As I want to achieve the best possible performance, I used that "trick" to
have a faster (but accurate enough) alternative which gives in my MapBench
benchmarks ~ 3% performance.

I advocate 3% is small but it will depend on the drawing complexity: the
more complex your shapes are, the bigger is the speedup.

Since JDK 7 the JDK has used a pure-Java implementation of floor and ceil:
>
>     6908131 Pure Java implementations of StrictMath.floor(double) &
> StrictMath.ceil(doublele)
>     https://bugs.openjdk.java.net/browse/JDK-6908131
>

Thanks for the pointer, I will look at the StrictMath.ceil code to see if I
can optimize it for general use (any kind of inputs).


> So in this case I would suggest we follow the implications of Knuth's
> observation that "premature optimization is the root of all evil" and only
> look to further speed up floor / ceil if there is some appreciable
> performance problem  with them today as shown in benchmarks, etc.
>

I know that principle, but I am pragmatic: ceil(float) has a noticeable
impact on rendering performance according to my benchmarks which are based
on real map use cases (geoserver).

On 3/25/2015 2:44 PM, Phil Race wrote:
>
>> FastMath: http://cr.openjdk.java.net/~lbourges/marlin/marlin.3/src/
>> java.desktop/share/classes/sun/java2d/marlin/FastMath.java.html
>>
>> says its from here : http://www.java-gaming.org/index.php?topic=24194.0
>>
>> but that in turn may be from somewhere else unknown ..
>>
>> Aside from the provenance of the code, and even though its a 'trick'
>> rather than a body of code,
>> other options are preferable, so it might be interesting to get Joe's
>> input on what other options
>> there are that maintain correctness and give better performance - I
>> assume this gives
>> a measurable benefit ?
>>
>
1. I agree that trick comes from a java forum, but it is more an idea that
a body of code:
    ceil = upper_bound_integer - (int) ( upper_bound_double - x_float)

As I said, the code could be rewritten to ensure values are positive so a
integer cast is enough. Of course, the floating-point precision can have
tricky side-effects (and also overflow / NaN handling).

2. Yes, it gives speedup: here are the synthetic benchmark results with /
without the trick enabled [-Dsun.java2d.renderer.useFastMath=true|false] :

==> marlin_TL_Tile_1.log <==

Tests    27    9    9    9
Threads    4    1    2    4
Pct95    132.498    131.176    132.641    133.677

==> marlin_TL_Tile_noFM_3.log <==

Tests    27    9    9    9
Threads    4    1    2    4
Pct95    135.669    134.989    135.541    136.477


>
>> Is the limitation on the input range an issue ? There's no test here for
>> that, and
>> correctness-wise this does seem to break down if the input is NaN.
>>
>
1. In Pisces / Marlin, ceil is used in a specific context to upper integer
values:

    private void addLine(float x1, float y1, float x2, float y2) {
...
        /* convert subpixel coordinates (float) into pixel positions (int)
*/
        // upper integer (inclusive)

        final int firstCrossing = Math.max(FastMath.ceil(y1), _boundsMinY);

        // upper integer (exclusive ?)
        final int lastCrossing  = Math.min(FastMath.ceil(y2),
_boundsMaxY);


    boolean endRendering() {
...

        /* bounds as inclusive intervals */
        final int spminX = Math.max(Math.ceil(edgeMinX), boundsMinX);
        final int spmaxX = Math.min(Math.ceil(edgeMaxX), boundsMaxX - 1);
        final int spminY = Math.max(Math.ceil(edgeMinY), boundsMinY);
        final int spmaxY = Math.min(Math.ceil(edgeMaxY), boundsMaxY - 1);

However, to improve the coordinate rounding arround subpixel centers, it
should be rewritten (as jim pointed out):
ceil(float) => ceil(x - 0.5f) or floor(x + 0.5f)

Anway, the returned values are then clamped into [min | max] ranges.

Do you know any faster alternative to do such rounding + boundary clipping ?


2. I agree it does not handle overflow or NaN but that is a real problem
with Pisces in general: I think it should be tested and fixed in a second
step.

For example, I you draw a vertical or horizontal line with x / y = NaN then
it fails !
Moreover, as coordinates are multiplied by 8, it can overflow float and
integer maximum values !!

I think clipping should be implemented to ensure all coordinates belongs to
the clip boundaries.


To conclude, I propose to remove the FastMath utility class now and later
use a new Renderer utility method to perform both rounding and bound checks
at the same time with your help.

Cheers,
Laurent
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://mail.openjdk.java.net/pipermail/graphics-rasterizer-dev/attachments/20150327/a901d2ab/attachment-0001.html>


More information about the graphics-rasterizer-dev mailing list