Better GUIs for Java games

by Cuchaz

Progress on the new unannounced game is going well. But only if by 'going well', we mean I've been sidetracked by a million other things instead. =P

It's going to be great though, I promise. I've been working on a lot of great tech lately that's going to end up in my upcoming Java game engine, the Horde Engine.

You might have already noticed cuchazinteractive.com looks a little different now. I gave the website a facelift to focus more on my indie gaming work, and less on my older Minecraft work. So that was one sidetrack.

Hanging out with friends and drinking beer are another great sidetrack too. If the world didn't have friends and beer in it, I'd probably get a lot more work done. But then, what would be the point of it all? Beer is delicious.

Another of my sidetracking journeys has led me to discover a love for Java's fancy new UI system, JavaFX. It's basically a professional quality and fully-featured GUI system for Java that's a million and a half times better than the old Swing system. And it's much easier to develop thanks to a fancy meta-GUI called Scene Builder.

A screenshot of SceneBuilder That's right. It's a GUI that helps you make GUIs. So meta.

And JavaFX apps can get pretty intense in the visual design department too. Here's a JavaFX app almost looks like we're playing a game, rather than being an office drone.

A screenshot of a business app that looks like a video game Image courtesy Rob's Blog.

No, really... this is a Freight Management System for a shipping and logistics company. Which got me thinking, why couldn't we use JavaFX for the GUI inside a game?!

Turns out, there are a ton of technical reasons why not

JavaFX was designed to ignore most of your operating system's typical windowing system in favor of using your graphics card to draw the GUI elements. Which means it's designed to work really well with GPU-accelerated graphics systems like Games! JavaFX actually includes a rendering backend that's implemented using OpenGL. So all we need to do for cross-platform games in Java is to get access to that OpenGL context and then issue some drawing commands. Right?

... right?

Nothing is ever that easy

In their infinite wisdom, the designers of JavaFX never saw fit to let mere morals access the low-level rendering system from the outside. JavaFX is extremely cross-platform, so we can't even guarantee that a certain rendering backend will even be used on our favorite platforms. Even though OpenGL is very cross-platform, JavaFX still prefers DirectX on a Windows system for example.

If we want to use nice JavaFX GUIs in our Java games, we have a few challenges to overcome:

  1. Force JavaFX to use the OpenGL backend on the Platforms We Care About.
  2. Break into the code somehow so we can issue arbitrary rendering commands to the OpenGL context.
  3. Try to avoid a global state-fighting war between the game and JavaFX.
  4. Gets lots of FPS!

Introducing JFXGL

JFXGL is a project I started that does all of the things above, with varying degrees of success.

Since JavaFX is mercifully open-source via the OpenJFX project, the mission of JFXGL is to hack the OpenJFX source just enough to let us get to the OpenGL context hidden inside.

1. Forcing OpenGL

To fix 1., for platforms that have an OpenGL backend available (Linux, but not Windows. Not sure about Mac tho.), we can just coerce JavaFX into choosing that backend by fiddling with the internal configuration. JFXGL does by setting a few system properties.

For Windows though, the OpenGL backend isn't even an option, so JFXGL doesn't currently work on Windows. To get it to work, we'd have to create a custom build of OpenJFX that includes the OpenGL backend. Frankly, I've been trying hard to avoid maintaining a fork of OpenJFX to get this stuff to work, but maybe that's a lost cause at this point. To get Windows support, I may just have to take the plunge and make a customized version of OpenJFX for cross-platform games.

Thank goodness for open-source licenes though, right? If Oracle still had this stuff on lockdown, we'd all be boned.

2. Hacking the code

Fixing 2. is relatively easier. The aptly-named OpenJFX is open source, so we can do just about anything we want to the code. A goal of JFXGL though is to make the minimial edits to OpenJFX, so it's easy to migrate to different versions/revisions of JavaFX. All the edits JFXGL needs to work as of this writing are a removing a few 'final' keywords, and changing package-protected accesses to public and protected. That's it. All of the rest of the magic is done with subclassing.

Since these edits to OpenJFX are so simple (for now), JFXGL can even run on an unmodified version of JavaFX. We just apply the edits to the OpenJFX class bytecode at runtime (and deal with the classloader hell that's required to do this kind of black magic). That means using JFXGL is as simple as adding the JFXGL jar to your classpath and hitting the run button in your favorite IDE.

Until we're forced to make a custom build of OpenJFX that is. *sigh*

3. Prevent OpenGL state-fighting

Fixing 3. is pretty straightforward too. We just need to isolate JavaFX rendering from the main app rendering by using separate (but shared) OpenGL contexts. There's a bit of a performance penalty for switching OpenGL contexts every frame, but it's not terrible. And it completely eliminiates difficult-to-find global state-fighting bugs from JFXGL, so the tradeoff seems worth it to me.

4. FPS

4. is quite a bit trickier to deal with. JavaFX isn't really designed around the concept of FPS. It's a GUI app, which means it lives and breathes events and event handlers. Even the rendering is event-based. When scene graph changes, it submits a render job. But if the scene doesn't change, then no render job gets submitted.

There's no good/easy way to convert JavaFX's event-based rendering to the render loop paradigm used in games. So JFXGL used the hard way instead. JFXGL completely re-implemented the renderer for JavaFX to use an external render loop managed by the host app (eg, the game). The host app just calls a 'render' function every frame to tell the GUI when to render and JFXGL figures out the rest.

JFXGL is open-source!

As of this writing, JFXGL is at version 0.2 and getting more stable all the time. Contributions are always welcome, so if you want JavaFX GUIs in games badly enough that you somehow have free time to help out, you can find the JFXGL project at the stupidly large link below.