ArcadeShooter NaCl Postmortem

A couple of people on Google Plus asked for a write-up on my Native Client demo. Here it is. Maybe someone will find it useful.

The game itself is my long-running pet project, with first version stretching back ~6 years. I sometimes port it to various platforms (360, Linux, MacOS, iOS) for (what I consider to be) fun.

It is built on top of my cross-platform(ish) prototyping toolkit LibRush, which already partially supported GLES2 and was structured with new platforms in mind. This simplified a lot of things.

I did most of my development on Windows, some on MacOS. I used Pepper 15 SDK (recommended version at the time). I just followed the steps from the official docs to install the SDK. I already had Python and SCons installed on all of my machines.

To begin with, I used the bundled bootstrap script, located in %NACL_SDK_ROOT%\pepper_15\project_templates. It generates a couple of useful things: a simple HTML file with some boilerplate NaCl module initialization code, some bare-bone C++ source and some basic SCons scripts. The build scripts are cross-platform. If I remember correctly, they just worked out of the box for me on Windows, MacOS and Linux. This was quite nice. I just needed to extend the boilerplate SCons script to include my files, defines and libs. SDK samples also contain a bunch of small SCons scripts, which are useful for reference.

By default, SCons compiles all configurations of the project and does it in serial mode. This can be quite slow, so I’d recommend always specifying a particular build config and adding the “-j n” switch to SCons command line (where “n” is the maximum number of parallel build processes). This speeds things up quite significantly.

To actually run the binary in Chrome, I used a slightly modified python HTTP server script from NaCl examples %NACL_SDK_ROOT%\pepper_15\examples\httpd.py (need to change SAFE_DIR_COMPONENTS value). I modified the default index.html to append received messages to the text log, instead of showing a default JavaScript alert pop-up. My final version is here. More on NaCl messages here and here.

The HTML page includes NaCl modules via manifest files (*.nmf). You can either write them by hand or let SCons auto-generate them (which it does by default).

When it comes to actually porting your app, the very first thing you should implement is probably logging. LibRush had some basic functionality for this, so I just directed all log output to JavaScript like so:

// global namespace

pp::Instance* g_instance;
void nacl_log_message(const char* msg)
{
    g_instance->PostMessage(msg);
}
// in your instance ctor (derived from pp::Instance)

g_instance = this;
Log::callback_message  = nacl_log_message;
Log::callback_warning  = nacl_log_message;
Log::callback_error    = nacl_log_message;
Log::callback_fatal    = nacl_log_message;
Log::callback_debugger = nacl_log_message;

Printf-debugging is still your best friend under NaCl. This is a bit unfortunate. I could probably even live with an integrated debugger. Perhaps something similar to Chrome’s JavaScript debugger. There were various discussions about a more “humane” debugger support on newsgroups. But as far as I know, GDB/WinGDB + lots of hoop-jumping is as good as it gets right now. More on the subject here.

NaCl graphics API is a pretty standard GLES2, like the one you might find on iOS. When I started looking at NaCl, I already had a basic GLES2 renderer. I did most of the graphics porting work on Windows using Angle. It emulates GLES2 interface on top of Direct3D9. It is super-simple to just compile and use. Also comes with a couple of neat bonuses: * Chrome uses Angle on Windows for NaCl and WebGL, so you will get the same behavior in the browser and in your Windows build. * If you are not quite sure about behavior of the API, you can just step into it (since you built it from source). The code is fairly easy to follow.

All I needed to do was to initialize GLES2 layer correctly and kick-off a “render loop”. This was a bit painful due to lack of good/concise sample code on the subject. “Tumbler” SDK sample (rotating cube) is probably as close as it gets. It has all of the necessary code, but it is smeared across several files, making it hard to follow (at least for my brain).

Here is my version of a minimal NaCl GLES2 application.

Next, File IO. The game does not use any. Files are pretty funky under NaCl and I could not find a good sample/doc to clear things up at the time. I was not motivated enough to dig deeper, so I went along the path of least resistance and wrote a python script to embed my assets. Luckily, all my uncompressed data adds up to ~120kb (64k of which is bitmap font), so it was a hack I could live with.

I don’t have any sound in the game, so I only had a quick look at the sound API. NaCl SDK example “sine_synth” looked pretty simple. At least if all you need to do is to output some PCM data. There also seems to be enough code on the net to do something more complex/high level, such as this OpenAL port.

Handling keyboard and mouse input was pretty straight-forward. Just implement HandleInputEvent(const pp::InputEvent& ev) in the pp::Instance-derived class. Docs and samples are enough to get going.

I think that about covers it, as far porting goes. After I had everything running, I got bored with the “game” part and moved to other NaCl-related things:

  • Thing 1: turns out, it is fairly simple to convince Premake to generate makefiles for NaCl.
  • Thing 2: turns out, it is fairly simple to convince Visual Studio 2010 to build NaCl binaries. Even though this project did not receive much TLC from me after initial proof of concept, it is technically functional. After copying required files to appropriate MSBuild locations, NaCl becomes a new platform in Visual Studio. Compiling, linking and intellisense seem to work. Mostly. Debugging, of course, doesn’t work. There is also a fork with few improvements.

Thats it. I would be happy to answer and questions or comments here.