Oracle has released the revamped JavaFX 2.0 on JavaOne 2011. It’s an interesting step which immediately invokes many questions. What is the target audience for JavaFX? Which technology segment does it aim at? What are the chances it will actually claim a piece of that segment? Is it any good at the segment it is aiming at? Only the future will tell the answers to all the questions. But some can be attempted to be answered today.
First of all a short summary of what I believe JavaFX 2.0 is: JavaFX attempts to be a higher performance graphics framework, with a UI layer on top, able to display different types of media.
- “Higher” means that it is not aiming at high end gaming (for example the recently released Battlefield 3 and Call of Duty Modern Warfare 3), but more at the level of platform games like Mario Bros or the fancier and animated user interfaces that some applications have lately.
- “Graphics framework” indicates that it is not a UI library in its core (like Swing or SWT), but its designed primarily for drawing, manipulating and animating shapes like circles, rectangles and lines.
- “UI layer on top” to note that it does come with a good set of UI controls, that are drawn using the graphics framework and thus can easily be made “fancy”.
- “able to display different types of media” means that there is support for playing audio or video streams.
This kind of framework is commonly known as a RIA, or rich internet application, framework.
Having established the technical intention of JavaFX, the first step would be to see if it is any good at what it’s trying to be. This means bringing out the tame racing driver, aka a common frame of reference. For benchmarking a RIA the unofficial first thing to do is throw the bubblemark test at it.
Bubblemark contains a lot of different implementations and because I do not like to reinvent the wheel, it seems a good idea to adapt an existing test to JavaFX 2.0. Naturally there is the JavaFX 1.0 implementation, but since that uses JavaFX script, porting it could be, ahm, more work. The other approach would be to adapt the Swing version and because it is implemented in Java, adapting that may be, ahm, less work. Furthermore it is fair to say that JavaFX will initially appeal to Swing developers, so running both side by side also makes for an interesting comparison.
The Swing (optimized version) Bubblemark code seems somewhat illogically structured, but after a closer look it turned out that most logic was put in the Ball class. Extending that with a JavaFXBall implementation was fairly easy. The Swing version uses a bitmap image of a ball, but testing a full vector JavaFX version is relevant, so in fact two classes were made: JavaFXBallBitmap and JavaFXBallVector.
Next the actual test case. The Swing version is implemented as an Applet, to make life not unnecessary complex at this stage, I chose to quickly rewrite that to use a JFrame and write the JavaFX code to use a regular Scene. A browser embedded version can be made later. Furthermore I wanted to put the results in a graph, so I adapted the test cases to calculate the number of frames per second (after a short warm-up period) and then output that on stderr and then automatically increment the number of balls with 100.
The Swing version uses javax.swing.Timer to move the balls, for JavaFX I decided to use javafx.animation.AnimationTimer. And basically that is it: start the timer, count the frames and see how many are done in a second. Below the test case code:
package javaballsopt; import java.net.URL; import java.util.ArrayList; import java.util.List; import javafx.animation.AnimationTimer; import javafx.application.Application; import javafx.scene.Group; import javafx.scene.Scene; import javafx.scene.image.Image; import javafx.stage.Stage; public class JavaFXTest extends Application { public static void main(String[] args) { launch(args); } @Override public void start(Stage stage) { try { // get the image URL url = javaballsopt.JavaBallsApplet.class.getResource(>resources/ball.png>); Image lImage = new Image(url.openStream()); // create the ball object final JavaFXBallBitmap lJavaFXBall = new JavaFXBallBitmap(lImage); //final JavaFXBallVector lJavaFXBall = new JavaFXBallVector(); // create the JavaFX container to hold the balls and add the first ball Group root = new Group(); root.getChildren().add(lJavaFXBall.img); javaFXBalls.add(lJavaFXBall); // add 99 more balls by cloning it (this is the way the Swing test does it... clone also adds the ball to the group) for (int i = 0; i < 99; i++) { javaFXBalls.add( lJavaFXBall.clone() ); } // create scene Scene scene = new Scene(root, 500, 300); // create and show stage stage.setTitle(>Bubblemark>); stage.setScene(scene); stage.show(); // create the incrementing test scenarion // let's be bold and time using nanoseconds; expectations are high starttimeNano = System.nanoTime(); new AnimationTimer() { @Override public void handle(long arg0) { // for all balls int s = javaFXBalls.size(); for (int i = 0; i < s; i++) { // move the ball javaFXBalls.get(i).move(); } // for all possible ball interactions for (int i = 0; i < s; i++) { for (int j = i+1; j < s; j++) { // detect collision and change movement accordingly javaFXBalls.get(i).doCollide( javaFXBalls.get(j) ); } } // count the frame frameCnt++; // check if a second has passed long currenttimeNano = System.nanoTime(); if (currenttimeNano > lasttimeFPS + 1000000000) { // print out each FPS on stdout System.out.println(javaFXBalls.size() + > balls > + frameCnt + > fps>); // increase the test counter measurementTest++; // after 5 tests (warm up period) if (measurementTest == 5) { // print the result on stderr in a Excel readable form System.err.println(javaFXBalls.size() + >;> + frameCnt + >;>); // add 100 balls for (int i = 0; i < 100; i++) { javaFXBalls.add( lJavaFXBall.clone() ); } // clear the test counter measurementTest = 0; // stop at 5000 balls if (javaFXBalls.size() > 5000) System.exit(0); } // reset frame count and time frameCnt = 0; lasttimeFPS = currenttimeNano; } } }.start(); } catch (Throwable t) { t.printStackTrace(); } } List<JavaFXBall> javaFXBalls = new ArrayList<JavaFXBall>(); int frameCnt = 0; long starttimeNano = 0; long lasttimeFPS = 0; int measurementTest = 0; }
Below is the output of the JavaFX run:
100 balls 1 fps 100 balls 56 fps 100 balls 61 fps 100 balls 60 fps 100 balls 61 fps 200 balls 60 fps 200 balls 60 fps 200 balls 61 fps 200 balls 60 fps 200 balls 61 fps 300 balls 60 fps 300 balls 61 fps 300 balls 60 fps 300 balls 60 fps ...
The first thing that stood out is that AnimationTimer caps the FPS as 60 fps. After an initial confusion this does not seem that bad; we’re not interested in values greater than 60 fps, those are smooth anyhow, but when it gets below. So with a little help from Excel, there is a nice graph:
The result was not what I expected. There seems to be some kind of stepping in the FPS, especially the sudden drop at around 1700 balls; I expected a curve. I reran the test several times, but the results are the same. Naturally I hope people will repeat it on their systems. For reference, I have a 2.4 Ghz quadcore Intel CPU (but the test does not do multi-threading) and a nVidia GTX 560 graphics board (since JavaFX uses hardware acceleration).
So, how about the vector version and the Swing version then? Well, after running and loading them into Excel I got this graph:
Again, not what I expected. I would expect JavaFX to be considerably faster than Swing. Zooming into the part where Swing also breaks the 60 fps barrier:
Swing has the curve I expected and JavaFX does not. The results as a whole are not what I expected and makes me question the Bubblemark test. If you comment out the collision code, the FPS go up. So the question is: what is the Bubblemark code actually testing? The performance of JavaFX or the implementation of the collision code?
For those interested to run the code for themselves: the JFrame version of the Swing test is in the JavaBallsApplet class, the JavaFX version is in the JavaFXTest. Alternating between line 32 and 33 will give the bitmap or vector version.
Good Article!
However your > and < are not rendered correctly. I do not see ” instead I see the ‘<‘ and ‘>’
SP
Yeah. Somehow WordPress escaped certain HTML symbols when I inserted a “continue reading” marker. I’ll fix it.
Dear ,
thanks for that JavaFX performance test, because i wanted to know how to count FPS with JavaFX (its a new API for me, i did things with Java2D/Swing before).
Now i can go further with my program (an isometric demo, i´ll upload it onto my Website)
My site is in german; if get enough time i will make an english translation. 😉
Gern geschehen 🙂
Pingback: JavaFX 8 Performance
Hm. You are starting the correct class, so it must be the classpath. I cannot make much more out of it. I just redownloaded the zip, unpacked it and ran this from inside the bin directory;
java -cp “C:\Program Files\Oracle\JavaFX 2.0 Runtime\lib\jfxrt.jar;.” javaballsopt.JavaFXTest
Thanks for this entry. I’m doing performance evaluation of ScalaFX (on JavaFX 2.2, JDK 7u7, OS X, NVidia GPU) and would very much like to rerun your code locally.
How should I easiest compile the .java code to class files (I know Scala, but hardly any Java). 🙂 I come from the C++ world (and yes, Scala is worth a jump!).
Answering my own call. 🙂
SBT can build Java as well. Trick is to use ‘sbt package’.
$ more build.sbt
organization := “fi.bmdesign”
name := “BubbleTest”
version := “0.0-SNAPSHOT”
//libraryDependencies ++= Seq( )
$ sbt package
[info] Set current project to BubbleTest (in build file:/Users/asko/Sources/JavaBallsOptimized/)
[success] Total time: 1 s, completed 17.10.2012 18:58:06
askonmacmini:JavaBallsOptimized asko$
$ java -cp bin javaballsopt.JavaBallsApplet 2> out.txt
100 balls 165.3 fps
100 balls 106.6 fps
200 balls 166.8 fps
200 balls 172.6 fps
300 balls 170.1 fps
300 balls 170.8 fps
400 balls 170.7 fps
400 balls 176.6 fps
500 balls 137.4 fps
500 balls 151.6 fps
600 balls 117.9 fps
600 balls 130.0 fps
700 balls 96.3 fps
700 balls 113.7 fps
800 balls 84.6 fps
800 balls 99.1 fps
900 balls 74.0 fps
900 balls 78.1 fps
…
But the other code failed to launch:
$ java -cp bin javaballsopt.JavaFXTest 2> out.txt
(nothing)
$ java -version
java version “1.7.0_07”
Java(TM) SE Runtime Environment (build 1.7.0_07-b10)
Java HotSpot(TM) 64-Bit Server VM (build 23.3-b01, mixed mode)
One more thing (the “2> out.txt” obviously ate up the error).
$ java -cp “/Library/Java/JavaVirtualMachines/jdk1.7.0_07.jdk/Contents/Home/jre/lib/jfxrt.jar;bin” javaballsopt.JavaFXTest
Error: Could not find or load main class javaballsopt.JavaFXTest
Should I know what I’m doing wrong here? 🙂
Aehm….JavaFX is capped at 60 FPS…yes it could do more but why? Hopping at 200 FPS with 100% CPU doesnt’t improve anything and power comsumption sky rockets.
The benchmak plateaus above 1700 balls could be thread synchronization problems, there is a seperate scene render thread – may be there are some interference effects here with your benchmark.
As I say in the article; we’re not interested in FPS above 60 anyhow, so the capping is not a problem, only unexpected. But as you can see in the “part 2” blog, the capping and plateaus are due to a behavioral settings of JavaFX.
However, if JavaFX ever wants to do 3D, it must do at least 120 fps (60 for each eye), so the above 60 numbers are interesting for future options.
Your link to BubbleMark isn’t correct: it must not have the www. part, as it leads to another site.
And indeed, you also test the collision algorithm, which isn’t much optimized (all other balls are tested, even if they are far away). A quadtree would help in making this more efficient. Or just don’t check collisions of balls between them, only against walls.
Anyway, it isn’t really important as we want one technology against another, for the same number of balls.
What link are you referring to? I saw one on twitter, FXExperience and Google+.
I think the fact that the collision detection is taking up so much of the CPU time probably is part of the reason why Swing and JavaFX are so close together. I intend to create a graph of the same code without collision detection (both JavaFX and Swing).
I’m not sure optimizing the little math that is done in the collision detection will impact the performance much, considering distance calculation must be done anyhow. And in the case of a quadtree, it must be maintained and modified as well; the balls basically free float, so they move from one quadrant to another.
Pingback: JavaFX links of the week, November 14 // JavaFX News, Demos and Insight // FX Experience
Pingback: Java desktop links of the week, November 14 | Jonathan Giles