Thursday, July 3, 2008

FlashCanvas performance

Updated: see below.

The FlashCanvas experiment didn't really perform as well as I had hoped so I took a look to see where the slow down happens.
First, the speed of javascript execution was ruled out as the limiting factor. From the limited testing I did it is clear that javascript executes slower on internet explorer than ff or safari -- this is not news. Since flash is available on the platforms I can see they all perform roughly equivalent. This means that javascript execution is not the major performance bottleneck at this point because using a browsers native canvas element shows much better frame rates.

So this meant that the slowdown is occurring somewhere between the javascript call and the actual rendering to the flash MovieClip. I mocked up a dummy ping-pong page/flash file which simply calls a "ping" method from javascript into actionscript. The results revealed where the problem was:


note: JS2AS = JavaScript calling ActionScript; JS2AS2JS = Javascript calling ActionScript and ActionScript calling Javascript; os x and windows machines were physically different

Each call via the ExternalInterface is taking approximately 0.5 ms. Since the time it takes simply to call an actionscript method from javascript was now known I could then test to see if flash's rendering was taking up significant time: For the example1.htm page this means that it takes ~24 ms to render 20 individual lines. In Safari a call into actionscript is taking roughly 0.4 ms -- this is only the call time, it does not include the time to render anything. For example1.htm a single "particle" calls actionscript 3 times with the commands: [lineStyle,moveTo,lineTo]; each particle takes 1.2 ms in JS-to-AS calls; to take 1 second to render the frame ~833 particles need to be rendered. Changed the example code to render that many particles (and also time itself) results in:

(where time is in milliseconds)
So the time it takes for the javascript canvas wrapper, the javascript particle simulation and the actionscript code/line rendering is pretty much insignificant.

For comparison this is safari doing the same exact thing using it's native canvas element:

(where time is in milliseconds)

Next steps:

  • Investigate batching draw commands into an array to be passed only once into the flash renderer. Immediately this raises an issue about how the canvas API has been typically used: there is no explicit end of rendering or flush equivalent so the canvas-wrapper would never really know when drawing is finished. The most fine-grained control would be at the individual path level, which would result in nearly the same performance for something like the example1.htm file. Most graphics API have some sort of buffer flush either explicitly, like glFlush in opengl, or implicitly during a buffer swap.

  • Investigate other plugin technologies?




4-3-08 Update:
Batching commnads into an array does not eliminate the performance problem in ExternalInterface. FlashCanvas was changed to batch commands and then send them to flash with only one call via ExternalInterface. The result is only slightly faster (800 ms instead of 1 sec and a really slow 20 secs /frame in Internet Explorer 7). Since that part of flash is all closed I can't really take this any further -- my guess is that some sort of serialization/translation is taking all the time.

4-5-08 Update:
Try it at home: FlashCanvas 0.2. This includes the batched mode (which requires a call to context.fc_flush() when you want to send the commands to flash), see examples/example1_stress.htm to see how it works.