libGDX 1.9.6 released

libGDX 1.9.6

Here’s what’s new (plus a ton of bug fixes, see the commit logs on GitHub).

[1.9.6]
- Fix performance regression in LWJGL3 backend, use java.nio instead of BufferUtils. Those are intrinsics and quite a bit faster than BufferUtils on HotSpot.
- Updated to latest Sound Manager 2
- Added mappings for Xbox 360 controller for Linux
- Separated error log for vertex/fragment shaders for easier debugging
- Minimum Android API level is now level 9 (Android 2.3)
- API addition: Configurable TexturePacker bleed iterations
- Updated IOS Multi-OS Engine backend to 1.3.0-beta-2
- API Change: Pixmap.setBlending, Pixmap.setFilter are now instance methods
- VertexAttribute expert constructors exposed. Short types can now be used for attributes.

Please update your projects as usual.

If you use MobiDevelop’s RoboVM fork make sure to update your Eclipse/IntelliJ IDEA/Android Studio plugin to version 2.3.0.

If you use the GWT plugin in Eclipse and don’t run your GWT project via Gradle, make sure you update your GWT plugin to version 2.8.0.

gdx-vr

In other news, I’ve been working on some VR related projects recently. Our friends from LWJGL have created wrapper for OpenVR, Valve’s SDK to talk to all kinds of VR hardware, from Oculus to the HTC Vive. I’ve built a libGDX specific wrapper on top of the OpenVR bindings, which should make working on VR projects with libGDX a little simpler.

You can find the repository include a sample application on GitHub.

There are a few caveats. OpenVR is currently really only working on Windows. OpenVR on Mac appears to be broken. Valve has released support for Linux a few weeks ago, I did not have time to test this yet. It should however just work out of the box.

Another caveat is the way rendering is performed currently. For each eye, a separate frame buffer is created. Rendering as in the example is then performed for each framebuffer, using a separate camera/projection matrix for each eye, essentially drawing the scene twice. With a 11ms/frame budget, that can be problematic if you have thousands of draw calls to render a scene for one eye, as it doubles the CPU/driver work in the VR case. Ideally, you only issue one batch of draw calls for both eyes, using instancing and a simple trick involving clipping planes in the vertex shader to redirect the rendering to either the left or right side of the combined frame buffer. Long story short: gdx-vr plus the libGDX 3D API will take you a long way, but for more complex stuff, you’ll have to get your hands dirty with custom rendering.

The frame buffers allocated per eye aren’t setup to use MSAA at the moment. The probpem with MSAA is that it’s no good for deferred rendering (not a problem with libGDX’s current 3D API, which is a basic forward renderer).

The final caveat is that the project hasn’t been released to Maven Central yet, and hardcodes dependencies on LWJGL version 3.1.2-SNAPSHOT. Once LWJGL 3.1.2 is released, I’ll also release gdx-vr to Maven Central, with its version synched to the libGDX version that uses LWJGL 3.1.2. For now, you can just import gdx-vr as a Maven project into the IDE of choice, play around with the sample or integrate it in your own project.

I have not made this an official extension yet, as the implementation currently only supports desktop system. I’ve received a Google Daydream and am looking into abstracting the gdx-vr API enough to have multiple backends, like any good extension citizen.

The minimal example looks like this:

You can find a more elaborate sample with teleportation controls here.

Happy coding,
The libGDX team

libGDX 1.9.5 released

libGDX 1.9.5

Christmas is almost here. Have a libGDX release. Here’s what’s new (plus a ton of bug fixes, see the commit logs on GitHub).

[1.9.5]
- Upgraded to MobiDevelop's RoboVM fork version 2.3.0, update your 
- Upgraded to GWT 2.8.0 for faster compiles and better compile output
- Fix NPE swallowing "video driver unsupported" error on LWJGL 2 backend.
- Allow window icons to be set in Lwjgl3ApplicationConfiguration or Lwjgl3WindowConfiguration.
- Allow window icon and title to be changed in Lwjgl3Window
- API Addition: ApplicationLogger interface, allowing easier access to custom logging
- DefaultRenderableSorter accounts for center of Renderable mesh, see https://github.com/libgdx/libgdx/pull/4319
- Bullet: added FilterableVehicleRaycaster, see https://github.com/libgdx/libgdx/pull/4361
- Bullet: updated to 2.85, see: http://bulletphysics.org/wordpress/?p=456
- Updated iOS native build scripts to iOS 10.1 and TVOS 10.0
- API Addition: BitmapFont#blankLineScale.
- Fixed rounding of Drawables in ProgressBar. Allow rounding to be disabled with setRound().
- Updated LWJGL3 backend to LWJGL 3.1.0, see https://blog.lwjgl.org/lwjgl-3-1-0-released/
- LWJGL3 backend now supports non-continuous rendering, see https://github.com/libgdx/libgdx/pull/3772
- API Change: Lwjgl3WindowListener.refreshRequested() is called when the windowing system (GLFW) reports contents of a window are dirty and need to be redrawn.
- API Change: Lwjgl3WindowListener.maximized() is called when a window enters or exits a maximized state.
- API Change: Lwjgl3WindowListener.deiconified() removed, combined with .iconified().
- API Change: Lwjgl3Window.deiconify() renamed to .restore() since it can also be used to de-maximize a window.
- Lwjgl3Window now has a maximize() method, and windows can be started maximized using the window or app configuration's setMaximized() method.
- NinePatch can now be drawn rotated or scaled.
- NinepatchDrawable is now a TransformDrawable.
- API Change: Group add* methods no longer remove and re-add the actor if it is already in the group, instead they do nothing.
- API Change: g2d.Animation is now generic so it can support Drawables, PolygonRegions, NinePatches, etc. To fix existing code, specify the TextureRegion type in animation declarations (and instantiations in Java 6), i.e. Animation myAnimation = new Animation(...);
- TiledDrawable throws unsupported operation if trying to draw rotated/scaled. #4005
- API Change: DragAndDrop now puts default position of drag actor at pointer location. The original default offset from the pointer was (14, -20).
- Added ShaderProgramLoader for AssetManager.
- BoundingBox#isValid now returns also true when min==max, see: https://github.com/libgdx/libgdx/pull/4460

