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.
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.
Ha wouai, comme même. ^^
Posted by ali_o_kan | 10 décembre 2008, 15 h 55 minI 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 minIt 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