Flipper

Update 20090502: I received a mail from “fungos” with a few corrections. I don’t have time to check what (s)he writes, but it sounds right, so I include the feedback as blockquotes below.

Overview

This describes how some parts of Flipper (the graphics chip of the Gamecube). I’ll probably add more descriptions if you want. I don’t describe how indirect tev works, because I don’t know that. If you know, tell me :-) If some of the things discussed in this document are wrong, let me know as well.

This document would not have been possible without libogc, especially gx.h and gx.c.

Texgen

You don’t always need explicit texture coordinates if you want to use a texture. For example, you could compute texture coordinates from the vertex positions, or you could use one set of texture coordinates for different texture units. Texgen (‘texture coord generation’) is a means to accomplish that. This section discusses the texgen unit.

You call

void GX_SetNumTexGens(u32 nr);

to specify for how many texture units you want to enable texgen for.

To control how texture coordinates are computed, you call

void GX_SetTexCoordGen(u16 texcoord,u32 tgen_typ,u32 tgen_src,u32 mtxsrc);

texcoord can be one of

GX_TEXCOORD0
GX_TEXCOORD1
GX_TEXCOORD2
GX_TEXCOORD3
GX_TEXCOORD4
GX_TEXCOORD5
GX_TEXCOORD6
GX_TEXCOORD7

and specifies texgen for which texture coordinate you’re going to configure.

tgen_typ is used to specify how texture coordinates are generated from the inputs. I don’t really know how all these functions work, but I provide a few guesses. I have no idea at all how the BUMP stuff works.

GX_TG_MTX3x4
    output = 3x4matrix * input (u, v, w generated)

GX_TG_MTX2x4
    output = 2x4matrix * input (u, v generated)

GX_TG_BUMP0
GX_TG_BUMP1
GX_TG_BUMP2
GX_TG_BUMP3
GX_TG_BUMP4
GX_TG_BUMP5
GX_TG_BUMP6
GX_TG_BUMP7
GX_TG_SRTG
    I believe this is 'spherical reflection coord generation' like
    implemented in OpenGL

fungos: The GX_TG_BUMPx are to use with embossed style bump texture coordinates and for this type, the valid tgen_src parameters are only GX_TG_TEXCOORD0-6 (*note) and mtxsrc is not used.

The GX_TG_SRTG is way different than what you thought :) it uses components from color channel output, that is, the S texture coordinate is generated by using R red component and the T texture coordinate is generated by using G green component (both 8-bits), the parameter tgen_src can be only GX_TG_COLOR0 or GX_TG_COLOR1 and
mtxsrc is not used too. I think this is used to generate cel-shading/toon effect from a 1D texture.

tgen_src is used to tell the texgen unit what is the input for the generation function. I don’t know what the TG_TEXn constants are for, the rest is self-explanatory.

GX_TG_POS
GX_TG_NRM
GX_TG_BINRM
GX_TG_TANGENT
GX_TG_TEX0
GX_TG_TEX1
GX_TG_TEX2
GX_TG_TEX3
GX_TG_TEX4
GX_TG_TEX5
GX_TG_TEX6
GX_TG_TEX7
GX_TG_TEXCOORD0
GX_TG_TEXCOORD1
GX_TG_TEXCOORD2
GX_TG_TEXCOORD3
GX_TG_TEXCOORD4
GX_TG_TEXCOORD5
GX_TG_TEXCOORD6
GX_TG_COLOR0
GX_TG_COLOR1

fungos: I think that there is a confusion between GX_TG_TEXCOORDx and GX_TG_TEXx. Maybe GX_TG_TEX0-7 are the real texture coordinates input (note, this is from 0 to 7, that are 8 total textures) and GX_TG_TEXCOORD0-6 may be only used with that GX_TG_BUMPx. I don’t understand that yet and it is very hard to follow it.

mtxsrc is one of the matrix identifiers and may be one of

