// you’re reading...

tech

full double-buffering alchemist

As you maybe know, Alchemy is out on the labs.

Quoting the official announcement: Alchemy is a research project that allows users to compile C and C++ code that is targeted to run on the open source ActionScript Virtual Machine (AVM2).

I decided to provide an example of double-buffering beetween C code and ActionScript code for people who need it.

Full Metal Alchemist

There are few traps to avoid.

Documentation on Adobe labs says to use GLEByteArrayProvider to get the ByteArray used as RAM by your C library directly. I tried it, but this class wasn’t public with my configuration.
If you experience the same, you have to reference it dynamically as shown below (like you did with Dictionary class during AS3 beta 2).

	var ns : Namespace = new Namespace( "cmodule.MyModule" );
	var ba : ByteArray = (ns::gstate).ds;

With this workaround, it works like a charm, except GLEByteArrayProvider.get() returns a byte array with a little endian sealed.

This code gives me an error:

var ns:Namespace = new Namespace("cmodule.CGraphicFilter");
baMem = (ns::gstate).ds;
baMem.endian = Endian.BIG_ENDIAN;
lib = (new CLibInit()).init();

LEByteArray endian set attempted
at LEByteArray/set endian()[56201.achacks.as:50]
at AlchemyTest()

You can use an intermediate byte array with big indian to get data as expected by Flash API (as shown below):

protected function enterFrame( e : Event ) : void
{
baMem.position = position;
var ba : ByteArray = new ByteArray();
baMem.readBytes( ba, 0, size );
bd.setPixels( rect, ba );
}

Finally, I felt more comfortable to hack endianness during ram writing on c side . I’m writing bytes values in reverse order (bgra instead of argb).

Here’s C code:

#include 
#include 
#include

#include 
#include "AS3.h"

AS3_Val initVideoData( void* self, AS3_Val args );
AS3_Val update( void* self, AS3_Val args );

int v;
unsigned char* rawImageData;

AS3_Val init( void* self, AS3_Val args )
{
	// malloc (width * height * 4 channels) size
	rawImageData = malloc( 800 * 600 * 4 );
	// fill with black
	memset( rawImageData, 0, sizeof( rawImageData ) );
	// return pointer position
	return AS3_Ptr( rawImageData );
}

AS3_Val update( void* self, AS3_Val args )
{
	int x, y, pos;
	v++;
	v%=16;

	for( x = 0; x < 800; x++ )
	{
		for( y = 0; y < 600; y++ )
		{
			pos = (x+y*800)*4;
			// stub code on red channel. I use BGRA order to simulate big endianness
			rawImageData[ pos +2 ] = 128 + 128 * sin( x / v );
		}
	}

	return 0;
}

int main()
{
	AS3_Val initMethod = AS3_Function( NULL, init );
	AS3_Val updateMethod = AS3_Function( NULL, update );

	AS3_Val result = AS3_Object("init: AS3ValType,update: AS3ValType",initMethod,updateMethod);

	AS3_Release( initMethod );
	AS3_Release( updateMethod );

	AS3_LibInit( result );

	return 0;
}

Here's Flash code to work with C code to update screen.

package
{
	import cmodule.DoubleBuffering.CLibInit;

	import flash.display.Bitmap;
	import flash.display.BitmapData;
	import flash.display.Sprite;
	import flash.display.StageAlign;
	import flash.display.StageQuality;
	import flash.display.StageScaleMode;
	import flash.events.Event;
	import flash.geom.Rectangle;
	import flash.utils.ByteArray;

	[SWF( width="800", height="600", frameRate="25" , backgroundColor="0x000000")]
	public class AlchemyDoubleBuffering
		extends Sprite
	{
		protected const w 			: int = 800;
		protected const h 			: int = 600;
		protected const lib 		: Object = (new CLibInit()).init();
		protected const ba 			: ByteArray = new ByteArray();

		protected var rect 			: Rectangle;
		protected var bd 			: BitmapData;

		protected var baAlchemyRAM 	: ByteArray;
		protected var videoPointer 	: uint;

		public function AlchemyDoubleBuffering()
		{
			stage.scaleMode = StageScaleMode.NO_SCALE;
			stage.align = StageAlign.TOP_LEFT;
			stage.quality = StageQuality.LOW;

			rect = new Rectangle( 0, 0, w, h );
			bd = new BitmapData( w, h, false, 0 );
			addChild( new Bitmap( bd ) );

			// get Alchemy RAM
			var ns : Namespace = new Namespace( "cmodule.DoubleBuffering" );
			baAlchemyRAM = (ns::gstate).ds;

			// call C code to init RAM and store video position
			videoPointer = lib.init();

			addEventListener( Event.ENTER_FRAME, enterFrame );
		}

		protected function enterFrame( e : Event ) : void
		{
			// call C code to update RAM data
			lib.update();

			// update screen with RAM data from stored position
			baAlchemyRAM.position = videoPointer;
			bd.setPixels( rect, baAlchemyRAM );
		}
	}
}

I had a conversation with Mario, and like him, I felt really disappointed with Alchemy graphical performances (BitmapData).

I tried to play with some plasma algorithms and color palettes, and despite of what has been publicised by Adobe I could not create any demo where Alchemy code runs faster than optimized Actionscript.

At this time, the only benefit we see is that you can reuse existing C libraries.

Discussion

7 Responses to “full double-buffering alchemist”

  1. Ha wouai, comme même. ^^

    Posted by ali_o_kan | 10 décembre 2008, 15 h 55 min
  2. I used the exact same technique, but used the intermediate Byte Array to change Endian.

    Good post and I’m hoping this problem can be solved. I’m sure we’ll be able to render quicker in C.

    Posted by TMakled | 19 janvier 2009, 15 h 53 min
  3. It actually seems to be possible to build a alchemy rasterizer.

    500 Squares ( 200x200px) per frame using a C-Buffer bytearray. (I haven’t coded C in years, got a bit of a memory leak, it’s just a rough draft)
    http://home.earthlink.net/~mattspencer1/AlchemyCBuffer.swf

    Posted by mspencer | 7 mars 2009, 21 h 44 min

Trackbacks/Pingbacks

  1. [...] two [...] Billet posté par Francis Bourre Francis Bourre’s Website While playing with Alchemy, I focused a lot on C and C++ CG resources (like this great one). Like many coders, I’ve [...]

  2. [...] l’affichage assez lent (on s’en serait douté, souvenez-vous des conclusions de ce billet), mais l’ensemble est [...]

    FlashSDL | tweenpix - 4 janvier 2009
  3. [...] Il a porté SFFE (un parseur de formules mathématiques pour sa librairie sfXaoS) en reprenant mes expérimentations sur le double-buffering avec Alchemy pour l’affichage. Une fois de plus, les résultats ne sont pas convaincants en termes de [...]

Post a comment