Nook and Emscripten: A technical look at C++ GameDev in the Browser

I run an online game-jam event called Ludum Dare, and this past weekend I got together with a friend (Derek Laufman of Halfbot) and we made a game. What did we make? A platformer about size.

Nook - A game where size matters. Click the image to Play!

The game was created over a period 72 hours. Somehow, amazing to me, we managed to pull it together despite our responsibilities.

Me running the event (Ludum Dare)

Family gathering at my brothers I attended on the Saturday

Car appointment on Monday

Derek taking care of his baby

Fortunately, running Ludum Dare is only busy for me before the event, and at submission time (two of them, 3-5 hours each). During the event I’m free to sit down and do whatever I want. This weekend was the Diablo 3 beta test, and if I was a lesser man, I’d have played that. Fortunately for me, Derek came to me a couple weeks ago and suggested we make a game together, I said yes, and the rest is history.

It’s totally silly, but I’m actually proud of the fact that I managed to attend the family gathering and the car appointment. Stupid me, I could have actually cancelled/rescheduled the appointment for Tuesday or Wednesday, but I decided to go ahead with it anyway. The car appointment was my yearly service for my smartcar, and I ended up staying in the neighborhood of the dealership for about 5 hours of that day. Working on my Ludum Dare game on my laptop, I had WIFI at the dealership, and also McDonalds, where I parked myself for a couple hours. To be honest, this was probably the first time I seriously “Coffee Shop Developed”, and where I actually got some serious work done. It was fun, because it was out of the ordinary. 🙂

Anyways, that’s enough back story. Lets get nerdy.

Emscripten

Emscripten is a wonderful tool. I’ve been jokingly referring to it as witchcraft and other colloquialisms for evil, since it does something rather outrageous: Compiles C++ to JavaScript. To some programmers (vocal programmers), that just might be one of the greatest heresies ever committed to page (thou shall not mix chocolate and peanut butter). Me, I actually quite like JavaScript, having developed a taste for it last Ludum Dare.

I’ve been a big supporter of the idea behind similar “C++ to Web” technologies (Adobe Alchemy, Google Native Client), but both seem to have some issues to deal with today (was not part of the early access program, not enabled by default in Chrome). Emscripten on the other hand is out there, it’s free for everyone, and it works on all browsers today. Emscripten’s problem seems to be one of obscurity (success stories forthcoming), how unbelievable the idea of C++ to JS is to most developers, and the technical know-how needed to use it.

Setting Up Emscripten

I wont bother with a step by step description. You should go here for details:

Me, I run Emscripten inside MSys, which is MinGW‘s faux Unix shell. If you’re not familiar with MinGW, it’s a GCC compiler suite and libraries needed for building apps for Windows. Emscripten’s “emcc” works just like GCC, but generates JavaScript code. So a nice bonus with my setup, I have everything needed to make both Windows and Web “binaries”.

To use Emscripten, you need version 3.0 of LLVM and Clang installed. As a Windows user, for whatever reason, 3.0 binaries were unavailable (older binaries were). So I had to manually build them on my PC via the MSys shell.

Read the instructions on installing Clang. Clang and LLVM are built at the exact same time via LLVM. Building is done in a typical “./configure; make; make install”, like you would do on a Linux. Browse to the LLVM root folder, run configure (“./configure”), make it (“make”), and make install it (“make install”).

If configure complains about a missing Linux’y package, you can use the tool “mingw-get” from the command-line to install additional packages (just like on a Linux). mingw-get install package

Grab Python 2.7, and add the path to it to your “path” environment variable. Parts of Emscripten are Python.

Grab Node.js. This is needed to run JavaScript code from the command line. Parts of Emscripten are written JS.

Grab the Java Runtime (OpenJDK if on Linux). This is needed by Google’s closure compiler, which is a tool that will later help you obfuscate and optimize.

Using Emscripten

Emscripten uses magic, also known as LLVM and Clang to convert C and C++ code to bytecodes, and reconstructs new JavaScript code based on those generated bytecodes. You should have an understanding of JavaScript coming in to this, as it’s often wise to check the generated code to see what LLVM+Clang+Emscripten are doing, especially when starting out. The project is mature and functional, but you will learn more from seeing and doing than you will from reading documentation. The documentation is good for filling in the gaps of things that aren’t typical with a normal GCC-like C and C++ compiler.

