Difference between revisions of "GPU/Shader Instruction Set"
(fixed MAD SRC order) |
(i0-i7 used for LOOP) |
||
Line 93: | Line 93: | ||
|} | |} | ||
− | Format 3 : (used for conditional flow control instructions) | + | Format 3 : (used for uniform-based conditional flow control instructions) |
{| class="wikitable" border="1" | {| class="wikitable" border="1" | ||
|- | |- | ||
Line 110: | Line 110: | ||
| 0x16 | | 0x16 | ||
| 0x4 | | 0x4 | ||
− | | Uniform | + | | Uniform ID (BOOL/INT) |
|- | |- | ||
| 0x1A | | 0x1A | ||
Line 245: | Line 245: | ||
| 0x23 | | 0x23 | ||
| 1 | | 1 | ||
− | | BREAK | + | | BREAK |
− | | Conditionally breaks out of LOOP block | + | | Conditionally breaks out of LOOP block. Conditions for this are assumed to work the same was as for IF. |
|- | |- | ||
| 0x24 | | 0x24 | ||
Line 270: | Line 270: | ||
| 0x29 | | 0x29 | ||
| 3 | | 3 | ||
− | | | + | | FORLOOP |
− | | Loops over the code between itself and DST | + | | Loops over the code between itself and DST. Increments lcnt after each loop. Stops looping once lcnt reaches the value contained by the integer uniform specified by INT. (i0-i7) |
|- | |- | ||
| 0x2A | | 0x2A | ||
Line 395: | Line 395: | ||
== Registers == | == Registers == | ||
− | + | Most registers (all the ones within the 0x00-0x7F range) are float[4] vectors. There are also boolean registers (b0-b7) and integer registers (i0-i7). How the latter ones are set is as of yet unknown. | |
Attribute (input, RO) registers are located within the 0x0-0xF range. What data they are fed is specified by the CPU. | Attribute (input, RO) registers are located within the 0x0-0xF range. What data they are fed is specified by the CPU. |
Revision as of 23:57, 22 November 2014
Overview
A compiled shader binary is comprised of two parts : the main instruction sequence and the operand descriptor table. These are both sent to the GPU around the same time but using separate GPU Commands. Instructions (such as format 1 instruction) may reference operand descriptors. When such is the case, the operand descriptor ID is the offset, in words, of the descriptor within the table. Both instructions and descriptors are coded in little endian. Basic implementations of the following specification can be found at [1] and [2] Please note that this page is being written as the instruction set is reverse engineered; as such it may very well contain mistakes.
Instruction formats
Format 1 : (used for register instructions)
Offset | Size (bits) | Description |
---|---|---|
0x0 | 0x7 | Operand descriptor ID (DESC) |
0x7 | 0x5 | Source 2 register (SRC2) |
0xC | 0x7 | Source 1 register (SRC1) |
0x13 | 0x2 | Address register index (IDX) |
0x15 | 0x5 | Destination register (DST) |
0x1A | 0x6 | Opcode |
Format 1i : (used for register instructions)
Offset | Size (bits) | Description |
---|---|---|
0x0 | 0x7 | Operand descriptor ID (DESC) |
0x7 | 0x7 | Source 1 register (SRC1) |
0xE | 0x5 | Source 2 register (SRC2) |
0x13 | 0x2 | Address register index (IDX) |
0x15 | 0x5 | Destination register (DST) |
0x1A | 0x6 | Opcode |
Format 2 : (used for flow control instructions)
Offset | Size (bits) | Description |
---|---|---|
0x0 | 0x8 | Number of instructions (NUM) |
0xA | 0xC | Destination offset (in words) (DST) |
0x1A | 0x6 | Opcode |
Format 3 : (used for uniform-based conditional flow control instructions)
Offset | Size (bits) | Description |
---|---|---|
0x0 | 0x8 | Number of instructions ? (NUM) |
0xA | 0xC | Destination offset (in words) (DST) |
0x16 | 0x4 | Uniform ID (BOOL/INT) |
0x1A | 0x6 | Opcode |
Format 4 : (used for SETEMIT)
Offset | Size (bits) | Description |
---|---|---|
0x16 | 0x2 | Primitive ID (PRIMID) |
0x18 | 0x2 | Vertex ID (VTXID) |
0x1A | 0x6 | Opcode |
Format 5 : (used for MAD)
Offset | Size (bits) | Description |
---|---|---|
0x0 | 0x5 | Operand descriptor ID (DESC) |
0x5 | 0x5 | Source 3 register (SRC3) |
0xA | 0x7 | Source 2 register (SRC2) |
0x11 | 0x7 | Source 1 register (SRC1) |
0x18 | 0x5 | Destination register (DST) |
0x1D | 0x3 | Opcode |
Instructions
Opcode | Format | Name | Description |
---|---|---|---|
0x00 | 1 | ADD | Adds two vectors component by component; DST[i] = SRC1[i]+SRC2[i] for all i (modulo destination component masking) |
0x01 | 1 | DP3 | Computes dot product on 3-component vectors; DST = SRC1.SRC2 |
0x02 | 1 | DP4 | Computes dot product on 4-component vectors; DST = SRC1.SRC2 |
0x08 | 1 | MUL | Multiplies two vectors component by component; DST[i] = SRC1[i].SRC2[i] for all i (modulo destination component masking) |
0x0C | 1 | MAX | Takes the max of two vectors, component by component; DST[i] = MAX(SRC1[i], SRC2[i]) for all i (modulo destination component masking) |
0x0D | 1 | MIN | Takes the min of two vectors, component by component; DST[i] = MIN(SRC1[i], SRC2[i]) for all i (modulo destination component masking) |
0x0E | 1 | RCP | Computes the reciprocal of the vector, component by component; DST[i] = 1/SRC1[i] for all i (modulo destination component masking) |
0x0F | 1 | RSQ | Computes the reciprocal of the square root of the vector, component by component; DST[i] = 1/sqrt(SRC1[i]) for all i (modulo destination component masking) |
0x12 | 1 | ARL | Address Register Load; sets (a0, a1, _, _) to SRC1 (cast to integer). |
0x13 | 1 | MOV | Moves value from one register to another; DST = SRC1. |
0x18 | 1i | DP4I? | Computes dot product on 4-component vectors; DST = SRC1.SRC2 ? |
0x21 | 1 | END2 | ? |
0x22 | 1 | END1 | ? |
0x23 | 1 | BREAK | Conditionally breaks out of LOOP block. Conditions for this are assumed to work the same was as for IF. |
0x24 | 2 | CALL | Jumps to DST and executes instructions until it reaches DST+NUM instructions |
0x26 | 3 | CALLC | Jumps to DST and executes instructions until it reaches DST+NUM instructions if BOOL is true |
0x27 | 3 | IFU | If condition BOOL is true, then executes instructions until DST, then jumps to DST+NUM; else, jumps to DST. |
0x28 | 2 | IF? | If condition (don't know how condition flags work yet) is true, then executes instructions until DST, then jumps to DST+NUM; else, jumps to DST |
0x29 | 3 | FORLOOP | Loops over the code between itself and DST. Increments lcnt after each loop. Stops looping once lcnt reaches the value contained by the integer uniform specified by INT. (i0-i7) |
0x2A | 0 (no param) | EMIT | (geometry shader only) Emits a vertex (and primitive if PRIMID is non-zero). SETEMIT must be called before this. |
0x2B | 4 | SETEMIT | (geometry shader only) Sets VTXID and PRIMID for the next EMIT instruction. VTXID is the ID of the vertex about to be emitted within the primitive, while PRIMID is zero if we are just emitting a single vertex and non-zero if are emitting a vertex and primitive simultaneously. Note that the output vertex buffer (which holds 4 vertices) is not cleared when the primitive is emitted, meaning that vertices from the previous primitive can be reused for the current one. (this is still a working hypothesis and unconfirmed) |
0x2C | 2 | JMPC? | Jumps to DST if condition (don't know how condition flags work yet) is true, else does nothing. |
0x2E | 1 | CMP1 | Presumably compares two vectors component by component and sets the appropriate flags. (unknown exactly how this works as of yet) |
0x2F | 1 | CMP2 | Presumably compares two vectors component by component and sets the appropriate flags. (unknown exactly how this works as of yet) |
0x38-0x3F | 5 | MAD | Multiplies two vectors and adds a third one component by component; DST[i] = SRC3[i] + SRC2[i].SRC1[i] for all i (modulo destination component masking) |
Operand descriptors
Sizes below are in bits, not bytes.
Offset | Size | Description |
---|---|---|
0x0 | 0x4 | Destination component mask. Bit 3 = x, 2 = y, 1 = z, 0 = w. |
0x4 | 0x1 | Source 1 negation bit |
0x5 | 0x8 | Source 1 component selector |
0xD | 0x1 | Source 2 negation bit |
0xE | 0x8 | Source 2 component selector |
0x16 | 0x1 | Source 3 negation bit |
0x17 | 0x8 | Source 3 component selector |
Component selector :
Offset | Size | Description |
---|---|---|
0x0 | 0x2 | Component 3 value |
0x2 | 0x2 | Component 2 value |
0x4 | 0x2 | Component 1 value |
0x6 | 0x2 | Component 0 value |
Value | Component |
---|---|
0x0 | x |
0x1 | y |
0x2 | z |
0x3 | w |
The component selector enables swizzling. For example, component selector 0x1B is equivalent to .xyzw, while 0x55 is equivalent to .yyyy.
Relative addressing
There are 3 global address registers : a0, a1 and a2 = lcnt (loop counter). For format 1 instructions, when IDX != 0, the value of the corresponding address register is added to SRC1's value.
For example, if IDX = 2, a1 = 3 and SRC1 = c8, then instead SRC1+a1 = c11 will be used for the instruction.
a0 and a1 can be set manually through the ARL instruction. lcnt is set automatically by the LOOP instruction. Note that lcnt is still accessible and valid after exiting a LOOP block.
Registers
Most registers (all the ones within the 0x00-0x7F range) are float[4] vectors. There are also boolean registers (b0-b7) and integer registers (i0-i7). How the latter ones are set is as of yet unknown.
Attribute (input, RO) registers are located within the 0x0-0xF range. What data they are fed is specified by the CPU.
Output (WO) registers are also located within the 0x0-0xF range. What type of data they are contain is specified by the CPU.
Temporary (RW) register are located within the 0x10-0x1F range. They can contain any type of data.
Uniform (RO) registers are located within the 0x20-0x7F range. Their content is set by the CPU.
SRC2 being only 5 bits long rather than 7 bits like its friend SRC1, it can only access v (input attribute) and r (temporary) registers.
Registers in the 0x88-0x97 range are uniform booleans.
It appears that writing twice to the same output register can cause problems, such as the GPU hanging.
DST mapping :
DST raw value | Register name | Description |
---|---|---|
0x0-0x7 | o0-o7 | Output registers. |
0x10-0x1F | r0-r15 | Temporary registers. |
SRC mapping :
SRC1 raw value | Register name | Description |
---|---|---|
0x0-0x7 | v0-v7 | Input attribute registers. |
0x10-0x1F | r0-r15 | Temporary registers. |
0x20-0x7F | c0-c95 | Vector uniform registers. |
Note that 5bit SRC registers (SRC2 in format 1 for example) can't access c0-c95 because they don't have enough bits.