构建通用计算环境

1. 硬件体系结构

图形处理器中主要的两种可编程部件是顶点处理器(Vertex Processor Units)和片元处理器(Fragment Processor Units)。顶点处理器又称为顶点渲染器(Vertex Shader),是运行顶点程序处理顶点数据的硬件单元。片元处理器又称作像素渲染器(Pixel Shader),它执行片元程序指令处理片元数据流。采用数据流并行处理概念,Nvidia的NV40图形渲染管线如图-3所示,有6个并行的顶点处理器和16个并行的像素处理器,其内部结构如图-4所示。顶点和片元处理器从的寄存器组中取用数据,没有大容量存储器的概念,因而不可以直接操作显存,但是它们都包含一个纹理取用部件,因此可以使用纹理缓存(Texture Buffer)作为大容量存储器。


图-3

图-4

处理器的寄存器组具有4元浮点向量数据结构,通过硬件的分量顺序调整(swizzle) 和输出掩模(mask)可实现从向量中操作每个标量元素。顶点处理器和片元处理器可同时取用并计算这种4分量向量数据,具有流处理中指令级并行的特点。所以4元向量加法乘法等算术运算和标量的加法乘法等运算同样高效。不仅如此,两种处理器还针对密集的算术运算提供一些原生指令(Native Instruction),如余弦、线性插值、向量点积等指令,可更快捷的执行复杂计算。然而,图形处理器目前对于分支循环语法支持还不够,如果程序中大量含有这两种语句,则会影响执行效率。以后还会提到,新一代的图形处理器已经没有顶点处理器和片元处理器之分,这两种计算在统一的硬件上实现称为统一执行单元(Unified Shader)。

在一遍绘制过程中,顶点和片元处理器都执行相同的顶点和片元程序拷贝,并行计算多个顶点和片元数据,提供数据级的并行即SIMD。数据流不断的从顶点和像素处理器流入流出,图形流水线提供任务级的并行即时间上并行。由于以上数据流并行处理的特点,图形处理器具有高性能并行计算的能力。


2. 软件编程接口

应用程序通过3D图形库的API和图形处理器交互,用渲染程序实现对顶点和片元处理器的编程。目前广泛使用的3D图形库是OpenGL和Direct3D。OpenGL和Direct3D各有自己的一套渲染程序语言。OpenGL标准定义了ARB_vertex_program 和ARB_fragment_program 两个扩展;Direct3D相应定义有vertex shader和fragment shader。两套渲染程序标准都采用汇编程序语言的形式,统称为低级渲染语言。

以OpenGL为例,其顶点和片元程序编程模型如图-5所示的虚拟机。在1.0版本的渲染语言中,除顶点程序中地址变量(Address Variables)外,渲染程序取用的参数都是4分量浮点数向量。属性寄存器传入上一级图形管线传来的顶点或片元属性的数据流,常用的顶点和片元的属性有位置坐标(vertex.position, fragment.position)、颜色(vertex.color, fragment.color)和纹理坐标(vertex.texcoord, fragment.texcoord)等,具体可查阅OPENGL标准的文献,输出寄存器保存计算后的输出数据流,比如顶点的新坐标(result.position)和点的大小(result.pointsize),片元的最终颜色(result.color)和深度(result.depth)。由于没有存储器的概念,中间变量保存于可读写的暂存寄存器中(Temporary Variables)。外部应用程序可通过OpenGL的API设置程序局部参数(Program Local Parameters)和程序环境参数(Program Environment Paramenters)。程序环境参数对于应用程序中所有的渲染程序都有效,而程序局部参数仅仅对当前要执行的渲染程序有效。实际应用中程序局部参数更实用些。渲染程序的属性寄存器是固定的常量,程序参数寄存器一旦被外部应用程序设定,在渲染程序中只能被读取。



图-5

OpenGL在其API中提供相应的函数来读取,绑定渲染程序。如果渲染程序状态有效,一旦有顶点数据流冲入图形管线,顶点和片元处理器便会执行相应的渲染程序来处理数据流。

低级渲染语言的编程模型接近硬件实际,有助于编程者理解程序执行过程,优化程序代码。但是汇编语言的可读性差,编程难度较大。高级渲染语言为编程者提供便利的编程方式。目前商用的高级渲染语言有Direct3D的HLSL(High Level shading Language),OpenGL的GLSL(GL Shading Language)和Nvidia开发的Cg(C for grahics)。Cg 是一个跨平台的高级着色语言,通过定义不同的编译描述(profile),Cg编译器可以把用Cg编写的程序编译成不同的低级着色语言,包括不同版本的OpenGL和Direct3D的低级着色语言。