A typical project generated by Emscripten will be an HTML file (eg. index.html) and a JS file (eg. mycode.js). I like having a separate HTML file, as it means you can include other things (external map data files) inside it. If you wanted to though, you could include the JavaScript code right inside the HTML file.

Crunch Time Compile Script

I didn’t have much time this weekend to think, so I did all my compiling via simple shell script that I tweaked and added to whenever I needed. Normally I’d have a proper makefile system for building, but I was still learning the ins and outs of Emscripten, so I opted for more hackibility.

I explicitly disabled the closure compiler in the optimizations (–closure 0). Eventually I re-added it, but manually (last line). I tracked down some very very unusual Chrome only performance bugs that were caused by closure Compiler optimizations. Whitespace removal works fine and is totally safe, so I’m doing only that now.

–pre-js takes only a single file, so I had to combine all my JS code in to a single file first. Someone should really put in a feature request in to allow multiple –pre-js calls. 😀

Audio support in browsers is *shrug*, so I have 3 audio libraries I enable/comment out between (SoundManager 2, Buzz, and none).

For a brief time, I was including my map file alongside my other JS code. I eventually moved mention of it in to the HTML file, so that file would be read every time (without the need to re-link the binary).

C and C++ Code

A C function named “MyFunction” will become “_MyFunction” inside JavaScript scope. Conversely, a JavaScript function prefixed with an underscore will be accessible from your C code. Just prototype it before hand (“void MyFunction();”) and you’re golden.

I’d recommend using basic types when passing data between JS and C++. Floats and Integers where possible, and char*’s for strings. Strings require a little more work to use, but aren’t bad. See the “Other Methods” section for details:

C++ functions are a little tricker. If you’ve ever tried mixing and matching C and C++ code, you’re probably familiar with C++ Name Mangling. You need to deal with mangling here if you want to use C++ code.

The encoding isn’t too hard to decipher, but it’s probably easiest to just open the output JavaScript file, and search for the symbol. “GameDraw”, and keep looking until you get one that’s wacky like those mangled names above.

Graphics

Emscripten ships with a version of the popular SDL game library. In addition, there’s a bunch of work done on a OpenGL port, based on WebGL. I’d expect to see more and more things ported as the GL libraries mature.

Me I’m slightly impatient, and have my own libraries that wrap OpenGL on the PC/Mobile side. I didn’t use any of my existing graphics code, but created a new JavaScript library based on the same naming scheme. Before this weekend, I had only minimal experience using Emscripten, so I didn’t want to pigeonhole myself in to my existing libraries. I don’t use floats in my libraries, but instead I use a type “Real” which is synonymous, and includes some extra features. After today, a change I would make is have my drawing libraries accept floats, and lightly wrap them with versions that take Real’s. That would make building a consistent interface across targets nicer.

Music and Audio

I wrote a generic interface to load and play sound files. All JS sound libraries use MP3 or OGG files, which makes them suitable for music playback too. However, unfortunately, no matter what library you use, looping of music has an annoying gap and isn’t seamless.

I use both. I started with SoundManager 2, because I occasionally notice weirdness in native HTML5 playback. But for the updated version (the link above), I switched back to Buzz. There are cases when the sound gets broken on Chrome, but SoundManager 2 has a delay. I prefer delayless.

SoundManager 2 also supports multi-shot sound playing (i.e. the sound doesn’t interrupt the last one played), but only with its Flash 9 based noisemaker. This is cool, but for the style of game we were making, interrupting sounds wasn’t a problem.

Optimizations and Performance

If you’ve ever implemented a scripting language in to a game engine, you’ll be familiar with the idea that native code is faster. A “Draw” function that blits a sprite or renders some 3D geometry should always be in native code, and not implemented in script. Working with Emscripten is a bit of that. For the most part, Emscripten does a really good job optimizing, and often does things you would forget if optimizing by hand. However, there are times when going native (in this case JavaScript) will speed up your game.

File IO

Emscripten includes a suite of function for faking data in a filesystem. They work fine and are totally easy to use (can be automatically done via command-line arguments to the linker). However, using a JSON parsing library written in C to read a 400k JSON file that has been artificially encoded in to the virtual file system can be … wasteful.

This is actually what I did at first. The map was originally a 200k JSON file, exported by Tiled. It was a bit horrible, taking about 4 seconds to parse, but tolerable. Once we grew the map to a >400k file, things got out of hand (12 seconds).

