Introduction to AGAL: Part 2
To continue the series on Flash 11 Stage3D
shader programming, this week we’ll take a look at the data types your shader has available and finally dive into some actual AGAL assembly syntax.
To begin, let’s look at the different types of data a shader program—vertex and fragment—deals with:
Type | Number Available | Name | Origin | Accessible From | Purpose |
---|---|---|---|---|---|
Vertex Temporary | 8 | vt0 – vt7 | Vertex shader code | Vertex shader | Temporary variables for vertex shader computations |
Fragment Temporary | 8 | ft0 – ft7 | Fragment shader code | Fragment shader | Temporary variables for fragment shader computations |
Vertex Attribute | 8 | va0 – va7 | Context3D.setVertexBufferAt | Vertex shader | Vertex-specific data (e.g. texture coordinates, color) |
Vertex Constant | 128 | vc0 – vc1 | Context3D.setProgramConstants | Vertex shader | Constants across the whole Context3D.drawTriangles call for vertex shader use |
Fragment Constant | 28 | fc0 – fc27 | Context3D.setProgramConstants | Fragment shader | Constants across the whole Context3D.drawTriangles call for fragment shader use |
Fragment Sampler | 8 | fs0 – fs7 | Context3D.setTextureAt | Fragment shader | Textures for fragment shader use |
Varying | 8 | v0 – v7 | Vertex shader code | Vertex and Fragment shader | Passing data from vertex shader to fragment shader with automatic interpolation |
Output Position | 1 | op | Vertex shader code | Vertex shader | Output of the vertex shader |
Output Color | 1 | oc | Fragment shader code | Fragment shader | Output of the fragment shader |
Together, these data types are your tools to, ultimately, draw the pixels (other than the background color) that make up your 3D scene. It’s therefore very important to master each of these data types so that you can put them to full use. Before we jump into the actual AGAL assembly syntax, let’s discuss what these data types are in the first place. With the exception of fragment samplers, all of them actually a collection of four floating point numbers. This is quite different to most CPU programming in that the “basic unit” is not a byte but instead four floats. You are, however, allowed to specifically read from and write to the data’s components. You do that through a familiar dot syntax:
vt0.x vt0.y vt0.z vt0.w
These are simple ways of accessing the components, but you can also access two, three, or four of the components at a time:
vt0.xy vt0.xyz vt0.xyzw
You can even “swizzle” around the components so the result is out of order:
vt0.yx vt0.zyx vt0.wzyx vt0.xzwy
And you can even duplicate values:
vt0.xx vt0.xxx vt0.xxxx vt0.xyyx vt0.zzzy
This is all very flexible and allows you to really make the most out of the data your shaders deal with. Now, let’s take a look at some basic AGAL assembly syntax:
mov ft0, va0
Each line of the shader program contains one instruction, which performs exactly the one operation listed. To show the anatomy of this instruction, I’ll annotate it:
Instruction | Argument 1 | Argument 2 mov | ft0, | va0
This code simply copies the data stored in the first vertex attribute (va0) to the first vertex shader temporary (ft0). It is not actually “moved” in the same sense as when you move files around on a file system: the origin (va0) is unchanged after this instruction.
Most of the instructions take three arguments though:
add vt0, vt1, vt2
This code adds vt1 and vt2 together and puts the output in vt0. You should be seeing a pattern here: the result of the operation is (except with the kil instruction) the first argument an the operands are the remaining arguments. Let’s see how this looks when we mix in some components and swizzling:
add vt0.x, vt1.x, vt2.x add vt0.xy, vt1.zw, vt2.yy add vt0.xyz, vt1.yzw, vt2.zzz add vt0, vt1.zyxw, vt2.yzwy
You’ll want to make sure you’re matching the same number of components across the board and that each of the components of the operands have been written to (by you or by Flash) before you read from them. Now, let’s look at the funkiest instructions starting with kil:
kil ft0.x
The kil instruction is only available in fragment shaders and it only takes one argument, which must be a simple component. If the component is less than zero, the fragment shader is immediately halted and no fragment/pixel is generated/drawn. Another set of weird instructions represent the only conditional logic we have in AGAL. Each sets a value only on a condition: sge (“set if greater or equal”) and slt (“set if less than”). If the check value doesn’t match the condition then zero is written instead. Here’s how they look: (with annotation)
Instruction | Destination | Check Value | Value to set sge | ft0, | ft1.x, | ft2 slt | ft0, | ft1.x | ft2
In order to use these, you’ll need to get tricky. For example, you can exploit some simple mathematical facts since the result if the check doesn’t pass is zero. Consider multiplying or adding the result. If you multiply by zero you’ll get zero and if you add zero you’ll get the same value. Obviously the exact logic will be up to your own fragment shader, but if you work hard you can come up with some simple conditional logic by exploiting these instructions.
Now we come to a crucial instruction that just so happens to be the weirdest one out there:
tex ft0, v0, fs0 <2d, linear, nomip>
The all-important tex instruction samples a texture (bound to a fragment sampler) at a given texture coordinate and returns an RGBA color value. The wacky part is that you must specify how you want to sample the texture by using flags in angle brackets after the arguments. These are the allowed values:
-
Texture Type
2d
(normal 2D textures)cube
(cube maps)
-
Filtering Method
nearest
(nearest-neighbor)- linear (bi-linear or tri-linear)
-
MIP-Mapping
mipnone
/nomip
(no MIP-mapping)mipnearest
(nearest MIP map only)miplinear
(blend between two nearest MIP maps)
-
Texture Repeat
clamp
(clamp texture coordinates to [0:1])wrap
(wrap texture coordinates by ignoring the integer part)repeat
(texture coordinates outside [0:1] are the border color)
Stay tuned for next time when we’ll actually get a shader program up and running! Until then, if you’ve spotted a bug or have a suggestion, leave a comment!
#1 by Smily on December 19th, 2011 ·
Hi
Is “3d” as a texture type an undocumented feature? Because it’s not documented in the AGAL reference ( http://help.adobe.com/en_US/as3/dev/WSd6a006f2eb1dc31e-310b95831324724ec56-8000.html ) or the Stage3D documentation.
As a side note, if you have the time, can you take a look at the comment I posted in Part 1 at http://jacksondunstan.com/articles/1661#comment-17692 ?
I’ve since used the PIX debugger available at http://blogs.msdn.com/b/manders/archive/2006/12/15/a-painless-introduction-to-pix-for-windows.aspx to determine that a single sin or cos instruction that uses all four of the components translates to 4 sincos hardware instructions with surrounding temporaries (also true for pow and possibly others), which might be good source material for a more in-depth article in how AGAL is actually translated to hardware code and the implications of this.
I still think my original statement stands in that it looks like a bug / unfriendly API to me, because you get a generic error back, where you should be getting a more specific one regarding limits.
Thanks!
#2 by jackson on December 19th, 2011 ·
Interestingly, “3d” is still listed in the
AGALMiniAssembler
(and several other sites) but it’s been removed from Adobe’s intro article. So I’ve removed it from my article. Thanks for pointing this out.As for your
sin
/cos
question, I’ve just tried it out on Windows for the first time and realized that it is throwing this exception:What you’ve found is the likely cause: more native instructions per single AGAL instruction. I agree that it would be helpful to have more information about the underlying error so that it’s not as necessary to to into a tool like PIX and debug the internals. Thanks for the tip; I’ll adjust that article so it no longer crashes.
#3 by AlexG on December 19th, 2011 ·
Some working examples of constructing different shaders would be cool. Obviousle with swf or jpg previews. Thanks
#4 by jackson on December 19th, 2011 ·
The next article in the series will have real, working shaders. Stay tuned!
#5 by Sam on January 22nd, 2012 ·
A few notes:
– You say “You’ll want to make sure you’re matching the same number of components across the board”
This is true, except the destination. for example:
– There is a limitation on the source arguments: If there are two source arguments, only one of them can be a constant register.
For example this won’t work:
workaround:
– mul gives wrong results under some (still unknown) conditions. I encountered such cases I while ago but I didn’t investigated it (yet).
I’ve seen it works predictably at least in the following component combinations (example):
#6 by Sam on January 22nd, 2012 ·
I forgot to mention that using GPU to process constants every vertex or fragment of course would be far less efficient vs using as3 to process them every frame. Still, a limitation though …for the coder’s comfort! :)
#7 by Balthazar on February 3rd, 2013 ·
If you use .x/.y/.z notation to mask the results of an instruction into just one component, the other components will still be overwritten. At least it does on my machine, a macbook from mid 2010, with an NVIDIA GeForce GT 330M 256 MB graphics card. So, the instructions
The second instruction will overwrite all components in ft0, not just the red color component, even though you set the other components in the previous instruction. I have tried several instructions this way, with the same result. It also happens when you mask the other operands the same way. I haven’t tried this in vertex shaders, but it’s true for my machine with pixel shaders. I thought that should be mentioned, because the tutorials and documentation on Adobe’s website makes it look like you can use .r/.g/.b etc to leave the other components in the register untouched, which is either not intended at all from Adobe’s side, or unreliable.
#8 by Balthazar on February 4th, 2013 ·
Not sure, but perhaps this page provides a clue to solving the problem: http://barliesque.com/easy_agal/docs/
Quote – “AGALMiniAssembler.as in this package has been modified to correct the mishandling of .rgba component selectors, as in the version currently provided by Adobe.”
I haven’t tried it, for now I have sticked to code which avoids having to select components individually.