GX_PNMTX0
GX_PNMTX1
GX_PNMTX2
GX_PNMTX3
GX_PNMTX4
GX_PNMTX5
GX_PNMTX6
GX_PNMTX7
GX_PNMTX8
GX_PNMTX9
GX_TEXMTX0
GX_TEXMTX1
GX_TEXMTX2
GX_TEXMTX3
GX_TEXMTX4
GX_TEXMTX5
GX_TEXMTX6
GX_TEXMTX7
GX_TEXMTX8
GX_TEXMTX9
GX_IDENTITY
GX_DTTMTX0
GX_DTTMTX1
GX_DTTMTX2
GX_DTTMTX3
GX_DTTMTX4
GX_DTTMTX5
GX_DTTMTX6
GX_DTTMTX7
GX_DTTMTX8
GX_DTTMTX9
GX_DTTMTX10
GX_DTTMTX11
GX_DTTMTX12
GX_DTTMTX13
GX_DTTMTX14
GX_DTTMTX15
GX_DTTMTX16
GX_DTTMTX17
GX_DTTMTX18
GX_DTTMTX19
GX_DTTIDENTITY

(perhaps not all of them are valid)

There is another function to control texgen that provides a few more options:

void GX_SetTexCoordGen2(u16 texcoord,u32 tgen_typ,u32 tgen_src,u32 mtxsrc,
                        u32 normalize,u32 postmtx);

This is like GX_SetTexCoordGen with a few additional parameters. I don’t know what this does (I guess it does the same as GX_SetTexCoordGen, but optionally normalizes the generated texture coordinate to unit length and multiplies it with a post processing matrix).

TEV

The Gamecube doesn’t really have a programmable graphics pipeline, only a set of 16 TEV stages that can combine colors in a fixed way. A single TEV stage has 4 inputs and 1 output. The output is calculated by selecting one of several fixed functions on the inputs. In only discuss the ‘color’ functions, the alpha functions are very similar. You should be able to figure them out yourself by looking at gx.h.

In the remainder, the 4 inputs of a tev a called a, b, c and d, the output is called out.

GX_SetNumTevStages

You configure how many of the 16 tev stages are used by calling

void GX_SetNumTevStages(u8 num);

For color computation, the first num tev stages are executed. The color which ends up in the ‘previous color register’ at the end of these num stages is used as the color for the current pixel.

GX_SetTevColorOp

The central function to configure a tev unit is

void GX_SetTevColorOp(u8 tevstage,u8 tevop,u8 tevbias,u8 tevscale,u8 clamp,u8 tevregid);

tevstage is one of GX_TEVSTAGE0 to GX_TEVSTAGE15 and identifies the tev unit you want to configure.

tevop is one of the following functions:

GX_TEV_ADD
    out = a*(1 - c) + b*c + d

GX_TEV_SUB
    out = -a*(1 - c) - b*c + d  (not 100% sure with this, please confirm)

GX_TEV_COMP_R8_GT
    if(a.r > b.r) out = c; else out = d;

GX_TEV_COMP_R8_EQ
    if(a.r == b.r) out = c; else out = d;

GX_TEV_COMP_GR16_GT
GX_TEV_COMP_GR16_EQ
    same as r8, but use gr as 16 bit value for comparing

GX_TEV_COMP_BGR24_GT
GX_TEV_COMP_BGR24_EQ
    same as r8, but use bgr as 24 bit value for comparing

GX_TEV_COMP_RGB8_GT
GX_TEV_COMP_RGB8_EQ
    Not sure with this, but I think this compares rgb independently. Can
    you confirm this? (this is not done correctly in bmdview's source)

and controls which operation the tev stage will perform (discussed in more detail below).

tevbias is one of

GX_TB_ZERO
GX_TB_ADDHALF
GX_TB_SUBHALF

tevscale is one of

GX_CS_SCALE_1
GX_CS_SCALE_2
GX_CS_SCALE_4
GX_CS_DIVIDE_2

clamp is GX_TRUE or GX_FALSE.

After the tev operation has executed, the output is modified by bias, scale and clamp as follows (in this order):

out = out + bias; //for each component; bias is 0, 0.5 or -0.5
out = out * scale; //scale is 1, 2, 4 or 0.5
if(clamp)
  clamp out to [0.0, 1.0] in each component

If tev op is not GX_TEV_ADD or GX_TEV_SUB, bias, scale and clamp shall be ignored.

tevregid is one of

GX_TEVPREV
    regPrev.rgb = out.rgb

GX_TEVREG0
    reg0.rgb = out.rgb

GX_TEVREG1
    reg1.rgb = out.rgb

GX_TEVREG2
    reg2.rgb = out.rgb

and specifies in which register out is written.

fungos: GX_TEV_ADD you got right and GX_TEV_SUB a little wrong, we can generalize it this way:

output register = (d (tevop) ((1.0-c)*a + c*b) + tevbias) * tevscale

where (tevop) is ‘+’ (GX_TEV_ADD) or ‘-’ (GX_TEV_SUB).

there are two cases, when using GX_SetTevColorOp you’re right (GX_TEV_COMP_RGB8_[GT|EQ]), it will compare each component independently, but when using GX_SetTevAlphaOp it will compare the alpha component only. But the rules is slightly different than what you said (lets use GX_TEV_COMP_R8_GT as a sample):

if (a.r > b.r) out = c; else out = d;

if think is this way:

d + ((a.r > b.r) ? c : 0)

So, the entire rule table is:

GX_TEV_COMP_R8_GT 
d + ((a.r > b.r) ? c : 0)

GX_TEV_COMP_R8_EQ 
d + ((a.r == b.r) ? c : 0)

GX_TEV_COMP_GR16_GT   
d + ((a.gr > b.gr) ? c : 0)

GX_TEV_COMP_GR16_EQ   
d + ((a.gr == b.gr) ? c : 0)

GX_TEV_COMP_BGR24_GT  
d + ((a.bgr > b.bgr]) ? c : 0)

