[OpenJDK Rasterizer] Fwd: Re: Fwd: RFR: Marlin renderer #3
Jim Graham
james.graham at oracle.com
Wed Jul 29 04:54:29 UTC 2015
My "1 minor functional issue" in my last email was actually in error...
On 7/28/15 8:11 PM, Jim Graham wrote:
> Renderer.java, line 461: What happens if x1_cor was already at VPC? The
> error should be 0, but you end up setting it to ERR_STEP_MAX.
Ignore this. I see now. At VPC, any fractional movement forward should
bump you by another pixel so the error really is ERR_STEP_MAX.
Basically error is "how far we've progressed since the previous VPC
crossing" and at a VPC crossing you've crossed about as far as you can
go before you are going to default over to the next pixel crossing.
Instead I'd change the comment on 461 to something like:
// Crossings bump by a whole pixel just after (to the right of) a VPC.
// Error is how far since we last bumped the crossing.
And/or:
// Error is ERR_STEP_MAX right on a VPC where we are about to bump
1 more pixel
// and 0 just after VPC where we have just recently bumped
It's a minor nit, but your calculation is still slightly off in that we
would not necessarily compute a value of 0 right after the VPC because
(x1_cor - istartx) would be slightly greater than 0.0f. The calculation
you are using is correctly placing ERR_STEP_MAX at "on a VPC", but it is
also placing 0.0 at "on the previous VPC" when the correct placement for
0.0 is just after the previous VPC. The error is thus almost always
slightly larger than it should be (but by a very very tiny amount as in
1/ERR_STEP_MAX).
It should be:
err(VPC(n-1)) = ERR_STEP_MAX
err(VPC(n-1)+epsilon) = 0
...
err(VPC(n)) = ERR_STEP_MAX
but your calculation is:
err(VPC(n-1)) = 0
err(VPC(n-1)+epsilon) = 1
err(VPC(n)) = ERR_STEP_MAX
This calculation might be ever so slightly more accurate, I'm not sure
if it is any faster:
long x1_fixed = (long) Math.scalb(x1_cor, 32);
// x1_fixed is now 32.32 representation
x1_fixed -= 1;
// x1_fixed is now reduced by "epsilon"
int x1_whole = ((int) (x1_fixed >> 32));
int x1_fract = (((int) (x1_fixed)) >>> 1);
// x1_whole/fract are now a split 32.31 representation (of x1_fixed -
epsilon)
x1_whole += 1;
// x1_whole is now ceil(x1_cor)
Or, more compactly:
long x1_fixed_m_eps = ((long) Math.scalb(x1_cor, 32)) - 1;
store(CURX, ((int) (x1_fixed_m_eps >> 32)) + 1);
store(ERRX, ((int) (x1_fixed_m_eps)) >>> 1);
It performs ceil() in fixed point (32.31+1) math as floor(v + 1 -
epsilon) and ends up with the error/fract term being exactly
ERR_STEP_MAX on a whole number and the same err/fract term to be 0
exactly at the next possible fixed point step after a whole number.
Note that since x1_cor is only single-precision it is unlikely to ever
see a fract value of "0" since the float doesn't have enough precision
to represent a number that is "1/ERR_STEP_MAX" greater than an integer
unless the integer happens to be 0. But if it could represent that
number, it would be scaled exactly to an error value of 0. It would be
slightly more accurate if the x1_cor value was calculated in double (to
keep as many bits of fractional value as possible). A double mantissa
could maintain 31 bits of sub-pixel precision for coordinates in the
range of +/- a million or so, though it isn't clear how much value that
would have since all of the calculations up to this point have been done
in single precision.
So, I present those equations more in case they might be faster than
because they may be "more accurate". Note that there is no call to
ceil_int() here at all, only a cast to long...
...jim
More information about the graphics-rasterizer-dev
mailing list