Since JSON stands for JavaScript Object Notation (duh, the native object format of JavaScript), my solution was to include the JSON file alongside the rest of the JavaScript code in the HTML page.

JavaScript

1

<script src='Content/MapData.json.js'type='text/javascript'></script>

A JSON file however lacks an actual usable name in JavaScript. So to avoid weird cross-file crying by a “secure” browser, I wrote a tool to append an appropriate name on to the file.

C++

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

#include <stdio.h>

#include <Core/DataBlock.h>

#include <Core/GelFile.h>

intmain(intargc,char*argv[]){

if(argc==1){

printf("You did it wrong!\n");

return-1;

}

charText[4096];

sprintf(Text,"%s.js",argv[1]);

DataBlock*InData=new_read_DataBlock(argv[1]);

GelFile*OutFile=open_writeonly_GelFile(Text);

sprintf(Text,"var ContentMapData = ");

write_GelFile(OutFile,Text,strlen(Text));

write_GelFile(OutFile,InData->Data,InData->Size);

sprintf(Text,";");

write_GelFile(OutFile,Text,strlen(Text));

close_GelFile(OutFile);

delete_DataBlock(InData);

return0;

}

The tool was placed alongside the JSON file, so we could simply drag+drop the exported JSON file on top of it to regenerate a usable game map. Oh and my apologies, the above code uses a FileIO library of mine, but what it’s doing should still make sense. A better tool would generate a type name based on the filename. Me, I hardcoded it to save time and thinking.

Finally, I wrote a series of functions for looking at the data. With the JSON C loader, I would pinpoint the map data and the dimensions of the map file inside the JSON file. I wrote similar functions, one for each piece of data I wanted, and dropped them in to my existing loader code.

Done. We now had instant file loading, and a way for the artist to change/edit the map and see his changes.

Graphical Rendering and Performance

Firefox and Chrome both have good profiling tools. If you happen to fire them up, and play the game for about 30 seconds, you’ll notice a few functions stand out. The one I want us to take note of is “_gelDrawTiles“. This is a function written in JavaScript that draws a layer of the map. That’s lots and lots of little 8×8 tiles. The function itself is rather complicated, but it’s an improvement before. Originally, the C++ code called a function “_gelDrawTile” (note the lack of S on the end), one for every single tile in the world. At 40 by 30 tiles, by 4 layers, that’s potentially A LOT of overhead wasted by function calls. Since this draw function was written in JS, it couldn’t exactly be inlined (as expected in C++).

So, I changed the tilemap layer rendering in to a single function call written in JavaScript.

The map data was located inside the C++ code though, so I had to get a pointer and call an Emscripten provided function “getValue” to get the actual data. However, this was a BRAND NEW wasteful excessive function call, so like the author of Emscripten suggests, I went in to the code to see how to access the data I wanted (HEAP16[Pointer+Offset]).

The other heavy functions seen by the profiler relate to the collision detection. While writing this though, I just realized I might be able to improve that, so *ahem* disregard them, okay? 🙂

Standard Template Library – STL

This isn’t particularly a speed optimization, but a size one. If you’ve ever worked with STL as both a C and C++ programmer, you’ll note that STL adds quite a lot of size to program. It performs well, but depending on the compiler, you can see a whopping 500k to 1MB increase in file size over a C program, or a C++ program simply using your own C++ containers.

If you read above, you’ll see that I have my own suite of libraries for doing various things. I use C++, but a number of my libraries use a C style aesthetic (or Pure Functions if you’re in a buzzword mood). So instead of STL, I used my own equivalents and variants.

That said, I’m not saying don’t use STL. If you like it, use it. I like it actually, but I’ve been coding professionally for over a decade now, and have built up my own libraries and ways of doing things. There are some things STL does well, and others STL doesn’t. I actually was planning to use it at first, but after I saw the code size grow by ~500k, I decided not to.

And manipulating the IntervalHandle to stop (lost focus), and restarting it via setInterval again (focus gained).

But hey, the RequestAnimationFrame stuff is supposed to be better.

Wrapup

That’s all I can think of at the moment. If there’s something you want to know about that I didn’t cover, feel free to ask. I may expand this post accordingly.

This entry was posted
on Friday, April 27th, 2012 at 3:44 PM and is filed under Ludumdare, Technobabble.
You can follow any responses to this entry through the RSS 2.0 feed.
Both comments and pings are currently closed.