Please update your projects as usual.

If you use MobiDevelop’s RoboVM fork make sure to update your Eclipse/IntelliJ IDEA/Android Studio plugin to version 2.3.0.

If you use the GWT plugin in Eclipse and don’t run your GWT project via Gradle, make sure you update your GWT plugin to version 2.8.0.

Happy coding,
The libGDX team

RoboVM is no more, what now?

For the better part of 2 years, RoboVM has been my life. Today, RoboVM announced its discontinuation, abruptly ending that chapter. The amount of things I learned during that short period has easily overshadowed anything I learned during previous jobs, both on the technical and non-technical side. I’ll deeply miss this daily experience.

So, what does this mean for libGDX? There’s good and bad news. Let’s get the bad news out of the way.

RoboVM is dead, what now?

Quite a few libGDX folks have free RoboVM license keys to deploy their libGDX app on iOS. These license keys will continue to work until the 17th of April 2017. However, there will be no further updates to RoboVM, be it new features or bug fixes. The same is true for all the RoboPods the community came to rely on. Moreover, new sign-ups for free RoboVM Indie licenses will also no longer be fulfilled.

In short, if you have a game using RoboVM already, you can continue using RoboVM until the license expiration date. If you start a new game, RoboVM is not an option. In either case, you should move to an alternative as soon as possible.

What are the alternatives?

There are many alternatives to RoboVM, however, none comes quite close to what RoboVM offered. Let’s examine all the available options. We’ll look at them from various viewpoints: tooling support (IDEs, Maven, Gradle), bindings support, binary size, performance and Bitcode compatibility.

Before we dive in, let me elaborate on the Bitcode compatibility, as it is probably the most important feature. Apple recently introduced a requirement that prohibits submission of apps in raw machine code form for watchOS and tvOS. Instead, Apple wants you to submit your apps as Bitcode. Bitcode is a (not quite) machine independent intermediate language from which Apple generates actual machine code on app submission. Any solution that has no clear path to Bitcode is hence at a big disadvantage.

Mobile OpenJDK 9

Oracle recently announced OpenJDK 9 availability for both iOS and Android. While this seems like the most sensible option, it has a metric ton of down-sides as of this writing.

  • Tooling: No integration with IDEs, Maven or Gradle
  • Bindings: No bindings for Objective-C/Swift APIs. No easy consumption of Objective-C/Swift APIs, everything goes through JNI
  • Binary size: The generated binaries include all of OpenJDK, both native code and the class libraries. Apps are huge
  • Performance: It uses Zero VM on iOS, which is essentially an interpreter. Say goodbye to performance
  • Bitcode: Uncertainty about Bitcode compatibility, the interpreter relies on some assembler and signals, both of which don’t work in Bitcode land

None of this is likely to change in the short- to midterm future, so OpenJDK 9 is out.

CodenameOne

No. Next.

J2ObjC

A while ago, Google open-sourced J2Objc. It falls under the category of “transpiler” meaning it translates one high-level source (Java) to another one (Objective-C). Google is successfully using it for a few of their apps, like Google Inbox. Conceptually, it’s very close to GWT, which we employ to get your libGDX games running on the web.

  • Tooling: Essentially a command line tool. There exists a Gradle plugin that takes away some of the pain, but no other tooling is provided
  • Bindings: J2ObjC is mean to allow you to share business logic while you write your UI in ObjC/Swift. As such, there are no bindings. Also notable is the lack of support for most of the Java SE APIs. This makes including 3rd party Java libraries a no-go.
  • Binary size: very good, mostly due to the fact that it doesn’t have to ship a proper managed environment or Java SE APIs.
  • Performance: very good, it’s essentially Objective-C
  • Bitcode: Out-of-the-box, as things are compiled to Objective-C

With a lack of support for Java SE level APIs, the use of reference counting instead of GC, and the fact it only works for Java source code, J2ObjC is not fit for use with libGDX. One GWT based backend is enough 🙂

Avian

