When I set out to write a very simple Pixel Bender (PB) kernel/script thingy, I expected it to be relatively straight-forward, mostly because Adobe has been so good writing quality documentation for its products and/or there is a wealth of info on their products produced by their users. Unfortunately I didn’t find the dev guide in the Help menu and I missed some key AS3 bits from the links I’ll post below, but even so I still had a lot of trouble finding some info that really should already have been out on the interwebs. So this post is to fill in the gaps when going from AS3 to Pixel Bender.
Hope it helps Image may be NSFW.
Clik here to view.
Best resources for beginners
Here’s the best tutorials and explanations I’ve found so far:
- The official guide and reference. Slap that F1 key! Also, don’t forget the Help menu in the toolkit like I did (there’s the PB-specific language spec and dev guide there).
- Using PB as a calculator. Completes the official tutorial.
- Great in-depth article with general+performance tips.
- Very straight-forward examples + comedy. Can’t go wrong there.
- Nice user article on optimizations and some speed tests (turns out, for input
bitmapData
beatsByteArray
beatsVector.
)
Syntax overview
As a casual programmer of high-level languages and no mid- to low-level ones, I was thrown off by PB’s awkward syntax. It’s strongly typed, which is fine, except I’m not familiar with low-level languages like C or any previous shader language (PB is based on GLSL from what I hear). Here’s a basic difference:
AS3:
var i:Number = 12;
…in PB is:float i = 12.0;
The decimal in12.0
tells PB it’s a floating point number. If it was just12
PB would think it’s anint
.
When dealing with “vectors” (which are arrays, as in Flash 10′s odd use of the word “vector”) it’s float2 i = float2(12.0, 2.0)
. Notice there’s no brackets or anything suggesting any type of array present. It’s simply the type + how big the array is, eg float3
. It goes up to 4, for the 4 channels in images: Red, Green, Blue, Alpha). Then, as you can see, intializing the array is a simple matter of putting in the numbers you said would be there. So float4(1.0, 24.2, 0.1, 3.4)
is valid whereas float2(1.0, 24.2, 2)
is not, because there’s an extra number in there and it’s an int
(adding insult to injury).
An important thing to keep in mind is that Pixel Bender sees channels in the order described above (RGBA). This little detail cost me a bit of time because I’m used to AS3′s order of ARGB–the alpha channel is first in AS3 while it’s last in PB.
To use any of the built-in functions you simply call them as they appear in the reference doc: all(x); atan(x,y);
etc., no Math
calls like we have to do in Flash (this through me for a loop, lemme tell ya).
For those of you who use the shortcut operations like var i:int = foo < bar ? foo : bar;
, then you'll be pleased to hear they're supported in PB too. Just about the only thing "Hydra" (formally the codename for Pixel Bender, now--I guess--the name of the PB Flash implementation) does not support is loops of any kind and their related break/continue statements. That last bit is bad because there are times when I want to stop the filter if a certain condition is met. You just have to design your logic to take this into account from the start.
That's the basics but take special note that shader.data
is actually an array, so you can do some naughty stuff like iterate through some kinky data and set the input as shader.data['whatever'+i].input = kinky[i]
. Also note that in order to set the input for a shader in AS3 you must set it to shader.data.vdipb.INPUT = inputImage
where vdipb
is the variable name you declared in the PB shader itself.
More performance tips
Turns out that after I converted my kernel from using boolean methods (like boolean4()
etc.) to if
statements, it consistently executed faster. I also got some fractional FPS increases by using the age-old optimization of putting real values (eg 1.2) in the evaluation statement instead of references (eg pixel.r). See the last blog I linked above for some other obvious and not-so-obvious performance tips.
PB math is much faster than straight AS3 math but it's slightly brighter (I wouldn't say it shines per se...) when it runs in asynchronous mode, which is enabled by using ShaderJob
. You use addEventListener(Event:ShaderEvent...)
and then run start(false
) to put it in asynchronous mode (the param is waitForCompletion
and it's already false
by default). See Ryan's post for a great example of how the ShaderJob
has to be setup (Squize's post, linked above, is an even clearer example).
Realtime use of Pixel Bender
Asynchronous mode
Reading the above linked performance tips for PB you, like me, might be thinking "OH MAN I can rule the universe with ShaderJob doing bitchwork like heavy collision solving" and you'd be totally wrong. You only have to set the input for a shader once, but you do have to create a new ShaderJob per computation of the filter every frame/tick. This is the depressing because ShaderJob takes a LONG time to start up and it, unlike the job itself, does use CPU from Flash's main thread--you do not get the asynchronous hotness until the actual job is started. And it is sloooooowwwww. Unless you're doing some serious number crunching or temporary special effect, you will not see a huge benefit from using PB in your game.
To be clear, ShaderJob works well enough for realtime usage but my experiments have shown that it's not fast enough to be executed every frame (unless you're SWF is running at 20 FPS or so). I tried to get it to fulfill my game's collision detection system by processing the entire map, but PB just couldn't handle the job. Still, I do recommend you try it (using Squize's recursive implementation) and see if it works for you. Just keep your hopes down Image may be NSFW.
Clik here to view.
Synchronous mode
You can set ShaderJob.start(true)
but if you're going the synchronous route, I suggest applying your Pixel Bender kernel by using the filter method. It starts up much faster than ShaderJob and doesn't have to be restarted every time you want to update the input. You do have to wait for it to finish processing before it will move to the next line in your code though, but as I mentioned above, PB math is a lot faster than AS3's Math library. So if you're doing something intense(r) like severe image distortion then PB is definitely worth a shot, but if it's just conditionals then you're probably better off in pure AS3 land.
Conclusion
Besides doing complex calculations, another place where PB would really help though is processing large (like 600 or 10000 pixels wide or bigger) images. It loops through each pixel faster than AS3 does. But remember that in AS3 you can do loops, so easily skipping pixels (using the continue
statement) can result in some significant performance gains if you're not processing all the pixels in the input data (eg skipping transparent pixels). There's a lot of pro/cons so experimenting is, as always, the way to go (if you can afford it).
So there it is, all the info I wish was out there when I started learning PB (I can't stop thinking of Peanut Butter!). Hope some of it was helpful and of course I could be wrong about something (except about declaring input once, I've heard otherwise but in practice it worked fine) so please don't hesitate to correct me if so. Good luck and have fun Image may be NSFW.
Clik here to view.