Dealing With AGAL Registers

A thorough tutorial on the use of registers for AGAL shaders. How to go about setting their values and accessing them in the shader.

This tutorial is weighted towards theory rather than the practical writing of code. You might find it beneficial to refer to my simple rendering example to see how this all gets applied.

Registers:

Each register consists of four components, which are floating point values. A particular component is accessed by the notation [registerName].[componentLetter], where componentLetter is x, y, z, or w. For example va0.x is the first component of the first vertex attribute register. For a listing of the available registers, please see my AGAL reference page.

Vertex Buffer:

The vertex buffer is how you pass per-vertex data into your vertex shader, where it will appear in the va (vertex attribute) registers. Usually this will include the position of that vertex in the world and some data used to colour it – either by passing in a colour for the vertex, uv coordinates for texture mapping, or something more complex. Keep in mind that the vertex buffer is just a series of floating point numbers which you can use for anything you like in your shader.

To deal with the vertex buffer you first create a VertexBuffer3D object by using the function context3D.createVertexBuffer (you cannot just instantiate it yourself with new VertexBuffer3D().)  This object will store the values until you’re ready to upload them into registers and have a shader use them.

Imagine the VertexBuffer3D object as a table. When you create it with context3D.createVertexBuffer you specify the number of vertices (the number of rows in the table) and the number of data32 per vertex (the number of columns in the table). In this illustration I’ve labelled each column with what I plan to use the values for, but remember they’re just a series of meaningless numbers as far as Flash is concerned.

You upload data into the VertexBuffer3D table using vertex3D.uploadFromVector, where you supply:

  • data - A Vector.<Number> of the values you’re going to upload. Must be a whole number of rows’ worth of data.
  • startVertex - The row in the vertex3D table at which to start writing.
  • numVertices - The number of rows of data to copy.

In this illustration see how the startVertex determines where in the VertexBuffer3D object our Vector of numbers will be placed. If numVertices was set to 1 then only one row of the table would have been filled.

You set the va registers using context3D.setVertexBufferAt. When doing so you specify:

  • index - Which va register you want to upload into, e.g. 0 for va0.
  • buffer - The vertexBuffer3D object to use.
  • bufferOffset - Which column in the vertexBuffer3D table to start from when reading in data
  • format - How many columns of the table to read in, from FLOAT_1 for one to FLOAT_4 for four. Alternatively read in a single column but interpret it as four individual bytes instead of a single float.

As seen here, bufferOffset (0 in this case) and format (FLOAT_2) determine which columns of the VertexBuffer3D object to copy. Index (0 for the va0 register) determine which register those values should be placed in.

In this second example bufferOffset is 2, format is FLOAT_4, and index is 1. This results in copying the four columns shown from the VertexBuffer3D and using them to fill the va1 register.

Keep in mind that when performing the setVertexBufferAt operation you are setting the registers for as many vertices as you have rows in the vertexBuffer3D object you’re using.

Note! Rendering will fail if you have set values for registers that are then not used by your shader. Similarly it will fail if your shader tries to access a va register that hasn’t had data uploaded to it. Make sure you have context3D.enableErrorChecking set to true so you receive these error messages.

Vertex and Fragment Constants:

The other way to get numbers into a shader is through the vertex constant and fragment constant registers. These are accessed in the shader at vc0 to vc127 and fc0 to fc27. Keep in mind that only the vertex shader can access vc registers and only the fragment shader can access fc registers. Unlike the va and v (see below for details on these Varying registers) registers, the contents of vc and fc are the same for all vertices and all fragments.

To set values in the constant registers you use the context.setProgramConstantsFromVector or context.setProgramConstantsFromMatrix functions.

When using .setProgramConstantsFromVector you supply:

  • programType – Must be either Context3DProgramType.FRAGMENT or Context3DProgramType.VERTEX, use this to indicate if you want to set a fc or a vc register.
  • firstRegister - Which register to set, or if you supply enough data for multiple registers which is the first you’d like to set. For example use 0 to set fc0 or vc0.
  • data - The Vector.<Number> of values you want to upload. This must contain a multiple of 4 values so as to fill the x, y, z and w components of however many registers you’re writing to. Even if you only actually need to upload a single number, you are not allowed to have only some components of a constant register set.
  • numRegisters - Optional. If your data has enough for 3 registers but you only want to upload into 1 then you can do that here.

When using .setProgramConstantsFromMatrix you supply:

  • programType - Must be either Context3DProgramType.FRAGMENT or Context3DProgramType.VERTEX, use this to indicate if you want to set  fc or  vc registers.
  • firstRegister - The first register to set with the data from your matrix.
  • matrix - The Matrix3D object you want to upload into constant registers. Matrix3Ds are always 4×4 matrices so four registers are needed to store their contents.
    If you specify the firstRegister as 2 and programType as Context3DProgramType.FRAGMENT, then fc2 fc3 fc4 and fc5 will be written to.
  • transposedMatrix - If set to false the matrix is written into the registers with the first register being the first row of the matrix. If set to true the matrix is written in transposed order, meaning the first register stores the first column of the matrix.

Unlike vertex attributes, setting a constant and then not using it seems to pass without error. Obviously a shader trying to access a constant that you haven’t set will fail.

Varying Registers

Varying registers (v0 to v7) allow per-vertex data to be indirectly used in your fragment shader. To access a varying register within a fragment shader your vertex shader must be feeding it values by including an operation that writes data to one of the v registers.

