Sunday, December 30, 2012

Migrating from Alchemy to FlasCC - Compiling C/C++ to SWC



New year, new tools! The updated new version of Alchemy, the C/C++ to AS3 compiler - FlasCC was released some time ago. Many things have been changed. These two tools - Alchemy and FlasCC have very different APIs. However, if you have some experience with the old Alchemy, migrating from it to FlasCC is not hard.

In this post I will share my experience of migrating from Alchemy to FlasCC. I ported the very classic Alchemy example to FlasCC - Ralph Hauwert's Alchemy lookup-table effects. I will use that example to show you how to compile C/C++ code to SWC, how to use FlasCC to manipulate screen buffers, as well as the changes of the APIs.

The installation of FlasCC has been greatly simplified. You don't need to install Cygwin separately like Alchemy because the new FlasCC tool is already integrated with Cygwin. First, you have to get the FlasCC tools at
http://gaming.adobe.com/technologies/flascc/.
Unzip the package to somewhere (mine is "D:\FlasCC_1.0.0"), click "run.bat", then you will open the Cygwin window.

Let compile the sample first:

cd 05_SWC
make FLASCC=/cygdrive/d/FlasCC_1.0.0/sdk FLEX=/cygdrive/d/Program\ Files/FlashDevelop/Tools/flexsdk
This sample is a very simple and good start. You should try to read the source code and the official notes:
http://www.adobe.com/devnet-docs/flascc/docs/samples.html#T5

To create a SWC from some C/C++ functions, we need to write some wrapper code (as3api.cpp) to expose the functions to AS3.
In Alchemy, it will be something in the C/C++'s main() function like this:
int main()
{
AS3_Val initializeScreenDiffuseBufferMethod = AS3_Function(NULL, initializeScreenDiffuseBuffer);
AS3_Val rasterizeMethod = AS3_Function(NULL, rasterize);
AS3_Val setupLookupTablesMethod = AS3_Function(NULL, setupLookupTables);
AS3_Val initializeDiffuseBufferMethod = AS3_Function(NULL, initializeDiffuseBuffer);
AS3_Val result = AS3_Object("initializeScreenDiffuseBuffer: AS3ValType, rasterize:AS3ValType,setupLookupTables:AS3ValType,initializeDiffuseBuffer:AS3ValType"
,initializeScreenDiffuseBufferMethod, rasterizeMethod,setupLookupTablesMethod,initializeDiffuseBufferMethod);
AS3_Release( initializeScreenDiffuseBufferMethod );
AS3_Release( rasterizeMethod );
AS3_Release( setupLookupTablesMethod );
AS3_Release( initializeDiffuseBufferMethod );
AS3_LibInit( result );
return 0;
} 
While in FlasCC, it will be some declarations and wrapper functions outside the main function like this:
void rasterize_AS3() __attribute__((used,
annotate("as3sig:public function rasterize_AS3():void"),
annotate("as3package:FlasCCTest.lookupeffect")));

void rasterize_AS3()
{
rasterize();
}

void setupLookupTables_AS3() __attribute__((used,
annotate("as3sig:public function setupLookupTables_AS3():void"),
annotate("as3package:FlasCCTest.lookupeffect")));

void setupLookupTables_AS3()
{
setupLookupTables();
}
And the main function in FlasCC:
int main()
{
AS3_GoAsync();
}

The basic framework for using C/C++ to manipulate screen buffers:
1. Pass the texture's pixel data as ByteArray to C/C++ array.
2. Process the textures in C/C++.
3. Retrieve the screen buffer array (as bytearray) to flash and use a bitmap to render it.
The best way to pass bytearray between C/C++ and AS3 is to use pointers and C machine's RAM.

Please read the full source code of my example for details.

To pass parameters from AS3 to C/C++, and return the pointer of the array tBuffer (the screen buffer) in Alchemy:
AS3_Val initializeScreenDiffuseBuffer(void* self, AS3_Val args)
{
AS3_ArrayValue(args, "IntType, IntType", &resX, &resY);
tBuffer = malloc( resX * resY * sizeof(int) );
return AS3_Ptr(tBuffer);
}
In FlasCC:
int* initializeScreenDiffuseBuffer(int resX, int  resY)
{
tBuffer = (int*)malloc( resX * resY * sizeof(int) );
return tBuffer;//&(tBuffer[0]);//return the pointer to the screen buffer
}

void initializeScreenDiffuseBuffer_AS3() __attribute__((used,
annotate("as3sig:public function initializeScreenDiffuseBuffer_AS3(resX0:int,resY0:int):uint"),
annotate("as3package:FlasCCTest.lookupeffect")));

void initializeScreenDiffuseBuffer_AS3()
{
int* result;
//copy the AS3 resolution variables resX0, resY0 (parameters of the swc function initializeScreenDiffuseBuffer_AS3) 
//to C variables resX, resY in lookupeffect.c
AS3_GetScalarFromVar(resX,resX0);
AS3_GetScalarFromVar(resY,resY0);
//get the pointer of the screen buffer
result = initializeScreenDiffuseBuffer(resX,resY);
// return the result (using an AS3 return rather than a C/C++ return)
AS3_Return(result);
}

Now let's see some differences of the AS3 APIs:
To initialize the C/C++ SWC Lib in Alchemy:
cLibInit = new CLibInit();
alcLookupLib = cLibInit.init();
In FlasCC:
CModule.startAsync(this);
To call the SWC functions:
Alchemy:
alcLookupLib.setupLookupTables();
alcDiffuseBitmapPointer = alcLookupLib.initializeScreenDiffuseBuffer(IMAGE_WIDTH,IMAGE_HEIGHT);
FlasCC:
setupLookupTables_AS3();
alcDiffuseBitmapPointer = initializeScreenDiffuseBuffer_AS3(IMAGE_WIDTH, IMAGE_HEIGHT); 
To use domain memory - the C machine's RAM (http://www.adobe.com/devnet-docs/flascc/docs/apidocs/com/adobe/flascc/CModule.html#ram):
Alchemy:
var ns : Namespace = new Namespace( "cmodule.lookupeffect");
alchemyMemory = (ns::gstate).ds;
alchemyMemory.position = alcDiffusePointer;
alchemyMemory.writeBytes(ba,0,ba.length);

alchemyMemory.position = alcDiffuseBitmapPointer;
screenDiffuseBitmapData.setPixels(screenDiffuseBitmapData.rect, alchemyMemory);
FlasCC:
CModule.writeBytes(alcDiffusePointer, ba.length, ba);
CModule.readBytes(alcDiffuseBitmapPointer, 512 * 512 * 4, ba);
or
CModule.ram.position = alcDiffuseBitmapPointer;
screenDiffuseBitmapData.setPixels(screenDiffuseBitmapData.rect, CModule.ram);

The source code:
https://flaswf.googlecode.com/svn/trunk/flaswfblog/Tutorials/FlasCC_SWC

Links:
http://www.adobe.com/devnet-docs/flascc/docs/Reference.html
http://unitzeroone.com/blog/2009/04/06/more-play-with-alchemy-lookup-table-effects/
http://blog.debit.nl/2009/03/using-bytearrays-in-actionscript-and-alchemy/

No comments:

Post a Comment

Sponsors