Home       About Me       Contact       Downloads       Part 80    Part 82   

Part 81: Emscripten Demos

May 5, 2013

I continued playing with Emscripten this week. I have several of the demos working, including some complex ones like Crafty.


Crafty

Landscape
 
 

SeaOfMemes

Trees

Controls on all the demos are similar: WASD or the arrow keys for movement. Page Up (or blank), Page Down (or 'X') move vertically. In Crafty and Landscape, 'N' toggles night/day. "+" and "-" control speed.

These demos are all fairly large and take awhile to load on a slow connection. It really needs some kind of overall progress bar during load. The code size is smaller than it was and performance is better, but still not great. Crafty on my machine is only running at 20 fps, compared with 300 fps for the native version.

On Android, SeaOfMemes runs with the bad skybox I noticed back when I started using WebGL there. None of the other demos run at all. There's no Javascript console for Android Chrome, so I don't know where the problem is. I guess I have to install the Android development environment to see the console.

Images

Last part, I mentioned that I could not load 32-bit images with an alpha plane. You'll see the effects of that in SeaOfMemes, where land on the planet is black. Another issue cropped up in that same area.

In my Texture Atlas code, I load all the component images and create the mip-map levels myself. This lets me scale each image individually, without averaging in pixels from adjacent images in the atlas. Under Emscripten, the SDL IMG_load function returns a pointer to image data, but it's not real. All the pixels read as zeros, so my scaling code did not work.

I assume that what's going on here is the IMG_load function actually returns some byte array object, but this is not copied to the Emscripten heap. When calling glTexImage, the code gets the byte array object, not a heap offset. That makes me wonder if I can even generate texture data in memory. I haven't tried that yet.

Input Loop

I had been using SDL calls, with an additional Emscripten call "emscripten_set_main_loop", to handle input. This wasn't working well, and wasn't what I wanted. I also had the window resize problem I described last part.

I posted a comment on the Emscripten discussion group, and got some helpful advice. If I just let my C++ main initialize things and then end, I can call into my code from Javascript without using the SDL processing. I can also use Emscripten bind to more easily access my C++ code from Javascript. Note that I wasn't able to get the simple example given in the link to work. They write:

EMSCRIPTEN_BINDINGS(my_module) 
{
    function("lerp", &lerp);
}
But in the Emscripten tests, the code looks like this:
EMSCRIPTEN_BINDINGS(([]()
{
    function("lerp", &lerp);
}));
I'm not sure what "([]()" is all about, but it seems to work.

That wasn't the end of the input problems. First, turn off all the SDL input processing by defining "Module.doNotCaptureKeyboard: true" Then I discovered that I wasn't getting keyPress events and couldn't figure out why. I had been debugging under Chrome and switched to Firefox in frustration. To my surprise, the code worked there. Apparently, if you trap keyDown and prevent normal input processing, under Chrome, you don't get the keyPress events. Under Firefox, you do.

For the resize event, I changed the framework to check the canvas size before each animation cycle, and send a resize event if it has changed. Page resize works correctly now.

Finally, I tried the code again at optimization level "-O2". It seems to work, even though I haven't changed anything important. I'm not sure if the infinite loop I got before was due to the input processing, or some other issue. The code is faster now, with most of the demos running at 60 FPS. The code is smaller too -- Crafty.js is 1250K, where the Windows binary is 947K.

Code Changes

The goal is to run this C++ code through Emscripten without changes. Here is what I'm still having to do:

  • Take out all the UI code. I still don't have overlay text working, so that code has to be removed.

  • Take out all threads. Since multithreaded code is a pain to debug, all my demos have a "threadCount=0" option, where the thread function is called in the animation cycle and does a step of work in the main thread. I'm forcing this case in the WebGL versions to avoid using threads.

  • Fix the shaders. I start with the OpenGL 2.1 shaders and then fix the things WebGL complains about. One new problem in this area was using glUniform3f to set a vec4 uniform. Normal OpenGL does not complain about this, but WebGL does.

  • Stay under 64K vertexes on meshes. I will probably do something in my framework to hide this difference between WebGL and OpenGL.

  • Repack the images. Instead of using two files, JPG+GIF for my transparent images, I have changed them to PNG files. I can use the libPNG code in my C++ demos so that they also use these images.

Bottom Line

After working through the simpler demos, Crafty, which contains a lot of code, took less than an hour to get running. That's a pleasant surprise! Once I fix some more things in my library, I should be able to just build for WebGL and have it work.

On the other hand, the texture atlas in the demo looks pretty poor, the performance is bad, it doesn't run under Android, and the demo makes for a large download. That problem is only going to get worse as the demos get more elaborate.

I also don't have the demos sending log files to my tracking server. Because of the way that code worked (the log file gets sent to the server after the demo exits), I don't think I can get that to run under WebGL. Emscripten simulates a virtual file system for read, but I don't think my writes to the log file are going anywhere.

It makes a lot of sense to publish the demos in this form, but for decent performance, the native Windows, Linux and Mac versions are still the way to go.

Home       About Me       Contact       Downloads       Part 80    Part 82   

blog comments powered by Disqus