|
3.1 数据并行架构
不同处理器框架采用不同策略来避免卡顿。CPU通过优化来处理各个不同的数据结构的执行大型代码库。CPU可能有多个核心,但每个核心都是以串行方式运行,有了减小延迟,CPU通过配置高速本地缓存,CPU其它优化技术有:分支 预测,指令重排序,寄存器重命名和预取缓存等。
GPU采用了不同的方法,GPU芯片的大部分区域用一个专用处理器-Shader核心,通常有几千个。GPU对于相似的数据用相同的处理单元进行并行处理,各种处理器之前以流式排列,因此当一个处理器在等待另一处理器时可能会有延迟。
GPU中数据处理的最大速率称为吞吐量。
单指令多数据(SIMD)架构:
每个片元分配给一个线程,此处的线程和CPU中的线程不一样,它只是包含shader输入值的内存空间和执行shader所需的寄存器空间。处理相同shader片段的线程被打包成一个组,在NVIDIA中称为warps, AMD中称为wavefronts。假如我们有2000个线程,在NVIDIA中有一个warps有32个线程,则最后将分配2000/32=62.5,即将分配63个warp。
上图中当一个warp开始执行时,他将同时执行warps中的4个线程(实际有32个,没有画出),当执行到指令txt时,将挂起线程,等待从内存中获取纹理数据,此时只需要一次获取获取纹理指令即可满足所有线程的需要,因为所有线程执行的相同指令并同时执行到相同位置。在等待的同时,别一组warps将开始运行,直接遇到访问内存指令......
3.2 GPU管线概述
绿色表示完全可编程,虚线框表示可选阶段,黄色表示可配置但不可编程,比如在Merger阶段可配置各种混合模式,蓝色表示功能完全固定。
3.3 可编程着色阶段
在DirectX中,Shader语言为High-Level Shading Language(HLSL), OpenGL中为OpenGL Shading Language(GLSL)。HLSL可以被编译为虚拟机字节码 intermediate language(IL或DXIL)。通过不同的GPU驱动,IL将被转换成 instruction set archiecture(ISA)。
draw call调用图形API绘制几何图元,并驱动图形管线执行着色。着色(Shader)中包含两种类型输入:uniform输入和varying输入。uniform输入用于传入每个draw call中的常量,在不同的draw call间可以改变。Varying输入的数据来源于三角形顶点或光栅化阶段。
Shader虚拟器架构,图中输入寄存器中16/16/32分别表示顶点着色器,几何着色器和像素着色器中的寄存器数量,对于输出寄存器也是类似。
3.4 可编程Shader及API的演化
shader编程最早可追溯到1984年shade树:
图形硬件API发展时间线:
3.5 顶点着色器(Vertex Shader)
顶点着色器是可编程控制的第一阶段。DirectX中称为input assembler, 将各种数据汇集到一起,形成一组顶点和片元发送到图形管线。
三角形网格由顶点组成,顶点和模型表面位置相对应。除了位置,和顶点相关的属性还有颜色(color)或纹理坐标。表面法线也可以储存在顶点中。在数学上,每个三角形都有一个表面法线,但是在渲染中,三角形通常用来表示一些曲面,顶点法线用来表示曲面的朝向。
左边顶点法线表示曲面的法线,因此产生光滑曲面。右边表示中间顶点表示2个顶点并有2个方向法线,产生了曲面褶皱。
顶点着色器提供了一种修改创建或忽略三角形顶点中的颜色(color),法线(normal),纹理坐标和位置(position)等属性的方式。 顶点着色器将顶点从模型空间转到齐次裁切空间(homogeneous clp space)。即使最简单的shader也必须实现此转换。
顶点着色器不能创建或删除顶点,一个顶点中生成的结果也不能传给另一个顶点。因为所有顶点都是互相独立处理的。顶点流由GPU中的着色(Shader)处理单元并行处理。
顶点着色器可以做的效果:
生成一个网格对象并对其进行变形。对角色的身体及脸部使用蒙上或变形技术。程序化动画,比如旗帜,布,或水的飘动。创建粒子。通过屏幕后处理产生失真,水波,扭曲等镜头效果。使用顶点纹理获取地形高度场。
左边是一个普通的茶壶,中间是在顶点中通过切变(shear)操作后的效果。右边是通过噪声(noise)函数操作后的结果。
3.6 细分(Tesselation) 阶段
通常细分由三部分组成,在DirectX中它们是hull shader, tessellator和domain shader。在OpenGL中hull shader称为tessellation control shader, domain shader称为tessellation evaluation shader,固定功能函数tessellator称为primitive generator。
hull shader的输入是特殊的patch片元,它是由几个控制点定义的细分曲面,贝塞尔片元或其它曲线元素。hull shader有两个功能,首先配置tessellator及告诉tessellator生成多少个三角形。然后,它在控制点上执行处理(transform等)。hull shader能够修改输入的patch, 根据需要添加或删除控制点。最后,hull shader输出控制点集及tessellator数据到domain shader.
tessellator在管线中是固定功能管线,它的功能是添加新顶点并传送到domain shader。hull shader 发送细分表面类型(如三角形,四边形或等值线)到tessellator。等值线用于头发渲染。hull shader发送的其它重要的数据是细分因子(OpenGL中为细分 levels),有两种类型:内(inner )和外边(outer edge), 内因子决定三角形或四边形内部进行多少次细分,外边因子决定外边切分为多少次。
茶壶用32个patches组成,内和外边因子从左到右分别为1,2,4,8。
hull shader也可以用patch的预估距离或屏幕大小动态计算细分因子,如地形渲染。或者hull shader也可以简单给所有patch 传递应用中计算或提供的固定数值。tessellator能够执行复杂但是固定的函数来生成三角形,并计算出它的位置,及计算它构成三角形或线的形式。这个数据细分的步骤放在着色器外执行能更大地提高效率。
domain以重心坐标为中心为每个点生成坐标,并在patch的评估方程中使用这些坐标来生成位置(position),法线(normal),纹理坐标(texture coordinates)和其他所需的顶点信息。
左边模型有6000个三角形,右边为每个三角形使用PN三角细分方法替换后的效果
3.7 几何着色器(Geometry Shader)
几何着色器可以将一个几何体转换成另一个几何体,这是细分阶段不能做到的。例如一个三角形网格可以通过将每个边创建一个线形边来转换成线框显示。或者将线转换成面向摄像机的四边面来渲染出边缘加粗的线框。随着2016年末DirectX 10的发布,几何着色器器被添加到硬件管线中,但它是可选的。虽然它是Shader Model 4.0中的一部分,但是早期的Shader Model中并没有用到。OpenGL 3.2和OpenGL 3.2也支持这种着色器。
几何着色器的输入是一个对象及其相关的顶点。对像通常由条形,线段或简单的点组成的三角形。
流式输出(Strem Output):
标准的GPU管线中,通常是使用顶点着色器发送数据,然后将三角形进行栅格化处理,并在像素着色器中处理这些数据。数据总是在管线中传递,无法对中间结果进行访问。流式输出是在Shader Model 4.0中引入的。当顶点被顶点着色器(或细分着色器或几何着色器)处理时,在发送数据到栅格化阶段外,还可以在流中输出,例如一个有序数组输出 。
事实上,栅格化阶段可以被完全关闭,然后管线纯粹用于非图形流处理。以这种方式处理的数据可以通过管道发送回来,从而允许迭代处理。这种类型的操作可以用于模拟流水或粒子效果,它还可以用于对模型进行蒙皮并重用模型的顶点。
流式输出仅以浮点数形式返回数据,因此可能造成显示的内存开销。流输出作用于几何体而不直接作用于顶点。网格在管线传递过程中,每个三角形生成自己的三个输出顶点。网格中共享的顶点将会丢失。因此更典型的用法是将顶点作为几何体点集在管线中传递。在OpenGL中,流式输出阶段称为转换反馈(transform feedback),因为它的主要作用是转换顶点并返回进行进一步处理。几何体能保证按照输入的顺序发送到流式输出目标中,因此顶点的顺序将会被保持不变。
左边,metaball的等值面细分是由几何着色器(GS)动态生在;
中间,通过线段在几何着色器中进行分形细分,显示出广告牌的闪电效果;
右边,通过对顶点和流式几何着色器输出进行布料模拟。
3.8 像素着色器(Pixel Shader)
在顶点着色器,细分和几何着色器操作后,几何体被裁剪并设置为栅格化。然后进入像素着色器阶段。管线的这步分处理是相对固定的,不可编程,但在某种程度上可配置。每个三角形被遍历以确定它覆盖了哪些像素。光栅化器也可以粗略计算三角形覆盖每个像素的单元的面积,三角形中部分或完全覆盖的像素部分称为片段(fragment)。
三角形顶点处的值,包括z-buffer中的z值,被插值到三角形表面的每个像素中。这些值被传递到像素着色器并处理成片段。在OpenGL中,像素着色器称为片段着色器。管线中的点和线也为所覆盖的像素创建了片段。
三角形的插件类型由像素着色器指定,通常使用透视校正插件,从而使像素表面位置之间的距离随物体距离的减小而增加。例如渲染延伸到地平线的铁轨,在轨道较远的地方,铁路枕木的间距更近。其他插件屏幕空间插值(不考虑透视投影)也是可选的。
最初,像素着色器只能输出到合并(Merging)阶段,以便最终显示,随着时间的推移,一个像素着色器可以执行的指令大大增加。因此产生了多渲染目标(MRT)的想法。和只将像素着色器的结果发送到颜色缓冲(buffer)和z缓冲(buffer)不同,还可以为每个片段生成多个值并保存到不同的被称为渲染目标的缓冲区中,渲染目标通常具有相同的x,y维度。虽然一些api允许不同缓冲区的大小,但渲染区域将是所有缓冲区中最小的区域。一些架构要求渲染目标要有相同的位深度甚至要有相同的数据格式。根据GPU不同,可用的渲染目标数是4个或8个。
即使有这些限制,MRT功能仍然对高效执行渲染算法有帮助,在一个渲染路径中,可以在一个渲染目标中生成彩色图像,在另一个渲染目标中生成物体标识,在第三个渲染目标中生成世界空间距离。这种能力也产生了一种不同类型的渲染管线,你为延迟渲染管线,其中着色和物体可见性在不同的通道中完成。
像素着色器提供了沿屏幕坐标的x,y的变化量。所有现代GPU都通过处理你为块的2X2片段来实现这一功能。
左边,三角形被栅格化成四边形,一组2X2像素。右边标记出了黑点处像素的剃度计算。v 为四边形的像素值。
3.9 合并(Merging)阶段
合并阶段是将在像素着色器中生成的单个片段的深度和着色与framebuffer结合在一起。DirectX将这一阶段称为输出合并,OpenGL中称为per-sample操作。在大多数渲染管线中,这个阶段是模板缓冲(stencil-buffer)和z缓冲(z-buffer)进行操作的地方。如果片段可见,此阶段的另一个操作是颜色混合。对于不透明的表面,不会有真正的混合 ,因为片段的颜色只是取代之前储存的颜色。
为了避免像素着色器进行不必要的处理,放多GPU在像素着色器执行之前先要进行一些合并测试,通常使用片段的z-depth或模板缓冲或裁剪来测试片段的可见性,如果不可见,片段将被剔除,这个功能称为earyly-z。像素着色器有能力改变片段的z-depth或完全丢弃片段,一旦像素着色器发生上述中的任何一种操作,early-z将会被关闭,通常会使渲染效率降低。DirectX 11和OpenGL 4.2 允许像素着色器强制进行early-z测试,尽管有一些限制。有效利用early-z将对性能产生很大的影响。
3.10 计算着色器(Compute Shader)
GPU不仅可用于传统图形渲染管线,在计算股票期权估值和训练神经网络进行深度学习等领域也有许多非图形化的应用,以这种方式使用硬件被称为GPU计算。
在DirectX11中引入的计算着色器(Compute Shader)是GPU计算的一种形式,因为他是一个着色器,所以并没有固定在图形管线中的某个位置中。它与渲染过程密切相关,因为它是由图形API调用的。它与顶点,像素和其他着色器一起使用。它和其他着色器一样,有一些输入数据,并可以访问输入和输出缓冲(例如纹理)。在计算着色器中线程和Warps更常用 。例如,每次调用 都获得一个它可以访问的线程索引。还有线程组的概念,在DirectX 11中由1到1024个线程组成 ,这些线程由x-,y-,z-坐标指定 ,主要是为了在着色器代码中简单使用。每个线程组都有一小块在线程之间共享的内存,在DirectX11中是32kb。计算着色器由线程组执行,所以组中的所有线程都保证并发运行。
计算着色器的一个重要优势是它们可以访问在GPU上生成的数据。从GPU向CPU发送数据会有延迟,因此如果处理和结果能够驻留在GPU上,性能可以大大提升。后处理,即以某种方式 修改渲染图像是计算着色器的常用用法。共享内存意味着采样图像像素的中间结果可以与相邻的线程共享。例如使用计算着色器来确定图像的分享或平均亮度,运行速度通常是像素着色器的2倍。
计算着色器在粒子系统,网格处理(如面部动画),剔除,图像滤波,提高深度精度,阴影,景深以及任何其他GPU处理器可以胜任的任务中也很有用。
左边,计算着色器用来模拟头发受风吹的效果,头发本身使用细分阶段渲染。
中间,计算着色器执行快速模糊操作。
右边,是海浪模拟。 |
本帖子中包含更多资源
您需要 登录 才可以下载或查看,没有账号?立即注册
×
|