GX_TEV_COMP_BGR24_EQ  
d + ((a.bgr == b.bgr) ? c : 0)

GX_TEV_COMP_RGB8_GT   
r: d.r + ((a.r > b.r) ? c.r : 0)
g: d.g + ((a.g > b.g) ? c.g : 0)
b: d.b + ((a.b > b.b) ? c.b : 0)

GX_TEV_COMP_RGB8_EQ
r: d.r + ((a.r == b.b) ? c.r : 0)
g: d.g + ((a.g == b.b) ? c.g : 0)
b: d.b + ((a.b == b.b) ? c.b : 0)

GX_TEV_COMP_A8_GT
d + ((a.a > b.a) ? c : 0)

GX_TEV_COMP_A8_EQ
d + ((a.a > b.a) ? c : 0)

GX_SetTevColorIn

To control which are the 4 inputs of a tev stage, you call

void GX_SetTevColorIn(u8 tevstage,u8 a,u8 b,u8 c,u8 d);

tevstage is again a tev stage identifier.

a, b, c and d specify which is loaded into a, b, c and d in the equations above:

GX_CC_CPREV
    a.rgb = regPrev.rgb

GX_CC_APREV
    a.rgb = regPrev.aaa

GX_CC_C0
    a.rgb = reg0.rgb

GX_CC_A0
    a.rgb = reg0.aaa

GX_CC_C1
    a.rgb = reg1.rgb

GX_CC_A1
    a.rgb = reg1.aaa

GX_CC_C2
    a.rgb = reg2.rgb

GX_CC_A2
    a.rgb = reg2.aaa

GX_CC_TEXC
    a.rgb = textureBoundToTev.rgb (see GX_SetTevOrder below)

GX_CC_TEXA
    a.rgb = textureBoundToTev.rgb (see GX_SetTevOrder below)

GX_CC_RASC
    a.rgb = rasColor.rgb (see below)

GX_CC_RASA
    a.rgb = rasColor.aaa (see below)

GX_CC_ONE
    a.rgb = (1.0, 1.0, 1.0)

GX_CC_HALF
    a.rgb = (0.5, 0.5, 0.5)

GX_CC_KONST
    a.rgb = konst (see GX_SetTevKColorSel below)

GX_CC_ZERO
    a.rgb = (0.0, 0.0, 0.0)

In the list above, a is of course only used as an example — could be b, c or d as well :-P

rasColor is the color which is passed from the ‘vertex shader’, usually this is simply the vertex color. More information later if you need it (TODO: see GX_SetTevOrder?).

GX_SetTevOrder

If you use GX_CC_TEXC, GX_CC_TEXA, GX_CC_RASC or GX_CC_RASA in GX_SetTevColorIn, you use

void GX_SetTevOrder(u8 tevstage,u8 texcoord,u32 texmap,u8 color);

to specify which texture is used for the texture input and which color for the ras color.

tevstage is the usual tev stage identifier.

texcoord is one of

GX_TEXCOORD0
GX_TEXCOORD1
GX_TEXCOORD2
GX_TEXCOORD3
GX_TEXCOORD4
GX_TEXCOORD5
GX_TEXCOORD6
GX_TEXCOORD7
GX_TEXCOORDNULL