Each triangle that is drawn will of course have three vertices, each of which provide a value to a particular v register. Every fragment contained within that triangle will have its corresponding v register contain a value that has been interpolated between that of the three vertices in that triangle.

In this illustration we look at a single register component as it is interpolated between the three vertices of a triangle. The image is shaded to indicate what value v0.x has at each point on the triangle. The value increases at positions closer to the vertex which has it set to 1.

Using Registers Within a Shader

Now that we have assigned values to the various registers we can deal with actually using them in a shader. To do anything in a shader you must use operations, which you can find outlined on my quick reference page.

Here we will deal with how to use registers in general rather than how to use specific operations. This mainly comes down to understanding how the components which make up each register work.

Recall that every register consists of four values, which are accessed through the a dot-suffix notation. For instance fc3.x is the first component of the register “fragment constant 3″. The components are referred to as .x .y .z and .w, you can also refer to them as .r .g .b and .a. Keep in mind that fc3.x is exactly the same thing as fc3.r; for this reason I prefer to stick with using the xyzw notation throughout my shaders even when I’m dealing with colours.

AGAL operations almost all act in a “component-wise” fashion, meaning we can supply them with more than a single component and they will apply the operation to all components at once. Some operations require multiple components be given, namely those meant to operate on vectors. To specify multiple components from a register simply use multiple component letters after the register name, for instance fc3.xyz gives the first three components of the “fragment constant 3″ register.

Note! If you specify just a register name then AGAL assumes you want to use all four components. So fc3.xyzw is exactly the same as fc3.

How Component-wise Operations Work

add vt0 va0 va1
or
add vt0.xyzw va0.xyzw va1.xyzw

before:
 vt0: ? ? ? ?
va0: 2 7 4 2
va1: 1 4 9 0

after:
 vt0: 3 11 13 2
va0 and va1 unchanged

Each component of va0 is put through the addition operation with the matching component of va1 and the result is put in the matching component of vt0.

In the above example:
vt0.x = va0.x + va0.x
vt0.y = va0.y + va0.y
vt0.z = va0.z + va0.z
vt0.w = va0.w + va0.w

Warning! I’ve encountered some situations where component-wise multiplication doesn’t work as I would expect. Specifically the operation:
mul vt0.xy va0.xy vc0.xy

Gives a result which is different from that of performing these two operations:
mul vt0.x va0.x vc0.x
mul vt0.y va0.y vc0.y

I’ve yet to work out exactly what’s going on. Expect this section to be edited.

Limited Component Operations

add vt0.xy va0.xy va1.xy

before:
 vt0: ? ? ? ?
va0: 2 7 4 2
va1: 1 4 9 0

after:
 vt0: 3 11 ? ?
va0 and va1 unchanged

Does as you’d expect by adding the values on a component-by component basis. As the .zw components of vt0 are not included in the target they are left untouched by the operation.

Mismatched Component Counts

add vt0 va0.xy va1.xy

Here we’re assigning the result of a two-component operation to a four-component target (remember that vt0 is the same as saying vt0.xyzw). In this case I believe it will put the result of the addition in vt0.xy, but your code isn’t clear so it is much better to specify where you want the result to go.

Swizzled Components

mov vt0.xy va0.zw

before:
vt0: ? ? ? ?
va1: 1 2 3 4

after:
 vt0: 3 4 ? ?
va1: 1 2 3 4

The fact that the source and destination components are not the same doesn’t matter. So far as AGAL is concerned it’s just a group of numbers. You can also use the same components but in a different order such as:

mov vt0.xyz va0.zyx

before:
vt0: ? ? ? ?
va1: 1 2 3 4

after:
 vt0: 3 2 1 ?
va1: 1 2 3 4

Finally, you can even use the same component multiple times.

mov vt0.xyz va0.xxx

before:
vt0: ? ? ? ?
va1: 1 2 3 4

after:
 vt0: 1 1 1 ?
va1: 1 2 3 4

Next Time

That’s quite enough theory. Next tutorial will definitely be something more direct and practical. Probably stepping through the process of creating a shader, from initial idea to final code.

7 comments on “Dealing With AGAL Registers

  1. Wow!
    Super long post!
    You explained this perfectly!
    Thank you a lot for listening, and helping!
    If anyone needs help with AGAL I’ll link them to your blog for sure.

  2. genechen on said:

    wonderful!!!! Thx very much!
    Look forward to the next stepping through the process of creating a shader, from initial idea to final code.

  3. Pingback: Ave AGAL! morituri te salutant « HIDIHO!

  4. Matt Lockyer on said:

    Hello, I also found a scenario where division was not working component wise. The results were fine when I expanded it per component. Any clues on this?

    • Vincent Pride on said:

      @ Sam Driver
      @ Matt Lockyer

      Multiplication / division / etc doesnt work as expected in this scenario (mul vt0.xy va0.xy vc0.xy) because it seems like the compiler substitutes it with (mul vt0.xyyy va0.xyyy vc0.xyyy) and therefore y is multiplied multiple times per operaion. In contrast, when multiplying component-wise, that substitution doesnt take place.

  5. sigman.pl on said:

    Great tutorial. I was wondering if there is any way of looking up the content of registers at some random stage. It would be so useful when debugging.

Leave a Reply

Your email address will not be published.

21,041 Spam Comments Blocked so far by Spam Free Wordpress

HTML tags are not allowed.