Skip to main content

First look at JavaFX 1.2

Posted by opinali on June 2, 2009 at 8:52 AM PDT

Migration

JavaFX 1.2 is not a fully backwards compatible release. There are language and API changes, and the compiled code is not binary compatible even for sources that still compiles without changes in the new version. Check Stephen Chin's migration guide. This continues the trend of 1.1; it seems Sun is not yet caring too hard about backwards compatibility. "Legacy" JavaFX 1.0 and 1.1 apps will keep running because the JPI or JAWS will fetch the right runtime for them; the bad news is, if you have deployed some JavaFX app, you must at the very least recompile and redeploy if you want that your users benefit from the improved 1.2 runtime.

Breaking backwards compatibility is not in the "spirit" of Java, or other Sun platforms. Sun is clearly using the opportunity, as JavaFX's adoption by real-world projects is very small to date, to fix JavaFX's early mistakes. But the platform must eventually stabilize, and it's good to see that the number of breaking changes is really minimal for the language, although still significant for the APIs.

For JavaFX Balls, the single source code fix that I had to do was adding an extra comma to obey the new javafxc rules (can't write a sequence like [a b c] anymore).

Then I proceeded to drop the Swing components in favor of the new JavaFX native controls (the picture below shows those new controls, with the default style). I started by deleting the "Swing" prefix, e.g. SwingToggleGroup -> ToggleGroup, etc. Surprisingly, that was 95% of the porting work; the only remaining compilation problem was because the new RadioButton component doesn't have a action property. I guess I am supposed to bind stuff into its selected property; but with my code, that would spoil the MVC-like structure because I would have that binding inside the model class. Some refactoring could avoid that, but I was in a hurry so I just used the onMouseReleased event and everything worked even though it's not very clean.

Next problem is that when I run the program, every time some ball "hits" the top or left borders, this causes a weird bug. The ball movement algorithm allows balls to be temporarily positioned in negative coordinates, e.g. if the ball was at x=1 with a speed dx=-3, it would fall in x=-2 and only in the next keyframe it would bounce to a positive coordinate. Up to JavaFX 1.1 that was not a problem, a small piece of the ball would just be clipped off. But JavaFX 1.2 has a new default behavior that causes a Group's boundsInParent to be adjusted, so no objects will have negative coordinates. I suppose this is a smart default that can do the right thing for many apps, but JavaFX Balls was not prepared for that, and the result is very weird:

The problem is more severe for large numbers of balls, because the movement algorithm is very dumb and will put balls on even more negative coordinates if they don't have space to bounce back. In the picture above, the origin coordinate is the position of the "128 Image Balls, 321 fps" string; this show how much the bounding box was pushed to the bottom and right. I spent a few minutes looking at the javadocs (still very thin"...) but I couldn't figure out how to properly customize this behavior. So I worked around it with some extra code in the ball movement to force balls to bounce immediately, avoiding negative coordinates in the first place:

// left
if (this._x < (model.walls.minX) and this._vx < 0) {
    this._vx = -this._vx;
    this._x += this._vx;
}
// top
if (this._y < (model.walls.minY) and this._vy < 0) {
    this._vy = -this._vy;
    this._y += this._vy;
}

Unfortunately this creates a very slight deviation from the other Bubblemark implementations, and for some reason a small bounds-related jitter remains; so I'll welcome any tips to fix this more properly. If you want to help, get the updated sources here. This is the worst kind of backwards-compatibility breaking / migration: I can fix in seconds most changes documented by Stephen's guide (even the change from MI to mixins is easy); but the problems that take more work are those that don't cause a compilation failure"... remarkably for a platform that is still very thinly documented.

Another interesting change is the code that re-creates the balls after the user changes the rendering option (Image or Vector) or the ball count. Such actions would always produce a small "pause" because replacing a sequence of nodes bound to the Scene Graph is expensive (more than it should, which I reported as JFXC-2911: Whole-sequence replacement interacts badly with binding, unfortunately not fixed for JavaFX 1.2). So I used smarter code that does the minimum possible work:

public var imageBalls = not useVector on replace { resetBalls(-1) }
public var N:Integer on replace oldN { resetBalls(oldN) }

function resetBalls (oldN: Integer) {
    fpsTimer.stop();

    if (oldN == -1)
        balls = for (i in [1 .. N]) newBall()
    else if (oldN > N)
        delete balls[N - 1 ..<]
    else if (oldN < N)
        insert (for (i in [oldN ..< N]) newBall()) into balls;

    startFPS();
}

Performance Results

Now let's move to the fun part. These are my preliminary results with this "beta" JavaFX Balls code, running on the JavaFX 1.2 runtime:

  • 16 Balls: 665fps (1.1 = 340fps; 1.2 is 1.95X faster)
  • 128 Balls: 330fps (1.1 = 20fps; 1.2 is 16.5X faster)
  • Adaptive mode (200fps): 221 Balls (1.1 = 30 Balls; 1.2 handles 7,3X more load). I could also get 560 Balls @ 60fps.
  • Mobile (emulated), Adaptive mode (25fps): 91 Balls (1.1 = 82 Balls; 1.2 handles 11% more load)

JavaFX 1.2 has a completely rewritten animation core, and it shows. When I evaluated JavaFX 1.0, I complained and cried and whined about the poor scalability of its SceneGraph package, with the conclusion that JavaFX would "stand no chance" to implement serious animations (e.g. for action games, except for clones of trivial 1970's hits like Pac-Man). But this is now past.

The performance can be even bigger in a "pure" animation: I found that the controls are relatively heavyweight (even with caching). If I remove the control toolbar, I get 16 Balls @ 992fps (almost reaching the 1000fps cap), or 128 Balls @ 380fps. Even more interesting is CPU usage: the 16 Balls score consumed less than 1% CPU (Q6600), and even the 128 Balls score consumed only 16% CPU. So the program is clearly not constrained by CPU, but rather by other factors like timers or VRAM/DRAM bandwidth.

These results compare very favorably with most other Bubblemark contenders (both Java and other platforms). The very best score that I've seen before was for PulpCore: it scores 16 Balls @ 410fps, but 128 Balls @ 90fps only; and both saturated a full CPU core (25% of a quad-core). I tested again other competitors (including Flash and Silverlight 2), and they are all much worse than JavaFX in either FPS or CPU usage (often both).

The "Swing optimized" program is slightly better as it delivers 128 Balls @ 60fps [capped] with <1% CPU, and 512 Balls @ 32fps usage with 13% CPU. I tweaked JavaFX Balls (60fps cap and 512 Balls option) and it was almost as good, with 3% CPU for 128 Balls and 20% for 512.

Now, the real champion seems to be another recent contender, LWJGL/Slick; that beast can do 128 Balls @ ~1600fps with 25% CPU (one full core); or alternatively, up to 256 Balls (there's no 512 option) @ 75fps [VSync-capped] with 1% CPU. There's nothing better than a raw OpenGL binding. Java2D and JavaFX are optimized to use OpenGL or Direct3D, but extra layers of APIs and abstractions will always have some price. Java2D will always be a little worse than something like LWJGL, and JavaFX will always be a little worse than plain Java2D. But I expect JavaFX to narrow the gap even further, as the Scene Graph engine is new in 1.2 and it surely has bottlenecks to optimize out, not to mention javafxc, runtime, and other APIs, which are all still relatively new.

My verdict: You can now write a top-class 2D action game (or other programs with high-end animation) in JavaFX. Your performance will be second (but not a distant second) only to really low-level options like pure Java2D or OpenGL bindings, or a specialized package like the Slick game engine. But now, the convenience of JavaFX costs so few CPU cycles that nobody should care, except high-end action game developers. Last, but not least, Java totally smokes its most important RIA competitors, Flash and Silverlight.

There are many other interesting performance aspects to see but don't have time to measure now: Performance in Linux; Effects; Memory management (1.0/1.1 would severely cap the performance of some programs, like the SmokeParticles demo, due to excessive allocation from the scene graph). Loading time seems much better but I must check that objectively.

Related Topics >>

Comments

Larry already committed to JavaFX (words are cheap, so I read yesterday's comments as "at least as long as Sun keeps it in the promising or successful category"). The Beta tag of the Linux/Solaris ports was expected. I guess these ports may have unique bugs in the GStreamer-based media backend... hopefully the ports will mature quickly.

Hopefully, Oracle's superior marketing will prevent JavaFX from becoming another one of Sun's superior technologies that fails to gain traction because of inferior marketing. That is, of course, if Oracle decides not to kill JavaFX. Hopefully, they see a hidden gem that they can exploit. Also, from what I understand, there are now "beta" plugins for Linux and Solaris. The new OpenSolaris 2009.06 includes the JavaFX SDK in the "extras" repository. I installed it but haven't had a chance to try it out yet.

@mykolagolubyev: Weird results that you have. This high CPU usage happens when you are scrolling the componente with lots of text, or all the time? You may be hit by a well-known JavaFX bug: http://javafx-jira.kenai.com/browse/RT-5069. This is schedule for fix in next release, it screws up JavaFX's performance in the GUIMark benchmark. Basically, if you have tons of text in a component but only a small part of this text is visible, JavaFX will do lots of work to compute the layout of the entire text, including non-visible parts. So, JavaFX probably sucks for a tail-like app unless you continously drop the older lines of text.

Hi. I've found your article about CPU consuming is the right place to find solution to my problem. I am trying to implement simple file tail tool with JavaFX: show lines of text and add another one and scroll them when you line in a file appears. And I've found that all the ways I've tried leads to 30~50% of CPU usage of one of the Core. I tried use Text Elements. Pool of Text to avoid recreation. Pool of Labels. I even tried to not reconstruct all the scene and just move existing labels or Texts. I tried list box and started add elements there with auto scroll to the last one. And it was even worse: 70-90% of CPU. I can't figure out what is the reason for this. I'd really like to use JavaFX for any tool with UI as it as simple as I never thought it could be. Could you help me to find a right direction?