3.   图形处理器上的通用计算

通过这些软件接口操作图形硬件,还原这些图形概念为抽象而通用的计算概念,我们就可以实现在图形处理器上的通用计算了。第二代图形处理器中,片元处理器比顶点处理器的数目更多、支持的原生算术指令更强、对纹理操作更方便,GPU通用计算因此使用片元处理器作为主要的数据处理器。把纹理作为输入数据,绑定的片元程序实现流处理中的计算核,帧缓存或纹理缓存保存输出数据。


图-6

      

 

图-7

GPU通用计算的操作过程:在屏幕上绘制一个带有多纹理绑定的矩形,把该矩形投影到全部视区上,每绘制一遍矩形相当于驱动一次图形管线,光栅化后的矩形具有视区设定大小的片元数。例程-1的代码实现片元和纹理单元在图-7表示的坐标系下纹理单元和像素片元一一对应。 

//视区投影设定

glMatrixMode(GL_MODELVIEW);

glLoadIdentity();

glMatrixMode(GL_PROJECTION);

glLoadIdentity();

gluOrtho2D(0, 1, 0, 1);

glViewport(0, 0, x, y);


    //绘制矩形

glBegin(GL_QUADS);

{    glMultiTexCoord3d(GL_TEXTURE0,  0,   0, z);

glVertex3i( 0, 0, z);      

glMultiTexCoord3d(GL_TEXTURE0,  x,  0, z);

glVertex3i( 1, 0, z);      

glMultiTexCoord3d(GL_TEXTURE0,  x,   y, z);

glVertex3i( 1, 1, z );

glMultiTexCoord3d(GL_TEXTURE0,  0,   y, z);

glVertex3i( 0, 1, z);}

glEnd();


例程- 1 OpenGL下片元和纹理单元一一对应的设置

 一般的图形应用对色彩精度要求不高,(r,g,b,a)4颜色分量,最高情况下每分量8比特位会得到32位色彩的屏幕显示设置。另外,OpenGL的色彩值被限定在[0,1]范围内,不符合通用计算对精度和数值范围的要求。现在的GPU已经提供32位浮点纹理和帧缓存支持,可以通过OpenGL的浮点纹理GL_ARB_texture_float和像素帧缓存WGL_ARB_pbuffer等扩展标准获得高精度的内部数据格式。图形应用中的纹理大小必须是2的幂,且纹理坐标也被限定在[0,1]范围内,OpenGL扩展中的ARB_texture_non_power_of_two也消除这些限制。

GPU通用计算以二维纹理作为数据源,按纹理坐标取用纹理单元值,常会遇到纹理坐标和片元坐标偏移的问题,即在SIMD并行程序中根据当前纹理或片元坐标位置计算其它欲取用纹理单元的坐标。坐标偏移类似与CPU串行程序中数组指针的偏移,不同的是GPU中的坐标偏移是浮点数,CPU中为整数。OpenGL中的纹理单元的坐标和片元的窗口坐标以该纹理和片元中心的坐标为准,如图-7元和non_power_of_two纹理左下角的第一个坐标为(0.5, 0.5),具体计算坐标偏移时需要注意减去该0.5的偏移量。


   图-8

 有时需要在显存(Video Memory)和计算机主内存(Host Memory)之间的交换数据,二维纹理和主内存一维数组有如图-8顺序填充关系,总体来说这种数据交换是种耗时的过程,虽然现在的PCI-E提高显存和主存的数据传输带宽,但具体的应用程序仍应当尽量减少这种数据交换。

编写GPGPU程序不仅需要相关应用领域背景还要有丰富的图形学的知识和图形编程技巧,不利于推广该计算技术。Standford 的Brook和 Waterloo的Sh对C语言语法进行扩展,使具有SIMD特点的代码编译运行与GPU上,它们屏蔽了复杂的图形概念和硬件信息使得对GPU编程更方便,但是由于目前图形处理器硬件结构和编程接口处于高速发展中,变化相当迅速,这两种语言现在还没有成熟到应用推广的阶段,或许将来会出现更友好易用的编程方式。

posted on 2007-03-26 21:02  Brainter  阅读(957)  评论(0编辑  收藏  举报