Vortex 3D Runtime
Vortex Runtimes are lightweight apps built on the Vortex Engine that allow loading a Vortex Archive (generated via the Vortex Editor) and running it on the target platform.
Runtimes allow us to bring playgrounds to any platform without having to port the entire editor over, nor requiring the user to build her custom C++ app that hosts the engine and without having to worry about how to draw it all.
The following sections present short technical breakdowns of the iOS and Android runtimes.
Vortex Runtime for iOS
The Vortex Runtime for iOS is structured as a native iOS App that can run on iPhone and iPad. It is built in Objective-C, which makes it extremely simple to link against the Vortex Engine, as both components will share the same memory space and can be statically linked at compile-time.
The runtime app itself consists of a simple UI that allows selecting which archive to run. These playgrounds come from a predefined list, however nothing prevents downloading a runtime from a web server in the future.
The iOS runtime also supports two different rendering backends: OpenGL ES 3.0 and Metal.
Engine Interface
Although Objective-C and C++ can be mixed almost freely, it is in my opinion a good practice to keep a clean architecture with both programming languages separated, only mixing both in the same file at the bounds between app and engine code.
The Engine Interface is such place. It is a centralized location (a front controller) that exposes a pure Objective-C interface to the rest of the app but internally leverages the Objective-C++ language to send work into the engine.
The Engine Interface is responsible for loading Vortex Playgrounds, providing the script execution environment, catching and sending events and rendering the world. It relies heavily on the Vortex Engine to achieve these tasks.
Vortex Runtime for Android
As of Vortex V3, there is a Vortex Runtime for Android capable of running user-authored playgrounds on Android and Chromebook devices!
The Vortex Engine had not run on Android since November 21, 2011, so when we decided to revisit support for this platform, it essentially required building the runtime from scratch.
Now, because the Vortex Engine is built in C++, we knew we would have to leverage the NDK. Unlike back in 2011, there are different options to architect an App that requires calling native code.
One possibility is to build the entire App as a NativeActivity. This has the advantage of keeping all the code in C/C++, with no need to develop Java. The other option is to build and package the native code into a shared object (.so) and call into it via JNI.
For the Vortex Runtime, we decided for the latter. A thin layer of Java code was built to handle the interactions with the platform and creating the OpenGL ES 3 view. This layer is also responsible from loading the shared object.
Engine Interface
The key component for calling into the native engine code from Java was the EngineInterface class. This class, written in Java, acts as a front controller and a facade.
Under this architecture, all requests to the engine are sent to an instance of this class. Internally, all work will be forwarded to the EngineInterfaceBackend C++ class via JNI. There is a thin procedural Android Bridge that wraps the class in a pure C interface and also holds a pointer to the backend instance.
The Engine Interface is responsible for unpacking Vortex Archives, setting up the script execution environment, running the simulation and rendering the world.
Big Endian vs Little Endian
One thing to keep in mind when passing data as arrays of bytes from Java to C is that the JVM (and therefore Dalvik and ART) are Big Endian machines. This is important, as both Intel and ARM will be running in Little Endian mode. This means that a conversion needs to be made whenever passing bytes through JNI.
It begs the question of why the JVM is Big Endian when the underlying platform is Little Endian. This goes back to the old days of Sun Microsystems. Back then, Sun’s Hardware, the SPARC was a Big Endian architecture. It’s possible that the JVM was defined as a Big Endian machine to avoid having to alter the byte order when passing data from Java to native on their own hardware. Nowadays, of course, we need to account for this discrepancy in data representation.
Conclusion for Vortex Runtime for Android
With good software architecture, it was possible to encapsulate and call into the native Vortex code from an Android App, supporting therefore both Android and Chrome OS.
Ironing out all the smaller details around the platform differences that Android has with iOS and Windows comprised the majority of the work.