Avian is a great piece of OSS tech. JMonkeyEngine chose Avian as their driver on iOS a while ago, so there is some precedent. Avian let’s you chose whether you want to use the Android class library or OpenJDK’s class library, definitely a plus. However, Avian suffers from a few minor and one major shortcomingy.

  • Tooling: No integration with IDEs, Maven or Gradle, save for the integration JMonkeyEngine did with NetBeans
  • Bindings: No bindings for platform APIs, no easy consumption of Platform APIs, JNI hell it is
  • Binary size: Not terrible when using Proguard
  • Performance: The AOT mode essentially takes the JIT mode output and bakes it into an executable. The JIT is rather primitive, and performance isn’t where it should be
  • Bitcode: No path forward to be compatible with Bitcode, as the AOT directly generates machine code. This is the big one

Avian has always been a favorite of mine, as the engineering behind it is top notch. However, using it in production is just a major pita, and the missing path to Bitcode rules it out completely.

Xamarin + IKVM

Back in the olden days, Michael Bayne of PlayN fame took it upon himself to create a somewhat functional path for Java bytecode to live on iOS. The solution consisted of compiling Java bytecode to .NET CIL via IKVM and shipping a heavily cut down version of the OpenJDK class library. I added support for JNI so we could also use it with libGDX. Suffice it to say, it was a Frankensteinian monster of epic proportions. But it worked. Until Xamarin released a new version, in which case non-trivial bug fixes had to be made. Let’s give this a fair shake:

  • Tooling: No integration with IDEs, Maven or Gradle. Integrates with Xamarin Studio and Visual Studio though, including debugging. Otherwise it’s a bunch of shell scripts.
  • Bindings: Excellent, you are able to call into pretty much all native APIs through the IKVM C#/Java bridge
  • Binary size: Good, were it not for the fact that you also need to ship a huge Java class library next to the Xamarin runtime.
  • Performance: Pretty good, although IKVM introduces some suboptimal paths, which makes this solution slower than RoboVM
  • Bitcode: clear path forward, as Xamarin is based on LLVM

Note that the IKVM fork Michael and I worked on is mostly defunct. The cut down class library is also a huge problem, with things like java.net completely missing. Were it not for the last alternative in this list, this solution would probably win.

Intel Multi-OS Engine

Formerly known as Migeran, Multi-OS Engine is a pretty nice piece of tech that let’s you run Java bytecode on iOS. Under the hood, Multi-OS Engine is powered by ART, Android’s virtual machine, which has an AOT compilation mode Multi-OS Engine exploits.

  • Tooling: Integrates with Android Studio, IntelliJ IDEA and Gradle. Used to have support for Eclipse, but it seems that’s no longer available. Also let’s you build and deploy from a Windows machine (using a Mac as the build slave).
  • Bindings: Provides a custom bridge called Nat/J, which is quite similar to RoboVM’s Bro, albeit quite a bit more cumbersome. It appears that all native APIs are accessible from Java via Nat/J, and 3rd party libraries can be bound (semi-)automatically.
  • Binary size: OK but not great. Bigger than comparable RoboVM binaries.
  • Performance: Good, faster than RoboVM in some workloads, slower in others.
  • Bitcode: Currently unsupported, but apparently Intel is working on it.

Multi-OS Engine comes closest to RoboVM along all these axes.

What is the future of libGDX on iOS?

Tomski has already written a new libGDX backend for Multi-OS engine. Here’s the roadmap for libGDX:

  1. Clean-up the Multi-OS backend, integrate it with the libGDX Setup UI, and update the documentation. We are at a 95% status with this, i hope to have the code drop sometime this weekend
  2. Release a new libGDX version next week, that will make Multi-OS engine the default iOS backend for newly created libGDX projects
  3. Begin creating bindings for popular 3rd party iOS libraries. This is where we hope the community can jump in
  4. Keep the RoboVM backend around until the licenses expire (April 17 2017). We’ll try our best to fix any bugs in the backend, however we won’t be able to fix bugs in RoboVM itself

The roadmap of your existing apps should look something like this:

  1. Keep publishing updates using RoboVM 1.14.0 and the corresponding backend
  2. Immediately start adding a Multi-OS engine based version of your app for iOS
  3. Test it and report bugs on our issue tracker
  4. Switch over to Multi-OS Engine as soon as possible

In Closing

There will be some rough edges with the new Multi-OS Engine backend. We are in contact with Intel, who have been very reactive in fixing issues we identified while creating the new iOS backend. Please help test the new backend and report issues, so Intel is able to fix problems on their end.

Multi-OS Engine is free to use, but not OSS. Before you guys start screaming for my head again, I’d like you to read the above analysis carefully one more time. None of the OSS options are even close to production ready. I will however happily merge any and all backend based on those OSS options.

I’d be grateful if everyone funnels their emotional energy created by this announcement into helping out with the new backend, e.g. testing, bindings, reporting bugs etc. I believe this is a much better use of everyone’s time. If you feel the need to vent, I understand. But please don’t expect me to vent with you. The winding down of RoboVM is already emotionally taxing enough for me. My goal is it to keep things running smoothly for everyone, I have no intention to waste time on a screaming contest.

Happy coding,
Mario