and tells the tev unit which texture coordinate channel is used to access the texture. Note: The texture coordinate after texgen is used (texgen is not described in this document yet — for now pretend that this makes no difference). Use GX_TEXCOORDNULL if you don’t need a texture input for the tev unit.

texmap is

GX_TEXMAP0
GX_TEXMAP1
GX_TEXMAP2
GX_TEXMAP3
GX_TEXMAP4
GX_TEXMAP5
GX_TEXMAP6
GX_TEXMAP7
GX_TEXMAP_NULL

and specifies which texture image should be used as input for tevstage. Pass GX_TEXMAP_NULL if the texture input is not used in the current tev.

I’m pretty sure that color is one of

GX_COLOR0
GX_COLOR1
GX_ALPHA0
GX_ALPHA1
GX_COLOR0A0
GX_COLOR1A1
GX_COLORZERO
GX_BUMP 
GX_BUMPN
GX_COLORNULL

but I don’t really know what all these constants mean. The do specify what is used as ras color in the tev. I guess COLOR0 and COLOR1 are simply the primary and secondary vertex color, but I don’t know how the bump stuff works.

GX_SetTevKColorSel

If GX_CC_KONST is used as one of the 4 inputs to a tev stage in GX_SetTevColorIn, you specify which constant is used with

void GX_SetTevKColorSel(u8 tevstage,u8 sel);

tevstage is the usual tev stage identifier.

sel is one of

GX_TEV_KCSEL_1
GX_TEV_KCSEL_7_8
GX_TEV_KCSEL_3_4
GX_TEV_KCSEL_5_8
GX_TEV_KCSEL_1_2
GX_TEV_KCSEL_3_8
GX_TEV_KCSEL_1_4
GX_TEV_KCSEL_1_8
GX_TEV_KCSEL_K0
GX_TEV_KCSEL_K1
GX_TEV_KCSEL_K2
GX_TEV_KCSEL_K3
GX_TEV_KCSEL_K0_R
GX_TEV_KCSEL_K1_R
GX_TEV_KCSEL_K2_R
GX_TEV_KCSEL_K3_R
GX_TEV_KCSEL_K0_G
GX_TEV_KCSEL_K1_G
GX_TEV_KCSEL_K2_G
GX_TEV_KCSEL_K3_G
GX_TEV_KCSEL_K0_B
GX_TEV_KCSEL_K1_B
GX_TEV_KCSEL_K2_B
GX_TEV_KCSEL_K3_B
GX_TEV_KCSEL_K0_A
GX_TEV_KCSEL_K1_A
GX_TEV_KCSEL_K2_A
GX_TEV_KCSEL_K3_A

The first few functions are simply the constants 1.0, 7.0/8.0, …, 1.0/8.0. The other constants select which of the 4 constant color registers are used. You load values in the constant color registers with XXX.

GX_SetTevColor

To load a color into a tev register, you call

void GX_SetTevColor(u8 tev_regid,GXColor color);

tev_regid is a tev register identifier — these registers are shared by all tev stages. Valid values are

TODO (I guess values from 0 to 3 for the 4 constant color registers)

color is the color to load into tev_regid. Who would have thought?

Swap Mode Tables

No bmd/bdl file I know of uses the stuff described in this section. It’s not that important to emulate it I guess ;-)

You can swizzle the ras color and the tex colors before they are given to the tev stages. For this, there are four different swap mode tables,

GX_TEV_SWAP0
GX_TEV_SWAP1
GX_TEV_SWAP2
GX_TEV_SWAP3

which are configures via GX_SetTevSwapModeTable (below). To use these tables, you call

void GX_SetTevSwapMode(u8 tevstage,u8 ras_sel,u8 tex_sel);

tevstage is the usual tev stage id.

ras_sel is the swap mode table identifier to use for the ras color.

tex_sel is the swap mode table identifier to use for the tex color.

To configure what a swap mode table does, you call

void GX_SetTevSwapModeTable(u8 swapid,u8 r,u8 g,u8 b,u8 a);

swapid is a swap mode table identifier.

r, g, b and a specify which channel of the original color should be passed to the tev stage as r, g, b or a. Valid values are

GX_CH_RED
GX_CH_GREEN
GX_CH_BLUE
GX_CH_ALPHA

Examples

TODO. For now, void GX_SetTevOp(u8 tevstage,u8 mode) from gx.c is a good example.

By thakis@users.sourceforge.net.