Hello All,

A few weeks ago I promised to do an analysis of the Java 3D
benchmark used in Jacob Marner's report entitled "Evaluating
Java for Game Development."  In the report, Java 3D was performing
at a much lower level that I thought was representative of true
Java 3D performance, so I decided to look into it.

There were a number of factors that caused the benchmark to run
slowly.  They included benchmark issues, Java 3D bugs, and the
default behavior of Java 3D as a system.

Throughout this analysis, I will be showing a number of framerate
numbers.  All of these numbers were generated on a Dell Inspiron 8000
laptop with a Pentium 3 (933 MHz), 128 Mb memory, and a Geforce2 2go
graphics card.

I - TransformGroup usage

   The first area that I suspected was the use of TransformGroups.  The
GL4Java version was using no transforms when rendering the scene, while
the Java 3D version was using TransformGroup nodes.  It was relying on
a documented compile feature of Java 3D that pre-transforms verticies
when a specific set of conditions were met.  First, it did not meet all the
requirements (which include disabling picking and collision on the nodes).
So I fixed that in the benchmark, but it made no difference.  After debugging,
I found a bug in Java 3D that effectively disabled that optimization.  So, I
changed the benchmark to pre-transform verticies just as the GL4Java version
does.  The lesson here is to never assume that an optimization is working
unless you can measure the difference.

II - Benchmark warmup

   The next thing I noticed was that display list creation was showing up
as a time consumer in my profile data.  Then I realized that the benchmark
was timing the framerate from the very first frame.  It is common practice
for 3D benchmarks to do a complete "dry-run" pass of the benchmark to make
sure that the system has time to create optimized structures for the data set.
For example, Viewperf for OpenGL does this.  So, I changed the benchmark to
do a dry run of the banchmark before doing a timing run.

III - RenderBin caching behavior

   After this, display list creation was still showing up in the profile data,
so I knew that something else was happening.  This problem turned out to be
due to the default behavior of our render caching structure - the RenderBin.
Whan a Java 3D application starts up, not all the geometry is immediately
prepared for rendering in the RenderBin.  Only those objects visible are
populated into the RenderBin.  This explains why the benchmark warm-up was
needed.  But, once all the objects in the scene were encountered, why was the
system still creating display lists.  The answer lies in the behavior of
the RenderBin.  If the RenderBin were to continue to add objects from the
world without bound, the performance would slowly start to degrade for very
large worlds.  The RenderBin would have to process a large percentage of
geometry objects that are not visible.  So, by default, when the RenderBin
sees that there are twice as many non-visible objects than visible objects,
it gets rid of the non-visible objects.  We call this process compaction.
The benchmark was having a problem with this because by the time the benchmark
had visited all the geometry, the RenderBin had thrown out the geometry
that it saw in the beginning.  So, I have added a property to
Java 3D 1.3 beta2, which can disable this behavior.  We have always known that
there are some applications that would like all the live geometry to be in
the RenderBin, so this property allows applications to enable this behavior.

IV - Using multiple threads

   After these changes, Java 3D was behaving much more like what I would
expect.  At extremely high frame rates (>150), there would be a visible
overhead in Java 3D.  But, as framerates came down to realistic application
levels (50-150), the overhead for using Java 3D was minimal.  There was
one other option that I wanted to test to see the overhead implications.
Java 3D uses multiple threads to do its work by default.  However, there is
a property that allows applications to make Java 3D run as if it is running
a single thread.  This does cause the per-frame overhead to drop.  And, as
expected, it makes the most difference in very high frame rates.

V - Data

Here are the frame rates that I finally achieved through these changes.
When 1.3 beta2 comes out, all of these numbers can be achieved.

The properties are j3d.nocompaction=false to disable compaction, and
j3d.threadLimit=1 to run single threaded.

J3D (1) => Java 3D with no compaction
J3D (2) => Java 3D with no compaction and threadLimit=1
All number are in frames per second.

Boxes    GL4Java    J3D (1)    J3D (2)
-----------------------------------------
1000   |   490    |   290    |   359    |
-----------------------------------------
5000   |   170    |   140    |   150    |
-----------------------------------------
10000  |    94    |    81    |    87    |
-----------------------------------------

VI - Summary

   There are a few more things that could have been done to the tweaked
Java 3D benchmark to get even more performance out of it.  But, I stopped
at this point because I had found out where the main problems were.  My take
on all this is exactly what it has always been.  A good GL programmer with
enough resources and application specific knowledge can usually out perform
Java 3D.  But, for normal application data, the amount of performance win
is usually less than 10%.  It is up to the application writer to determine
if that performance win is worth the time and effort that could be used for
other application writing tasks.

Doug Twilleager
Java 3D Team
Sun Microsystems

===========================================================================
To unsubscribe, send email to [EMAIL PROTECTED] and include in the body
of the message "signoff JAVA3D-INTEREST".  For general help, send email to
[EMAIL PROTECTED] and include in the body of the message "help".

Reply via email to