
Ralph Hauwert created a great demo of the power of Flash 10 / PixelBender / Alchemy that you're probably familiar with, then Joa Ebert made a fantastic Flash 9 pure AS3 version that's very impressive, then I gave it a go with haXe and flash.Memory which turned out rather well too.
It's been bugging me for some time that the code just wasn't real world enough though - so I thought I'd give it a go using AS3 and flash 10, using only native methods and no fancy 3D calculations that us commoners don't quite get. In short I've stripped it down and turned it into a simple class which uses flash 10 Vectors and Matrix3D.
I've taken a different approach with these demo's, rather than all in realtime, you simply click and it runs the enterFrame code once and displays the time in ms that it took. There is however a final realtime version using this code at the end.
Four examples are provided, Pure AS3, then the same code converted to haxe, then the haxe "inline" keyword added and finally the whole thing working in realtime.
AS3 Flash 10 Version with Vector / Matrix3D
Here's the main source used:
private function enterFrameHandler( event : Event ): void {
var d : Date = new Date();
var t : Number = d.getTime();
updateMatrix( mouseX , mouseY );
_matrix.transformVectors( particleVector , realVector );
var pxs : Vector.<uint> = new Vector.<uint>( PIXELS , true );
var po : int = 0;
var a : int = 0;
while( a < PARTICLES ) {
po = pointToOffset( int(realVector[a*3]) , int(realVector[(a*3)+1]) , WIDTH );
if( 0 < po && po < PIXELS) {
pxs[po] = increaseColor( pxs[po] );
}
a++;
}
// update display
this.bitmapData.lock();
this.bitmapData.setVector( this.bitmapData.rect , pxs );
this.bitmapData.unlock( this.bitmapData.rect );
d = new Date();
textField.text = "single frame time: " + ( d.getTime() - t );
}
private function updateMatrix( mx : Number , my : Number ) : void {
tx = tx + ((mx - tx)/10);
ty = ty + ((my - ty)/10);
_matrix.identity();
_matrix.appendRotation( tx , Vector3D.Y_AXIS );
_matrix.appendRotation( ty , Vector3D.X_AXIS );
_matrix.appendTranslation( CX, CY, 10 );
}
private static function increaseColor( c : uint ) : uint {
return c < MAXCOLOR ? c + SHADE : 0xFFFFFF;
}
private static function pointToOffset( vx : int , vy : int , vw : int ) : uint {
return vx + ( vy * vw );
}
And here's the AS3 result (with full source)
The same code in haXe
next up I spent literally 2 minutes converting this to haXe syntax, changed no functionality - just slight syntax changes between the languages.
private function enterFrameHandler( event : Event ) : Void {
var d : Date = Date.now();
var t : Float = d.getTime();
updateMatrix( mouseX , mouseY );
_matrix.transformVectors( particleVector , realVector );
var pxs : Vector<UInt> = new Vector<UInt>( PIXELS , true );
var po : Int = 0;
var a : Int = 0;
while( a < PARTICLES ) {
po = pointToOffset( Std.int(realVector[a*3]) , Std.int(realVector[(a*3)+1]) , WIDTH );
if( 0 < po && po < PIXELS) {
pxs[po] = increaseColor( pxs[po] );
}
a++;
}
// update display
this.bitmapData.lock();
this.bitmapData.setVector( this.bitmapData.rect , pxs );
this.bitmapData.unlock( this.bitmapData.rect );
d = Date.now();
textField.text = "single frame time: " + ( d.getTime() - t );
}
private function updateMatrix( mx : Float , my : Float ) : Void {
tx = tx + ((mx - tx)/10);
ty = ty + ((my - ty)/10);
_matrix.identity();
_matrix.appendRotation( tx , Vector3D.Y_AXIS );
_matrix.appendRotation( ty , Vector3D.X_AXIS );
_matrix.appendTranslation( CX, CY, 10 );
}
private static function increaseColor( c : UInt ) : UInt {
return c < MAXCOLOR ? c + SHADE : 0xFFFFFF;
}
private static function pointToOffset( vx : Int , vy : Int , vw : Int ) : UInt {
return vx + ( vy * vw );
}
And here's the identical haXe result (with full source)
Optimized haXe Version
Next up I spent another minute optimizing (literally, one minute) - haXe supports inlining of variables and methods, so I added the inline keyword to the static vars and two of the static methods (increaseColor and pointToOffset)
private function enterFrameHandler( event : Event ) : Void {
var d : Date = Date.now();
var t : Float = d.getTime();
updateMatrix( mouseX , mouseY );
_matrix.transformVectors( particleVector , realVector );
var pxs : Vector<UInt> = new Vector<UInt>( PIXELS , true );
var po : Int = 0;
var a : Int = 0;
while( a < PARTICLES ) {
po = pointToOffset( Std.int(realVector[a*3]) , Std.int(realVector[(a*3)+1]) , WIDTH );
if( 0 < po && po < PIXELS) {
pxs[po] = increaseColor( pxs[po] );
}
a++;
}
// update display
this.bitmapData.lock();
this.bitmapData.setVector( this.bitmapData.rect , pxs );
this.bitmapData.unlock( this.bitmapData.rect );
d = Date.now();
textField.text = "single frame time: " + ( d.getTime() - t );
}
private function updateMatrix( mx : Float , my : Float ) : Void {
tx = tx + ((mx - tx)/10);
ty = ty + ((my - ty)/10);
_matrix.identity();
_matrix.appendRotation( tx , Vector3D.Y_AXIS );
_matrix.appendRotation( ty , Vector3D.X_AXIS );
_matrix.appendTranslation( CX, CY, 10 );
}
static inline function increaseColor( c : UInt ) : UInt {
return c < MAXCOLOR ? c + SHADE : 0xFFFFFF;
}
static inline function pointToOffset( vx : Int , vy : Int , vw : Int ) : UInt {
return vx + ( vy * vw );
}
And here's the inlined haXe result (with full source)
Summary
In under five minutes and with tiny code changes we've gained well over 100% speed increase; using nothing fancy and the speed rivals all the heavily optimized versions using every trick in the book.
The final result (simply with the event listener changed to Event.ENTER_FRAME and fps added) is available here.














very cool - great work
I am really enjoying watching the progress of these experiments.
Good stuff:)
E
So the inline methods and variables is the main optimisation in the final version? Can you explain where and why the HaXe methods greatly improve performance please :)
I know inlines are so great but haven't found an explaination on why :)
Cheers
Elliot