[OpenJDK Rasterizer] RFR: Marlin renderer #2
Jim Graham
james.graham at oracle.com
Thu Jun 11 23:43:52 UTC 2015
Hi Laurent,
I reviewed the buffer management code this time.
A general thought - should we have foo_initial arrays if we are getting
everything from a cache?
Another general thought - in places you have a reset/dispose method that
puts the array back in the cache and then resets the array pointer to
the _initial version, but in other places that do not have an _initial
version you put the array back and then leave the pointer alone.
Shouldn't the "non-_initial" places set the associated array to null to
make sure we don't leak a previously cached array into a new operation?
For that matter, perhaps they should all be returned to null anyway
and rely on the constructor to initialize them to the "_initial" variant
where appropriate?
Renderer.java line 824 - I think you leak "edgePtrs_initial" here to the
cache...?
(Later I noticed some code in the RenderContext that does special
handling of arrays with odd sizes, but if that is a workaround for the
fact that you are leaking an "initial" array to the cache, I find that
alarmingly fragile and a more robust and obvious solution needs to be
found here...)
All in all, I find the caching code here to be alarmingly fragile and
non-obvious. A better mechanism needs to be found here that does not
require so much agreement of assumptions between various classes. It is
really hard to faithfully believe that you've squashed all the bugs
here. We can test, but bugs that are caused by interdependent
mechanisms like this cache code can be so hard to reproduce that we may
not discover issues until long after deployment. And, even if we test
now - every single change made to the code here will invalidate all of
our confidence that the interdependencies have remained valid through
the testing.
The caching code is an example of "code with no obvious flaws because it
is hard to figure out if it has flaws". We should strive for "code with
obviously no flaws because it is easy to verify that it is doing
everything correctly".
The primary advantage it provides - that of reuse of arrays and a
sensible growth strategy - can be provided with a simpler interface that
is easier to vet and review. I would argue that even if we came up with
such an interface and it cost us 1% or 2% in performance now we would
buy that back quickly over time in the amount of time we would save in
making further real improvements in the algorithms without having to
constantly worry about how we might disrupt a fragile caching scheme.
On 6/10/15 2:00 PM, Laurent Bourgès wrote:
> Jim,
>
> Here is the new webrev:
> http://cr.openjdk.java.net/~lbourges/marlin/marlin-s2.2/
I just noticed in CollinearSimplifier.getNativeConsumer() - that should
return 0 because otherwise some code that uses this could decide that it
is going to call your native consumer directly and bypass all of your
code. Basically, unless there is a native version of the code in
CollinearSimplifier, and it implements the native interfaces required
for direct native-to-native calling, you should return 0. On the other
hand, I don't think this hurts anything because it should only be called
before any work is done and if you allow the native caller to forward to
your delegate's native consumer then all we'd miss out on would be the
simplification done here. Also, I don't think this gets used with any
down-stream consumers that have a native consumer so this would
essentially be "return someone else's 0 instead of my own hard-coded 0",
but for purity, this should really be "return 0;"...
FloatMath, style note - personally I'd add parentheses around "~intpart"
at line 145 since I don't like to write code that assumes that the
reader knows the order of operations for Java code for anything beyond
"+-*/" and parentheses.
A couple thing to note for later:
CollinearSimplifier.lineTo(PreviousLine case):
- abs(slope - pslope) and "slope == pslope" both involve a virtual
subtraction of the two values, hopefully the compiler can optimize that out
- Slope of new segment compared to slope of original line segment isn't
sufficent, but it will likely work reasonably well in practice.
Consider two initial points with a slope of 0.0 (horizontal). Now add a
million short segments with a slope of -EPS. You can reach an arbitrary
negative Y value with those. Now add a million short segments with a
slope of +EPS. That can eventually bring you back to y==0. Now trigger
a drawLine and you will draw a horizontal line. But, the accumulated
segments could have reached any arbitrary negative Y displacement that
will be completely ignored in the end.
> What do you like me to work on in the step #3 ?
> - DDA ?
> - new Array Cache hierarchy ?
> - Stroker improvements to reduce collinear segments (caps, joins) ?
Unfortunately, I think we need a new caching mechanism sooner rather
than later. The current code is not maintainable. It may be somewhat
maintainable by you right now, but it will not remain so for long and it
will not be maintainable by the next engineer that needs to work on this
code.
...jim
More information about the graphics-rasterizer-dev
mailing list