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".
