R700程序由控制流(CF)、ALU(译者注:算术逻辑单元)、取纹理和取顶点指令组成。ALU可以具有多达三个源操作数和一个目的操作数。指令对32位或64位IEEE浮点值以及带符号或无符号整型进行操作。某些指令的执行致使断言(predicate)位被写,从而影响后面的指令。图形程序一般使用取顶点和取纹理指令来加载数据;而通用计算应用一般使用取纹理指令来加载数据。
2.1 程序类型
以下程序类型一般运行在R700上(见图1.2):
1、顶点着色器(VS)——读顶点,处理它们。依赖于是否有一个几何着色器(GS)在活动,它要么对一个VS环缓存,要么对一个参数Cache和位置缓存输出结果。它不引入新的图元。一个顶点着色器可以调用一个取子例程(FS,Fetch Subroutine),它是一个特殊的全局程序用于取顶点数据,出于执行目的,作为顶点程序的一部分。FS提供了在被一个VS所要求的取数据的过程和VS其本身之间的驱动独立性。
2、几何着色器(GS)——从VS环缓存读取图元,并且为每个输入图元,写一个或多个图元作为输出到GS环缓存。这个程序类型是可选的;当活动的时候,它要求一个DMA拷贝(DC)程序处于活动。GS从由VS所创建的片外存储器缓存读多达6个顶点;它输出一变量个数的图元到一第二个存储器缓存。
3、DMA拷贝(DC)——从GS环缓存到参数Cache和位置缓存传输数据。它对于正在运行一个几何着色器的系统而言是所必要的。
4、像素着色器(PS)或片段着色器——这类程序:
——接收来自将被着色的光栅化器的像素数据
——处理四像素集(以一个2×2阵列所安排的四个像素元素),并且
——写输出到多达8个本地存储器缓存,称为多重渲染目标(MRT,Multiple Render Target),一个MRT可以包含一个或多个帧缓存。
5、计算着色器(CS)——一个通用程序,使用其线程ID作为一个索引来执行:
——收集对一组或多组输入数据的读
——算术计算,以及
——对一组或多组输出数据离散写到存储器
所有程序类型接受相同指令类型,并且所有程序类型可以运行在支持这些程序的任一可用的DPP阵列流水线;然而,每个内核类型有某些限制,这将会根据具体的类型进行描述。
2.1.1 数据流
主机可以初始化R700以一个或两个配置运行——带有或不带有一个几何着色器程序和一个DMA拷贝程序。见图1.2。每种数据流在以下部分描述。
2.1.2 不存在几何程序
这个处理配置由以下步骤组成:
1、VS程序发送一个指向本地存储器的一个缓存,包含多达64个顶点索引。
2、R700硬件将其输入缓存(远程存储器)中的这些顶点组织成向量。(译者注:可以想像glDrawArray这类OpenGL函数,硬件根据输入的绘制模式类型,比如GL_LINE、GL_TRIANGLE_STRIP等将顶点组织成向量)
3、当所有顶点准备好被处理时,R700分配GPR以及线程空间用于处理64个顶点的每一个,基于编译器提供的大小。
4、VS程序调用取子例程(FS)程序,将顶点数据取到GPR中,并将控制返回给VS程序。
5、变换、光照、以及VS程序的其它部分运行。
6、VS程序在位置缓存中分配空间,并输出顶点位置(XYZW)。
7、VS程序分配参数Cache和位置缓存空间,并输出每个顶点的位置和参数。
8、VS程序退出,R700释放其GPR空间。
9、当VS程序完成时,像素着色器(PS)程序开始。
10、R700硬件从位置缓存中的数据和顶点几何翻译器(VGT)中的数据汇编图元,执行扫描转换和最后的像素插值,并将这些值加载到GPR中。
11、PS程序然后为每个像素运行。
12、程序将数据输出到一个帧缓存,然后R700释放起GPR空间。
2.1.3 几何着色器存在
表2.2展示了当一个几何程序存在时,程序运行的次序。
这个处理配置由以下步骤组成。
1、R700硬件从顶点几何翻译器(VGT)将输入索引或图元以及顶点ID加载到GPR中。
2、VS程序取顶点(译者注:单数)或所需要的顶点(译者注:复数)。
3、变换、光照和VS程序的其它部分运行。
4、VS程序通过写顶点输出到VS环缓存结束。
5、GS程序从VS环缓存读多个顶点,执行其几何功能,并且针对每个输入顶点输出一个或多个顶点到GS环缓存。VS程序对每个输入只能写一单个顶点;GS程序对于每个输入可以写大量的顶点。每次一个GS程序输出一个顶点时,它对顶点VGT指示,一个新的顶点已经被输出(使用EMIT_*指令)。VGT计数由每个GS程序所创建的顶点个数的总和。GS程序通过发布CUT_VERTEX指令划分图元条带(primitive strip)。
6、当所有顶点已被输出时GS程序结束。没有位置和参数被输出。
7、DC程序从GS环缓存读取顶点数据,并使用一条MEM_*存储器输出指令将此数据传输到参数Cache和位置缓存。
8、DC程序退出,R700释放GPR空间。
9、PS程序运行。
10、R700从位置缓存、参数Cache和VGT中的数据汇编图元。
11、硬件执行扫描转换以及最后的像素插值,并且硬件将这些值加载到GPR中。
12、PS程序运行
13、当PS程序到达数据末尾时,它使用EXPORT指令将数据输出到一个帧缓存或其它渲染目标(多达8个)。
14、程序通过执行一条EXPORT_DONE指令退出,并且处理器释放GPR空间。
2.2 指令术语
表2.3概括了本文档所用到的某些指令相关的术语。指令其本身在剩下的章节中描述。每条指令的细节在第9章中给出。寄存器类型在“寄存器”中描述。
1、微代码格式——32位。对于所有指令的一种或若干中编码格式。它们在3.1、4.1、5.1、6.1小节以及第10章中描述。
2、指令——64或128位。两个到四个微代码格式,以指定:
(1)控制流(CF)指令(64位)。这些包括:通用控制流指令(诸如分支和循环),分配缓存空间和输出数据的指令,以及启动ALU、取纹理、或取顶点子句(Clause)的执行的指令。
(2)ALU指令(64位)
(3)取纹理指令(128位)
(4)取顶点指令(128位)
(5)数据共享指令(128位)
(6)存储器读指令(128位)
指令在微代码格式中通过它们的域名和助记符中的_INST_字符串进行标识。指令的功能在第9章中描述。
3、ALU指令组——64到448位。可变大小的指令和常量组,由以下构成:
(1)一到五个ALU指令
(2)零到两个64位字面常量
ALU指令组在4.3小节中描述
4、字面常量——64位。字面常量指定了两个32位值,它们可以表示与一个128位向量的两个元素相关联的值。这些常量可选地可以包含在ALU指令组中。
字面常量在4.3小节中描述。
5、槽(Slot)——64位。在一个ALU指令组内一个有序的位置。每个ALU指令组有1到7个槽,相应与ALU指令组中的ALU指令与字面常量的号码。
槽在4.3小节中描述
6、子句(Clause)——64到64x128位(64个128位字)。一组相同类型的指令。子句的类型有:
(1)ALU子句(包含ALU指令组)
(2)取纹理子句
(3)取顶点子句
子句由控制流(CF)指令启动,并在2.3小节中描述
7、输出(Export)——做下列任意一件事:
(1)从GPR写数据到输出缓存(一个“临时缓存(scratch buffer)”,“帧缓存”,“环缓存”,“流缓存”,或“缩减(reduction)缓存”)。
(2)将一个数据输入的地址写到存储器控制器。
(3)从一个输入缓存(“临时缓存”或“环缓存”)读取数据到GPR。
7、取(Fetch)——使用一个取顶点或取纹理指令子句加载数据。加载对于通用目的寄存器(GPR)不是必须的;加载的指定类型可能受限于存储目的的指定类型。
8、顶点——一组(x, y)2D坐标。
9、四元(Quad)——安排为一个2乘2阵列的四个(x, y)数据元素。
10、图元(Primitive)——一个光栅化之前的点、线段或多边形。它具有由几何坐标指定的顶点。通过跨图元的线性插值,顶点可以与额外的数据相关联。
11、片断(Fragment)——对于图形编程:
(1)光栅化一个图元的结果。一个片断没有顶点;取而代之的是它由(x, y)坐标表示。
对于通用目的编程:
(1)一组(x, y)数据元素
12、像素(Pixel)——对于图形编程:
(1)将一个片断放置在一个(x, y)帧缓存中的结果。
对于通用目的编程:
(1)一组(x, y)数据元素
2.3 控制流和子句
每个程序由两个部分组成:
1、控制流——控制流指令可以:
——启动ALU、取纹理、或取顶点指令的执行。
——输出数据到一个缓存。
——控制分支、循环和栈操作。
2、子句——一组同构指令;每个子句独立地,由ALU、取纹理、取顶点、本地数据共享、或存储器读指令组成。启动一个ALU、取纹理、或取顶点子句的一条控制流指令通过引用一个合适的子句执行。
表2.4提供了一个典型的程序流例子
功能 微代码格式
控制流(CF)代码 子句代码
启动循环 CF_DWORD[0, 1]
启动取纹理子句 CF_DWORD[0, 1]
取纹理或取顶点子句,以从存储器将数据加载到GPR TEX_DWORD[0, 1, 2]
启动ALU子句 CF_ALU_DWORD[0, 1]
ALU子句在已被加载的数据和字面常量上计算。 ALU_DWORD[0, 1]
这个例子展示了由一单个包含五条ALU指令 ALU_DWORD[0, 1]
(每条两个四字)以及两个四字的字面常量的 ALU_DWORD[0, 1]
ALU指令组所构成的一单个子句(译者注: ALU_DWORD[0, 1]
这里,一个字为16位) ALU_DWORD[0, 1] 最后一位,置1
Literal[X, Y]
Literal[Z, W]
结束循环 CF_DWORD[0, 1]
在一个输出缓存中分配空间 CF_ALLOC_EXPORT_DWORD0
CF_ALLOC_EXPORT_DWORD1_BUFFER
将结果从GPR输出(写)到输出缓存 CF_ALLOC_EXPORT_DWORD0
CF_ALLOC_EXPORT_DWORD1_BUFFER
控制流指令(译者注:复数):
1、构成主程序。分支语句、循环以及子例程调用在程序的控制流部分中被直接表达。
2、包含同步操作的机制。
3、指示一个子句什么时候完成时。
4、对于缓存而言是所要求的,在程序块的输出缓存中分配缓存,以及写到一个程序块的输出缓存。
一些程序类型(VS、GS、DC、PS)具有与其它块同步的控制流指令。
每个子句被一条控制流指令调用,是一有限长度指令的顺序列表。子句不包含控制流语句,但ALU子句指令可以基于每条指令应用一个断言。在一单个子句内的指令串行执行。一个程序的多个子句可以并行执行,如果它们包含不同类型的指令,并且子句相互独立(这种并行执行对程序员是不可见的,除了性能提升)。
ALU子句包含以每5个ALU(ALU.[X, Y, Z, W]和ALU.Trans)执行操作的指令,包括设置和使用断言,以及像素扼杀(pixel kill)操作(见4.8.1小节)。取纹理子句包含执行纹理以及从存储器读取常量的指令。取顶点子句专用于从存储器获取顶点数据。缺乏一个顶点Cache的系统可以在一个纹理子句中执行取顶点操作。
一个断言(predicate)是一个比特,它可以被置1或清0,作为计算某些条件的结果;从而,它被用来屏蔽写一个ALU的结果或本身作为一个条件。有两种断言,每种都在一个ALU子句中被置1:
1、第一种对于ALU子句本身而言,是一单个本地断言。一旦被计算,断言可以在后面的一条指令中被引用,以带条件地将一个ALU计算结果写到所指示的通用目的寄存器。
2、第二种类型是一个断言栈中的一个比特。一个ALU子句在栈中计算断言位,并对栈进行操作。在栈中的一个断言位可以在一条控制流指令中被引用,以引起带条件的分支。
2.4 指令类型和分组
R700家族设备识别以下指令类型:
1、控制流指令
2、子句类型:ALU、取纹理、取顶点、本地数据共享子句,以及存储器读子句
在处理器中对于每种指令类型有独立的指令Cache。
一个CF程序没有最大大小;然而,每个子句有一个最大大小。当一个程序在存储器中被组织时,指令必须如下安排:
1、所有CF指令
2、所有ALU子句
3、所有取纹理和取顶点子句
4、所有本地数据共享子句
5、所有读存储器子句
CPU主机在执行一个程序前,配置每个程序类型的基地址。